ROS学习笔记05、ROS运行管理(元功能包、launch文件、空间覆盖与重名问题、分布式通信)

前言

马上开学,目前学校很多实验室都是人工智能这块,大部分都是和机器人相关,然后软件这块就是和cv、ros相关,就打算开始学习一下。

本章节是虚拟机安装Ubuntu18.04以及安装ROS的环境。

学习教程:【Autolabor初级教程】ROS机器人入门,博客中一些知识点是来源于赵老师的笔记在线笔记,本博客主要是做归纳总结,如有侵权请联系删除。

视频中的案例都基本敲了遍,这里给出我自己的源代码文件:

链接:https://pan.baidu.com/s/13CAzXk0vAWuBsc4oABC-_g 
提取码:0hws 

所有博客文件目录索引:博客目录索引(持续更新)


目标解决问题:

1、如何关联不同的功能包,繁多的ROS节点应该如何启动?

2、功能包、节点、话题、参数重名时应该如何处理?

3、不同主机上的节点如何通信?

一、ROS元功能包

1.1、认识元功能包

若是我们编写了多个不同功能的包,那么在提供给用户使用时,难道需要用户一个一个包去下载呢?我们试想是否能够通过一个配置将所需要的包全部直接进行下载引入,将其组织到一起。

解答:这就设计到元功能包,在ROS中,提供了一种方式可以将不同的功能包打包成一个功能包,当安装某个功能模块时,直接调用打包后的功能包即可,该包又称之为元功能包(metapackage)。。

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

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

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

1.2、自定义一个元功能包

可参考:ros-wiki-metpackages

目标:再此之前我们在ROS通信机制中就创建了关于话题通信、服务通信、参数服务器的三个功能包,那么在此我们就可以使用一个元功能包将其进行组织在一起。

实际案例场景:navigation2

  • image-20220910180224291

步骤一:创建一个元空间包。

# 进入工程目录中的src目录下
cd /home/workspace/roslearn/src

# 创建功能包名为plumbing_my
catkin_create_pkg --rosdistro melodic plumbing_my

image-20220910175044588

步骤二:编写package.xml配置,引入相应的功能包

<exec_depend>plumbing_server_client</exec_depend>
<exec_depend>plumbing_pub_sub</exec_depend>
<exec_depend>plumbing_param_server</exec_depend>

<!-- 新增一个<metapackage />标签 -->
<export>
    <metapackage />
</export>

步骤三:修改CMakeLists.txt。

cmake_minimum_required(VERSION 3.0.2)
project(plumbing_my)
find_package(catkin REQUIRED)
catkin_metapackage()
  • 添加最后一行即可。

最后我们来编译整个项目即可,若是没有报错,说明元功能包配置没有问题。

image-20220910175759922

二、ROS节点管理launch文件

2.1、快速使用launch文件来启动节点

ros-wiki_roslaunch文档

# 进入工程目录中的src目录下
cd /home/workspace/roslearn/src

# 创建功能包名为launch01_basic
catkin_create_pkg --rosdistro melodic launch01_basic roscpp rospy std_msgs

image-20220911181541153

01start_turtle.launch:

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

接着我们来编译整个项目,并进行启动该节点:

source ./devel/setup.bash

roslaunch launch01_basic 01start_turtle.launch

image-20220911181624837


2.2、launch文件标签之group节点

介绍

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

属性:

  • ns=“名称空间” (可选)

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

实战示例

image-20220912095602048

目的:启动两组相同的服务,看在group下会不会加上对应的namespace前缀。

分两组启动小乌龟以及键盘控制程序:05demo_group.launch

<launch>
    <group ns="changlu1">
        <node pkg="turtlesim" type="turtlesim_node"  name="turtle_node" output="screen" />
        <node pkg="turtlesim" type="turtle_teleop_key"  name="turtle_key" output="screen" />
    </group>
    <group ns="changlu2">
        <node pkg="turtlesim" type="turtlesim_node"  name="turtle_node" output="screen" />
        <node pkg="turtlesim" type="turtle_teleop_key"  name="turtle_key" output="screen" />
    </group>
</launch>

接着去刷新环境变量并进行加载launch文件:

source ./devel/setup.bash

roslaunch launch01_basic 05demo_group.launch

image-20220912095312737

对于service、topic都增加上了命名空间:

