查看原文
其他

开发者说丨ROS理论与实践④:ROS运行管理

赵虚左 Apollo开发者社区 2022-07-29


ROS是多进程(节点)的分布式框架,一个完整的ROS系统实现,可能包含多台主机,每台主机上又有多个工作空间(workspace),每个的工作空间中又包含多个功能包(package),每个功能包又包含多个节点(Node),不同的节点都有自己的节点名称,每个节点可能还会设置一个或多个话题(topic)。 在多级层深的ROS系统中,大到某个功能模块,小到某个节点,可能是由官方、不同国家的ROS社区、多样的ROS组织和海量的研发人员共同研发成果的集合,在这个复杂系统的集成中,可能会出现一些问题,比如,如何关联不同的功能包,繁多的ROS节点应该如何启动?功能包、节点、话题、参数重名时应该如何处理?不同主机上的节点如何通信?


本章主要介绍在ROS中上述问题的解决策略,预期达成学习目标也与上述问题对应:


  • 掌握元功能包使用语法。

  • 掌握launch文件的使用语法。

  • 理解什么是ROS工作空间覆盖,以及存在什么安全隐患。

  • 掌握节点名称重名时的处理方式。

  • 掌握话题名称重名时的处理方式。

  • 掌握参数名称重名时的处理方式。

  • 能够实现ROS分布式通信。


下面是由社区开发者—赵虚左提供的文章,对ROS运行管理进行详细讲解,希望这篇文章能给感兴趣的开发者带来更多帮助。



  ENJOY THE FOLLOWING  




场景:完成一个系统性的机器人功能,工作空间下有多个功能包(运动控制、导航、摄像头等),为了方便管理,可以使用元功能包,MatePackage是Linux的一个文件管理系统的概念。是ROS中的一个虚包,里面没有实质性的内容,但是它依赖了其他的软件包,通过这种方法可以把其他包组合起来。例如我们通过sudo apt-get install roskinetic-desktop-full命令安装的ROS就是一个虚包。我们可以认为它是一本书的目录索引,告诉我们这个包集合中有哪些子包,并且该去哪里下载。例如Navigation这个package就是一个MatePackage,这个文件夹下面package.xml中的内容就是所依赖的包的名字。这样做的好处就是方便用户的安装,我们只需要这一个包就可以把其他相关的软件包组织到一起安装起来。


下面是一些常见的MetaPackage:navigation moveit! turtlebot3...


实现流程


首先,新建一个功能包。


然后,修改package.xml。


1<exec_depend>被集成的功能包exec_depend>
2.....
3<export>
4<metapackage />
5export>

<左右滑动以查看完整代码>


最后,修改CMakeLists.txt。


1cmake_minimum_required(VERSION 3.0.2)
2project(demo)
3find_package(catkin REQUIRED)
4catkin_metapackage()

<左右滑动以查看完整代码>




关于launch文件的使用我们已经不陌生了,在《开发者说丨ROS理论与实践①:ROS概述与环境搭建》内容中,就曾经介绍到:


一个程序中可能需要启动多个节点,比如:ROS内置的小乌龟案例,如果要控制乌龟运动,要启动多个窗口,分别启动roscore、乌龟界面节点、键盘控制节点。如果每次都调用rosrun逐一启动,显然效率低下,如何优化?


采用的优化策略便是使用roslaunch命令集合launch文件启动管理节点,并且在后续教程中,也多次使用到了launch文件。


① 概念


launch文件是一个XML格式的文件,可以启动本地和远程的多个节点,还可以在参数服务器中设置参数。


② 作用


简化节点的配置与启动,提高ROS程序的启动效率。


③ 使用


以turtlesim为例演示


1.新建launch文件


在功能包下添加launch目录,目录下新建xxxx.launch文件,编辑launch 文件。


1<launch>
2<node pkg="turtlesim" type="turtlesim_node"     name="myTurtle" output="screen" />
3<node pkg="turtlesim" type="turtle_teleop_key"  name="myTurtleContro" output="screen" />
4launch>

<左右滑动以查看完整代码>


2.调用launch文件


roslaunch 包名 xxx.launch

<左右滑动以查看完整代码>


注意:roslaunch命令执行launch文件时,首先会判断是否启动了roscore,如果启动了,则不再启动,否则,会自动调用roscore。


