Ubuntu 开机自启动 .py / .sh 脚本,可通过脚本启动 roslaunch/roscore等

前言

项目中要求上电自启动定位程序,所以摸索了一种 Ubuntu 系统下开机自启动的方法,开机自启动 .sh 脚本,加载 ROS 环境的同时启动 .py 脚本。在 . py 脚本中启动一系列 ROS 节点。

一、 .sh 脚本的编写

python 复制代码
#!/bin/bash
# gnome-terminal -- bash -c "/home/wu/startup.sh; exec bash"
sleep 2

echo "----------加载 ROS 环境----------"
source /opt/ros/noetic/setup.bash 
sleep 2
echo $ROS_PACKAGE_PATH

echo "----------启动 python 脚本----------"
/usr/bin/python3 /home/wu/test.py

# sh脚本等待所有后台进程完成后结束
wait
exit 0

启动 .sh 脚本时,首先沉睡 2 秒,等待其它部分启动完成后加载 ROS 环境,之后输出 $ROS_PACKAGE_PATH 确定 ROS 环境是否加载好,最后启动 .py 脚本。

二、 .py 脚本的编写

首先安装依赖 wmctrl。wmctrl 是一个用于控制 X Window 管理器的命令行工具,通常用于 Linux 系统,特别是对窗口进行操作和管理。

python 复制代码
sudo apt-get update
sudo apt-get install wmctrl
python 复制代码
#!/usr/bin/python3.8
# coding=utf8
import subprocess
import rospy

i = 101

