ros由于其分布式模块化的设计理念,会将一个完整任务分解成多个节点去实现,这些节点之间的协作通过topic话题和message消息.相关概念有
- 节点(Nodes):节点是一个可执行文件,它可以通过ROS来与其他节点进行通信。
- 消息(Messages):订阅或发布话题时所使用的ROS数据类型。
- 话题(Topics):节点可以将消息发布到话题,或通过订阅话题来接收消息。
- 主节点(Master):ROS的命名服务,例如帮助节点发现彼此。
如何通过协作完成一个目标任务,参考教程人工匠阿杰视频深入浅出的讲解:
背景任务,有个执行器需要从超声波节点获取距离信息(此处此执行器为马达)以便进行运动状态的调整,比如底盘电机通过获取超声波测距值来完成一个障碍物规避的动作,此例子是要实现一个跨包的通讯,创建好两个独立的package分别是前文所述的ssr_pkg,以及执行器对应的马达,在ros中对应的node节点叫做ma_node,节点不能脱离软件包独立存在,在此建立一个软件包atr_pkg,如上所示.
相互的关系是
小结:
- 话题Topic是节点间进行持续通讯(消息一直在不停的发送和接收)的一种形式
- 话题通讯的两个节点通过话题的名称(如上图在群里沟通但是群是有专有的名字的)建立起话题通讯连接
- 话题中通讯的内容/数据,叫做消息Message
- 消息Message通常会安装一定的频率持续不断的发送,以保证消息数据的实时性
- 消息的发送方叫做话题的发布者Publisher,接收方叫做话题的订阅者Subsciber
以上情况是对系统中两个节点的配合情况做出总结,通常一个系统中会有多个节点相互协作,例如本例中需要通过添加IMU传感器获取机器人的旋转倾斜等姿态信息。
创建IMU节点为yao_node(这个传感器跟瑶妹一样是附着在机器人身上哈哈),这个节点同样放在ssr_pkg包里面,类比上文两个节点的情况,在同一个群里进行发布话题,作为订阅者来说同一个群里接收话题太过杂乱,所以一般不这样做,通常会将不同传感器的消息话题进行分离,例如这里分成两个独立的群聊频道
这样看来在订阅者视角可以对不同情况进行灵活处理,需要测距信息就订阅超声波超哥的话题,需要机器人姿态信息就订阅IMU瑶妹的话题,此时系统中出现两个话题。
注意上文说的是一般情况下不会在一个话题里面产生多个发布者,但是总有不一般的情况,例如下图,这种情况一般要控制好发布者的发言顺序,同一时刻只有一个发布的节点,否则机器人就会面对多个相互冲突的指令无所适从行走困难
Message:消息的作用是携带数据从发布者流动到订阅者 ,这个数据有可能是传感器的数值,也可能是速度指令,所以不同的数据类型和大小容量,所以消息的格式分为很多类型,以满足不同的数据传输要求,所以生成消息包的时候通常要指定消息的类型,上文创建package的时候,依赖项中std_msgs标准消息包,具体内容可以在index.ros.org中搜索std_msgs查看不同版本的ros对应的std_msgs的定义。
接下来探究如何具体实现
依旧从前文(ros初学者如何创建node节点ros 初学者如何创建 node 节点-CSDN博客)创建的chao_node节点中继续更改代码,打开Visual Studio 打开节点代码文件,我们需要考虑两个事情1、考虑自己的发布的Message消息的类型,在代码中补充;2、发布的话题名称
本例中,超哥的话题名称是“快上车开黑群”,消息的类型是下面的“国服马超,带飞”所对应的类型,进入index.ros.org搜索std_msgs进入noetic版本,点击website查看ros message types
在这里string(字符串类型)符合要求
回到vscord
补充:
这里的代码相较于之前的内容添加了几行代码
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise<std_msgs::String>("快上车开黑群",10);
第一句NodeHandle对象是节点和ros通讯的关键 ,相当于一位管家,ros相当于管家打理的一所豪宅,有需求是吩咐管家,管家去管理内容
第二句定义一个对象名字叫pub,是发送消息的工具,后面暂时就看做一个adverttise()函数的参数,有两个一个是群名称,一个是消息缓存的长度,这样就完成了一个消息的发布
!注意,关于缓存长度,理想情况下消息发送后会立刻被订阅者接收,但是有的时候订阅者处理消息的时间比较长,之前的消息还没处理完毕,新的发布消息已经来了,此时这个消息会在外面排队等候,后续会一个个排列,当这个排队等候的队伍容量满了以后,排在最前面的消息会被丢弃挪出空位给后面新来的消息能够进入缓存,这个缓存长度太大太小都不合适,最好是根据具体情况计算
while(ros::ok)
{
printf("我要刷屏了!\n");
std_msgs::String msg;
msg.data = "国服马超带飞";
pub.publish(msg);
}
return 0;
-
消息创建:
std_msgs::String msg;
这行代码创建了一个名为msg
的字符串类型消息对象。 -
设置消息内容:
msg.data = "国服马超带飞";
这行代码给消息对象的data
属性赋值为“国服马超带飞”,这是将要发布的实际内容。 -
发布消息:
pub.publish(msg);
这行代码将刚创建的消息发布到指定的主题,使其他订阅该主题的节点能够接收到这条消息。
保存ctrl C运行ctrl+shift+B,去终端运行
首先打开终端运行rosecore,运行完成之后重新打开终端
rosrun ssr_pkg chao_node
这里可能会出现报错
大概是说话题名称不合法,只能字母加一些符号组成,我们将代码里面中文的话题名称“快上车开黑群”改成字母组成的即可
调整之后正常运行
拓展:rostopic用法1、2、3、
1、查看当前系统中活跃的话题有哪些
rostopic list
2、查看某活跃话题的消息内容rostopic echo /话题名称
rostopic echo /kuai_shang_che_kai_hei_qun
显示如下
对此展开新终端输入
echo -e "\u56FD\u670D\u9A6C\u8D85\u5E26\u98DE"
现行,那一串代码就是中文字符的编码。
3、rostopic hz/话题名称:查看话题里面消息发送的频率
可以看出一秒发送四万多条消息
如何控制发送频率,回到vscode
ros::Rate loop_rate(10);
这句话表示一秒钟发送10次
loop_rate.sleep();
这个函数会在while循环内做一个短时间的阻塞,以便让循环的执行频率能和刚才给的参数对应上
此时回到终端运行
发送频率可以稳定在每秒10次
总结1:发布一个话题和发送消息的操作
- 确定话题名称和消息类型,注意话题名称不能使用中文字符
- 在代码文件中include消息类型对应的头文件
- 在main函数中通过NodeHandler大管家发布一个话题并得到消息发送对象
- 生成要发送的消息包并进行发送数据的赋值
- 调用消息发送对象的pubilsh()函数将消息包发送到话题中
以上操作并不一定是要在while中进行
学习视频:机器人工匠阿杰