ROS1 C++代码配置Master

本篇博客目的是在代码中实现配置ROS_MASTER_URI和ROS_IP。

项目背景

本博客的灵感来源于一个现实的开发场景:开发者A与开发者B需要同时对同一个嵌入设设备各自进行ROS1框架的驱动开发。我们暂且搁置吐槽“为何要同时对同一设备进行开发”,来换个探讨的方式,尝试寻求解决问题的方法。

通过分析需求,可以得知:

  • 开发者A与开发者B各自开发,那么一共需要同时运行两个节点(下称A节点与B节点)。
  • 开发者A在开发的过程当中,只需要获悉A节点的数据内容,不需要B节点内容;开发者B同理。

节点图1

假设方案

众所周知的是,ROS1中存在Master节点,同一个“域”中,有且只能存在一个Master节点。通俗来讲就是,要实现节点间的通讯,就必须先启动主节点,而主节点只能存在一个。那Master应该放在哪里就成为了这个问题的关键。

假设方案一 在嵌入设设备上运行Master节点。

节点图2

通过图形,可以看出这个方案的优点显著:
1、操作简单
只需要在嵌入式设备上打开一个独立的终端运行Master节点;
然后在开发者A与开发者B的电脑上执行两条export语句即可。
2、一般的嵌入式设备IP不会频繁更改。

但是缺点也十分明显:
1、嵌入式设备性能开销大。
在开发的过程,频繁的开关程序加重了Master注册、注销节点信息的负担,降低了算力有限的嵌入式设备的系统稳定性;也因此降低多人合作开发的可能性。
2、强网络耦合性。
所有运行的节点都处于同一网络域中,加重了耦合性。且不满足需求分析第二项内容。

方案一小结

适用于小型项目开发、强算力嵌入式设备开发、强节点耦合性开发。
满足分析需求一,不满足分析需求二。

假设方案二 在开发者PC上运行Master节点。

在这里插入图片描述

针对方案一的缺陷,提出这个方案。而该方案的特点刚好与方案一完全相反。优点如下:
1、系统开销小。
不需要担忧Master节点崩溃,或者是嵌入式设备因Master超负荷崩溃的问题;
2、弱化网络耦合性。
每个节点只与各自的PC进行注册并通讯。节点之间不存在网络关联性。

缺点亦同方案一相反:
1、操作复杂繁琐。
需要在嵌入式设备的每个Node运行的终端都配置两条export语句。
2、各个Master的IP必然是不同的。

方案二小结

配置繁琐,不适合快速的项目开发。其低网络耦合度适合多人同时开发。
既满足分析需求一,也满足分析需求二。

方案二对比方案一:
方案二比方案一有性能上的提升,更具备项目开发的潜力。但增加了十分繁琐的配置手续。
因此,只要在方案二的基础上进行优化,就可以满足项目的需求。

优化方案

那么方案二的痛点在哪里呢?
自然就是需要反复地配置ROS_MASTER_URI和ROS_IP这两个环境变量了。

我们思考一下,“export+变量”这样的语句属于什么类型的操作?无论是在终端中执行,还是写入bashrc之中,是不是都属于Linux配置系统环境变量的操作?
既然是属于Linux系统功能的范畴,那就肯定是能够通过编程的方法来实现的。因此,这里优化的方向就是通过编程来植入环境变量。

此时,你是否想到了使用system函数来实现执行export语句?
如果是的话,那么很抱歉地说,这是行不通的。

