(九)ROS运行管理

前言

1.ROS是多进程(节点)的分布式框架,一个完整的ROS系统实现:可能包含多台主机;每台主机上又有多个工作空间(workspace);每个的工作空间中又包含多个功能包(package);每个功能包又包含多个节点(Node),不同的节点都有自己的节点名称;每个节点可能还会设置一个或多个话题(topic)...

2.在多级层深的ROS系统中,其实现与维护可能会出现一些问题,比如,如何关联不同的功能包,繁多的ROS节点应该如何启动?功能包、节点、话题、参数重名时应该如何处理?不同主机上的节点如何通信?

本章主要内容介绍在ROS中上述问题的解决策略。

掌握元功能包使用语法;

掌握launch文件的使用语法;

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

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

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

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

能够实现ROS分布式通信;

一、ROS元功能包

1.应用场景

A:完成ROS中一个系统性的功能,可能涉及到多个功能包,比如实现了机器人导航模块,该模块下有地图、定位、路径规划...等不同的子级功能包。那么调用者安装该模块时,需要逐一的安装每一个功能包吗?

Q:显而易见的,逐一安装功能包的效率低下,在ROS中,提供了一种方式可以将不同的功能包打包成一个功能包,当安装某个功能模块时,直接调用打包后的功能包即可,该包又称之为元功能包(metapackage)。

2.元功能包的概念

MetaPackage是Linux的一个文件管理系统的概念。是ROS中的一个虚包,里面没有实质性的内容,但是它依赖了其他的软件包,通过这种方法可以把其他包组合起来,我们可以认为它是一本书的目录索引,告诉我们这个包集合中有哪些子包,并且该去哪里下载。

例如:sudo apt install ros-noetic-desktop-full 命令安装ros时就使用了元功能包,该元功能包依赖于ROS中的其他一些功能包,安装该包时会一并安装依赖。

还有一些常见的MetaPackage:navigation moveit! turtlebot3 ....。

3.元功能包的作用

方便用户的安装,我们只需要这一个包就可以把其他相关的软件包组织到一起安装了。

4.元功能包的实现

1.要求

将之前学过的参数服务器、服务通信、话题通信三个功能包集合到一起。

2.实现流程

该部分内容我实现在了新建的demo06_ws工作空间下。

1.新建一个功能包 plumbing_my ,依赖不写

2.修改package.xml 文件

添加该元功能包所依赖的一些功能包(添加可执行性依赖):

  plumbing_pub_sub
  plumbing_param_server
  plumbing_server_client

这三个就是要整合到一起的功能包。

添加标签:

  
  
    
    
  

 3.修改CMakeList.txt文件

保留下边这些内容且把所有其他内容全部删除,不能有换行。

cmake_minimum_required(VERSION 3.0.2)
project(plumbing_my)
find_package(catkin REQUIRED)

添加一行内容(不能有换行)

catkin_metapackage()

4.编译

二、ROS节点运行之管理launch文件

1.应用场景

一个程序中可能需要启动多个节点,比如:ROS 内置的小乌龟案例,如果要控制乌龟运动,要启动多个窗口,分别启动 roscore、乌龟界面节点、键盘控制节点。如果每次都调用 rosrun 逐一启动,显然效率低下,如何优化?采用的优化策略便是使用roslaunch 命令集合 launch 文件启动管理节点,并且在后续教程中,也多次使用到了launch文件。

2.概念

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

3.作用

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

4.实现

1.新建一个功能包:launch01_basic ,依赖于turtlesim roscpp rospy std_msgs

2.在功能包下面建立launch文件夹,文件夹下建立文件 start_turtle.launch文件

3.编辑内容


    
    

4.执行launch文件

刷新环境变量

执行

roslaunch launch01_basic start_turtle.launch

5.launch文件的标签

1.launch

1.属性

deprecated = "弃用声明"。告知用户当前 launch 文件已经弃用。

2.子级标签

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

3.运行实例

    
    

4.输出结果

