ros2 自定义消息、服务、动作接口详细范例

在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

关键注意事项

  1. 依赖管理 :确保package.xml中声明<build_depend><exec_depend>,并在CMakeLists.txt中链接rosidl_default_runtime
  2. 命名规范 :接口名称需使用小写+下划线 (如my_message),避免大写开头。
  3. 编译顺序 :先编译custom_interfaces包,再编译使用它的包。
  4. 调试工具 :使用rqt_graph查看节点/话题连接,ros2 topic echo验证数据。

通过以上步骤,您可完整实现ROS2中自定义消息、服务和动作接口的定义、编译与使用。实际开发中,可根据需求扩展接口字段和业务逻辑。

相关推荐
老黄编程2 天前
- custom_action_cpp: 自定义动作创建与调用示例
ros
心无旁骛~2 天前
【OpenArm|Control】openarm机械臂ROS2仿真控制
人工智能·ros
@LuckY BoY11 天前
(四)routeros命令笔记:网络篇
ros
万俟淋曦15 天前
【ROS2】通讯机制 Topic 常用命令行
人工智能·ai·机器人·ros·topic·ros2·具身智能
休息一下接着来1 个月前
Ubuntu 22.04 安装 ROS 2 Humble 笔记
ros
nenchoumi31191 个月前
全网首发!Realsense 全新 D555 相机开箱记录与 D435i、L515、D456 横向测评!
数码相机·计算机视觉·机器人·ros·realsense
nenchoumi31192 个月前
手持 Mid360 + RealSense 相机 + Jetson Orin 一体平台
人工智能·目标检测·计算机视觉·机器人·ros
lxmyzzs2 个月前
基于深度学习CenterPoint的3D目标检测部署实战
人工智能·深度学习·目标检测·自动驾驶·ros·激光雷达·3d目标检测
MintonLee复现侠2 个月前
记录RK3588的docker中启动rviz2报错
docker·容器·ros·rk3588·rviz·rviz2