ROS2 核心概念与通信机制
ROS2核心概念与通信机制通俗解释一、核心概念类比想象一个餐厅的场景ROS2概念 餐厅类比 解释节点 不同的工作人员厨师、服务员、收银员 每个节点是一个独立的可执行程序负责特定功能话题 餐厅的广播系统 一方向频道发布消息感兴趣的人订阅接收服务 顾客点菜请求→响应 一问一答有反馈的通信动作 做一道复杂的菜目标→反馈→结果 长时间任务可反馈进度可取消二、通信机制详解节点 (Node)最基础的执行单元每个节点有独立的功能。2. 话题 (Topic)类型: 异步、单向、多对多 场景: 传感器数据、状态发布 特点: 发布者不关心谁接收订阅者不关心谁发布服务 (Service)类型: 同步/异步、双向、一对一场景: 查询、触发一次性操作特点: 客户端发送请求服务端返回响应动作 (Action)类型: 异步、双向、带反馈场景: 长时间执行的任务特点: 可获取进度反馈可中途取消三、C Demo 实现环境准备bash安装ROS2以Humble为例sudo apt updatesudo apt install ros-humble-desktop创建工作空间mkdir -p ~/ros2_demo/srccd ~/ros2_democolcon buildsource install/setup.bash创建功能包bashcd ~/ros2_demo/srcros2 pkg create --build-type ament_cmake demo_communication --dependencies rclcpp std_msgs example_interfaces action_msgs话题通信 Demo发布者代码 src/talker.cppcpp#include#include#include “rclcpp/rclcpp.hpp”#include “std_msgs/msg/string.hpp”using namespace std::chrono_literals;class Talker : public rclcpp::Node{public:Talker() : Node(“talker”), count_(0){publisher_ this-create_publisherstd_msgs::msg::String(“topic_demo”, 10);timer_ this-create_wall_timer(1000ms, std::bind(Talker::publish_message, this));RCLCPP_INFO(this-get_logger(), “话题发布者已启动”);}private:void publish_message(){auto message std_msgs::msg::String();message.data Hello ROS2! std::to_string(count_);publisher_-publish(message);RCLCPP_INFO(this-get_logger(), “发布: ‘%s’”, message.data.c_str());}rclcpp::Publisherstd_msgs::msg::String::SharedPtr publisher_;rclcpp::TimerBase::SharedPtr timer_;size_t count_;};int main(int argc, char * argv[]){rclcpp::init(argc, argv);rclcpp::spin(std::make_shared());rclcpp::shutdown();return 0;}订阅者代码 src/listener.cppcpp#include#include “rclcpp/rclcpp.hpp”#include “std_msgs/msg/string.hpp”class Listener : public rclcpp::Node{public:Listener() : Node(“listener”){subscription_ this-create_subscriptionstd_msgs::msg::String(“topic_demo”, 10, std::bind(Listener::topic_callback, this, std::placeholders::_1));RCLCPP_INFO(this-get_logger(), “话题订阅者已启动”);}private:void topic_callback(const std_msgs::msg::String::SharedPtr msg){RCLCPP_INFO(this-get_logger(), “收到: ‘%s’”, msg-data.c_str());}rclcpp::Subscriptionstd_msgs::msg::String::SharedPtr subscription_;};int main(int argc, char * argv[]){rclcpp::init(argc, argv);rclcpp::spin(std::make_shared());rclcpp::shutdown();return 0;}服务通信 Demo服务端代码 src/service_server.cppcpp#include#include “rclcpp/rclcpp.hpp”#include “example_interfaces/srv/add_two_ints.hpp”using AddTwoInts example_interfaces::srv::AddTwoInts;class ServiceServer : public rclcpp::Node{public:ServiceServer() : Node(“service_server”){service_ this-create_service(“service_demo”,std::bind(ServiceServer::handle_request, this,std::placeholders::_1, std::placeholders::_2));RCLCPP_INFO(this-get_logger(), “服务端已启动等待请求…”);}private:void handle_request(const std::shared_ptrAddTwoInts::Request request,std::shared_ptrAddTwoInts::Response response){response-sum request-a request-b;RCLCPP_INFO(this-get_logger(), “收到: %ld %ld %ld”,request-a, request-b, response-sum);}rclcpp::Service::SharedPtr service_;};int main(int argc, char * argv[]){rclcpp::init(argc, argv);rclcpp::spin(std::make_shared());rclcpp::shutdown();return 0;}客户端代码 src/service_client.cppcpp#include#include#include “rclcpp/rclcpp.hpp”#include “example_interfaces/srv/add_two_ints.hpp”using AddTwoInts example_interfaces::srv::AddTwoInts;class ServiceClient : public rclcpp::Node{public:ServiceClient() : Node(“service_client”){client_ this-create_client(“service_demo”);RCLCPP_INFO(this-get_logger(), “客户端已启动”);}void send_request(int64_t a, int64_t b) { while (!client_-wait_for_service(1s)) { if (!rclcpp::ok()) { RCLCPP_ERROR(this-get_logger(), 客户端被中断); return; } RCLCPP_INFO(this-get_logger(), 等待服务...); } auto request std::make_sharedAddTwoInts::Request(); request-a a; request-b b; auto result client_-async_send_request(request); if (rclcpp::spin_until_future_complete(this-get_node_base_interface(), result) rclcpp::FutureReturnCode::SUCCESS) { RCLCPP_INFO(this-get_logger(), %ld %ld %ld, a, b, result.get()-sum); } else { RCLCPP_ERROR(this-get_logger(), 服务调用失败); } }private:rclcpp::Client::SharedPtr client_;};int main(int argc, char * argv[]){rclcpp::init(argc, argv);auto client std::make_shared();client-send_request(5, 3);rclcpp::shutdown();return 0;}动作通信 Demo动作定义文件 action/Fibonacci.actionaction目标int32 order结果int32[] sequence反馈int32[] partial_sequence动作服务端代码 src/action_server.cppcpp#include#include#include#include “rclcpp/rclcpp.hpp”#include “rclcpp_action/rclcpp_action.hpp”#include “demo_communication/action/fibonacci.hpp”using Fibonacci demo_communication::action::Fibonacci;using GoalHandleFibonacci rclcpp_action::ServerGoalHandle;class ActionServer : public rclcpp::Node{public:ActionServer() : Node(“action_server”){action_server_ rclcpp_action::create_server(this,“action_demo”,std::bind(ActionServer::handle_goal, this, std::placeholders::_1, std::placeholders::_2),std::bind(ActionServer::handle_cancel, this, std::placeholders::_1),std::bind(ActionServer::handle_accepted, this, std::placeholders::_1));RCLCPP_INFO(this-get_logger(), “动作服务端已启动”);}private:rclcpp_action::Server::SharedPtr action_server_;rclcpp_action::GoalResponse handle_goal( const rclcpp_action::GoalUUID uuid, std::shared_ptrconst Fibonacci::Goal goal) { RCLCPP_INFO(this-get_logger(), 收到目标请求order%d, goal-order); if (goal-order 0) { return rclcpp_action::GoalResponse::REJECT; } return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; } rclcpp_action::CancelResponse handle_cancel( const std::shared_ptrGoalHandleFibonacci goal_handle) { RCLCPP_INFO(this-get_logger(), 收到取消请求); return rclcpp_action::CancelResponse::ACCEPT; } void handle_accepted(const std::shared_ptrGoalHandleFibonacci goal_handle) { std::thread{std::bind(ActionServer::execute, this, std::placeholders::_1), goal_handle}.detach(); } void execute(const std::shared_ptrGoalHandleFibonacci goal_handle) { auto goal goal_handle-get_goal(); auto result std::make_sharedFibonacci::Result(); auto feedback std::make_sharedFibonacci::Feedback(); result-sequence.push_back(0); result-sequence.push_back(1); feedback-partial_sequence result-sequence; for (int i 2; i goal-order; i) { if (goal_handle-is_canceling()) { goal_handle-canceled(result); return; } result-sequence.push_back(result-sequence[i-1] result-sequence[i-2]); feedback-partial_sequence result-sequence; goal_handle-publish_feedback(feedback); RCLCPP_INFO(this-get_logger(), 发布反馈: 已计算 %ld 项, result-sequence.size()); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } goal_handle-succeed(result); RCLCPP_INFO(this-get_logger(), 动作执行成功结果序列大小: %zu, result-sequence.size()); }};int main(int argc, char ** argv){rclcpp::init(argc, argv);auto server std::make_shared();rclcpp::spin(server);rclcpp::shutdown();return 0;}动作客户端代码 src/action_client.cppcpp#include#include#include#include “rclcpp/rclcpp.hpp”#include “rclcpp_action/rclcpp_action.hpp”#include “demo_communication/action/fibonacci.hpp”using Fibonacci demo_communication::action::Fibonacci;class ActionClient : public rclcpp::Node{public:ActionClient() : Node(“action_client”){client_ rclcpp_action::create_client(this, “action_demo”);RCLCPP_INFO(this-get_logger(), “动作客户端已启动”);}void send_goal(int order) { if (!client_-wait_for_action_server(std::chrono::seconds(10))) { RCLCPP_ERROR(this-get_logger(), 动作服务不可用); return; } auto goal Fibonacci::Goal(); goal.order order; auto send_goal_options rclcpp_action::ClientFibonacci::SendGoalOptions(); send_goal_options.goal_response_callback std::bind(ActionClient::goal_response_callback, this, std::placeholders::_1); send_goal_options.feedback_callback std::bind(ActionClient::feedback_callback, this, std::placeholders::_1, std::placeholders::_2); send_goal_options.result_callback std::bind(ActionClient::result_callback, this, std::placeholders::_1); client_-async_send_goal(goal, send_goal_options); }private:rclcpp_action::Client::SharedPtr client_;void goal_response_callback(const rclcpp_action::ClientGoalHandleFibonacci::SharedPtr goal_handle) { if (!goal_handle) { RCLCPP_ERROR(this-get_logger(), 目标被拒绝); } else { RCLCPP_INFO(this-get_logger(), 目标被接受); } } void feedback_callback( rclcpp_action::ClientGoalHandleFibonacci::SharedPtr, const std::shared_ptrconst Fibonacci::Feedback feedback) { RCLCPP_INFO(this-get_logger(), 收到反馈: 当前序列长度 %zu, feedback-partial_sequence.size()); } void result_callback(const rclcpp_action::ClientGoalHandleFibonacci::WrappedResult result) { switch (result.code) { case rclcpp_action::ResultCode::SUCCEEDED: RCLCPP_INFO(this-get_logger(), 动作成功完成); break; case rclcpp_action::ResultCode::CANCELED: RCLCPP_INFO(this-get_logger(), 动作被取消); break; default: RCLCPP_ERROR(this-get_logger(), 动作失败); break; } RCLCPP_INFO(this-get_logger(), 最终结果序列: ); for (auto num : result.result-sequence) { std::cout num ; } std::cout std::endl; }};int main(int argc, char ** argv){rclcpp::init(argc, argv);auto client std::make_shared();client-send_goal(10);rclcpp::spin(client);rclcpp::shutdown();return 0;}四、编译配置编辑 CMakeLists.txtcmakecmake_minimum_required(VERSION 3.8)project(demo_communication)find_package(ament_cmake REQUIRED)find_package(rclcpp REQUIRED)find_package(std_msgs REQUIRED)find_package(example_interfaces REQUIRED)find_package(rosidl_default_generators REQUIRED)添加动作定义find_package(action_msgs REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}“action/Fibonacci.action”)话题demoadd_executable(talker src/talker.cpp)ament_target_dependencies(talker rclcpp std_msgs)add_executable(listener src/listener.cpp)ament_target_dependencies(listener rclcpp std_msgs)服务demoadd_executable(service_server src/service_server.cpp)ament_target_dependencies(service_server rclcpp example_interfaces)add_executable(service_client src/service_client.cpp)ament_target_dependencies(service_client rclcpp example_interfaces)动作demoadd_executable(action_server src/action_server.cpp)target_include_directories(action_server PUBLICBUILDINTERFACE:BUILD_INTERFACE:BUILDINTERFACE:{CMAKE_CURRENT_SOURCE_DIR}$INSTALL_INTERFACE:include)ament_target_dependencies(action_server rclcpp rclcpp_action ${PROJECT_NAME})add_executable(action_client src/action_client.cpp)target_include_directories(action_client PUBLICBUILDINTERFACE:BUILD_INTERFACE:BUILDINTERFACE:{CMAKE_CURRENT_SOURCE_DIR}$INSTALL_INTERFACE:include)ament_target_dependencies(action_client rclcpp rclcpp_action ${PROJECT_NAME})install(TARGETStalker listenerservice_server service_clientaction_server action_clientDESTINATION lib/${PROJECT_NAME})ament_package()编辑 package.xmlxml?xml version1.0? demo_communication 0.0.0 ROS2 communication demobuildtool_dependament_cmake/buildtool_dependbuildtool_dependrosidl_default_generators/buildtool_dependrclcppstd_msgsexample_interfacesrclcpp_actionaction_msgsmember_of_grouprosidl_interface_packages/member_of_groupament_cmake五、编译与运行bash编译工作空间cd ~/ros2_democolcon build --packages-select demo_communicationsource install/setup.bash分别打开三个终端运行1. 话题演示ros2 run demo_communication talker # 终端1ros2 run demo_communication listener # 终端22. 服务演示ros2 run demo_communication service_server # 终端1ros2 run demo_communication service_client # 终端23. 动作演示ros2 run demo_communication action_server # 终端1ros2 run demo_communication action_client # 终端2六、常用调试命令bash查看所有节点ros2 node list查看所有话题ros2 topic listros2 topic echo /topic_demo # 查看话题内容查看所有服务ros2 service listros2 service call /service_demo example_interfaces/srv/AddTwoInts “{a: 10, b: 20}”查看所有动作ros2 action listros2 action send_goal /action_demo demo_communication/action/Fibonacci “{order: 5}”