ros2 自定义消息、服务、动作接口详细范例
在ROS2中,自定义消息(.msg)、服务(.srv)和动作(.action)接口是扩展系统功能的核心。以下以完整工程结构为例,详细说明定义、编译和使用的全流程:
1. 项目结构
custom_interfaces/
├── CMakeLists.txt
├── package.xml
├── msg/
│ ├── MyMessage.msg
│ └── ...
├── srv/
│ ├── MyService.srv
│ └── ...
└── action/├── MyAction.action└── ...user_pkg/ # 使用自定义接口的包
├── CMakeLists.txt
├── package.xml
├── src/
│ ├── publisher_node.cpp
│ ├── subscriber_node.cpp
│ ├── service_server.cpp
│ ├── service_client.cpp
│ ├── action_server.cpp
│ └── action_client.cpp
└── ...
2. 自定义接口定义
消息(.msg)
custom_interfaces/msg/MyMessage.msg
:
int32 id
string data
float64[3] values
服务(.srv)
custom_interfaces/srv/MyService.srv
:
# 请求部分
string query
---
# 响应部分
bool success
string result
动作(.action)
custom_interfaces/action/MyAction.action
:
# 目标(Goal)
int32 target_value
---
# 结果(Result)
bool achieved
float64 final_position
---
# 反馈(Feedback)
float64 progress
3. 编译配置(custom_interfaces包)
CMakeLists.txt关键部分:
cmake_minimum_required(VERSION 3.8)
project(custom_interfaces)find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)# 添加消息、服务、动作文件
rosidl_generate_interfaces(${PROJECT_NAME}"msg/MyMessage.msg""srv/MyService.srv""action/MyAction.action"DEPENDENCIES std_msgs
)ament_package()
package.xml关键依赖:
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
4. 使用自定义接口(user_pkg包)
消息发布/订阅示例
发布者节点(publisher_node.cpp):
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/msg/my_message.hpp"int main(int argc, char **argv) {rclcpp::init(argc, argv);auto node = rclcpp::Node::make_shared("msg_publisher");auto pub = node->create_publisher<custom_interfaces::msg::MyMessage>("my_topic", 10);auto msg = custom_interfaces::msg::MyMessage();msg.id = 1;msg.data = "Hello ROS2";msg.values = {1.0, 2.0, 3.0};rclcpp::Rate loop_rate(1);while (rclcpp::ok()) {pub->publish(msg);loop_rate.sleep();}rclcpp::shutdown();return 0;
}
订阅者节点(subscriber_node.cpp):
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/msg/my_message.hpp"void topic_callback(const custom_interfaces::msg::MyMessage &msg) {RCLCPP_INFO(rclcpp::get_logger("Subscriber"), "Received: ID=%d, Data=%s", msg.id, msg.data.c_str());
}int main(int argc, char **argv) {rclcpp::init(argc, argv);auto node = rclcpp::Node::make_shared("msg_subscriber");auto sub = node->create_subscription<custom_interfaces::msg::MyMessage>("my_topic", 10, topic_callback);rclcpp::spin(node);rclcpp::shutdown();return 0;
}
服务调用示例
服务端(service_server.cpp):
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/srv/my_service.hpp"void handle_service(const std::shared_ptr<rmw_request_id_t> request_header,const std::shared_ptr<custom_interfaces::srv::MyService::Request> req,const std::shared_ptr<custom_interfaces::srv::MyService::Response> res
) {res->success = (req->query == "valid");res->result = "Processed: " + req->query;
}int main(int argc, char **argv) {rclcpp::init(argc, argv);auto node = rclcpp::Node::make_shared("srv_server");node->create_service<custom_interfaces::srv::MyService>("my_service", handle_service);rclcpp::spin(node);rclcpp::shutdown();return 0;
}
客户端(service_client.cpp):
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/srv/my_service.hpp"int main(int argc, char **argv) {rclcpp::init(argc, argv);auto node = rclcpp::Node::make_shared("srv_client");auto client = node->create_client<custom_interfaces::srv::MyService>("my_service");auto request = std::make_shared<custom_interfaces::srv::MyService::Request>();request->query = "test_query";while (!client->wait_for_service(std::chrono::seconds(1))) {RCLCPP_WARN(node->get_logger(), "Service unavailable");}auto result = client->async_send_request(request);if (rclcpp::spin_until_future_complete(node, result) == rclcpp::FutureReturnCode::SUCCESS) {RCLCPP_INFO(node->get_logger(), "Result: %s", result.get()->result.c_str());}rclcpp::shutdown();return 0;
}
动作调用示例
动作服务器(action_server.cpp):
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/action/my_action.hpp"
#include "rclcpp_action/rclcpp_action.hpp"class ActionServer : public rclcpp::Node {
public:using MyAction = custom_interfaces::action::MyAction;explicit ActionServer() : Node("action_server") {action_server_ = rclcpp_action::create_server<MyAction>(this, "my_action",[this](const std::shared_ptr<Goal> goal) {RCLCPP_INFO(this->get_logger(), "Received goal: %d", goal->target_value);return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;},[this](const std::shared_ptr<Goal> goal) {// 取消逻辑return rclcpp_action::CancelResponse::ACCEPT;},[this](const std::shared_ptr<Goal> goal, const std::shared_ptr<Feedback> feedback) {feedback->progress = 0.5; // 更新反馈},[this](const std::shared_ptr<Goal> goal, const std::shared_ptr<Result> result) {result->achieved = true;result->final_position = 10.0;return rclcpp_action::ResultCode::SUCCEEDED;});}
private:rclcpp_action::Server<MyAction>::SharedPtr action_server_;
};
动作客户端(action_client.cpp):
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/action/my_action.hpp"
#include "rclcpp_action/rclcpp_action.hpp"class ActionClient : public rclcpp::Node {
public:using MyAction = custom_interfaces::action::MyAction;explicit ActionClient() : Node("action_client") {action_client_ = rclcpp_action::create_client<MyAction>(this, "my_action");auto goal = MyAction::Goal();goal.target_value = 100;action_client_->async_send_goal(goal, [this](auto future) {auto result = future.get();if (!result) {RCLCPP_ERROR(this->get_logger(), "Goal failed");} else if (result->code == rclcpp_action::ResultCode::SUCCEEDED) {RCLCPP_INFO(this->get_logger(), "Success! Final position: %f", result->result->final_position);}});}
private:rclcpp_action::Client<MyAction>::SharedPtr action_client_;
};
5. 编译与运行
编译自定义接口包:
cd ~/ros2_ws
colcon build --packages-select custom_interfaces
source install/setup.bash
编译用户包:
colcon build --packages-select user_pkg
source install/setup.bash
运行示例:
# 消息测试
ros2 run user_pkg publisher_node
ros2 run user_pkg subscriber_node# 服务测试
ros2 run user_pkg service_server
ros2 run user_pkg service_client# 动作测试
ros2 run user_pkg action_server
ros2 run user_pkg action_client
6. 验证接口生成
# 检查消息类型
ros2 interface show custom_interfaces/msg/MyMessage# 检查服务类型
ros2 interface show custom_interfaces/srv/MyService# 检查动作类型
ros2 interface show custom_interfaces/action/MyAction
关键注意事项
- 依赖管理:确保
package.xml
中声明<build_depend>
和<exec_depend>
,并在CMakeLists.txt
中链接rosidl_default_runtime
。 - 命名规范:接口名称需使用小写+下划线(如
my_message
),避免大写开头。 - 编译顺序:先编译
custom_interfaces
包,再编译使用它的包。 - 调试工具:使用
rqt_graph
查看节点/话题连接,ros2 topic echo
验证数据。
通过以上步骤,您可完整实现ROS2中自定义消息、服务和动作接口的定义、编译与使用。实际开发中,可根据需求扩展接口字段和业务逻辑。