0x00 为何需要开机自启动launch文件
在ROS开发后期阶段由于功能已经趋于稳定,因此就需要系统在一上电启动后就自动把ROS下的各节点程序加载运行,这样就省去了我们还得手动输入roslaunch命令来加载bringup的launch文件的操作。经过我的实际测试目前有两种方式可以实现开机自启动,一种就是使用系统自带的Startup Applications,另外一种就是编写一个service,开机后自动启动,在这里我们以树梅派下ubuntuMate安装ros kinetic系统作为示例来分别演示这两种方式各如何操作。
0x01 下载雷达源码并编译运行
在这里我以在树梅派下开机后自启动ydlidar-x4作为演示,首先就需要在树梅派中下载该雷达源码,在ROS工作空间的src目录下使用如下命令下载源码:
下载源码后就可以先来配置雷达的设备挂载点,使其挂载点从/dev/ttyUSBx映射到/dev/ydlidar,这样方便我们获取雷达的设备挂载点。
当编译完成后就可以来测试雷达是否可以正常启动了,需要首先source devel/setup.bash配置当前工作空间的环境变量,这样我们才能使用roslauch来启动ydlidar软件包下的lidar.launch,在这里需要注意的是由于雷达的USB口供电能力较弱,因此我们除了需要接上雷达的数据接口后还得接上供电接口,这样ydlidar-x4才能正常工作。
当编译完成后,我们可以来使用roslaunch启动雷达看看是否能正常工作,可以先source devel/setup.bash配置好环境变量后,使用如下命令来启动雷达roslaunch ydlidar lidar.launch,接下来就会发现雷达开始转动起来。
0x02 使用StartupApplication方式来自启动
这种方式的主要思路就是通过编写一个bash脚本,在脚本中来执行roslaunch命令启动相应的ros节点,然后我们在系统提供的startupApplication中添加我们要执行的脚本即可,这样就可以在每次开机的时候执行我们自定义的脚本了。在这里我们在当前ros工作空间源码目录创建一个软件包用于存放我们的脚本,具体操作如下所示:
在startup.sh脚本中添加如下代码:
#!/bin/bash
# Copyright: 2016-2018 www.corvin.cn
# Author: corvin
# Description: In raspeberry ubuntu Mate16.04 poweron
# startup ros's launch file.
# History:
# 20180530: initial this file.
source /opt/ros/kinetic/setup.bash
source ~/catkin_ws/devel/setup.bash
roslaunch ydlidar lidar.launch
exit 0
接下来就可以在ubuntuMate的界面上来配置添加启动程序了,如下图所示完整操作:
注意Command的添加,完整的命令如下:
terminator -x /home/corvin/catkin_ws/src/auto_startup/scripts/startup.sh
大家只需要修改terminator为mate_terminal,如果在x64的ubuntu的系统上,默认安装的终端是gnome-terminal,另外就是需要修改后面的路径为自己脚本的路径就可以了。当点击Add后,接下来就可以来重启树梅派来进行测试了,看看重启后是否开机时能将雷达自动启动,下图是树梅派刚启动时自动打开terminator终端,然后自动来执行脚本中的roslaunch命令将雷达启动:
0x03 编写service启动相关代码
我们仍然在创建的auto_startup软件包的scripts目录下创建需要的文件,使用如下命令来操作:
在ros_startup.service中添加的代码如下:
[Unit]
Description=powerOn startup ros
[Service]
Type=simple
Restart=always
RestartSec=30
ExecStart=/usr/sbin/ros_start
ExecStop=/usr/sbin/ros_stop
ExecRestart=/usr/sbin/ros_restart
[Install]
WantedBy=multi-user.target
该文件就是我们自定义的服务,我们可以使用systemctl来操作该服务使其可以开机时候来启动,执行其中相应的命令,里面的命令其实还是我们自定义的脚本,下面来简要解析下该服务的内容,service主要由三部分组成,分别时Unit,Service,Install下面来分别介绍这三部分:
Unit:该服务的类型描述,这里定义为单元类型,Description是这个服务的一个描述信息,可以自己根据需要来修改。
Service:服务的关键部分,需要配置的一些关键参数,这里配置了如下几个参数:
(1)Type=simple:表明这个服务主要由 ExecStart 设置的程序来启动,启动后常驻于内存中。这个simple是默认的参数,除了simple外还可以设置为oneShot,该参数与simple类似,区别在于这个ExecStart执行的命令执行完成后就退出了,不会常驻在内存中。还可以设置为idel,该参数与 simple 类似,意思是要执行这个服务必须要所有的工作都顺利执行完毕后才会执行,这类的服务通常是开机到最后才执行即可的服务。
(2)Restart=always:表明该服务具备重启功能,如果服务意外关闭后会一直尝试重新启动。
(3)RestartSec=30:表明服务在意外关闭后,经过多少秒后再次重新尝试启动该服务。
(4)ExecStart=/usr/sbin/ros_start:表明服务启动时需要执行的命令,后面就是执行的命令所在路径,这里的命令是我们自定义的脚本。
(5)ExecStop=/usr/sbin/ros_stop:当需要停止该服务时需要执行的命令,后面是命令的路径。
(6)ExecStop=/usr/sbin/ros_restart:当服务需要重新启动时需要执行的命令,后面是命令的路径。
Install:需要将该unit安装到那个target上。
然后开始编写需要启动的三个脚本,分别是ros_start,ros_stop,ros_restart,下面来依次介绍三个脚本的编写:
(1)编写ros_start脚本,该脚本就是服务启动时需要执行的命令,具体代码如下:
#!/bin/bash
# Copyright: 2016-2018 www.corvin.cn
# Author: corvin
# Description: In raspeberry ubuntu Mate16.04 poweron
# startup ros's launch file.
# History:
# 20180531: initial this file.
source /opt/ros/kinetic/setup.bash
source /home/corvin/catkin_ws/devel/setup.bash
roslaunch ydlidar lidar.launch
exit 0
(2)编写ros_stop脚本,该脚本是需要停止服务时执行的命令,具体代码如下:
#!/bin/bash
# Copyright: 2016-2018 www.corvin.cn
# Author: corvin
# Description: when stop service should execute cmd
# History:
# 20180531: initial this file.
source /opt/ros/kinetic/setup.bash
source /home/corvin/catkin_ws/devel/setup.bash
for i in $(rosnode list);do
rosnode kill $i;
done
killall roslaunch
exit 0
(3)编写ros_restart脚本,该脚本是当需要重启服务时执行的命令,具体代码如下:
#!/bin/bash
# Copyright: 2016-2018 www.corvin.cn
# Author: corvin
# Description: when should restart service execute this bash.
# History:
# 20180531: initial this bash file.
/usr/sbin/ros_stop
sleep 3
/usr/sbin/ros_start
exit 0
0x04 配置service并重启测试
当编写好service和相应的执行脚本后,接下来就需要将其放到指定的目录下,由于需要执行的命令较多,我们仍然编写一个bash脚本来执行配置的命令,该脚本仍然放在scripts目录下,文件命名为config_service.sh,脚本代码如下:
#!/bin/bash
# Copyright: 2016-2018 www.corvin.cn
# Author: corvin
# Description: config ros startup service and bash files.
# History:
# 20180531: initial this file.
sudo cp ros_start /usr/sbin/
sudo cp ros_stop /usr/sbin/
sudo cp ros_restart /usr/sbin/
sudo cp ros_startup.service /lib/systemd/system/
sudo systemctl enable ros_startup.service
exit 0
首先对该脚本进行简单解析,前面通过cp拷贝将三个执行脚本放到/usr/sbin目录下,然后将ros_startup.service拷贝到/lib/systemd/system目录下,接下来的sudo systemctl enable ros_startup.service就是为了使该服务生效,启用该服务。当为该脚本通过chmod +x config_service.sh来增加执行权限后就可以来执行该脚本配置service了,具体操作如下图:
当使用systemctl is-enabled ros_startup.service命令就是为了查看我们的自定义服务是否已经启用,当出现enable就说明我们的服务已经挂载在系统服务上,当系统启动时会加载我们的服务来一起启动的,下面我们就可以来重启树梅派系统来测试了,看看能否正常执行我们的ros_start脚本来加载雷达启动。
当我们重启后可以发现系统已经正常使用我们的ros_start脚本来将雷达启动起来了,下面来详细介绍下systemctl这个命令,我们来如何操控我们的自定义服务:
(1)当我们想停止我们的launch加载的雷达时,可以使用如下命令:
sudo systemctl stop ros_startup.service
(2)当我们想启动我们的launch文件时,可以使用如下命令,当执行完以下命令后可以发现我们再次将雷达启动起来:
sudo systemctl start ros_startup.service
(3)当我们在launch文件运行中,想重新启动时可以执行如下命令,当执行完以下命令是会发现雷达首先停止了转动,等待3秒钟然后又再次重新启动了:
sudo systemctl restart ros_startup.service
(4)如果想禁止我们的自定义服务在开机的时候启动,可以使用如下命令:
sudo systemctl disable ros_startup.service
(5)如果想再次重新在开机时启动我们的服务,可以使用如下命令:
sudo systemctl enable ros_startup.service
在我们enable了自定义的服务后,我们的服务就会在后台一直运行,但是我们却不知道该服务在后台如何运行的,因为看不到任何日志输出,如果想查看服务运行的相关日志该如何操作呢,在这里就需要使用journalctl标准日志服务来查看了,Systemd通过其标准日志服务Journald将其管理的所有后台进程打印到到std:out(即控制台)的输出重定向到了日志文件。日志文件是二进制格式的,因此必须使用特定的工具才能查看。
Journald提供了配套的程序Journalctl用于处理日志内容,Journalctl的使用非常简单,默认不带任何参数的时候会输出系统和所有后台进程的混合日志,常用的参数有--dmesg用于查看内核输出的日志,--system用于查看系统输出的日志,--unit加上Unit的名字来指定输出特定Unit的日志,例如我们想查看自定义服务ros_startup.service的日志输出,可以使用如下命令:
sudo journalctl --unit ros_startup.service
下面来测试下该命令,查看服务在后台的日志输出:
corvin@robot:~$ sudo journalctl --unit ros_startup.service
[sudo] password for corvin:
-- Logs begin at 五 2016-02-12 00:28:02 CST, end at 四 2018-05-31 15:58:43 CST. --
5月 31 15:57:46 robot systemd[1]: Started powerOn startup ros.
corvin@robot:~$ sudo systemctl restart ros_startup.service
corvin@robot:~$ sudo journalctl --unit ros_startup.service
-- Logs begin at 五 2016-02-12 00:28:02 CST, end at 四 2018-05-31 15:59:39 CST. --
5月 31 15:57:46 robot systemd[1]: Started powerOn startup ros.
5月 31 15:59:21 robot systemd[1]: Stopping powerOn startup ros...
5月 31 15:59:24 robot ros_stop[1981]: killing /base_link_to_laser4
5月 31 15:59:24 robot ros_stop[1981]: killed
5月 31 15:59:24 robot ros_stop[1981]: killing /rosout
5月 31 15:59:24 robot ros_stop[1981]: killed
5月 31 15:59:25 robot ros_stop[1981]: killing /ydlidar_node
5月 31 15:59:25 robot ros_stop[1981]: killed
5月 31 15:59:26 robot ros_start[615]: ... logging to /root/.ros/log/524e6f08-64a8-11e8-a578-b827eb148
5月 31 15:59:26 robot ros_start[615]: Checking log directory for disk usage. This may take awhile.
5月 31 15:59:26 robot ros_start[615]: Press Ctrl-C to interrupt
5月 31 15:59:26 robot ros_start[615]: Done checking log file disk usage. Usage is <1GB.
5月 31 15:59:26 robot ros_start[615]: [59B blob data]
5月 31 15:59:26 robot ros_start[615]: started roslaunch server http://robot:34151/
5月 31 15:59:26 robot ros_start[615]: SUMMARY
5月 31 15:59:26 robot ros_start[615]: ========
5月 31 15:59:26 robot ros_start[615]: PARAMETERS
5月 31 15:59:26 robot ros_start[615]: * /rosdistro: kinetic
5月 31 15:59:26 robot ros_start[615]: * /rosversion: 1.12.13
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/angle_fixed: True
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/angle_max: 180.0
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/angle_min: -180.0
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/baudrate: 115200
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/frame_id: laser_frame
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/frequency: 7.0
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/heartbeat: False
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/ignore_array:
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/low_exposure: False
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/port: /dev/ydlidar
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/range_max: 16.0
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/range_min: 0.08
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/resolution_fixed: True
5月 31 15:59:26 robot ros_start[615]: * /ydlidar_node/samp_rate: 9
5月 31 15:59:26 robot ros_start[615]: NODES
现在我们已经可以使用自定义服务的方式来开启引导启动ROS的launch文件,但是如果我们现在想卸载相关的service文件该如何操作呢,那就需要将我们放置在相应目录中的脚本和service文件删除即可,我们可以编写个脚本来完成该动作,脚本命名为uninstall_ros_service.sh,具体代码如下所示:
#!/bin/bash
# Copyright: 2016-2018 www.corvin.cn
# Author: corvin
# Description: Remove ourself service files,include bash file.
# History:
# 20180531: initial this file.
/usr/sbin/ros_stop
sudo systemctl disable ros_startup.service
sudo rm /usr/sbin/ros_start
sudo rm /usr/sbin/ros_stop
sudo rm /usr/sbin/ros_restart
sudo rm /lib/systemd/system/ros_startup.service
exit 0
可以发现当执行了卸载自定义service的脚本后,我们已经无法执行start和stop相关操作了,如果想再次启用自定义服务只要重新执行启用的脚本即可。
0x05 参考资料
0x06 问题反馈
大家在按照教程操作过程中有任何问题,可以关注ROS小课堂的官方微信公众号,在公众号中给我发消息反馈问题即可,我基本上每天都会处理公众号中的留言!当然如果你要是顺便给ROS小课堂打个赏,我也会感激不尽的,打赏30块还会邀请进ROS小课堂的微信群与更多志同道合的小伙伴一起学习和交流!