机器人系统大多数都是多功能的集合体,而每一个功能都可以定义为一个单独的进程,每一个进程都是独立运行的。采用进程的分布式框架,可以在提高不同功能的协同性的同时分散计算压力。而不同进程之间就是靠通信机制做数据交换的。ROS中的基本通信机制有三种实现策略:
- 话题通信(发布-订阅模式)
- 服务通信(请求-响应模式)
- 参数服务器(参数共享模式)
一、话题通信
1.概念及应用场景
以激光雷达信息的采集处理为例,在ROS中需要有一个节点实时发布雷达采集到的数据,而导航模块中就需要有一个节点订阅并解析该雷达数据。话题通信主要是用于连续高频、少逻辑处理的数据传输场景,属于多对多的异步通信。
2.整体架构
话题通信模型主要是由三个角色组成,分别是管理者、发布者、订阅者。不走底层逻辑,浅层理解的话,就是①发布者和订阅者要先向管理者注册自己的信息,让发布者和订阅者能够互相找到对方;②发布者开通某个话题,并在话题上发布信息(不用在乎谁是订阅者,发就是了);③订阅者打开这个对应的这个话题,就能收到信息了(不用在乎是谁发的,收就是了)。
3.主要API(C++)
创建发布者/订阅者对象
// 发布者对象
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);
// 订阅者对象
ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
该函数创建一个名为 发布者对象,用于向 ROS 网络中的 chatter 话题(Topic) 发送 std_msgs::String 类型 的消息。
-
ros::Publisher:
ROS 中的一个类,代表 “发布者”—— 用于向 ROS 话题发送消息的对象。 -
pub:
自定义的变量名,代表这个发布者对象(可以任意命名,比如chatter_pub)。 -
nh:
一个ros::NodeHandle类型的对象(通常称为 “节点句柄”),是 ROS 节点与系统交互的接口,用于创建发布者、订阅者等。 -
nh.advertise(...):
调用节点句柄的advertise方法,用于 “宣告”(注册)一个发布者。其作用是告诉 ROS 系统:“我要向某个个话题题发送某种类型的消息”。 -
std_msgs::String:
指定发布的消息类型 —— 这里是 ROS 标准消息息库中的String类型(字符串消息)。ROS 中有很多预定义的消息类型(如整数、点坐标等),也可以自定义。 -
"chatter":
指定要发布的 话题名称(Topic Name)。ROS 中,消息通过 “话题” 进行传递,订阅者(Subscriber)需要订阅相同的话题才能收到消息(类似广播的频道名)。 -
10:
消息队列长度(Queue Size)。当发布消息的速度超过接收速度时,ROS 会缓存最多 10 条消息,超出的会被丢弃,用于防止内存溢出。
创建要发送的消息变量
std_msgs::String msg;
msg.data = "Hello!!!";
话题中要用到的消息类型是std_msgs::String,故定义该类型的变量,并填充内容。如果要发送的消息内容过于复杂,也可以封装成msg文件,定义要发送的消息中的变量类型和变量名称:
string name
uint16 age
float64 height
发布方发布消息API
pub.publish(msg);
二、服务通信
1、概念及应用场景
服务通信属于异步通信,是基于请求-响应模式的一种应答机制。一个节点A向另外一个节点B发送请求,B接收处理请求并产生响应结果返回给A。实际应用中,比如机器人前进过程中,控制节点向激光雷达节点请求告知距离,激光雷达节点收到并处理请求,将距离信息返回控制节点。服务通信更适用于对实时性有要求、具有一定逻辑处理的应用场景。
2、整体架构
服务通信主要涉及到三个角色:管理者、服务端、客户端。管理者负责保管服务端和客户端注册的信息,并匹配话题相同的服务端与客户端,帮助服务端与客户端建立联系。客户端发送请求信号,服务端返回相应信息。
3、主要API(C++)
srv文件
与话题通信中的msg文件类似,在使用服务通信的时候需要定义一个srv文件,来装载客户端发送请求的数据类型和变量名+服务端响应时发送的数据类型和变量名,请求和响应两部分之间一定要用‘---’隔开。
# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum
创建服务端对象
//创建服务端对象
ros::ServiceServer server = nh.advertiseService("AddInts",doReq);
//创建客户端对象
ros::ServiceClient client = nh.serviceClient<包名::srv文件名>("AddInts");
创建一个服务服务器,用于响应 ROS 网络中名为 AddInts 的服务请求,并指定由 doReq 函数处理具体的请求逻辑。
-
ros::ServiceServer:
ROS 中的类,代表 “服务服务器” 对象,用于接收客户端的服务请求并返回响应。 -
server:
自定义的变量名,指代这个服务服务器对象(可任意命名,如add_server)。 -
nh:
ros::NodeHandle类型的节点句柄(同之前的发布者逻辑),是与 ROS 系统交互的接口。 -
nh.advertiseService(...):
节点句柄的方法,用于 “宣告” 一个服务服务器,告诉 ROS 系统:“我提供名为AddInts的服务,由doReq函数处理请求”。 -
"AddInts":
服务的名称(类似话题话题名),客户端需要通过这个名称找到对应的服务服务器。 -
doReq:
回调函数(处理函数)的名称,当有客户端发送服务请求时,ROS 会自动调用这个函数来处理请求并生成响应。
回调函数
回调函数就是在服务端收到请求时会自动处理的函数,形参就是在当前工具包->刚才定义的srv文件名->req/resp,分别对应刚才srv文件中的请求和响应两部分。
// bool 返回值由于标志是否处理成功
bool doReq(包名::srv文件名::Request& req,
包名::srv文件名::Response& resp){
int num1 = req.num1;
int num2 = req.num2;
ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2);
//逻辑处理
if (num1 < 0 || num2 < 0)
{
ROS_ERROR("提交的数据异常:数据不可以为负数");
return false;
}
//如果没有异常,那么相加并将结果赋值给 resp
resp.sum = num1 + num2;
return true;
}
三、参数服务器
1、概念及应用场景
参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据。
2、整体架构
参数服务器主要由三个部分组成:管理者、参数设置者、参数调用者。管理者作为一个公共容器保存参数,参数设置者可以向容器中设置参数,参数调用者可以获取参数。
3、主要API(C++)
通过节点修改参数
ros::NodeHandle nh;
nh.setParam("nh_int",10); //整型
nh.setParam("nh_double",3.14); //浮点型
nh.setParam("nh_bool",true); //bool
nh.setParam("nh_string","hello NodeHandle"); //字符串
nh.setParam("nh_vector",stus); // vector
nh.setParam("nh_map",friends); // map
//修改演示(相同的键,不同的值)
nh.setParam("nh_int",10000);
全局参数
ros::param::set("param_int",20);
ros::param::set("param_double",3.14);
ros::param::set("param_string","Hello Param");
ros::param::set("param_bool",false);
ros::param::set("param_vector",stus);
ros::param::set("param_map",friends);
//修改演示(相同的键,不同的值)
ros::param::set("param_int",20000);
四.CMakeList.txt中配置函数
1.add_executable编译源代码为可执行文件
add_executable(要生成的可执行文件的名称 src/源代码文件.cpp)
2.add_dependencie指定编译依赖
add_dependencies(必须和add_executable中定义的可执行文件名称完全一致 ${PROJECT_NAME}_gencpp),第二个参数的作用是告诉CMake,编译可执行文件之前,必须先完成消息生成的任务。
3.target_link_libraries链接ROS库
可执行文件名称也还是必须和add_executable中定义的可执行文件名称完全一
//编译源代码为可执行文件
add_executable(AddInts_Server src/AddInts_Server.cpp)
add_executable(AddInts_Client src/AddInts_Client.cpp)
//指定编译依赖
add_dependencies(AddInts_Server ${PROJECT_NAME}_gencpp)
add_dependencies(AddInts_Client ${PROJECT_NAME}_gencpp)
//链接ROS库
target_link_libraries(AddInts_Server
${catkin_LIBRARIES}
)
target_link_libraries(AddInts_Client
${catkin_LIBRARIES}
)
3035

被折叠的 条评论
为什么被折叠?



