上篇文章:ROS2发布自定义数据C++编程示例介绍了ROS2话题发布的C++编程示例,并通过ros2的topic指令查看了发布的消息,本篇,继续通过C++编来实现对话题的订阅,并简单了解QoS的概念。
1 回顾下发布器的写法
// 发布器声明
rclcpp::Publisher<test_msg::msg::PersonInfo>::SharedPtr publisher_;
// 创建发布器,话题名 /person_info,消息类型 test_msg::msg::PersonInfo,队列10
publisher_ = this->create_publisher<test_msg::msg::PersonInfo>("/person_info", 10);
// 发布数据
auto msg = test_msg::msg::PersonInfo();
msg.name = "zhangsan";
msg.age = 22;
msg.height = 1.75f;
RCLCPP_INFO(this->get_logger(), "发布: name=%s, age=%d, height=%.2f",msg.name.c_str(), msg.age, msg.height);
publisher_->publish(msg);
2 订阅器的写法也是类似
// 订阅器声明
rclcpp::Subscription<test_msg::msg::PersonInfo>::SharedPtr subscription_;
// 创建订阅器:话题 /person_info,队列10,回调函数
subscription_ = this->create_subscription<test_msg::msg::PersonInfo>(
"/person_info",
10,
std::bind(&PersonInfoSubscriber::topic_callback, this, std::placeholders::_1)
);
// 订阅回调,参数是收到的消息共享指针
void topic_callback(const test_msg::msg::PersonInfo::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(),
"收到消息:name=%s, age=%d, height=%.2f",
msg->name.c_str(),
msg->age,
msg->height);
}
说明:
create_subscription后面尖括号中test_msg::msg::PersonInfo表示订阅的话题对应的数据类型
create_subscription后面参数:
topic_callback:回调函数,函数格式:void 函数名(const MessageT::SharedPtr msg)
this:绑定当前类实例对象
std::placeholders::_1:占位符,表示把收到的消息传给回调第一个入参
"/person_info":字符串,订阅的话题名称
10:无符号整数,代表消息队列深度 (history depth)
std::bind...:对消息的回调处理
3 create_subscription函数原型
create_subscription的函数原型有两个:
// 原型1
template<typename MessageT>
typename rclcpp::Subscription<MessageT>::SharedPtr
create_subscription(
const std::string & topic_name,
size_t qos_depth,
std::function<void(const typename MessageT::SharedPtr)> callback,
const rclcpp::CallbackGroup::SharedPtr group = nullptr
);
// 原型2
template<typename MessageT>
typename rclcpp::Subscription<MessageT>::SharedPtr
create_subscription(
const std::string & topic_name,
const rclcpp::QoS & qos_profile,
std::function<void(const typename MessageT::SharedPtr)> callback,
const rclcpp::CallbackGroup::SharedPtr group = nullptr
);
下面再来看下参数
参数1:话题名称
开头带/ → 绝对话题名;不带/为相对名字,会加上节点命名空间发布和订阅
字符串必须完全一模一样
- (大小写敏感),否则无法匹配通信
参数2:队列深度/QoS 服务质量
队列的深度,是指DDS(Data Distribution Service,数据分发服务)层本地缓存最多保留多少条还没处理的消息,消息超出数量旧消息丢弃。
只填数字时,ROS2 会使用默认QoS 策略(Quality of Service,服务质量)
QoS 三个核心维度:
KeepLast(depth) / KeepAll
-
- 队列保存策略
KeepLast(depth):保存指定的深度
KeepAll:保存所有
Reliable / BestEffort
Reliable:可靠,保证送达,丢包会重传;适合业务消息。
BestEffort:尽力,不重传;适合高频摄像头、lidar。
Durability:消息持久化
Volatile:新订阅者收历史消息
TransientLocal:保存消息,晚加入订阅可以收到历史
rclcpp::QoS(qos_depth),使用默认策略:KeepLast | Reliable | Volatile
另外注意:发布者和订阅者QoS 必须兼容,否则可能收不到消息
参数3:回调函数
回调可以有两种写法
类成员函数bind
void topic_callback(const test_msg::msg::PersonInfo::SharedPtr msg) const;
std::bind(&PersonInfoSubscriber::topic_callback, this, std::placeholders::_1)
lambda 表达式
subscription_ = this->create_subscription<test_msg::msg::PersonInfo>(
"/person_info",
10,
[this](const test_msg::msg::PersonInfo::SharedPtr msg){
RCLCPP_INFO(this->get_logger(),"name=%s",msg->name.c_str());
}
);
参数4:回调组
rclcpp::CallbackGroup::SharedPtr group
回调组,控制回调在哪一个执行组,线程调度。一般都是默认 nullptr
nullptr:使用节点默认回调组,所有回调共用 spin 的同一个线程。如果自定义回调组,可以实现多线程,避免一个耗时回调阻塞其他订阅。
4 完整的代码
#include "rclcpp/rclcpp.hpp"
#include "test_msg/msg/person_info.hpp"
class PersonInfoSubscriber : public rclcpp::Node
{
public:
PersonInfoSubscriber() : Node("person_info_sub_node")
{
// 创建订阅器:话题 /person_info,队列10,回调函数
subscription_ = this->create_subscription<test_msg::msg::PersonInfo>(
"/person_info",
10,
std::bind(&PersonInfoSubscriber::topic_callback, this, std::placeholders::_1)
);
RCLCPP_INFO(this->get_logger(), "订阅节点已启动,等待 /person_info 消息...");
}
private:
// 订阅回调,参数是收到的消息共享指针
void topic_callback(const test_msg::msg::PersonInfo::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(),
"收到消息:name=%s, age=%d, height=%.2f",
msg->name.c_str(),
msg->age,
msg->height);
}
rclcpp::Subscription<test_msg::msg::PersonInfo>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<PersonInfoSubscriber>());
rclcpp::shutdown();
return 0;
}
结合上一篇的例程,先运行上一篇介绍发布器程序,再运行本篇的订阅器程序,结果如下:
5 总结
本篇介绍了ROS2的话题订阅的C++写法,并重点介绍了订阅接口的参数以及QoS的概念,结合上一篇的发布器程序,实现数据的发布和接收。
111