不过,Linux本身就提供了更有效的API函数——setenv。
函数的解释(来源

属性属性内容
头文件

stdlib.h

函数声明

int setenv(const char *name,const char * value,int overwrite);

函数说明

setenv()用来改变或增加环境变量的内容。

参数name为环境变量名称字符串。

参数value则为变量内容。

参数overwrite用来决定是否要改变已存在的环境变量。
如果没有此环境变量则无论overwrite为何值均添加此环境变量。
若环境变量存在,当overwrite不为0时,原内容会被改为参数value所指的变量内容;当overwrite为0时,则参数value会被忽略。

返回值执行成功则返回0,有错误发生时返回-1。

代码实现

方向有了,方法有了,尝试实现一下代码(源码链接)

#include <ros/ros.h> 
#include <stdlib.h>

int main(int argc, char** argv)
{
    setenv("ROS_MASTER_URI", "http://192.168.1.104:11311", 1);
    setenv("ROS_IP", "192.168.1.157", 1);
    sleep(2);
    printf("ROS_IP\t\t- %s\n", getenv("ROS_IP"));
    printf("ROS_MASTER_URI\t- %s\n", getenv("ROS_MASTER_URI"));
    printf("\n");

    // 注册ROS节点
    ros::init(argc, argv, "client_test");
    ros::NodeHandle nh;
    ROS_INFO("Hello world!");
    while(ros::ok());
}

验证效果

代码中初始化的测试节点名为“client_test”,假如代码能够生效,那么运行测试程序的终端满足两个条件即代表测试成功:
1、终端未初始化ROS环境
2、测试程序注册的节点名能被ROS其他节点所发现

测试结果如图所示:
效果图效果图一分为二,左边是本地的PC,右边是远程的嵌入式设备。

左边三个终端从上到下分别为:
左边终端1:运行roscore(Master节点)
左边终端2:查询本地PC的IP地址
左边终端3:通过ROS1指令查询到的节点列表。

右边三个终端从上到下分别是:
右边终端1:运行的测试程序
右边终端2:查询嵌入式设备的IP地址
右边终端3:未执行export环境下查询的节点列表结果。

右边终端1满足条件1;与终端3的对比,说明通过代码确实能够配置ROS的环境变量且生效,使得注册的“client_test”节点能够被检索找到,满足条件2。说明程序的验证结果是成功的。

解释代码

ros::init函数执行的时候,会根据系统的ROS_MASTER_URI指向去寻找Master所在的IP。然后向Master报告自己(节点)的IP,即ROS_IP。

如果ROS_MASTER_URI所指向的IP内没有Master启动,那么就会出现“Failed to contact master”的报错。因此,需要在ros::init执行前,先执行setenv设置环境变量。

如上面的函数解释所说,setenv第一个参数是环境变量,第二个参数是变量值,关键点在于第三个变量,不为零使得变量生效。

如果环境变量存在,getenv能够返回环境变量的值。

在执行setenv后需要sleep(2)休眠,等待环境变量生效。否则可能查询为空,或者查询到设置的变量未生效。

小结

在ros::init节点初始化之前,使用setenv函数,使得节点的ROS_MASTER_URI与ROS_IP变量发生变化,且只对当前节点生效。从而达到代替终端设置环境变量的效果。

至此,分析需求已经满足,且减少了方案二中反复在终端窗口配置环境的过程。

知识补充

ROS_MASTER_URI和ROS_IP的作用是什么?

我们基本上知道,ROS设计的初衷在于跨平台、跨设备、且分布式,它允许应用程序以节点的方式在不同的设备上进行通讯。它是如何做到的呢?
ROS1给出了它独特的配置方法,那就是指定ROS_MASTER_URI和ROS_IP。

如果把这两条指令理解为指针的话,那么ROS_MASTER_URI就是在ROS环境下,指向master所在的IP位置;而ROS_IP则是指向自身所在的IP。

节点图4

  • PC1的ROS1节点能够与PC2的ROS1节点进行通讯

如图,有PC1,PC2,PC3三台已经搭建ROS1环境的计算机。Master构建在IP为192.168.0.2的PC2之中。
如果PC1的ROS1节点希望与PC2的ROS1节点进行通讯,就需要在节点启动前,在终端中运行
export ROS_MASTER_URI=http://192.168.0.2:11311
export ROS_IP=192.168.0.1
ROS_MASTER_URI使得指明Master为PC2的IP地址;
ROS_IP使得指明PC1的终端启动的节点使用PC1的IP地址。

  • 同理,PC3的ROS1节点也需要运行两条export指令才能达到与PC2通讯的效果。
  • 这里还有一个隐藏的内容是:此时PC1与PC3是可以实现通讯的。因为它们都已经向PC2的Master注册了自身的信息。

如果不指明ROS_IP,会是如何的结果?

上面说到ROS_IP需要指向自身所在的IP,那么,假如不执行这一步,能够实现通讯吗?
答案是可能无法实现通讯。因为不执行该环境变量,Master将无法逆向寻找到该节点。

节点图5

如图,假如此时在PC1中开启turtlesim_node乌龟节点,在PC3上启动键盘节点,那么控制键盘乌龟是可以行动起来的。
反之,如果在PC3上启动乌龟节点,在PC1上启动键盘节点,乌龟则无法运行。

这说明在ROS1中,发布者节点可以不表明自己的IP地址,而订阅者节点必须表明自己的IP地址,否则将无法成功订阅到消息。
而背后的原因就是Master无法逆向地寻找到订阅者节点的IP,从而无法构建起有效的数据链路。

是否存在不配置环境变量的可能?

如果不配置环境变量,ROS1将会自动使用系统IP作为环境变量,即Master指向自身。所以启动Master节点的PC可以不用配置环境变量。

在bashrc配置与在终端窗口配置有什么不同?

在bashrc配置环境变量会使能所有终端窗口,也相当于全局环境变量的效果。
终端窗口配置环境变量只会在当前窗口有效。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

金三亲

用现在的金钱换取未来的金钱

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

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

打赏作者

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

抵扣说明:

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

余额充值