PS:本节主要介绍launch文件的使用语法,launch文件中的标签,以及不同标签的一些常用属性。


标签是所有launch文件的根标签,充当其他标签的容器。


1.属性


  • deprecated = "弃用声明"

    告知用户当前launch文件已经弃用。


2.子级标签


所有其它标签都是roslaunch的子级。


标签用于指定ROS节点,是最常见的标签,需要注意的是:roslaunch命令不能保证按照node的声明顺序来启动节点(节点的启动是多进程的)。


1.属性


  • pkg="包名"

    节点所属的包。


  • type="nodeType"

    节点类型(与之相同名称的可执行文件)。


  • name="nodeName"

    节点名称(在ROS网络拓扑中节点的名称)。


  • args="xxx xxx xxx" (可选)

    将参数传递给节点。


  • machine="机器名"

    在指定机器上启动节点。


  • respawn="true | false" (可选)

    如果节点退出,是否自动重启。


  • respawn_delay=" N" (可选)

    如果respawn为true,那么延迟N秒后启动节点。


  • required="true | false"(可选)

    该节点是否必须,如果为true,那么如果该节点退出,将杀死整个 roslaunch。


  • ns="xxx" (可选)

    在指定命名空间xxx中启动节点。


  • clear_params="true | false" (可选)

    在启动前,删除节点的私有空间的所有参数。


  • out_put="log | screen"(可选)

    日志发送目标,可以设置为log日志文件,或screen屏幕,默认是log。


2.子级标签


  • env环境变量设置。

  • remap重映射节点名称。

  • rosparam参数设置。

  • param参数设置。


include标签用于将另一个xml格式的launch文件导入到当前文件。


1.属性


  • file="$(find 包名)/xxx/xxx.launch"

    要包含的文件路径。


  • ns="xxx" (可选)

    在指定命名空间导入文件。


2.子级标签


  • env环境变量设置。

  • arg将参数传递给被包含的文件。


用于话题重命名。


1.属性


  • from="xxx"

    原始话题名称。


  • to="yyy"

    目标名称。


2.子级标签



标签主要用于在参数服务器上设置参数,参数源可以在标签中通过 value 指定,也可以通过外部文件加载,在标签中时,相当于私有命名空间。


1.属性


  • name="命名空间/参数名"

    参数名称,可以包含命名空间。


  • value="xxx" (可选)

    定义参数值,如果此处省略,必须指定外部文件作为参数源。


  • type="str | int | double | bool | yaml" (可选)

    指定参数类型,如果未指定,roslaunch会尝试确定参数类型,规则如下:

    ○ 如果包含 '.' 的数字解析为浮点型,否则为整型。

    ○ "true" 和 "false" 是bool值(不区分大小写)。

    ○ 其他是字符串。

  • textfile="外部文件"

    指定参数源文件。

  • command="外部文件"

比如:command="load" file="xxx" 从外部文件加载参数。


2.子级标签



标签可以从YAML文件导入参数,或将参数到处到YAML文件,也可以用来删除参数,标签在标签中时被视为私有。


1.属性


  • command="load | dump | delete" (可选,默认 load)

    加载、导出或删除参数。

  • file="$(find xxxxx)/xxx/yyy...."

    加载或导出到的yaml文件。


  • param="参数名称"

  • ns="命名空间" (可选)


2.子级标签



标签可以对节点分组,具有ns属性,可以让节点归属某个命名空间。


1.属性


  • ns="名称空间" (可选)

  • clear_params="true | false" (可选)

    启动前,是否删除组名称空间的所有参数。


2.子级标签


  • 除了launch标签外的其他标签。


标签是用于动态传参,类似于函数的参数,可以增强launch文件的灵活性。


1.属性


  • name="参数名称"

  • default="默认值" (可选)

  • value="数值" (可选)

    不可以与default并存。

  • doc="描述"

    参数说明。


2.子级标签



3.示例


  • launch文件传参语法实现,hello.lcaunch。

1<launch>
2<arg name="xxx" />
3<param name="param" value="$(arg xxx)" />
4launch>

<左右滑动以查看完整代码>


  • 命令行调用launch传参。

roslaunch hello.launch xxx:=值

<左右滑动以查看完整代码>






所谓工作空间覆盖,是指不同工作空间中,存在重名的功能包的情形。