image-20220912095424642

launch文件标签之node节点

节点的启动时多进程执行的,并非是按照配置文件顺序向下执行。

属性

# 节点所属的包
pkg="包名"

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

# 节点名称
name="nodeName"

# 参数传递
args="xxx xxx xxx" (可选)

# 机器名
machine="机器名"

# 若是该结点退出,是否自动重启
respawn="true | false" (可选)

# 一般搭配respawn使用,若是respawn为true,那么延迟 N 秒后启动节点
respawn_delay=" N" (可选)

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

# 在指定命名空间 xxx 中启动节点【效果就是在对应发布的话题、服务前缀加上命名空间】
ns="xxx" (可选)

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

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

子级标签

env 环境变量设置
remap 重映射节点名称
rosparam 参数设置
param 参数设置

launch文件标签之remap

若是我们想要将节点中的话题进行更改,就可以在node节点下新增一个remap节点:

image-20220911193253594

<launch>
    <node pkg="turtlesim" type="turtlesim_node"  name="myTurtle" output="screen">
        <!-- 话题重命名 -->
        <remap from="/turtle1/cmd_vel" to="/cmd_vel" />
    </node>
    <node pkg="turtlesim" type="turtle_teleop_key"  name="myTurtleContro" output="screen" />
</launch>

接着我们启动launch,并查看topic列表看是否有被重命名:

source ./devel/setup.bash
 
roslaunch launch01_basic 01start_turtle.launch

image-20220911200428927


2.3、launch文件标签之include

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

实战案例:使用include标签来包含之前的启动乌龟案例。

image-20220911183036296

02turtle_include.launch:

<launch>
    <!-- include表示直接包含某个launch文件,来直接启动 -->
    <include file="$(find launch01_basic)/launch/01start_turtle.launch" />
</launch>

接着去编译执行一下:

source ./devel/setup.bash

roslaunch launch01_basic 02turtle_include.launch

image-20220911183207369


2.4、launch文件标签之param

介绍

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

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

type="str | int | double | bool | yaml" (可选):指定参数类型,如果未指定,roslaunch 会尝试确定参数类型,规则如下:如果包含 ‘.’ 的数字解析未浮点型,否则为整型"true" 和 “false” 是 bool 值(不区分大小写)其他是字符串。

实战

创建launch文件:03turtle_parm.launch

image-20220911212703308

<launch>
    <!-- param标签:向param服务器添加键值对参数 -->
    <!-- 方式一:设置在与node节点同级 -->
    <param name="param_a" type="int" value="100" />
    <node pkg="turtlesim" type="turtlesim_node"  name="changlu" output="screen">
        <!-- 方式二:在node节点下添加param(这里在node节点对应的parm参数名称前要对应命名空间) -->
        <param name="param_b" type="double" value="3.14" />
    </node>
    <node pkg="turtlesim" type="turtle_teleop_key"  name="myTurtleContro" output="screen" />
</launch>

编译项目,然后去启动节点:

source ./devel/setup.bash

roslaunch launch01_basic 03turtle_parm.launch

可以看到下方的param参数第一个是node节点下的,第二个是与node节点平级的:

rosparam list

image-20220911211922324


2.5、launch文件标签之rosparam

介绍

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

参数:

  • command=“load | dump | delete” (可选,默认 load)加载、导出或删除参数
  • file="$(find xxxxx)/xxx/yyy…"加载或导出到的 yaml 文件
  • param=“参数名称”
  • ns=“命名空间” (可选)

实战

主要介绍load、dump以及delete删除操作。

image-20220911220020516

load命令:04demo_rosparam.launch

<launch>
    <!-- 
        rosparam位置:node节点平级、node的子节点
        commond="load",表示导入
     -->
    <rosparam command="load" file="$(find launch01_basic)/launch/params.yaml" />
    <node pkg="turtlesim" type="turtlesim_node"  name="changlu" output="screen">
        <rosparam command="load" file="$(find launch01_basic)/launch/params.yaml" />
    </node>
</launch>

params.yaml:

cl_r: 155
cl_g: 155
cl_b: 155

执行命令:

source ./devel/setup.bash

roslaunch launch01_basic 04demo_rosparam.launch

image-20220911220342028

dump命令:04demo_dumpparam.launch