WARNING: [/home/lvl/demo06_ws/src/launch01_basic/launch/start_turtle.launch] DEPRECATED: 此文件已经过时,不建议使用

输出了提示信息但是小乌龟程序可以继续使用。

2.node

1.作用

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

2.属性

节点类型:

1.pkg="包名"节点所属的包

2.type="nodeType"节点类型(与之相同名称的可执行文件)

3.name="nodeName"节点名称(在 ROS 网络拓扑中节点的名称)

4.args="xxx xxx xxx" (可选)将参数传递给节点

PS:[复习]传参可以采用命令行的方式

rosrun+包名+节点名称+参数1+参数2+....

machine="机器名"在指定机器上启动节点

respawn="true | false" (可选)如果节点退出,是否自动重启

respawn_delay=" N" (可选)如果 respawn 为 true, 那么延迟 N 秒后启动节点

required="true | false" (可选)该节点是否必须,如果为 true,那么如果该节点退出,将杀死整个 roslaunch

ns="xxx" (可选)在指定命名空间 xxx 中启动节点

clear_params="true | false" (可选)在启动前,删除节点的私有空间的所有参数

output="log | screen" (可选)日志发送目标,可以设置为 log 日志文件,或 screen 屏幕,默认是 log

3.respawn节点演示

    
    

执行launch文件显示小乌龟的GUI,当关闭时程序就结束了。

现在增加respawn参数:


    
    

此时也会出现小乌龟的GUI,当关闭时,小乌龟的GUI会再次重启。

说明:respawn_delay 等待N秒之后启动节点,需要respawn为true才能完成.

设置延后5秒进行重启:


     
    

3.include

1.应用场景

一个launch文件中,要启动乌龟GUI节点,还要启动乌龟控制节点;如果需要另外创建一个launch文件,在这个文件中也需要启动这两个节点(也有其他的节点实现),那应该怎么做? 新建一个launch文件,把代码复制一份? 不是最优解?代码复用可以吗?

2.简介

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

3.属性和子级标签
1.属性

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

要包含的文件路径

2.ns="xxx" (可选)

在指定命名空间导入文件

2.子级标签

1.env 环境变量设置

2.arg 将参数传递给被包含的文件

4.运行实例

Ⅰ.准备工作:已经有一个 startturtle.launch 文件


    
    

Ⅱ.需求:重复启动(复用)乌龟相关节点


    

Ⅲ.include格式:$(find + 功能包名称)/launch/launch文件名称

Ⅳ.运行查看结果

4.remap话题重命名

1.属性

from="xxx"原始话题名称

to="yyy"目标话题名称

无子级标签

2.运行实例

Ⅰ.代码

 这中有两个话题:turtlesim_node、turtle_teleop_key


    
    

乌龟内置的键盘控制包不太好用,以后要打算控制自己的机器人则采用其他的包进行控制。

通过下面的包来对小乌龟进行控制:

rosrun teleop_twist_keyboard teleop_twist_keyboard.py

如果提示找不到这个包请输入如下的命令进行安装:

sudo apt-get install ros-noetic-teleop-twist-keyboard

首先先运行小乌龟的launch,之后在运行新的控制的命令得到如下的结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ rosrun teleop_twist_keyboard teleop_twist_keyboard.py
the rosdep view is empty: call 'sudo rosdep init' and 'rosdep update'
Waiting for subscriber to connect to /cmd_vel
Waiting for subscriber to connect to /cmd_vel
Waiting for subscriber to connect to /cmd_vel
Waiting for subscriber to connect to /cmd_vel

为什么呢?提示信息,正在等待订阅链接到 /cmd_vel 中,解释一下,乌龟GUI会订阅一个速度消息,键盘控制会发布一个速度消息,但是两个之间的话题是不一样的。

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ rostopic list
/cmd_vel
/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose

 /cmd_vel 是键盘控制节点的话题,/turtle1/cmd_vel 是乌龟运动订阅的速度信息,显然两个话题不一样,如果想让这俩节点通信(乌龟GUI和teleop_twist_keyboard),必须两者话题相同。