ROS开发中,会自定义工作空间且可以同时存在多个,可能会出现一种情况:虽然特定工作空间内的功能包不能重名,但是自定义工作空间的功能包与内置的功能包可以重名或者不同的自定义的工作空间中也可以出现重名的功能包,那么调用该名称功能包时,会调用哪一个呢?比如:自定义工作空间A存在功能包turtlesim,自定义工作空间B也存在功能包turtlesim,当然系统内置空间也存在turtlesim,如果调用turtlesim包,会调用哪个工作空间中的呢?


① 实现


0.新建工作空间A与工作空间B,两个工作空间中都创建功能包:turtlesim。


1.在~/.bashrc文件下追加当前工作空间的bash格式如下:


1source /home/用户/路径/工作空间A/devel/setup.bash
2source /home/用户/路径/工作空间B/devel/setup.bash

<左右滑动以查看完整代码>


2.新开命令行:source .bashrc加载环境变量。


3.查看ROS环境环境变量echo $ROS_PACKAGE_PATH。


结果:自定义工作空间B:自定义空间A:系统内置空间。


4.调用命令:roscd turtlesim会进入自定义工作空间B。


② 原因


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中创建的节点是有名称的,C++初始化节点时通过API:ros::init(argc,argv,"xxxx");来定义节点名称,在Python中初始化节点则通过 rospy.init_node("yyyy") 来定义节点名称。在ROS的网络拓扑中,是不可以出现重名的节点的,因为假设可以重名存在,那么调用时会产生混淆,这也就意味着,不可以启动重名节点或者同一个节点启动多次,的确,在ROS中如果启动重名节点的话,之前已经存在的节点会被直接关闭,但是如果有这种需求的话,怎么优化呢?


在ROS中给出的解决策略是使用命名空间或名称重映射。


命名空间就是为名称添加前缀,名称重映射是为名称起别名。这两种策略都可以解决节点重名问题,两种策略的实现途径有多种:


  • rosrun命令

  • launch文件

  • 编码实现


以上三种途径都可以通过命名空间或名称重映射的方式,来避免节点重名,本节将对三者的使用逐一演示,三者要实现的需求类似。


案例


启动两个turtlesim_node节点,当然如果直接打开两个终端,直接启动,那么第一次启动的节点会关闭,并给出提示:


1[ WARN] [1578812836.351049332]Shutdown request received.
2[ WARN] [1578812836.351207362]Reason given for shutdown[new node registered with same name]

<左右滑动以查看完整代码>


因为两个节点不能重名。



1.rosrun设置命名空间


1.设置命名空间演示


语法:rosrun包名 节点名 __ns:=新名称。


rosrun turtlesim turtlesim_node __ns:=/xxx

<左右滑动以查看完整代码>

rosrun turtlesim turtlesim_node __ns:=/yyy

<左右滑动以查看完整代码>


两个节点都可以正常运行。


2.运行结果


rosnode list查看节点信息,显示结果:


/xxx/turtlesim
/yyy/turtlesim

<左右滑动以查看完整代码>


2.rosrun名称重映射


1.为节点起别名


语法:rosrun包名 节点名 __name:=新名称。


rosrun turtlesim  turtlesim_node __name:=t1 |  rosrun turtlesim   turtlesim_node /turtlesim:=t1(不适用于python)

<左右滑动以查看完整代码>


rosrun turtlesim turtlesim_node __name:=t2 | rosrun turtlesim turtlesim_node /turtlesim:=t2(不适用于python)

<左右滑动以查看完整代码>


两个节点都可以运行。


2.运行结果


rosnode list查看节点信息,显示结果:


1/t1
2/t2

<左右滑动以查看完整代码>


3.rosrun命名空间与名称重映射叠加


1.设置命名空间同时名称重映射


语法:rosrun包名 节点名 __ns:=新名称 __name:=新名称。


rosrun turtlesim turtlesim_node __ns:=/xxx __name:=tn

<左右滑动以查看完整代码>


2.运行结果


rosnode list查看节点信息,显示结果:


/xxx/tn

<左右滑动以查看完整代码>


使用环境变量也可以设置命名空间,启动节点前在终端键入如下命令:export ROS_NAMESPACE=xxxx。


