配置/etc/rc.local的内容如下:
#!/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.
source /opt/ros/kinetic/setup.bash
source ~/zfak_ws/install/setup.bash
echo "y" | rosclean purge
du -sh ~/.ros
#-------------------Route Model--------------------#
export ROS_IP=192.168.1.101
export ROS_HOSTNAME=$ROS_IP
export ROS_MASTER_URI=http://192.168.1.101:11311
#--------------------------------------------------#
#-------------------Default WS---------------------#
cd ~/zfak_ws/install/lib/system_scripts
echo 4 | ./start_choose.sh
#--------------------------------------------------#
exit 0
注意:原始的为/bin/sh -e,ROS的应该修改为 /bin/bash -e.
在 /lib/systemd/system/rc-local.service 的【service】中添加User属性为你当前登录的session,如果不设置,默认使用的是root。完整内容如下:
# 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
ConditionFileIsExecutable=/etc/rc.local
After=network.target
[Service]
Type=forking
User=jld
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
其中jld为你当前登录的session.
在shell脚本的开头往往有一句话来定义使用哪种sh解释器来解释脚本。
最开始在ubuntu desktop版本可以通过Startup Applications配置sh脚本能够正常启动机器人所需要的所有进程(包括:定位、导航、激光、串口交互等),但是使用ubuntu server后没有Startup Applications,所以需要更换一种开机自启动的策略,所以选择Systemd。
然后,遇到了问题。在/etc/rc.local中配置sh脚本,其中两个进程都存在打开串口失败的问题。最开始把该问题归结于权限的问题,查找了几天,参考了stackoverflow的问题描述等。
链接:https://stackoverflow.com/questions/11422639/opening-dev-ttyusb0-returns-bad-file-descriptor
后来尝试使用root编译catkin_ws,给串口交互的这个Node节点赋予root权限。还是同样的错误。
bad file descriptor
也尝试其他自启动的方式:How to run ROS on startup (bootup),发现和我的策略以及实现方式一样。
链接:https://blog.roverrobotics.com/how-to-run-ros-on-startup-bootup/
实在想不出哪个地方有问题了,难道是我不清楚systemd的启动顺序和流程,(确实不清楚)
是不是系统开机自启动的时候,串口还没有准备好,roslaunch就去打开这个串口设备,导致bad file descriptor。
最终,我开始去从代码端入手去查找问题,先看打印perror的地方在哪里?哪一部分导致了这个错误。
open串口正常,设置串口属性的位置出错,说明fd不正常。往前查。。。fd来自于哪里?
if (tcgetattr(fd,&newtio) < 0) {
perror("tcgetattr error");
return -1;
}
原始返回fd的代码,如下:
int open_port(char *comport)
{
int fd;
fd = open(comport, O_RDWR|O_NOCTTY|O_NDELAY);
if (-1 == fd)
{
perror("Can't Open Serial Port");
return(-1);
}
if(fcntl(fd, F_SETFL, 0)<0)
printf("fcntl failed!\n");
else
//printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));
if(isatty(STDIN_FILENO)==0)
printf("standard input is not a terminal device\n");
else
//printf("isatty success!\n");
//printf("fd-open=%d\n",fd);
return fd;
}
看着有啥问题没有?仔细分析这个open_port函数到底有没有问题。