更改话题名称


    
        
    
    

 

运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ rosrun teleop_twist_keyboard teleop_twist_keyboard.py
the rosdep view is empty: call 'sudo rosdep init' and 'rosdep update'

Reading from the keyboard  and Publishing to Twist!
---------------------------
Moving around:
   u    i    o
   j    k    l
   m    ,    .

For Holonomic mode (strafing), hold down the shift key:
---------------------------
   U    I    O
   J    K    L
   M    <    >

t : up (+z)
b : down (-z)

anything else : stop

q/z : increase/decrease max speeds by 10%
w/x : increase/decrease only linear speed by 10%
e/c : increase/decrease only angular speed by 10%

CTRL-C to quit

5.param

1.作用

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

2.属性

1.name="命名空间/参数名"参数名称,可以包含命名空间

2.value="xxx" (可选)定义参数值,如果此处省略,必须指定外部文件作为参数源

3.type="str | int | double | bool | yaml" (可选)指定参数类型,如果未指定,roslaunch 会尝试确定参数类型,规则如下:

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

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

其他是字符串

3.运行实例

Ⅰ.向参数服务器设置参数

Ⅱ.格式1:launch下,node外


    
    
        
    
    

Ⅲ.格式2:node下


    
    
        
        
    
    

格式2的运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ rosparam list
/my_turtle/background_b
/my_turtle/background_g
/my_turtle/background_r
/my_turtle/param_B
/param_A
/rosdistro
/roslaunch/uris/host_lvl_legion_y7000p_irx9__33527
/roslaunch/uris/host_lvl_legion_y7000p_irx9__35939
/roslaunch/uris/host_lvl_legion_y7000p_irx9__42293
/roslaunch/uris/host_lvl_legion_y7000p_irx9__46135
/rosversion
/run_id

我们发现 param_A 是独立参数,param_B 是my_turtle下的参数(私有命名空间)。

6.rosparam

1.作用

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

2.属性

1.command="load | dump | delete" (可选,默认 load)加载、导出或删除参数

2.file="$(find xxxxx)/xxx/yyy...."加载或导出到的 yaml 文件

3.param="参数名称"

4.ns="命名空间" (可选)

3.运行实例

1.首先在launch文件夹下先创建一个params.yaml文件。

bg_R: 100
bg_G: 50
bg_B: 78

2.创建一个从yaml文件中导出参数的实例:


    
    
    
        
        
        
 
    
    

运行结果: 和param相似

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ rosparam list
/bg_B
/bg_G
/bg_R
/my_turtle/background_b
/my_turtle/background_g
/my_turtle/background_r
/my_turtle/bg_B
/my_turtle/bg_G
/my_turtle/bg_R
/my_turtle/param_B
/param_A
/rosdistro
/roslaunch/uris/host_lvl_legion_y7000p_irx9__33527
/roslaunch/uris/host_lvl_legion_y7000p_irx9__35939
/roslaunch/uris/host_lvl_legion_y7000p_irx9__40999
/roslaunch/uris/host_lvl_legion_y7000p_irx9__42293
/roslaunch/uris/host_lvl_legion_y7000p_irx9__46135
/rosversion
/run_id

3.创建一个将参数导入到yaml文件中的实例:


    
    
    
        
        
        
        
    
    

 
 

运行结果:

它自动创建了一个yaml文件把参数导入了进去。

background_b: 255
background_g: 86
background_r: 69
bg_B: 78
bg_G: 50
bg_R: 100
param_B: 3.14

但是参数服务器参数有很多,为什么只导出来几个呢?

为什么?rosparam标签会率先执行,无论是在node里面还是node外面, 因为node里面执行了turtle相关,但是rosparam率先执行,因此没有相关信息,不符合预期。

如果想导出所有参数呢?新建一个launch文件(dump.launch)

