目录
参考于2.1.2 话题通信基本操作A(C++) · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程
2.1话题通信
2.1.1 分析
话题通信的角色:
1.master ---->管理者
2.Talker ----->发布者
3.listener----->订阅者
话题通信的关注点:(由于连接的建立已经封装,所以关注点有以下4点)
1.话题的设置
2.发布者的实现
3.订阅者的实现
4.关注消息载体
2.1.2 发布者,订阅者实现(C++实现)
mkdir -p demo04_ws/src //建立目录
catkin_make //编译
code . //利用vscode打开
选择Create catkin Package创立包
并添加依赖 roscpp,rospy,std_msgs
vscode中编译ros
快捷键 ctrl + shift + B 调用编译,选择:catkin_make:build
可以点击配置设置为默认,修改.vscode/tasks.json 文件
{
// 有关 tasks.json 格式的文档,请参见
// https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"label": "catkin_make:debug", //代表提示的描述性信息
"type": "shell", //可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
"command": "catkin_make",//这个是我们需要运行的命令
"args": [],//如果需要在命令后面加一些后缀,可以写在这里,比如-DCATKIN_WHITELIST_PACKAGES=“pac1;pac2”
"group": {"kind":"build","isDefault":true},
"presentation": {
"reveal": "always"//可选always或者silence,代表是否输出信息
},
"problemMatcher": "$msCompile"
}
]
}
pub_c.cpp 发布者实现
/*
实现流程:
1.包含头文件
2.初始化 ROS 节点:命名(唯一)
3.实例化 ROS 句柄
4.实例化 发布者 对象
5.组织被发布的数据,并编写逻辑发布数据
*/
#include "ros/ros.h"
#include"std_msgs/String.h"
#include<sstream>
int main(int argc,char *argv[])
{
//设置编码
setlocale(LC_ALL,"");
//2.初始化ROS节点:命名唯一
ros::init(argc,argv,"talker");
//3.实例化ROS句柄
ros::NodeHandle nh;
//4.实例化发布者对象
ros::Publisher pub =nh.advertise<std_msgs::String>("chatter",10);
//5.组织被发布的数据,并编写逻辑发布数据
std_msgs::String msg;
std::string msg_front="Hello,你好";
int count=0;//消息计数器
//发布频率,1秒十次
ros::Rate r(1);
//若节点不死
while(ros::ok)
{
//使用stringsteam拼接字符串与编号
std::stringstream ss;
ss<<msg_front<<count;
msg.data =ss.str();
//发布消息
pub.publish(msg);
//加入调试,打印发送消息
ROS_INFO("发送的消息:%s",msg.data.c_str());
//休眠时间=1/频率
r.sleep();
count++;//循环结束前,让count递增
//暂无应用
ros::spinOnce();
}
return 0;
}
编写好发布方文件后,可以通过 rostopic echo 节点名称
来看看是否发布成功
sub_c.cpp 订阅者实现
/*
实现流程:
1.包含头文件
2.初始化 ROS 节点:命名(唯一)
3.实例化 ROS 句柄
4.实例化 发布者 对象
5.组织被发布的数据,并编写逻辑发布数据
*/
#include "ros/ros.h"
#include "std_msgs/String.h"
//回调函数
void doMsg(const std_msgs::String::ConstPtr msg_p)
{
ROS_INFO("我听见:%s",msg_p->data.c_str());
}
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
//2.初始化ROS节点
ros::init(argc,argv,"Listener");
//实例化ROS句柄
ros::NodeHandle nh;
//4.实例化订阅者者对象
ros::Subscriber sub =nh.subscribe<std_msgs::String>("chatter",10,doMsg);
//6.设置循环调用回调函数
ros::spin();//调用回调函数,循环读取接受的数据
return 0;
}
修改cMakeLists.txt
其中 pub_c是pub_c.cpp文件的一个映射
2.1.3编译并实现
快捷键 ctrl + shift + B 调用编译成功后,打开终端
roscore
打开所在目录 cd demo04_ws
source ./devel/setup.bash
rosrun 包名 节点
2.2 通信过程中的函数解析(c++)
n.advertise<>()函数
例句:ros::Publisher pub = n.advertise<std_msgs::String>("chatter", 1000);
n–句柄名称,advertise<topic_message_type>()函数接受两个参数,第一个参数为要发布的topic名称,第二个参数为设置缓冲区的大小。<>内部为topic发布的数据类型。
advertise()函数将会告知topic master要发布的话题名称。调用advertise()函数后,master node将master node将通知订阅该话题的subscriber,然后进行数据传输。
advertise()返回一个Publisher的对象,调用该对象可以再此topic上发布消息。
ros::Rate loop_rate(int)函数
ros::Rate类可定制频率的函数,接受一个int型参数,通过调用主函数末尾的上来sleep()函数,将该参数置为发布频率。
pub.publish(msg)函数
publisher类的成员函数,接受一个消息对象的引用,利用此函数可以将消息对象msg里面的内容发布出去,消息对象msg的类型要和advertise<topic_message_type>()函数中标定的消息类型相一致。
ros::Rate sleep()函数
与loop_rate()函数成套使用,使程序能按照设定的频率发布数据。
n.subscriber()函数
例句:ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
设置subscriber函数,该函数为一个topic生成subscriber,函数接受三个参数,第一个参数设置为订阅话题的名称,第二个参数为接受缓冲区的大小,第三个参数为接收到函数后需操作的调用函数。
"beginner_tutorials/AddTwoInts.h"头文件
该头文件在设置srv文件时由ros自动生成,头文件原型为<package_name>/<file_name>.h,头文件内定义了该service接收的每个参数的类型以及服务返回值的类型。
n.advertiseService()函数
例句:ros::ServiceServer service = n.advertiseService("add_two_ints", add);
设置service函数,该函数将会在ros系统中生成一个service,函数接受两个参数,第一个参数将被设置为service的名称,第二个参数作为该service功能执行函数,供相应的client节点调用。
n.serviceClient()函数
例句:ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
设置client函数,该函数将在ros中生成一个client,该函数接受一个服务名作为参数,向ros指明client要调用的服务。
ros::ok()函数
该函数会设置一个SIGINT监听,函数返回True,仅当以下四种情况发生时,该函数返回False:
1. SIGINT被触发(Ctrl+c)
2.被另一同名节点踢出
3.函数ros::shutdown()被程序另一部分调用
4.程序所有句柄被销毁
ros::spin(),ros::spinOnce()函数
ros回调程序,spin()不会执行原程序,spinOnce()会继续执行原程序
ROS_INFO()函数
例句: ROS_INFO("发送的消息:%s",msg.data.c_str());打印输出
解释:句柄
系统为每个进程在内存中分配一定的区域,用来存放各个句柄,即一个个32位无符号整型值(32位操作系统中)。每个32位无符号整型值相当于一个指针,指向内存中的另一个区域(我们不妨称之为区域A)。而区域A中存放的正是对象在内存中的地址。当对象在内存中的位置发生变化时,区域A的值被更新,变为当前时刻对象在内存中的地址,而在这个过程中,区域A的位置以及对应句柄的值是不发生变化的。这种机制,用一种形象的说法可以表述为:有一个固定的地址(句柄),指向一个固定的位置(区域A),而区域A中的值可以动态地变化,它时刻记录着当前时刻对象在内存中的地址。这样,无论对象的位置在内存中如何变化,只要我们掌握了句柄的值,就可以找到区域A,进而找到该对象。而句柄的值在程序本次运行期间是绝对不变的,我们(即系统)当然可以掌握它。
2.3服务通信
2.3.1 原理
2.3.2服务通信自定义srv
在建立并且编译项目后,建立srv 文件夹,接着建立.srv文件
接着对package.xml文件进行修改,添加
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
接着修改CMakeList.txt文件
添加中间文件include的路径到.json文件中
编译文件,Cmake需要修改的地方