在ROS2中,自定义消息(.msg)、服务(.srv)和动作(.action)接口是扩展系统功能的核心。以下以完整工程结构为例,详细说明定义、编译和使用的全流程:
1. 项目结构
bash
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
:
plaintext
int32 id
string data
float64[3] values
服务(.srv)
custom_interfaces/srv/MyService.srv
:
plaintext
# 请求部分
string query
---
# 响应部分
bool success
string result
动作(.action)
custom_interfaces/action/MyAction.action
:
plaintext
# 目标(Goal)
int32 target_value
---
# 结果(Result)
bool achieved
float64 final_position
---
# 反馈(Feedback)
float64 progress
3. 编译配置(custom_interfaces包)
CMakeLists.txt关键部分:
cmake
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关键依赖:
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):
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):
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):
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):
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):
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):
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. 编译与运行
编译自定义接口包:
bash
cd ~/ros2_ws
colcon build --packages-select custom_interfaces
source install/setup.bash
编译用户包:
bash
colcon build --packages-select user_pkg
source install/setup.bash
运行示例:
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. 验证接口生成
bash
# 检查消息类型
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中自定义消息、服务和动作接口的定义、编译与使用。实际开发中,可根据需求扩展接口字段和业务逻辑。