先启动start_turtle.launch,然后再启动dump.launch

运行结果:

导出的数据如下:

bg_B: 78
bg_G: 50
bg_R: 100
my_turtle:
  background_b: 255
  background_g: 86
  background_r: 69
  bg_B: 78
  bg_G: 50
  bg_R: 100
  param_B: 3.14
param_A: 100
rosdistro: 'noetic

  '
roslaunch:
  uris:
    host_lvl_legion_y7000p_irx9__33527: http://lvl-Legion-Y7000P-IRX9:33527/
    host_lvl_legion_y7000p_irx9__33691: http://lvl-Legion-Y7000P-IRX9:33691/
    host_lvl_legion_y7000p_irx9__35939: http://lvl-Legion-Y7000P-IRX9:35939/
    host_lvl_legion_y7000p_irx9__40999: http://lvl-Legion-Y7000P-IRX9:40999/
    host_lvl_legion_y7000p_irx9__41957: http://lvl-Legion-Y7000P-IRX9:41957/
    host_lvl_legion_y7000p_irx9__42293: http://lvl-Legion-Y7000P-IRX9:42293/
    host_lvl_legion_y7000p_irx9__43963: http://lvl-Legion-Y7000P-IRX9:43963/
    host_lvl_legion_y7000p_irx9__46135: http://lvl-Legion-Y7000P-IRX9:46135/
rosversion: '1.17.0

  '
run_id: e8126614-d190-11ef-b4cc-4f7744f2eb99

4.删除参数新建一个delete.launch文件


    

运行结果:只删除了外部变量,私有变量没有删除

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ rosparam list
/bg_G
/bg_R
/my_turtle/background_b
/my_turtle/background_g
/my_turtle/background_r
/my_turtle/bg_B
/my_turtle/bg_G
/my_turtle/bg_R
/my_turtle/param_B
/param_A
/rosdistro
/roslaunch/uris/host_lvl_legion_y7000p_irx9__33527
/roslaunch/uris/host_lvl_legion_y7000p_irx9__33691
/roslaunch/uris/host_lvl_legion_y7000p_irx9__35939
/roslaunch/uris/host_lvl_legion_y7000p_irx9__38959
/roslaunch/uris/host_lvl_legion_y7000p_irx9__40999
/roslaunch/uris/host_lvl_legion_y7000p_irx9__41957
/roslaunch/uris/host_lvl_legion_y7000p_irx9__42293
/roslaunch/uris/host_lvl_legion_y7000p_irx9__43963
/roslaunch/uris/host_lvl_legion_y7000p_irx9__46135
/roslaunch/uris/host_lvl_legion_y7000p_irx9__46501
/rosversion
/run_id

7.group

1.作用

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

2.属性

1.ns="名称空间" (可选)

2.clear_params="true | false" (可选)启动前,是否删除组名称空间的所有参数(慎用....此功能危险)

3.运行实例

如果出现下列错误:

RLException: Invalid roslaunch XML syntax: not well-formed (invalid token): line 7, column 97
The traceback for the exception was written to the log file

请检查你的launch文件的格式,它已经给你指定出了位置。

运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ rosnode list
/first/my_key
/first/my_turtle
/rosout
/second/my_key
/second/my_turtle

8.arg

1.作用

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

2.属性

1.name="参数名称"

2.default="默认值" (可选)

3.value="数值" (可选)不可以与 default 并存

4.doc="描述"参数说明

3.运行实例

演示arg的使用,需要设置多个参数,这些参数是使用的是同一个值,怎么设置?

1.不友好形式:

如果改了数据这样会导致所有的数据都会发生修改。


 
    
    
    
 

2.友好形式:


    
    
    
    

可以通过命令行动态传参。

运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ roslaunch launch01_basic arg.launch car_length:=0.66
... logging to /home/lvl/.ros/log/e8126614-d190-11ef-b4cc-4f7744f2eb99/roslaunch-lvl-Legion-Y7000P-IRX9-30437.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://lvl-Legion-Y7000P-IRX9:38321/

