在机器人开发中,节点间的通信是核心基础。今天我将分享如何在ROS2环境下用C++实现最基本的发布-订阅模型。这个"说话者-听者"系统虽然简单,但包含了ROS2通信的所有关键要素。
首先确保已安装ROS2 Jazzy版本(其他版本需相应调整命令)。我习惯在工作空间src目录下操作,这样可以保持项目结构清晰:
bash复制mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
创建CMake包时,我推荐添加license信息,这对后续开源很重要:
bash复制ros2 pkg create --build-type ament_cmake --license Apache-2.0 cpp_pubsub
注意:包名不要用大写字母和下划线,ROS2包命名规范建议全小写
进入包目录后,你会看到自动生成的CMakeLists.txt和package.xml。我习惯先完善package.xml的元信息:
xml复制<description>Minimal publisher/subscriber demo using rclcpp</description>
<maintainer email="your@email.com">Your Name</maintainer>
<license>Apache-2.0</license>
在src目录下创建publisher_member_function.cpp:
cpp复制#include <chrono>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
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 ROS2! " + 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;
}
经验:比起lambda表达式,成员函数方式更易维护,适合复杂项目
在CMakeLists.txt中添加:
cmake复制find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
install(TARGETS talker DESTINATION lib/${PROJECT_NAME})
创建subscriber_member_function.cpp:
cpp复制#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
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, std::placeholders::_1));
}
private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg) const {
RCLCPP_INFO(this->get_logger(), "Received: '%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;
}
注意:主题名必须完全匹配,包括大小写
在CMakeLists.txt中追加:
cmake复制add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME})
bash复制cd ~/ros2_ws
colcon build --packages-select cpp_pubsub
开启第一个终端运行发布者:
bash复制source install/setup.bash
ros2 run cpp_pubsub talker
开启第二个终端运行订阅者:
bash复制source install/setup.bash
ros2 run cpp_pubsub listener
你应该看到类似输出:
发布者终端:
code复制[INFO] [minimal_publisher]: Publishing: 'Hello ROS2! 0'
[INFO] [minimal_publisher]: Publishing: 'Hello ROS2! 1'
订阅者终端:
code复制[INFO] [minimal_subscriber]: Received: 'Hello ROS2! 0'
[INFO] [minimal_subscriber]: Received: 'Hello ROS2! 1'
消息不接收:
编译错误:
性能问题:
bash复制ros2 topic list
bash复制ros2 topic echo /topic
bash复制ros2 node info /minimal_publisher
在实际机器人项目中,我通常会在此基础上添加异常处理、日志分级和性能监控。这个基础通信模型虽然简单,但经过适当扩展完全可以满足复杂系统的通信需求。