ROS2机器人编程简述humble-第二章-Controlling the Iterative Execution .3.1
官方示例pub和sub使用std_msgs/msg/string.hpp,数据类型std_msgs::msg::String。
这本书中使用是std_msgs/msg/int32.hpp,数据类型:std_msgs::msg::Int32。
对于机器人系统而言,实际情况下pub/sub对应于传感器数据的发布和接收。
比如激光/图像等。
ros2案例通常都用类来实现各种功能,需要具备现代C++基础。
具体参考:
蓝桥ROS机器人之现代C++学习笔记支持C++17(已完成)
推荐掌握C++17/20之后,也就是在此基础上开启ROS2的学习之路。
不推荐如下方式:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/int32.hpp"
using namespace std::chrono_literals;
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("publisher_node");
auto publisher = node->create_publisher<std_msgs::msg::Int32>(
"int_topic", 10);
std_msgs::msg::Int32 message;
message.data = 0;
rclcpp::Rate loop_rate(500ms);
while (rclcpp::ok()) {
message.data += 1;
publisher->publish(message);
rclcpp::spin_some(node);
loop_rate.sleep();
}
rclcpp::shutdown();
return 0;
}
std_msgs::msg::Int32推荐如下
pub发布:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/int32.hpp"
using namespace std::chrono_literals;
using std::placeholders::_1;
class PublisherNode : public rclcpp::Node
{
public:
PublisherNode()
: Node("publisher_node")
{
publisher_ = create_publisher<std_msgs::msg::Int32>("int_topic", 10);
timer_ = create_wall_timer(
500ms, std::bind(&PublisherNode::timer_callback, this));
}
void timer_callback()
{
message_.data += 1;
publisher_->publish(message_);
}
private:
rclcpp::Publisher<std_msgs::msg::Int32>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
std_msgs::msg::Int32 message_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<PublisherNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
String
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
/* This example creates a subclass of Node and uses std::bind() to register a
* member function as a callback from the timer. */
class MinimalPublisher : public rclcpp::Node
{
public:
MinimalPublisher()
: Node("minimal_publisher"), count_(0)
{
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
timer_ = this->create_wall_timer(
500ms, std::bind(&MinimalPublisher::timer_callback, this));
}
private:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
sub接收:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/int32.hpp"
using std::placeholders::_1;
class SubscriberNode : public rclcpp::Node
{
public:
SubscriberNode()
: Node("subscriber_node")
{
subscriber_ = create_subscription<std_msgs::msg::Int32>(
"int_topic", 10,
std::bind(&SubscriberNode::callback, this, _1));
}
void callback(const std_msgs::msg::Int32::SharedPtr msg)
{
RCLCPP_INFO(get_logger(), "Hello %d", msg->data);
}
private:
rclcpp::Subscription<std_msgs::msg::Int32>::SharedPtr subscriber_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<SubscriberNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
String
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;
class MinimalSubscriber : public rclcpp::Node
{
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}
private:
void topic_callback(const std_msgs::msg::String & msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalSubscriber>());
rclcpp::shutdown();
return 0;
}
注意,代码的区别。
另一个重点!!!
rclcpp::QoS
pub和sub的QoS要匹配!
ROS 2提供了多种服务质量(QoS)策略,允许调整节点之间的通信。使用正确的服务质量策略集,ROS 2可以像TCP一样可靠,也可以像UDP一样尽最大努力,其间有许多可能的状态。与ROS 1不同,ROS 1主要只支持TCP,ROS 2受益于底层DDS传输在有损无线网络环境中的灵活性,在这种环境中,“尽力而为”策略更为合适,或者在实时计算系统中,需要正确的服务质量配置文件来满足截止日期。
一组QoS“策略”组合起来形成一个QoS“配置文件”。考虑到为给定场景选择正确的QoS策略的复杂性,ROS 2为常见用例(例如传感器数据)提供了一组预定义的QoS配置文件。同时,开发人员可以灵活地控制QoS配置文件的特定策略。
可以为发布者、订阅、服务服务器和客户端指定QoS配置文件。QoS配置文件可以独立地应用于上述实体的每个实例,但是如果使用不同的配置文件,则它们可能不兼容,从而阻止消息的传递。
QoS兼容性
注意:本节涉及发布者和订阅,但内容同样适用于服务器和客户端。
可以独立地为发布者和订阅配置QoS配置文件。只有当发布者和订阅之间具有兼容的QoS配置文件时,才会建立该对之间的连接。
QoS配置文件兼容性是基于“请求与提供”模型确定的。订阅请求的QoS配置文件是它愿意接受的“最低质量”,而发布者提供的QoS配置是它能够提供的“最高质量”。仅当所请求的QoS配置文件的每个策略不比所提供的QoS配置的每个策略更严格时,才进行连接。多个订阅可以同时连接到单个发布者,即使它们请求的QoS配置文件不同。发布者与订阅之间的兼容性不受其他发布者和订阅的影响。
下表显示了不同策略设置的兼容性及其结果:
可靠性QoS策略的兼容性:
Publisher | Subscription | Compatible |
Best effort | Best effort | Yes |
Best effort | Reliable | No |
Reliable | Best effort | Yes |
Reliable | Reliable | Yes |
耐久性QoS策略的兼容性:
Publisher | Subscription | Compatible |
Volatile | Volatile | Yes |
Volatile | Transient local | No |
Transient local | Volatile | Yes |
Transient local | Transient local | Yes |
最后期限QoS策略的兼容性:
假设x和y是任意有效的持续时间值。
Publisher | Subscription | Compatible |
Default | Default | Yes |
Default | x | No |
x | Default | Yes |
x | x | Yes |
x | y (where y > x) | Yes |
x | y (where y < x) | No |
活泼QoS策略的兼容性:
Publisher | Subscription | Compatible |
Automatic | Automatic | Yes |
Automatic | Manual by topic | No |
Manual by topic | Automatic | Yes |
Manual by topic | Manual by topic | Yes |
租赁期限QoS策略的兼容性:
假设x和y是任意有效的持续时间值。
Publisher | Subscription | Compatible |
Default | Default | Yes |
Default | x | No |
x | Default | Yes |
x | x | Yes |
x | y (where y > x) | Yes |
x | y (where y < x) | No |
为了建立连接,所有影响兼容性的策略都必须兼容。例如,即使请求的和提供的QoS配置文件对具有兼容的可靠性QoS策略,但它们具有不兼容的耐久性QoS策略,仍然不会建立连接。
未建立连接时,发布者和订阅之间不会传递任何消息。有检测这种情况的机制,这将在后面的章节中介绍。
服务质量事件
某些QoS策略可能有与之相关的事件。开发人员可以为每个发布者和订阅提供由这些QoS事件触发的回调函数,并以他们认为合适的方式处理它们,类似于主题上接收的消息的处理方式。
开发人员可以订阅与发布者关联的以下QoS事件:
错过了提供的截止日期
发布者未在期限QoS策略规定的预期持续时间内发布消息。
失去活力
出版商未能在租约期限内表明其活跃程度。
提供不兼容的QoS
发布者遇到了同一主题的订阅,该订阅请求提供的QoS配置文件无法满足的QoS配置,导致发布者与该订阅之间没有连接。
开发人员可以订阅与订阅相关联的以下QoS事件:
请求的截止日期已错过
订阅在期限QoS策略规定的预期持续时间内未收到消息。
生活改变了
订阅已注意到订阅主题的一个或多个发布者未能在租约期限内显示其活跃状态。
请求的不兼容QoS
订阅遇到了同一主题的发布者,该发布者提供的QoS配置文件不满足请求的QoS配置,导致订阅与该发布者之间没有连接。
具体参考书和官网,如上是机器翻译。