SUMMARY
========

PARAMETERS
 * /A: 0.66
 * /B: 0.66
 * /C: 0.66
 * /rosdistro: noetic
 * /rosversion: 1.17.0

NODES

ROS_MASTER_URI=http://localhost:11311

No processes to monitor
shutting down processing monitor...
... shutting down processing monitor complete

三、ROS的工作空间覆盖

之前我们只是简单的介绍了一下这部分,现在我们来详细的了解一下。

1.什么是空间覆盖?

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

2.实现

0.在工作空间demo01,demo02中,两个工作空间中都创建功能包: turtlesim。

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

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

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

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

结果:自定义工作空间02:自定义空间01:系统内置空间

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

结论:后刷新的优先级高

3.原因

ROS 会解析 .bashrc 文件,并生成 ROS_PACKAGE_PATH ROS包路径,该变量中按照 .bashrc 中配置设置工作空间优先级,在设置时需要遵循一定的原则:ROS_PACKAGE_PATH 中的值,和 .bashrc 的配置顺序相反--->后配置的优先级更高,如果更改自定义空间A与自定义空间B的source顺序,那么调用时,将进入工作空间A。

4.结论

功能包重名时,会按照 ROS_PACKAGE_PATH 查找,配置在前的会优先执行。

5.隐患

存在安全隐患,比如当前工作空间B优先级更高,意味着当程序调用 turtlesim 时,不会调用工作空间A也不会调用系统内置的 turtlesim,如果工作空间A在实现时有其他功能包依赖于自身的 turtlesim,而按照ROS工作空间覆盖的涉及原则,那么实际执行时将会调用工作空间B的turtlesim,从而导致执行异常,出现安全隐患。

四、ROS节点名称重名

1.现实中的场景

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

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

2.解决方法

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

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

3.三种解决办法

1.rosrun设置命名空间与重映射
1.rosrun设置命名空间

要求:启动两个turtlesim_node

语法:

rosrun + 功能包名字 + 节点名称 + space + __ns:=命名空间

代码:

​rosrun turtlesim turtlesim_node __ns:=a
​rosrun turtlesim turtlesim_node __ns:=b

具体实现:

同时运行两个小乌龟的GUI

运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~$ rosnode list
/a/turtlesim
/b/turtlesim
/rosout
/turtlesim
2.rosrun实现重映射

要求:启动两个turtlesim_node

语法:

rosrun + 功能包名字 + 节点名称 + space + __name:=重命名

代码:

rosrun turtlesim turtlesim_node __name:=a
rosrun turtlesim turtlesim_node __name:=b

具体实现:

同时运行两个小乌龟的GUI

运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~$ rosnode list
/a
/b
/rosout
/turtlesim
2.launch文件设置命名空间与重映射
1.名称重映射

2.命名空间 

3.命名空间 + 名称重映射 

运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo06_ws$ ​rosnode list
/a/turtlesim
/b/t2
/rosout
/t1
3.编码设置命名空间与重映射 
1.命名空间
ros::init(argc,argv,"zhangsan",ros::init_options::AnonymousName);
2.重映射

这种形式不常用。

  std::map map;
  map["__ns"] = "xxxx";
  ros::init(map,"wangqiang");

五、ROS话题名称设置

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

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

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

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

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

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

1.三种实现话题名称设置

1.rosrun实现话题重映射

1.语法:

rosrun + 功能包名 + 文件名 + 话题名:=重命名话题名

2.具体代码:

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

2.launch文件实现话题重映射

前边已经提及这里不再过多赘述:


 
    
        
    
 
    
 

3.以编码方式设置话题名称

1.话题类型

节点名称构成及话题类型类别

以节点名 /ergouzi/wangqiang 为例,包含三部分内容,开始的斜杠(/)是根目录,接下来是命名空间(ergouzi),在接下来是节点的具体名字(wangqiang)。

全局:/liaotian

相对:/ergouzi/liaotian

