ROS中的重名问题
工作空间覆盖
所谓工作空间覆盖,是指不同工作空间中,存在重名的功能包的情形。
当存在多个重名功能包时,后刷新的路径优先级最高,如下:
在
source /opt/ros/noetic/setup.bash source /home/ld/demo01_ws/devel/setup.bash source /home/ld/demo02_ws/devel/setup.bash
在三个路径下均存在
demo01_ws 输出 hello_ws1 demo02_ws 输出 hello_ws2
此时输入指令
工作空间重名的案例
实现
0.新建工作空间A与工作空间B,两个工作空间中都创建功能包: turtlesim。
1.在 ~/.bashrc 文件下追加当前工作空间的 bash 格式如下:
source /home/用户/路径/工作空间A/devel/setup.bash source /home/用户/路径/工作空间B/devel/setup.bash
2.新开命令行:
3.查看ROS环境环境变量
结果:自定义工作空间B:自定义空间A:系统内置空间
4.调用命令:
原因
ROS 会解析 .bashrc 文件,并生成 ROS_PACKAGE_PATH ROS包路径,该变量中按照 .bashrc 中配置设置工作空间优先级,在设置时需要遵循一定的原则:ROS_PACKAGE_PATH 中的值,和 .bashrc 的配置顺序相反—>后配置的优先级更高,如果更改自定义空间A与自定义空间B的source顺序,那么调用时,将进入工作空间A。
结论
功能包重名时,会按照 ROS_PACKAGE_PATH 查找,配置在前的会优先执行。
隐患
存在安全隐患,比如当前工作空间B优先级更高,意味着当程序调用 turtlesim 时,不会调用工作空间A也不会调用系统内置的 turtlesim,如果工作空间A在实现时有其他功能包依赖于自身的 turtlesim,而按照ROS工作空间覆盖的涉及原则,那么实际执行时将会调用工作空间B的turtlesim,从而导致执行异常,出现安全隐患。
BUG 说明:
当在 .bashrc 文件中 source 多个工作空间后,可能出现的情况,在 ROS PACKAGE PATH 中只包含两个工作空间,可以删除自定义工作空间的 build 与 devel 目录,重新 catkin_make,然后重新载入 .bashrc 文件,问题解决。
ROS节点名称重名
案例
启动两个 turtlesim_node 节点,当然如果直接打开两个终端,直接启动,那么第一次启动的节点会关闭,并给出提示:
[ WARN] [1578812836.351049332]: Shutdown request received. [ WARN] [1578812836.351207362]: Reason given for shutdown: [new node registered with same name]
因为两个节点不能重名,接下来将会介绍解决重名问题的多种方案。
在ROS中给出的解决策略时使用命名空间或者名称重映射
命名空间就是为名称添加前缀,名称重映射是为名称起别名。这两种策略都可以解决节点重名问题,两种策略的实现途径有多种:
- rosrun 命令
- launch 文件
- 编码实现
以上三种途径都可以通过命名空间或名称重映射的方式,来避免节点重名,本节将对三者的使用逐一演示,三者要实现的需求类似。
1. rosrun设置命名空间与重映射
1.rosrun设置命名空间
1.1设置命名空间演示
语法: rosrun 包名 节点名 __ns:=新名称
rosrun turtlesim turtlesim_node __ns:=/xxx
rosrun turtlesim turtlesim_node __ns:=/yyy
两个节点都可以正常运行
1.2运行结果
/xxx/turtlesim /yyy/turtlesim
2.rosrun名称重映射
2.1为节点起别名
语法: rosrun 包名 节点名 __name:=新名称
rosrun turtlesim turtlesim_node __name:=t1
rosrun turtlesim turtlesim_node __name:=t2
两个节点都可以运行
2.2运行结果
/t1 /t2
3.rosrun命名空间与名称重映射叠加
3.1设置命名空间同时名称重映射
语法: rosrun 包名 节点名 __ns:=新名称 __name:=新名称
rosrun turtlesim turtlesim_node __ns:=/xxx __name:=tn
3.2运行结果
/xxx/tn
使用环境变量也可以设置命名空间,启动节点前在终端键入如下命令:
export ROS_NAMESPACE=xxxx
2. launch文件设置命名空间与重映射
介绍 launch 文件的使用语法时,在 node 标签中有两个属性: name 和 ns,二者分别是用于实现名称重映射与命名空间设置的。使用launch文件设置命名空间与名称重映射也比较简单。
1.launch文件
<launch> <node pkg="turtlesim" type="turtlesim_node" name="turtle" /> <node pkg="turtlesim" type="turtlesim_node" name="t1" /> <node pkg="turtlesim" type="turtlesim_node" name="t1" ns="hello"/> </launch>
在 node 标签中,name 属性是必须的,ns 可选。
2.运行
/t1 /t2 /t1/hello
3. 编码设置命名空间与重映射
如果自定义节点实现,那么可以更灵活的设置命名空间与重映射实现。
1.名称别名设置
核心代码:
rospy.init_node("lisi",anonymous=True)
2.执行
会在节点名称后缀时间戳。
ROS话题名称设置
在ROS中节点名称可能出现重名的情况,同理话题名称也可能重名。
在 ROS 中节点终端,不同的节点之间通信都依赖于话题,话题名称也可能出现重复的情况,这种情况下,系统虽然不会抛出异常,但是可能导致订阅的消息非预期的,从而导致节点运行异常。这种情况下需要将两个节点的话题名称由相同修改为不同。
又或者,两个节点是可以通信的,两个节点之间使用了相同的消息类型,但是由于,话题名称不同,导致通信失败。这种情况下需要将两个节点的话题名称由不同修改为相同。
在实际应用中,按照逻辑,有些时候可能需要将相同的话题名称设置为不同,也有可能将不同的话题名设置为相同。在ROS中给出的解决策略与节点名称重命类似,也是使用名称重映射或为名称添加前缀。根据前缀不同,有全局、相对、和私有三种类型之分。
- 全局(参数名称直接参考ROS系统,与节点命名空间平级)
- 相对(参数名称参考的是节点的命名空间,与节点名称平级)
- 私有(参数名称参考节点名称,是节点名称的子级)
名称重映射是为名称起别名,为名称添加前缀,该实现比节点重名更复杂些,不单是使用命名空间作为前缀、还可以使用节点名称最为前缀。两种策略的实现途径有多种:
- rosrun 命令
- launch 文件
- 编码实现
1. rosrun设置话题重映射
rosrun名称重映射语法: rorun 包名 节点名 话题名:=新话题名称
实现teleop_twist_keyboard与乌龟显示节点通信方案由两种:
1.方案1
将 teleop_twist_keyboard 节点的话题设置为
启动键盘控制节点:
启动乌龟显示节点:
二者可以实现正常通信
2.方案2
将乌龟显示节点的话题设置为
启动键盘控制节点:
启动乌龟显示节点:
二者可以实现正常通信
2. launch文件设置话题重映射
launch 文件设置话题重映射语法:
<node pkg="xxx" type="xxx" name="xxx"> <remap from="原话题" to="新话题" /> </node>
实现teleop_twist_keyboard与乌龟显示节点通信方案由两种:
1.方案1
将 teleop_twist_keyboard 节点的话题设置为
<launch> <node pkg="turtlesim" type="turtlesim_node" name="t1" /> <node pkg="teleop_twist_keyboard" type="teleop_twist_keyboard.py" name="key"> <remap from="/cmd_vel" to="/turtle1/cmd_vel" /> </node> </launch>
二者可以实现正常通信
2.方案2
将乌龟显示节点的话题设置为
<launch> <node pkg="turtlesim" type="turtlesim_node" name="t1"> <remap from="/turtle1/cmd_vel" to="/cmd_vel" /> </node> <node pkg="teleop_twist_keyboard" type="teleop_twist_keyboard.py" name="key" /> </launch>
二者可以实现正常通信
3. 编码设置话题名称
话题的名称与节点的命名空间、节点的名称是有一定关系的,话题名称大致可以分为三种类型:
- 全局(话题参考ROS系统,与节点命名空间平级)
- 相对(话题参考的是节点的命名空间,与节点名称平级)
- 私有(话题参考节点名称,是节点名称的子级)
1.演示准备
1.初始化节点设置一个节点名称
2.设置不同类型的话题
3.启动节点时,传递一个 __ns:= xxx
4.节点启动后,使用 rostopic 查看话题信息
2.1全局名称
格式:以
示例1:
结果1:
示例2:
结果2:
2.2相对名称
格式:非
示例1:
结果1:
示例2:
结果2:
2.3私有名称
格式:以
示例1:
结果1:
示例2:
结果2:
ROS参数名称设置
1. rosrun设置参数
rosrun 在启动节点时,也可以设置参数:
语法:
1.设置参数
启动乌龟显示节点,并设置参数 A = 100
rosrun turtlesim turtlesim_node _A:=100
2.运行
/turtlesim/A /turtlesim/background_b /turtlesim/background_g /turtlesim/background_r
结果显示,参数A前缀节点名称,也就是说rosrun执行设置参数参数名使用的是私有模式
2. launch文件设置参数
通过 launch 文件设置参数的方式前面已经介绍过了,可以在 node 标签外,或 node 标签中通过 param 或 rosparam 来设置参数。在 node 标签外设置的参数是全局性质的,参考的是 / ,在 node 标签中设置的参数是私有性质的,参考的是 /命名空间/节点名称。
1.设置参数
以 param 标签为例,设置参数
<launch> <param name="p1" value="100" /> <node pkg="turtlesim" type="turtlesim_node" name="t1"> <param name="p2" value="100" /> </node> </launch>
2.运行
/p1 /t1/p1
运行结果与预期一致。
3. 编码设置参数
python 中关于参数设置的语法实现比 C++ 简洁一些,调用的API时 rospy.set_param,该函数中,参数1传入参数名称,参数2是传入参数值,参数1中参数名称设置时,如果以 / 开头,那么就是全局参数,如果以 ~ 开头,那么就是私有参数,既不以 / 也不以 ~ 开头,那么就是相对参数。代码示例:
rospy.set_param("/py_A",100) #全局,和命名空间以及节点名称无关 rospy.set_param("py_B",100) #相对,参考命名空间 rospy.set_param("~py_C",100) #私有,参考命名空间与节点名称
运行时,假设设置的 namespace 为 xxx,节点名称为 yyy,使用 rosparam list 查看:
/py_A /xxx/py_B /xxx/yyy/py_C