前言
本人ROS小白,利用寒假时间学习ROS,在此以笔记的方式记录自己每天的学习过程。争取写满15篇(7/15)。
环境:Ubuntu20.04、ROS1:noetic
环境配置:严格按照下方学习链接的教程配置,基本一次成功。
学习链接:【Autolabor初级教程】ROS机器人入门
对应链接文档:ROS机器人入门课程《ROS理论与实践》
笔记绝大部分代码使用Python语言编写。
本期关键词:功能包重名,节点重名,话题名称设置,参数名称设置
一、功能包重名
- 功能包重名指的是在不同工作空间下有相同名称的功能包,比如在
demo1_ws
和domo2_ws
工作空间下都有名为turtlesim
的功能包,初次之外,ROS自带的工作空间下也有turtlesim
的功能包。 - 以上存在三个同名的功能包,我们在运行
rosrun
指令时,输入turtlesim
功能包,运行的是哪一个呢?这个就涉及到~/.bashrc
文件中工作空间环境变量的优先级设置。 ~/.bashrc
:在ROS中,~/.bashrc
文件是一个shell配置文件,它用于设置用户的bash shell环境。这个文件通常位于用户的主目录下(即用户的home目录),并且每次打开一个新的终端会话时,bash shell都会读取这个文件来执行一些初始化命令。在ROS的上下文中,~/.bashrc
文件可能包含一些特定的命令,这些命令用于配置ROS环境,使得用户能够在终端中方便地使用ROS的命令和工具。- 简单来说,
~/.bashrc
是一个配置文件,我们之前设置的快捷指令g-path
、get-x
,待会要设置的工作空间优先级,以及之后要进行的ROS主从机设置,都和~/.bashrc
文件配置有关。 - 对应的环境变量是功能包运行的前提。大家在学习的过程中可能会有这样的疑问?为什么在运行自定义工作空间的功能包前,需要先加载环境变量(即运行
source
指令),而运行ROS自带的包(比如乌龟运动控制的包)就不需要先加载环境变量呢?这是因为我们安装ROS的时候,ROS功能包的环境变量路径已经保存在了~/.bashrc
文件里,每次我们启动终端,~/.bashrc
文件就会帮我们自动加载对应的环境变量,所以才可以直接运行ROS的功能包。 - 打开终端,只执行
echo echo $ROS_PACKAGE_PATH
指令,终端输出的信息为:/opt/ros/noetic/share
。这时我们进入工作空间,执行source ./devel/setup.bash
后,再执行echo echo $ROS_PACKAGE_PATH
,得到的结果为:/home/用户名/XXX_ws/src:/opt/ros/noetic/share
。 /home/用户名/XXX_ws/src:/opt/ros/noetic/share
表示的是前者的工作空间优先级大于后者,如果两个工作空间存在相同的功能包,rosrun
指令默认执行前者(高优先级)的功能包。- 综上,如果存在功能包重名的情况,我们可以通过设置工作空间优先级的方法来确定要执行哪个工作空间下的功能包。
~/.bashrc
文件的配置方法:在~/.bashrc
文件中source /opt/ros/noetic/setup.bash
语句下添加:
source /home/用户/路径/工作空间A/devel/setup.bash
source /home/用户/路径/工作空间B/devel/setup.bash
(语句排位越下,工作空间优先级越高)
10. 我们要尽量避免功能包重名的情况!
二、节点重名
ROS中如果启动重名节点,先启动的节点就会被强制关闭。解决节点重名问题主要有以下三种方法:rosrun命令法、launch文件法、编程法
rosrun命令方法
以我们最熟悉的乌龟运动控制为例。
rosrun设置命名空间
- 在三个终端分别运行以下指令:
rosrun turtlesim turtlesim_node
rosrun turtlesim turtlesim_node __ns:=/aaa
rosnode list
- 结果为:
/aaa/turtlesim
/rosout
/turtlesim
rosrun名称重映射
- 在三个终端中运行以下指令:
rosrun turtlesim turtlesim_node __name:=name1
rosrun turtlesim turtlesim_node __name:=name2
rosnode list
- 结果为:
/name1
/name2
/rosout
- 这时结果可能还包含刚刚的
/aaa/turtlesim
和/turtlesim
,问题不大,执行rosnode cleanup
清除一下不可用节点就行。
rosrun设置命名空间+名称重映射
- 就是刚刚两个方法的叠加:
rosrun turtlesim turtlesim_node __name:=name1 __ns:=aaa
rosrun turtlesim turtlesim_node __name:=name2 __ns:=bbb
rosnode list
- 结果为:
/aaa/name1
/bbb/name2
/rosout
- 在上述命令中,经过测试,节点名称前加入的命名空间,指令
__ns:=aaa
和指令__ns:=/aaa
的效果是一样的。(这里注意与下面的话题名称重命名区分。)
launch文件设置
launch文件里的设置比较简单,也是包含命名空间设置和重映射两种。
代码如下:
<launch>
<node pkg="turtlesim" type="turtlesim_node" name="t1" />
<node pkg="turtlesim" type="turtlesim_node" name="t2" />
<node pkg="turtlesim" type="turtlesim_node" name="t1" ns="hello"/>
</launch>
执行rosnode list
查看结果:
/hello/t1
/t1
/t2
编码设置
测试代码:
#! usr/bin/env python
import rospy
if __name__=="__main__":
rospy.init_node("rename1",anonymous=True)
rospy.loginfo("this is a rename test!")
rospy.sleep(10.0)# 延时10s,不然程序运行太快,rosnode查看不到节点
执行rosnode list
查看,节点名会后缀时间戳,以此避免重名。
三、话题名称设置
rosrun命令方法
rosrun名称重映射语法: rorun 包名 节点名 话题名:=新话题名称
示例1:
# 启动ROS自带键盘控制节点控制乌龟运动
# 将话题/cmd_vel改为/turtle1/cmd_vel
rosrun teleop_twist_keyboard teleop_twist_keyboard.py /cmd_vel:=/turtle1/cmd_vel
rosrun turtlesim turtlesim_node
示例2:
# 将话题/turtle1/cmd_vel改为/cmd_vel
rosrun teleop_twist_keyboard teleop_twist_keyboard.py
rosrun turtlesim turtlesim_node /turtle1/cmd_vel:=/cmd_vel
launch文件设置
launch文件将话题名称重映射要用到remap标签,示例代码如下:
<launch>
<node pkg="turtlesim" type="turtlesim_node" name="gui" >
<remap from="/turtle1/cmd_vel" to="/cmd_vel"/>
</node>
<node pkg="turtlesim" type="turtle_teleop_key" name="key" />
</launch>
编码设置
以下代码均使用该指令执行:
rosrun helloworld demo_pub_msg.py __ns:=space
- 全局名称:以
/
开头的名称,和节点名称无关。代码示例如下:
#! /usr/bin/env python
import rospy
from helloworld.msg import Person
if __name__ == "__main__":
rospy.init_node("pub_msg")
# 以下两种都是将话题名设置为全局名称
# pub = rospy.Publisher("/msg_test",Person,queue_size=10)# 结果:/msg_test
pub = rospy.Publisher("/aaa/msg_test",Person,queue_size=10)# 结果:/aaa/msg_test
p=Person()
p.name="Zhang San"
p.age=18
p.height=1.78
rate=rospy.Rate(1)
while not rospy.is_shutdown():
pub.publish(p)
rate.sleep()
rospy.loginfo("name is: %s, age is: %d, height is: %f", p.name,p.age,p.height)
- 相对名称:非
/
开头的名称,参考命名空间(与节点名称平级)来确定话题名称。
#! /usr/bin/env python
import rospy
from helloworld.msg import Person
if __name__ == "__main__":
rospy.init_node("pub_msg")
# 以下两种都是将话题名设置为全局名称
pub = rospy.Publisher("msg_test",Person,queue_size=10)#结果:/space/msg_test
# pub = rospy.Publisher("aaa/msg_test",Person,queue_size=10)#结果:/space/aaa/msg_test
p=Person()
p.name="Zhang San"
p.age=18
p.height=1.78
rate=rospy.Rate(1)
while not rospy.is_shutdown():
pub.publish(p)
rate.sleep()
rospy.loginfo("name is: %s, age is: %d, height is: %f", p.name,p.age,p.height)
- 私有名称:以
~
开头的名称。
# 将上面代码的话题名称前加个~号即可
pub = rospy.Publisher("~msg_test",Person,queue_size=10)#结果:/space/pub_msg/msg_test
# pub = rospy.Publisher("~aaa/msg_test",Person,queue_size=10)#结果:/space/pub_msg/aaa/msg_test
四、参数名称设置
因为参数名称设置之前的笔记也提到过,所以这里只给出代码示例啦:
rosrun命令方法
rosrun turtlesim turtlesim_node _A:=100
launch文件设置
<launch>
<param name="p1" value="100" />
<node pkg="turtlesim" type="turtlesim_node" name="t1">
<param name="p2" value="100" />
</node>
</launch>
编码设置
这里仍然是使用rospy.set_param
的方法来设置服务器参数,另外还结合了刚刚提到的“全局”、“相对”、“私有”的概念。
rospy.set_param("/py_A",100) #全局,和命名空间以及节点名称无关
rospy.set_param("py_B",100) #相对,参考命名空间
rospy.set_param("~py_C",100) #私有,参考命名空间与节点名称