私有:/ergouzi/wangqiang/liaotian

2.实现逻辑

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

2.设置不同类型的话题

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

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

3.设置三种话题
1.设置全局话题
# include"ros/ros.h"
#include "std_msgs/String.h"
 
 
int main(int argc, char  *argv[])
{
    ros::init(argc,argv,"hello");
    ros::NodeHandle nh;
 
    //核心操作:设置不同类型的话题
 
    //1.全局
    ros::Publisher pub = nh.advertise("/chatter",1000);
    while(ros::ok())
    {
 
    }
    return 0;
}

执行:

之间的过渡设置就不一一展示了。

rosrun huati a __ns:=aaa

结果展示:

lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rosnode list
/hello
/rosout
lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rosnode list
/aaa/hello
/rosout
2.设置相对话题
# include"ros/ros.h"
#include "std_msgs/String.h"
 
 
int main(int argc, char  *argv[])
{
    ros::init(argc,argv,"hello");
    ros::NodeHandle nh;
    //2.相对话题设置
    ros::Publisher pub = nh.advertise("chatter",1000);
    while(ros::ok())
    {
 
    }
    return 0;
}

执行:

rosrun huati b __ns:=bbb

结果展示:

lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rostopic list
/bbb/chatter
/chatter
/rosout
/rosout_agg
lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rosnode list
/aaa/hello
/bbb/hello
/rosout
3.设置私有话题
# include"ros/ros.h"
#include "std_msgs/String.h"
 
 
int main(int argc, char  *argv[])
{
    ros::init(argc,argv,"hello");
    ros::NodeHandle nh2("~");
    ros::Publisher pub = nh2.advertise("chatter",1000);
 
    while(ros::ok())
    {
 
    }
    return 0;
}

执行:

rosrun huati c __ns:=ccc

结果展示:

lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rostopic list
/bbb/chatter
/ccc/hello/chatter
/chatter
/rosout
/rosout_agg
lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rosnode list
/aaa/hello
/bbb/hello
/ccc/hello
/rosout

4.ROS参数名称设置

在ROS中节点名称话题名称可能出现重名的情况,同理参数名称也可能重名。关于参数重名的处理,没有重映射实现,为了尽量的避免参数重名,都是使用为参数名添加前缀的方式,实现类似于话题名称,有全局、相对、和私有三种类型之分。

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

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

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

设置参数的方式也有三种:rosrun 命令、launch 文件、编码实现

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

1.通过rosrun进行ROS参数名称设置
lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rosrun turtlesim turtlesim_node _radius:=3.56

运行结果:
这种情况设置的就是私有的:

lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rosparam list 
/turtlesim/radius
2.通过launch文件进行ROS参数名称设置

    
    
            
    
 

运行结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo07_ws$ rosparam list
/radius
/xxx/t1/radius
3.通过编码进行ROS参数名称设置  

可以使用 ros::param 或者 ros::NodeHandle 来设置参数。

Ⅰ.ros::param设置参数

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

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

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

/set_A
/xxx/set_B
/xxx/yyy/set_C

Ⅱ.ros::NodeHandle设置参数

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

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

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

/nh_A
/xxx/nh_B
/xxx/yyy/nh_C

六、ROS分布式通信

1.为什么要实现分布式通信

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

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

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

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

由于这里需要多个设备,目前手里无设备故无法实现后续操作,如果后续有设备会及时补上该内容。

结束语

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

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

2.使用 launch 文件来管理维护 ROS 中的节点

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

4.如何实现 ROS 分布式通信?

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

1.包名重复,会导致覆盖。

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

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

4.参数名称重复,会导致参数设置的覆盖

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

1.重映射(重新起名字)

2.为命名添加前缀

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

以上就是我学习到的内容,如果对您有帮助请多多支持我,如果哪里有问题欢迎大家在评论区积极讨论,我看到会及时回复。

你可能感兴趣的:(ROS从入门到实践,机器人,c++,ubuntu,学习)