至少有两种途径,一种是使用ROS提供的功能包,一种自然是借助Ubuntu自身的启动机制。
ROS提供了robot_upstart包http://wiki.ros.org/robot_upstart(源码:https://github.com/clearpathrobotics/robot_upstart)可以用来设置开机自启动程序(通过把ros launch文件安装到service里去) ,首先安装这个包:
sudo apt-get install ros-<distro>-robot-upstart #distro 为ROS版本绰号,indigo, kinetic, etc.
然后执行下面的命令把你的包(例如,我自己开发的包facedetect_wrapper_d)的launch文件安装到/etc/ros/<distro>/ (例如/etc/ros/kinetic)下面去 :
rosrun robot_upstart install facedetect_wrapper_d/launch/facedetectservice.launch
上面的命令在/etc/ros/kinetic/下新建一个文件夹facedetect.d,将facedetectservice.launch拷贝到里面,上面的命令如果增加 --job <service名字>,那么则会以你指定的service名字+.d 创建文件夹,如果没有用--job指定名字,默认使用你的包名里第一个下划线前的字符串作为名字,robot_upstart install命令的详细用法可参考http://docs.ros.org/jade/api/robot_upstart/html/install.html
这里要注意的是,一旦launch文件被安装到了/etc/ros/kinetic/<service>.d/下,以后想要修改时必须是修改/etc/ros/kinetic/<service>.d/目录下的launch文件,改原来包下面的launch文件没有用,除非再来一次install,要解决这个问题,可以在上面的命令中增加 --symlink=True,这样launch文件就是以创建链接的方式安装到/etc/ros/kinetic/<service>.d/下,而不是拷贝launch文件本身到/etc/ros/kinetic/<service>.d/下。另外,还可以使用 --logdir <log_path>指定日志输出的路径。
执行下面的命令可以启动/停止facedetect服务:
sudo service facedetect start 或 sudo service facedetect restart
sudo service facedetect stop
如果没有使用--logdir指定日志输出路径,日志默认输出在/var/log/upstart/下,查看日志:
sudo tail -n 50 /var/log/upstart/facedetect.log
或 sudo tail -f /var/log/upstart/facedetect.log
第二种方式是借助Ubuntu自身的启动机制,有的人可能喜欢修改profile或bashrc之类的文件去实现自启动,我觉得最好还是不要去动那些系统本身的脚本,容易不小心弄出错误(比如修改.bashrc实现启动service是不合适的也是错误的选择),最好在系统提供的供我们自己定制的rc.local sevice里设置我们需要启动的程序,这样不易弄出错误和带来什么副作用。
Ubuntu16.04里设置很简单,可以直接在/etc/rc.local里exit 0之前增加设置环境变量(必须以export或'source <设置文件>'方式设置, 因为新启动程序被以fork出独立子进程方式运行,父进程从profile和bashrc之类的文件获得的设置都没了)和需要开机时启动的程序, 假设需要开机时启动ROS的rosbridge_websocket节点(用作websocket远程访问的代理节点,应用层级的交互协议遵循rosbridge v2.0 Protocol Specification)和自行开发的一个人脸识别的ROS服务节点,可作在/etc/rc.local里如下设置(注意把/etc/rc.local和/etc/init.d/rc.local里的第一行的sh都改成bash):
#!/bin/bash -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
export HOME=/home/fy
source /opt/ros/kinetic/setup.bash
source $HOME/catkin_ws/devel/setup.bash
roscore > $HOME/roscore.log 2>&1 &
sleep 5
roslaunch rosbridge_server rosbridge_websocket.launch > $HOME/rosbridge_websocket.log 2>&1 &
sleep 3
roslaunch facedetect_wrapper_d facedetectservice.launch > $HOME/facedetectservice.log 2>&1 &
exit 0
Ubuntu开机时会执行/etc/init.d下的文件,/etc/init.d/rc.local文件自然会被执行,而这个文件会检查和执行/etc/rc.local文件:
do_start() {
if [ -x /etc/rc.local ]; then
[ "$VERBOSE" != no ] && log_begin_msg "Running local boot scripts (/etc/rc.local)"
/etc/rc.local
ES=$?
[ "$VERBOSE" != no ] && log_end_msg $ES
return $ES
fi
}
在Ubuntu18.04里就没上面这么简单了,因为ubuntu18.04不再使用init.d而是使用systemd来做开机启动,systemd 默认会读取 /etc/systemd/system 下的配置文件,可是/etc/systemd/system 下没有rc.local.service文件,这个文件在 /lib/systemd/system/ 下,在 /lib/systemd/system/ 下可以看到rc.local.service,它是指向rc-local.service的链接,因此我们可以创建链接把 /lib/systemd/system/rc.local.service或者 /lib/systemd/system/rc-local.service链接到/etc/systemd/system/rc.local.service或/etc/systemd/system/rc-local.service:
ln -s /lib/systemd/system/rc-local.service /etc/systemd/system/rc-local.service
然后修改rc-local.service的内容,增加[install]这个块:
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=/etc/rc.local
After=network.target
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no
[Install]
WantedBy=multi-user.target
Alias=rc-local.service
从上面可以看到,rc.local.service检查/etc/rc.local,如果这个文件存在就执行它。
下面我们在/etc/下创建rc.local文件并赋予相应权限:
sudo touch /etc/rc.local
sudo chmod 755 /etc/rc.local
并且增加内容如下,以在开机时启动一个作为调用某个AI模型入口的TCP server供客户端应用调用:
#!/bin/bash
export CUDA_HOME=/usr/local/cuda
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/home/fy/project/ai/src/lib/caffe/caffe:$LD_LIBRARY_PATH
export PATH=/usr/local/cuda/bin:$PATH
cd /home/fy/project/ai/src
nohup /usr/bin/python -u server.py >/var/ai.log 2>&1 &
exit 0
然后在Ubuntu的系统设置->用户帐号设置里把帐号的为开机自动登录设置打开,再重启板子,可以看到Ubuntu顺利启动并自动进入桌面(为省内存还可以设置为进入控制台文本界面),上面设置的程序作为开机服务会被自动起来,检查相关日志即可看到相关ros节点或TCP server都正常启动了,执行命令查询也可以看到相关进程都在运行。
上面的设置已分别在Ubuntu16.04 LTS和Ubuntu18.04 LTS测试验证过可行。
使用ROS的robot_upstart工具包的好处是配置快,还能编程调用它的python API,缺点是还是不大灵活,如果所有的启动程序都可以放到一个lauch文件里那就没什么,如果需要执行多个包的launch文件就显得分散了有点不便于集中管理和修改,如果在Ubuntu下集中设置rc.local service还显得方便管理一些,当然这也不是什么大问题,看个人喜好选择哪种设置方式。
有点要注意的是ROS core启动过程中需要用到ROS_MASTER_URI,如果你没设置,ROS自动设置为http://<hostname>:11311, 所以要保证/etc/hostname里的主机名和 /etc/hosts里的 127.0.0.1 <hostname>这里的hostname值保持一致,如果你在Ubuntu的系统设置->详细信息修改了hostname,它只修改了/etc/hostname,没有修改/etc/hosts里面的地址映射关系中的hostname,会导致ros core启动不了,因为不能解析hostname 而导致ping不通,例如,假若在Ubuntu的设置界面里把主机名改成了robot-camera,启动ROS core时会报错:
ping robot-camera
RLException: Unable to contact my own server at [http://robot-camera:44821/]
这是因为在Ubuntu的设置界面里把主机名改成robot-camera时,只有/etc/hostname文件里的主机名改成了robot-camera,而/etc/hosts文件里的主机名还没改,例如: 127.0.0.1 robot-ECL089S,导致http://robot-camera解析不了,需要把
127.0.0.1 robot-ECL089S
改成
127.0.0.1 robot-camera
当然还有种办法就是在Ubuntu的脚本.bashrc或者ros的初始化脚本setup.bash等文件里增加:
ROS_MASTER_URI=http://127.0.0.1
但是显然这不是解决hostname地址解析的根本办法。