def close_terminal_by_name(terminal_name):
    # 使用 wmctrl -l 来列出所有窗口和其标题
    wmctrl_process = subprocess.Popen(["wmctrl", "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    stdout, _ = wmctrl_process.communicate()

    # 在输出中查找指定名称的终端
    for line in stdout.splitlines():
        if terminal_name in line:
            # 获取终端的窗口 ID,并使用 wmctrl -i -c 命令关闭该终端
            window_id = line.split()[0]
            subprocess.run(["wmctrl", "-i", "-c", window_id])
            print("已关闭终端:", terminal_name)
            return

    # 如果未找到指定名称的终端
    print("未找到名称为", terminal_name, "的终端")

def step(commands):
    command_str = " && ".join(commands)  # 将命令列表连接为一个字符串,使用 && 分隔
    # print(command_str)
    # 使用 exec bash 保持终端窗口打开
    command_str = command_str + "; exec bash"

    # subprocess.run() 函数用于执行外部命令。它会创建一个新的子进程,并等待子进程执行完成后返回。
    # gnome-terminal" 是要执行的命令,即打开一个新的终端
    # "--" 表示后面的参数将被传递给 gnome-terminal 命令
    # 使用 bash -c 执行命令
    rospy.sleep(1)
    subprocess.run(["gnome-terminal", "--", "bash", "-c", command_str]) # title 无效???
    # 添加标题:"--title", str(i),

def step_name(commands):
    global i
    command_str = " && ".join(commands)  # 将命令列表连接为一个字符串,使用 && 分隔
    # print(command_str)
    # 使用 exec bash 保持终端窗口打开
    command_str = command_str + "; exec bash"

    # subprocess.run() 函数用于执行外部命令。它会创建一个新的子进程,并等待子进程执行完成后返回。
    # gnome-terminal" 是要执行的命令,即打开一个新的终端
    # "--" 表示后面的参数将被传递给 gnome-terminal 命令
    # 使用 bash -c 执行命令
    rospy.sleep(1)
    subprocess.run(["gnome-terminal", "--title", str(i), "--", "bash", "-c", command_str]) # title 无效???
    # 添加标题:"--title", str(i),
    i = i + 1

def start_roscore():
    command = "roscore"
    print("---启动{}".format(command))
    subprocess.Popen(["gnome-terminal", "--", "bash", "-c", command])

def kill_roscore():
    close_terminal_by_name("roscore http://WP:11311/")



if __name__ == '__main__':
    start_roscore() # 第一时间启动roscore
    rospy.sleep(2)
    
    rospy.init_node('test')
    rospy.loginfo('*****************************')

    command_str = [
        "cd /",
        "sudo apt-get update"
    ]

    rospy.sleep(1)
    step(command_str)
    rospy.sleep(1)

.py 脚本主要包括 3 个函数, step(commands), step_name(commands) 和 close_terminal_by_name(terminal_name)

  • step(commands):将要输入到终端的命令以如下方式定义
python 复制代码
    # 雷达驱动
    command_drive = [
        "cd /home/wu/3rdparty/driver/ws_livox",
        "source ./devel/setup.bash",
        "roslaunch livox_ros_driver2 msg_MID360.launch"
    ]

然后将 command_drive 以参数形式传入 step(commands) 函数中,启动 rosrun 节点。

当以这种方式启动 rosrun 节点时,wmctrl 中没有相应标题,无法通过 title 关闭该节点。

当以这种方式启动 roslaunch 节点时,wmctrl 中有相应标题,可通过 title 关闭该节点,默认 title 如下:

python 复制代码
/home/wu/3rdparty/driver/ws_livox/src/livox_ros_driver2/launch_ROS1/msg_MID360.launch http://localhost:11311

前半部分为 .roslaunch 文件路径,后半部分 http://localhost:11311 是 ROS 中的 ROS Master运行的地址。

  • step_name(commands)

通过 step_name(commands) 函数启动 rosrun 节点时,可以为其定义 title,定义的 title 为字符串 101。当启动多个step_name(commands) 函数时,title 以 101 为起点递增。此时,可将 101 作为参数传入 close_terminal_by_name(terminal_name) 函数中关闭 ros 节点。

  • close_terminal_by_name(terminal_name)

将 wmctrl 的 title 作为参数传入该函数,关闭相应的 ros 节点。

三、sudo 无需输入密码

当 command 中包含 sudo 命令时,需要手动输入密码,会影响脚本的自动化,故通过修改 ubuntu 设置 sudo 时无需输入密码。

使用 visudo 命令编辑 sudoers 文件:

python 复制代码
sudo visudo

在最后一行添加如下命令,然后 ctrl + X 离开。

python 复制代码
UserName ALL=(ALL) NOPASSWD: ALL

设置成功后输入 sudo 命令将不会要求输入密码。

四、Ubuntu 启动应用程序 设置开机自启动 .sh 脚本

完成上述设置后,按照如下配置

输入以下命令开机自启动 .sh 脚本

gnome-terminal -- bash -c "/home/wu/startup.sh; exec bash"

参考

gnome-terminal - A terminal emulator for GNOME

Ubuntu 开机自启动python程序或roslaunch的方法

ubuntu中的sudoers文件损坏,修复办法(附图、详细)

普通用户执行 sudo 命令而不用输入密码

相关推荐
Karoku06627 分钟前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
为什么这亚子30 分钟前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
布值倒区什么name1 小时前
bug日常记录responded with a status of 413 (Request Entity Too Large)
运维·服务器·bug
孤客网络科技工作室1 小时前
VMware 虚拟机使用教程及 Kali Linux 安装指南
linux·虚拟机·kali linux
。puppy2 小时前
HCIP--3实验- 链路聚合,VLAN间通讯,Super VLAN,MSTP,VRRPip配置,OSPF(静态路由,环回,缺省,空接口),NAT
运维·服务器
颇有几分姿色2 小时前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
光芒再现dev2 小时前
已解决,部署GPTSoVITS报错‘AsyncRequest‘ object has no attribute ‘_json_response_data‘
运维·python·gpt·语言模型·自然语言处理
AndyFrank2 小时前
mac crontab 不能使用问题简记
linux·运维·macos
筱源源3 小时前
Kafka-linux环境部署
linux·kafka
成都古河云3 小时前
智慧场馆:安全、节能与智能化管理的未来
大数据·运维·人工智能·安全·智慧城市