使launch文件开机自启动

robot_upstart

机器人开发中,一般都要求工控机开机后要启动某些节点或launch,ROS已经考虑到了这个需求。专门开发了一个package叫做robot_upstart,它提供脚本来安装和卸载随机启动的节点,但是经过很长时间的研究,发现这个包并不好用,需要额外很多设置。

安装: sudo apt-get install ros-kinetic-robot-upstart,现在有一个package,名称是nav,在它的launch文件夹里有个launch文件叫做all.launch,下面是第一个命令:

1
rosrun robot_upstart install nav/launch/all.launch

这实际是把nav做成了一个service,就跟MySQL服务器一样,然后按照系统自动提示执行:

1
sudo systemctl daemon-reload && sudo systemctl start foo

如果是普通节点,重新开机就能启动all.launch所有节点了,但是我的程序用到了很多自定义的环境变量,结果自启动总是失败。

生成的文件

按上面官方流程执行产生了几个文件,到/etc/ros/kinetic查看是否自启动的包有对应文件夹,里面是设定的launch文件,文件名就是启动名称

跟自启动服务相关的脚本是/usr/sbin/nav-start,停止脚本是同目录的nav-stop,内容比较复杂,也没有分析的必要了。

官方说自启动的日志文件在/var/log/upstart,但是我试了试发现从来都没有,后来看了看nav-start脚本,虽然没有完全看懂,但是在一定条件下会在/tmp下创建日志.不过我看了看日志,在自启动节点失败时,也没有什么有用的信息.

两个service文件其实完全一样,而且只修改一个就可以,/lib/systemd/system/nav.service的内容:

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description="bringup nav"
After=network.target

[Service]
Type=simple
EnvironmentFile=/etc/robotenv
ExecStart=/usr/sbin/nav-start

[Install]
WantedBy=multi-user.target

这里就是复杂的地方了,如果有自定义的环境变量,需要在执行rosrun robot_upstart install后加到这里,再执行后面的启动服务命令。

我新建了一个文件/etc/robotenv,里面定义环境变量:

1
2
MAP_FILE=/home/user/maps
BAG_FILE=/home/user/bags

不要加export,否则无法生效。然后用EnvironmentFile=/etc/robotenv调用,而且这句应当在ExecStart之前

但是在启动跟串口有关的节点时发现失败了,虽然我之前已经对串口进行了永久使能,但在开机过程中可能需要重新使能,问题在于串口使能前先启动了节点,结果失败。所以在rc.local中添加:

1
2
chmod 777 /dev/ttyS0
chmod 777 /dev/ttyS1

停止开机启动:rosrun robot_upstart uninstall nav,会删除除了/tmp里几个文件的其他所有文件
robot_upstart uninstall的注意使用包名

如果之前设置开机启动失败了,必须先用 rosrun robot_upstart uninstall 解除之前的设置,然后重新设置 ,如果是别人设置的开机启动,这个名称nav可以去到`/etc/ros/kinetic`里寻找 设置成功后,修改launch文件或更新节点文件不影响开机启动的设置

问题

在小强机器人上对startup.launch开机启动,在另一台电脑上执行rosnode listrostopic list都是正常的,但rostopic echo却没有结果,rosnode ping也不行。把所有ROS关闭,重启startup.launch,又正常了。

rc.local,只用于无界面节点

如果启动的节点比较简单,可以用rc.local的方式,在文件里添加roslaunch命令和环境变量即可,需要注意的是下面的失败的情况

环境变量

第一次开机启动后,节点都启动了,但发现一些节点不能正常运行,后来发现是一些节点中还使用了自定义的环境变量,比如某些目录的地址,所以把这些也要加上:

1
2
3
export CONFIG_PATH=/home/`hostname`/workspace/config
export MAP_FILES_PATH=/home/`hostname`/data
export LAUNCH_PATH=/home/`hostname`/workspace/src

mini-httpd用到的所有环境变量也要加入到rc.local当中,因为开机时加载的环境变量不是bashrc的,而是rc.local

脚本出错

编辑/etc/rc.local进行程序自启动时没有生效。可能是脚本出错,脚本第一行为#!/bin/sh -e,-e标志脚本如果有错误,就不再向下执行。加入下面内容,可以检查错误的原因:

1
2
3
exec 2> /tmp/rc.local.log    # send stderr from rc.local to a log file
exec 1>&2 # send stdout to the same log file
set -x # tell sh to display commands before execution

错误日志都在log文件里,第二行是把一般输出也放了进去,有时会很多,这行可以不加。
打开错误日志发现source not found,不识别source,这是shell的原因。把第一行改为#!/bin/bash,也就是换成bash,最终生效了。

权限和服务

有时按上面步骤还是失败,那么进行下面操作:

1
2
3
4
sudo chmod +x /etc/rc.local
# rc.local被认为是服务,默认是关闭的,需要打开
sudo systemctl enable rc-local.service
重启

完整脚本如下:

1
2
3
4
5
6
7
8
9
10
11
exec 2> /tmp/rc.local.log  # send stderr from rc.local to a log file
#exec 1>&2 # send stdout to the same log file
set -x

source /opt/ros/kinetic/setup.bash
source /home/user/name/workspace/devel/setup.bash
export ROS_IP=`hostname -I`
export ROS_HOSTNAME=`hostname -I`
export ROS_MASTER_URI=http://192.168.73.14:11311
roslaunch /home/user/name/workspace/src/package/launch/start.launch & mini_httpd -C /etc/mini_httpd.conf # 同一行
exit 0

只启动了一个launch文件

rc.local设置开机启动,最好只有一个launch,否则常常只启动一个,我们可以把所有launch放到一个里面.

启动roslaunch失败

rc.local中启动launch文件用的是roslaunch ~/Robot/workspace/package/bringup.launch,结果失败,查看日志发现实际路径是/root/Robot/workspace/package/bring.launch,可见开机时Linux不能把~识别为/home/user

gnome-session-properties,只用于有界面(带显示器)节点

以上方法用于普通ROS程序没问题,但带界面的程序就失灵了。这种情况下先新建一个脚本,比如test.sh:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
echo -----------准备开机自启动程序 robot-----------
sleep 10
source /opt/ros/kinetic/setup.sh
source /home/user/work/workspace/devel/setup.bash
export ROS_IP=`hostname -I`
export ROS_HOSTNAME=`hostname -I`
export ROS_MASTER_URI=http://192.168.31.14:11311

rosrun robot robot

注意文件要给权限 sleep 10是让脚本阻塞10秒钟,因为程序需要等待与roscore的通信,如果不阻塞,可能会先于roscore启动节点,导致失败而退出,其他就是加载需要的环境和执行程序。

然后在终端输入gnome-session-properties,打开应用程序首选项设置开机启动程序。

命令中填写gnome-terminal -x /path/test.sh。保存后重启,会出现一个终端执行脚本中的命令

这种方法只适用于有界面的情况,假如机器不带显示器,就无法启动成功。 最好每个节点就要对应一个sh文件,逐个添加。因为都放在一起会阻塞在第一个节点,退出以后会接着执行下一个,不能同时执行

参考:
ROS程序开机自启动的四种方法