说明:一般dump命令都需要在其他launch文件执行完成之后再执行,若是直接写在launch中不太推荐,因为可能会漏掉一些参数。

<launch>
    <!-- dump所有param -->
    <rosparam command="dump" file="$(find launch01_basic)/launch/params_out.yaml" />
</launch>

执行命令:

source ./devel/setup.bash

roslaunch launch01_basic 04demo_dumpparam.launch

image-20220911220609361

delete功能:04demo_deleteparam.launch

<launch>
    <!-- 删除指定param -->
    <rosparam command="delete" param="cl_r" />
</launch>

执行命令:

source ./devel/setup.bash

roslaunch launch01_basic 04demo_deleteparam.launch

image-20220911220736810


实战

image-20220912101238870

编写launch文件06demo_arg.launch:根据launch命令添加指定的参数并借助param标签读取arg标签参数值保存到param服务器。

<launch>
    <!-- 
        default表示默认值,在命令行启动launch时可以不用传参也不会报错。
        name表示参数名称
     -->
    <arg name="param1" default="haha"/>
    <!-- 想要获取到arg标签中的参数需要使用表达式:$(arg xxx) -->
    <param name="param1" value="$(arg param1)" />
</launch>

实际测试:

source ./devel/setup.bash
 
# 携带参数
roslaunch launch01_basic 06demo_arg.launch param1:=12

rosparam get /param1

image-20220912101421126

roslaunch launch01_basic 06demo_arg.launch

image-20220912101456958


三、ROS工作空间覆盖

当我们进行source /home/用户/路径/工作空间A/devel/setup.bash,实际上就会将我们对应工程的src目录添加到ros的包路径,我们可以使用命令来查看:

echo $ROS_PACKAGE_PATH

image-20220912103036101

空间覆盖问题:若是我们有两个工程A和B,在两个工程中都有一个相同名称包turtle,那么最后执行source命令的其环境变量优先级更高。

创建一个新的工程并进行动态刷新配置来查看下效果:

mkdir -p roslearn2/src
cd roslearn2
catkin_make
source ./devel/setup.bash

image-20220912103410387

隐藏的一个bug问题

image-20220912104040100

若是按照上面的123顺序来进行source加载依赖,那么就会出现问题,注意此时pg2/A的优先级最高,若是在执行pg1的时候,其依赖的是pg3/A,但是在程序代码中引用一般只是A/执行文件,所以此时就会去执行pg2/A,最终造成严重问题!

解决方案:尽可能不同的工程中不要设置相同的包,以免出现问题。


四、重名问题

4.1、ROS节点名称重名(三种方式)

方式一:rosrun设置命名空间

# 语法: rosrun 包名 节点名 __ns:=新名称
rosrun turtlesim turtlesim_node __ns:=/xxx

方式二:launch文件编写时在node标签中添加ns键值对

<launch>
	<!-- ns表示命名空间 -->
    <node pkg="turtlesim" type="turtlesim_node" name="t1" ns="hello"/>
</launch>

方式三:代码编写

//代码1:name_时间戳
ros::init(argc,argv,"zhangsan",ros::init_options::AnonymousName);

//代码2:传入map的形式
std::map<std::string, std::string> map;
map["__ns"] = "xxxx";
ros::init(map,"wangqiang");

4.2、ROS话题名称设置

方式一:启动节点命令时进行话题重映射

# 示例:将话题/cmd_vel重映射为/turtle1/cmd_vel
rosrun teleop_twist_keyboard teleop_twist_keyboard.py /cmd_vel:=/turtle1/cmd_vel

方式二:launch文件中ndoe节点下编写remap节点来实现话题重映射

<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>

方式三:代码编写

//全局名称:与节点名称无关
ros::Publisher pub = nh.advertise<std_msgs::String>("/chatter",1000);  //结果:/chatter

//相对名称:与命名空间、节点名称连接
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",1000); //结果:xx/chatter

//私有名称:节点名称为hello,并且设置了命名空间xx
ros::NodeHandle nh("~");
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",1000);  //结果:/xx/hello/chatter

//使用~,而话题名称有时/开头时,那么话题名称是绝对的(不受影响)
ros::NodeHandle nh("~");
ros::Publisher pub = nh.advertise<std_msgs::String>("/chatter/money",1000); //结果依旧是:/chatter/money

4.3、ROS参数名称设置