介绍launch文件的使用语法时,在node标签中有两个属性:name和ns,二者分别是用于实现名称重映射与命名空间设置的。使用launch文件设置命名空间与名称重映射也比较简单。


1.launch文件


1 <launch>
2
3<node pkg="turtlesim" type="turtlesim_node" name="t1" />
4<node pkg="turtlesim" type="turtlesim_node" name="t2" />
5<node pkg="turtlesim" type="turtlesim_node" name="t1" ns="hello"/>
6
7launch>

<左右滑动以查看完整代码>


在node标签中,name属性是必须的,ns可选。


2.运行


rosnode list查看节点信息,显示结果:


1/t1
2/t2
3/t1/hello

<左右滑动以查看完整代码>


如果自定义节点实现,那么可以更灵活的设置命名空间与重映射实现。


1.C++ 实现:重映射


1.名称别名设置


核心代码:


ros::init(argc,argv,"zhangsan",ros::init_options::AnonymousName);

<左右滑动以查看完整代码>


2.执行


会在名称后面添加时间戳。


2.C++ 实现:命名空间


1.命名空间设置


核心代码:


1std::map<std::stringstd::stringmap;
2map["__ns"] = "xxxx";
3ros::init(map,"wangqiang");

<左右滑动以查看完整代码>


2.执行


节点名称设置了命名空间。


3.Python实现:重映射


1.名称别名设置


核心代码:


rospy.init_node("lisi",anonymous=True)

<左右滑动以查看完整代码>


2.执行


会在节点名称后缀时间戳。




在ROS中节点名称可能出现重名的情况,同理话题名称也可能重名。


在ROS中节点终端,不同的节点之间通信都依赖于话题,话题名称也可能出现重复的情况,这种情况下,系统虽然不会抛出异常,但是可能导致订阅的消息非预期的,从而导致节点运行异常。这种情况下需要将两个节点的话题名称由相同修改为不同。

又或者,两个节点是可以通信的,两个节点之间使用了相同的消息类型,但是由于,话题名称不同,导致通信失败。这种情况下需要将两个节点的话题名称由不同修改为相同。


在实际应用中,按照逻辑,有些时候可能需要将相同的话题名称设置为不同,也有可能将不同的话题名设置为相同。在ROS中给出的解决策略与节点名称重命类似,也是使用名称重映射或为名称添加前缀。根据前缀不同,有全局、相对、私有三种类型之分。


  • 全局(参数名称直接参考ROS系统,与节点命名空间平级)

  • 相对(参数名称参考的是节点的命名空间,与节点名称平级)

  • 私有(参数名称参考节点名称,是节点名称的子级)


名称重映射是为名称起别名,为名称添加前缀,该实现比节点重名更复杂些,不单是使用命名空间作为前缀、还可以使用节点名称最为前缀。两种策略的实现途径有多种:


  • rosrun命令

  • launch文件

  • 编码实现


本节将对三者的使用逐一演示,三者要实现的需求类似。


案例


在ROS中提供了一个比较好用的键盘控制功能包:ros-noetic-teleop-twist-keyboard,该功能包,可以控制机器人的运动,作用类似于乌龟的键盘控制节点,可以使用sudo apt install ros-noetic-teleop-twist-keyboard 来安装该功能包,然后执行: rosrun teleop_twist_keyboard teleop_twist_keyboard.py,在启动乌龟显示节点,不过此时前者不能控制乌龟运动,因为,二者使用的话题名称不同,前者使用的是cmd_vel话题,后者使用的是/turtle1/cmd_vel话题。需要将话题名称修改为一致,才能使用,如何实现?


rosrun名称重映射语法: rorun包名 节点名 话题名:=新话题名称。


实现teleop_twist_keyboard与乌龟显示节点通信方案由两种:


1.方案1


将teleop_twist_keyboard节点的话题设置为/turtle1/cmd_vel。


启动键盘控制节点:


rosrun teleop_twist_keyboard teleop_twist_keyboard.py /cmd_vel:=/turtle1/cmd_vel

<左右滑动以查看完整代码>


启动乌龟显示节点:


rosrun turtlesim turtlesim_node

<左右滑动以查看完整代码>


二者可以实现正常通信。


2.方案2


将乌龟显示节点的话题设置为/cmd_vel。


启动键盘控制节点:


rosrun teleop_twist_keyboard teleop_twist_keyboard.py

<左右滑动以查看完整代码>


启动乌龟显示节点:


rosrun turtlesim turtlesim_node /turtle1/cmd_vel:=/cmd_vel

<左右滑动以查看完整代码>


二者可以实现正常通信。


launch文件设置话题重映射语法:


1<node pkg="xxx" type="xxx" name="xxx">
2<remap from="原话题" to="新话题" />
3node>

<左右滑动以查看完整代码>


实现teleop_twist_keyboard与乌龟显示节点通信方案由两种:


1.方案1


将teleop_twist_keyboard节点的话题设置为/turtle1/cmd_vel。


1<launch>
2
3<node pkg="turtlesim" type="turtlesim_node" name="t1" />
4<node pkg="teleop_twist_keyboard" type="teleop_twist_keyboard.py" name="key">
5    <remap from="/cmd_vel" to="/turtle1/cmd_vel" />
6node>
7
8launch>

<左右滑动以查看完整代码>


二者可以实现正常通信。


2.方案2


将乌龟显示节点的话题设置为/cmd_vel。


1<launch>
2<node pkg="turtlesim" type="turtlesim_node" name="t1">
3    <remap from="/turtle1/cmd_vel" to="/cmd_vel" />
4node>
5<node pkg="teleop_twist_keyboard" type="teleop_twist_keyboard.py" name="key" />
6
7launch>

<左右滑动以查看完整代码>


二者可以实现正常通信。


话题的名称与节点的命名空间、节点的名称是有一定关系的,话题名称大致可以分为三种类型:


  • 全局(话题参考ROS系统,与节点命名空间平级)

  • 相对(话题参考的是节点的命名空间,与节点名称平级)

  • 私有(话题参考节点名称,是节点名称的子级)


结合编码演示具体关系。


1.C++ 实现


演示准备:


1.初始化节点设置一个节点名称:ros::init(argc,argv,"hello")。

2.设置不同类型的话题。

3.启动节点时,传递一个__ns:= xxx。

4.节点启动后,使用rostopic查看话题信息。


1.全局名称


格式:以/开头的名称,和节点名称无关。


比如:/xxx/yyy/zzz


示例1:


ros::Publisher pub = nh.advertise<:string>("/chatter",1000);

<左右滑动以查看完整代码>


结果1:/chatter


示例2:


ros::Publisher pub = nh.advertise<:string>("/chatter/money",1000);

<左右滑动以查看完整代码>


结果2:/chatter/money


2.相对名称


格式:非/开头的名称,相对节点名称来确定话题名称。


示例1:


ros::Publisher pub = nh.advertise<:string>("chatter",1000);

<左右滑动以查看完整代码>


结果1:xxx/chatter


示例2:


ros::Publisher pub = nh.advertise("chatter/money",1000);<:string>

<左右滑动以查看完整代码>


结果2:xxx/chatter/money


3.私有名称


格式:以~开头的名称。


示例1:


ros::NodeHandle nh("~");

<左右滑动以查看完整代码>

ros::Publisher pub = nh.advertise("chatter",1000);

<左右滑动以查看完整代码>


结果1:/xxx/hello/chatter


示例2:


ros::NodeHandle nh("~");

<左右滑动以查看完整代码>

ros::Publisher pub = nh.advertise("chatter/money",1000);

<左右滑动以查看完整代码>


结果2:/xxx/hello/chatter/money


PS:当使用~,而话题名称有时/开头时,那么话题名称是绝对的。


示例3:


ros::NodeHandle nh("~");

<左右滑动以查看完整代码>

ros::Publisher pub = nh.advertise("/chatter/money",1000);

<左右滑动以查看完整代码>

结果3:/chatter/money


2.Python实现


演示准备:


1.初始化节点设置一个节点名称:rospy.init_node("hello")

2.设置不同类型的话题。

3.启动节点时,传递一个__ns:= xxx。

4.节点启动后,使用=rostopic查看话题信息。


1.全局名称


格式:以/开头的名称,和节点名称无关。


示例1:


pub = rospy.Publisher("/chatter",String,queue_size=1000)

<左右滑动以查看完整代码>


结果1:/chatter


示例2:


pub = rospy.Publisher("/chatter/money",String,queue_size=1000)

<左右滑动以查看完整代码>


