1.话题通信
话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。
用于不断更新的、少逻辑处理的数据传输场景。
ROS Master (管理者)
Talker (发布者)
Listener (订阅者)
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。
上述实现流程中,RPC协议,TCP协议
(发布者)
int main(int argc, char* argv[])
{
setlocale(LC_ALL, "");
ros::init(argc, argv, "pub");//初始化ROS节点,命名唯一
ros::NodeHandle nh;//实例化ROS句柄
ros::Publisher pub = nh.advertise<std_msgs::String>("hellotopic", 10);//实例化发布者对象,第3参数为true时保留最后的msg
std_msgs::String msg;
ros::Rate rate(5);//5Hz
int count = 0;
ROS_INFO("pub start");
while(ros::ok())
{
++count;
std::stringstream ss;
ss << "hello --- " << count;
msg.data = ss.str();
pub.publish(msg);//发布消息
ROS_INFO("pub data: %s", ss.str().c_str());
rate.sleep();//5Hz, 0.2s
ros::spinOnce();//处理回调
}
ROS_INFO("pub end");
return 0;
}
(订阅者)
void doMsg(const std_msgs::StringConstPtr &msgptr)//处理订阅消息回调
{
ROS_INFO("sub data: %s", msgptr->data.c_str());
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
ros::init(argc, argv, "sub");//初始化ROS节点,命名唯一
ros::NodeHandle nh;//实例化ROS句柄
ros::Subscriber sub = nh.subscribe("hellotopic", 10, doMsg);//实例化订阅者对象
ros::spin();//处理回调
return 0;
}
自定义消息类型
功能包下新建 msg 目录,添加文件 Person.msg
package.xml中添加编译依赖与执行依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
CMakeLists.txt编辑 msg 相关配置
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation #需要加入 message_generation,必须有 std_msgs
)
# 配置 msg 源文件
add_message_files(
FILES
Person.msg
)
# 生成消息时依赖于 std_msgs
generate_messages(
DEPENDENCIES
std_msgs
)
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
#配置原文件多加一个
add_dependencies(person_talker ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(person_listener ${PROJECT_NAME}_generate_messages_cpp)
2.服务通信
服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。
用于偶然的、对实时性有要求、有一定逻辑处理需求的数据传输场景。
ROS master(管理者)
Server(服务端)
Client(客户端)
ROS Master 负责保管 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client ,帮助 Server 与 Client 建立连接,连接建立后,Client 发送请求信息,Server 返回响应信息。
ROS Master 会根据注册表中的信息匹配Server和 Client,并通过 RPC 向 Client 发送 Server 的 TCP 地址信息。
服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用---分割,具体实现如下,功能包下新建 srv 目录,添加 xxx.srv 文件
package.xml中添加编译依赖与执行依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
CMakeLists.txt编辑 srv 相关配置
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
add_service_files(
FILES
AddInts.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
#配置原文件多加一个
add_dependencies(AddInts_Server ${PROJECT_NAME}_gencpp)
add_dependencies(AddInts_Client ${PROJECT_NAME}_gencpp)
(服务端)
bool doaddints(plumb_server_client::addintsRequest &request,
plumb_server_client::addintsResponse &response)
{
int num1 = request.num1;
int num2 = request.num2;
int sum = num1 + num2;
response.sum = sum;
ROS_INFO("请求数据: num1 = %d, num2 = %d", num1, num2);
ROS_INFO("结果: sum = %d", sum);
return true;
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
ROS_INFO("sever start");
ros::init(argc, argv, "sever");
ros::NodeHandle nh;
ros::ServiceServer sever = nh.advertiseService("addints", doaddints);
ros::spin();
ROS_INFO("sever end");
return 0;
}
(客户端)
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
ROS_INFO("client start");
ros::init(argc, argv, "client");
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient<plumb_server_client::addints>("addints");
plumb_server_client::addints ai;
ai.request.num1 = 100;
ai.request.num2 = 200;
bool ret = client.call(ai);
if(ret)
{
ROS_INFO("request success\nsum = %d", ai.response.sum);
}
else
{
ROS_INFO("request error");
}
ROS_INFO("client end");
return 0;
}
在客户端发送请求前添加:client.waitForExistence();或:ros::service::waitForService("AddInts");这是一个阻塞式函数,只有服务启动成功后才会继续执行
3.参数服务器
参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据。
ROS Master (管理者)
Talker (参数设置者)
Listener (参数调用者)
ROS Master 作为一个公共容器保存参数,Talker 可以向容器中设置参数,Listener 可以获取参数。
存储一些多节点共享的数据,类似于全局变量。
注意:参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据
//增(2方案)
nh.setParam("type", "xiaohuang");
nh.setParam("radius", 0.15);
ros::param::set("type_param", "xiaobai");
ros::param::set("radius_param", 0.15);
//改(2方案)
nh.setParam("radius", 0.2);
ros::param::set("radius_param", 0.25);
//查(3方案)
double radius = nh.param("radius", 0.0);
bool ret = nh.getParam("radius", radius2);
bool ret2 = nh.getParamCached("radius", radius3);//(性能上有提升)
//查询键
nh.getParamNames(names);
nh.hasParam("radius");
nh.searchParam("radius", key);
//删除(2方案)
bool ret = nh.deleteParam("radius");
bool ret2 = ros::param::del("radius_param");