方式一:rosrun命令设置参数

# 设置参数为A=100
rosrun turtlesim turtlesim_node _A:=100

方式二:launch文件来设置参数,node平级与node节点下

<launch>
    <param name="p1" value="100" />
    <node pkg="turtlesim" type="turtlesim_node" name="t1">
        <param name="p2" value="100" />
    </node>
</launch>

方式三:编码设置参数

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分布式通信

准备两台机子

步骤1、确定ip地址以及hostname主机名

# 获取ip地址
ifconfig

# 获取主机名
hostname

主机:192.168.3.41   changlu-VirtualBox
从机:192.168.3.18   changlu-VirtualBox2

步骤2、配置本地的hostname映射(ip与主机域名)

打开并编辑/etc/hosts文件

主机:

127.0.1.1	changlu-VirtualBox
192.168.3.41 changlu-VirtualBox2

从机:

127.0.1.1	changlu-VirtualBox2
192.168.3.18 changlu-VirtualBox

生效hosts文件命令如下:

sudo /etc/init.d/networking restart

步骤3、打开/root/.bashrc文件,添加两条参数配置信息

模板如下:

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

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

主机:

export ROS_MASTER_URI=http://192.168.3.18:11311
export ROS_HOSTNAME=192.168.3.18

# 或者
# export ROS_MASTER_URI=http://changlu-VirtualBox:11311
# export ROS_HOSTNAME=changlu-VirtualBox

从机(多个从节点同样如此配置Master_URI都填一个):

export ROS_MASTER_URI=http://192.168.3.18:11311
export ROS_HOSTNAME=192.168.3.41

# 或者
export ROS_MASTER_URI=http://changlu-VirtualBox:11311
export ROS_HOSTNAME=changlu-VirtualBox2

分别令其生效:

source /root/.bashrc

步骤四:主机从机来进行测试通信

主机:

roscore

# 弹出图像化界面
rosrun turtlesim turtlesim_node

从机:

# 读取键盘上下左右移动信息控制乌龟行动
rosrun turtlesim turtle_teleop_key

image-20220912154838385

其实也可以从机来打开图形化界面,主机来进行键盘控制,不过需要注意的是必须是master启动roscore。

参考资料

[1]. Centos Ubuntu 修改hosts文件后,(重启)生效命令

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要通过launch文件设置C++写的ROS功能的接口地址,您可以使用ROS的参数服务器来传递接口地址的值。具体步骤如下: 1. 在C++代码中添加ROS参数服务器的支持。您可以使用`ros::NodeHandle`类来创建一个ROS节点句柄,并在其中调用`getParam`方法来获取参数服务器中的值,示例如下: ``` #include <ros/ros.h> int main(int argc, char** argv) { ros::init(argc, argv, "my_node"); ros::NodeHandle nh("~"); std::string interface; nh.getParam("interface", interface); // TODO: 处理接口逻辑 return 0; } ``` 2. 在launch文件中设置参数服务器的值。您可以使用`<param>`标签来设置参数服务器的值,其中`name`属性指定参数名,`value`属性指定参数值,示例如下: ``` <launch> <node pkg="my_package" type="my_node" name="my_node" output="screen"> <param name="interface" value="/api/v1/files"/> </node> </launch> ``` 在上面的例子中,launch文件中设置了参数`interface`的值为`/api/v1/files`。当ROS节点启动时,会自动从参数服务器中获取该值,并传递给C++代码中的接口。 3. 在C++代码中使用参数服务器的值。当ROS节点启动时,C++代码会自动获取参数服务器中的值,并将其传递给接口函数。您可以在接口函数中使用该值来设置接口地址的路径,示例如下: ``` #include <ros/ros.h> int main(int argc, char** argv) { ros::init(argc, argv, "my_node"); ros::NodeHandle nh("~"); std::string interface; nh.getParam("interface", interface); std::string url = "http://localhost" + interface; // TODO: 处理接口逻辑 return 0; } ``` 在上面的代码中,`interface`变量保存了参数服务器中的值`/api/v1/files`,您可以使用该值来构建完整的接口地址,例如`http://localhost/api/v1/files`。注意,根据不同的接口实现方式,您可能还需要在接口函数中检验参数的合法性,避免输入错误和恶意攻击等问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长路 ㅤ   

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值