结果2:/chatter/money


2.相对名称


格式:非/开头的名称,相对节点名称来确定话题名称。


示例1:


pub = rospy.Publisher("chatter",String,queue_size=1000)

<左右滑动以查看完整代码>


结果1:xxx/chatter


示例2:


pub = rospy.Publisher("chatter/money",String,queue_size=1000)

<左右滑动以查看完整代码>

结果2:xxx/chatter/money


3.私有名称


格式:以~开头的名称。


示例1:


pub = rospy.Publisher("~chatter",String,queue_size=1000)

<左右滑动以查看完整代码>


结果1:/xxx/hello/chatter


示例2:


pub = rospy.Publisher("~chatter/money",String,queue_size=1000)

<左右滑动以查看完整代码>


结果2:/xxx/hello/chatter/money




在ROS中节点名称话题名称可能出现重名的情况,同理参数名称也可能重名。


当参数名称重名时,那么就会产生覆盖,如何避免这种情况?


关于参数重名的处理,没有重映射实现,为了尽量的避免参数重名,都是使用为参数名添加前缀的方式,实现类似于话题名称有:全局、相对、和私有三种类型之分。


  • 全局(参数名称直接参考ROS系统,与节点命名空间平级)

  • 相对(参数名称参考的是节点的命名空间,与节点名称平级)

  • 私有(参数名称参考节点名称,是节点名称的子级)


设置参数的方式也有三种:


  • rosrun命令

  • launch文件

  • 编码实现


三种设置方式前面都已经有所涉及,但是之前没有涉及命名问题,本节将对三者命名的设置逐一演示。


案例


启动节点时,为参数服务器添加参数(需要注意参数名称设置)。


rosrun在启动节点时,也可以设置参数:


语法:rosrun包名 节点名称 _参数名:=参数值。


1.设置参数


启动乌龟显示节点,并设置参数A =100。


rosrun turtlesim turtlesim_node _A:=100

<左右滑动以查看完整代码>


2.运行


rosparam list查看节点信息,显示结果:


1/turtlesim/A
2/turtlesim/background_b
3/turtlesim/background_g
4/turtlesim/background_r

<左右滑动以查看完整代码>


结果显示,参数A前缀节点名称,也就是说rosrun执行设置参数参数名使用的是私有模式。


通过launch文件设置参数的方式前面已经介绍过了,可以在node标签外,或node标签中通过param或rosparam来设置参数。在node标签外设置的参数是全局性质的,参考的是/ ,在node标签中设置的参数是私有性质的,参考的是/命名空间/节点名称。


1.设置参数


以param标签为例,设置参数。


1<launch>
2
3<param name="p1" value="100" />
4<node pkg="turtlesim" type="turtlesim_node" name="t1">
5    <param name="p2" value="100" />
6node>

<左右滑动以查看完整代码>


2.运行


rosparam list查看节点信息,显示结果:


1/p1
2/t1/p1

<左右滑动以查看完整代码>


运行结果与预期一致。


编码的方式可以更方便的设置:全局、相对与私有参数。


1.C++实现


在C++中,可以使用ros::param或者ros::NodeHandle来设置参数。


1.ros::param设置参数


设置参数调用API是ros::param::set,该函数中,参数1传入参数名称,参数2是传入参数值,参数1中参数名称设置时,如果以/开头,那么就是全局参数,如果以~开头,那么就是私有参数,既不以/也不以~开头,那么就是相对参数。代码示例:


1ros::param::set("/set_A",100); //全局,和命名空间以及节点名称无关
2ros::param::set("set_B",100); //相对,参考命名空间
3ros::param::set("~set_C",100); //私有,参考命名空间与节点名称

<左右滑动以查看完整代码>


运行时,假设设置的namespace为xxx,节点名称为yyy,使用rosparam list查看:


1/set_A
2/xxx/set_B
3/xxx/yyy/set_C

<左右滑动以查看完整代码>


2.ros::NodeHandle设置参数


设置参数时,首先需要创建NodeHandle对象,然后调用该对象的setParam函数,该函数参数1为参数名,参数2为要设置的参数值,如果参数名以/开头,那么就是全局参数,如果参数名不以/开头,那么,该参数是相对参数还是私有参数与NodeHandle对象有关,如果NodeHandle 对象创建时如果是调用的默认的无参构造,那么该参数是相对参数,如果NodeHandle对象创建时是使用:


1ros::NodeHandle nh;
2nh.setParam("/nh_A",100); //全局,和命名空间以及节点名称无关
3
4nh.setParam("nh_B",100); //相对,参考命名空间
5
6ros::NodeHandle nh_private("~");
7nh_private.setParam("nh_C",100);//私有,参考命名空间与节点名称

<左右滑动以查看完整代码>


运行时,假设设置的namespace为xxx,节点名称为yyy,使用rosparam list查看:


1/nh_A
2/xxx/nh_B
3/xxx/yyy/nh_C

<左右滑动以查看完整代码>


2.python实现


python中关于参数设置的语法实现比C++简洁一些,调用的API时rospy.set_param,该函数中,参数1传入参数名称,参数2是传入参数值,参数1中参数名称设置时,如果以/开头,那么就是全局参数,如果以~开头,那么就是私有参数,既不以/也不以~开头,那么就是相对参数。代码示例:


1rospy.set_param("/py_A",100)  #全局,和命名空间以及节点名称无关
2rospy.set_param("py_B",100)  #相对,参考命名空间
3rospy.set_param("~py_C",100)  #私有,参考命名空间与节点名称

<左右滑动以查看完整代码>


运行时,假设设置的namespace为xxx,节点名称为yyy,使用rosparam list查看:


1/py_A
2/xxx/py_B
3/xxx/yyy/py_C

<左右滑动以查看完整代码>





ROS是一个分布式计算环境。一个运行中的ROS系统可以包含分布在多台计算机上多个节点。根据系统的配置方式,任何节点可能随时需要与任何其他节点进行通信。


因此,ROS对网络配置有某些要求:


  • 所有端口上的所有机器之间必须有完整的双向连接。

  • 每台计算机必须通过所有其他计算机都可以解析的名称来公告自己。


① 实现


1.准备


先要保证不同计算机处于同一网络中,并分别设置固定IP,如果为虚拟机,需要将网络适配器改为桥接模式。


2.配置文件修改


分别修改不同计算机的/etc/hosts文件,在该文件中加入对方的IP地址和计算机名:


从机的IP 从机计算机名

<左右滑动以查看完整代码>

主机端:


主机的IP 主机计算机名

<左右滑动以查看完整代码>


设置完毕,可以通过ping命令测试网络通信是否正常。


IP地址查看名: ifconfig

计算机名称查看: hostname


3.配置主机IP


配置主机的IP地址。


~/.bashrc追加。


1export ROS_MASTER_URI=http://主机IP:11311
2export ROS_HOSTNAME=主机IP

<左右滑动以查看完整代码>


4.配置从机IP


配置从机的IP地址,从机可以有多台,每台都做如下设置:


~/.bashrc追加。


1export ROS_MASTER_URI=http://主机IP:11311
2export ROS_HOSTNAME=从机IP

<左右滑动以查看完整代码>


② 测试


1.主机启动roscore(必须的)。


2.主机启动订阅节点,从机启动发布节点,测试通信是否正常。


3.反向调试,主机启动发布节点,从机启动订阅节点,测试通信是否正常。




本章主要介绍了ROS的运行管理机制,内容如下:


  • 如何通过元功能包关联工作空间下的不同功能包。

  • 使用launch文件来管理维护ROS中的节点。

  • 在ROS中重名是经常出现的,重名时会导致什么情况?以及怎么避免重名?

  • 如何实现ROS分布式通信?


本章的重点是"重名"相关的内容:


  • 包名重复,会导致覆盖。

  • 节点名称重复,会导致先启动的节点关闭。

  • 话题名称重复,无语法异常,但是可能导致通信实现出现逻辑问题。

  • 参数名称重复,会导致参数设置的覆盖。


解决重名问题的实现方案有两种:


  • 重映射(重新起名字)。

  • 为命名添加前缀。


本章介绍的内容还是偏向语法层面的实现,下一章将开始介绍ROS中内置的一些较为实用的组件。


以上是"ROS运行管理"的全部内容,更多话题讨论、技术交流可以扫描下方二维码添加『Apollo小哥哥』为好友,进开发者交流群。






* 以上内容为开发者原创,不代表百度官方言论,已获开发者授权。




您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存