211 lines
5.3 KiB
Markdown
211 lines
5.3 KiB
Markdown
# 16 · 自定义接口 (.msg / .srv / .action) 完全指南
|
|
|
|
> **目标**: 理解 ROS2 自定义接口的设计原理、文件格式、rosidl 工具链、能独立写 .msg / .srv / .action。
|
|
|
|
---
|
|
|
|
## 目录
|
|
|
|
- [1. 为什么需要自定义](#1-为什么需要自定义)
|
|
- [2. .msg 文件格式](#2-msg-文件格式)
|
|
- [3. .srv 文件格式](#3-srv-文件格式)
|
|
- [4. .action 文件格式](#4-action-文件格式)
|
|
- [5. rosidl_generate_interfaces](#5-rosidl_generate_interfaces)
|
|
- [6. CMakeLists.txt + package.xml 配置](#6-cmakeliststxt--packagexml-配置)
|
|
- [7. C++ / Python 使用](#7-c--python-使用)
|
|
- [8. 设计原则](#8-设计原则)
|
|
- [9. 推荐阅读](#9-推荐阅读)
|
|
|
|
---
|
|
|
|
## 1. 为什么需要自定义
|
|
|
|
ROS2 标准消息(`std_msgs` / `sensor_msgs` / `geometry_msgs`)覆盖了 80% 场景,但有些项目特定需求:
|
|
|
|
- 工业传感器读数(温度 + 压力 + 校准日期)
|
|
- 机器人状态(电量 + 温度 + 故障码)
|
|
- 业务动作(下单 + 支付 + 物流)
|
|
|
|
这些都需要**项目特定**的自定义接口。
|
|
|
|
## 2. .msg 文件格式
|
|
|
|
```
|
|
# 注释(以 # 开头)
|
|
<type> <field_name> # 字段
|
|
```
|
|
|
|
字段类型:
|
|
|
|
| ROS 类型 | C++ 类型 | Python 类型 |
|
|
|---|---|---|
|
|
| `bool` | `bool` | `bool` |
|
|
| `int8`/`int16`/`int32`/`int64` | `int8_t`/... | `int` |
|
|
| `uint8`/.../`uint64` | `uint8_t`/... | `int` |
|
|
| `float32`/`float64` | `float`/`double` | `float` |
|
|
| `string` | `std::string` | `str` |
|
|
| `time`/`duration` | `builtin_interfaces::msg::Time` | `Time` |
|
|
| 其他消息类型 | `pkg::msg::Type` | `pkg.msg.Type` |
|
|
| `type[]` | `std::vector<T>` | `List[T]` |
|
|
| `type[3]` | `std::array<T,3>` | `Tuple[T,T,T]` |
|
|
|
|
示例: `msg/SensorReading.msg`
|
|
|
|
```
|
|
std_msgs/Header header
|
|
string sensor_id
|
|
string unit
|
|
float64 value
|
|
```
|
|
|
|
## 3. .srv 文件格式
|
|
|
|
```
|
|
# Request 字段(--- 上)
|
|
<type> <field_name>
|
|
---
|
|
# Response 字段(--- 下)
|
|
<type> <field_name>
|
|
```
|
|
|
|
示例: `srv/GetCalibration.srv`
|
|
|
|
```
|
|
string sensor_id
|
|
---
|
|
float64[9] intrinsic_matrix
|
|
float64[3] bias
|
|
string calibration_date
|
|
bool valid
|
|
```
|
|
|
|
## 4. .action 文件格式
|
|
|
|
```
|
|
# Goal 字段(第 1 段)
|
|
<type> <field_name>
|
|
---
|
|
# Result 字段(第 2 段)
|
|
<type> <field_name>
|
|
---
|
|
# Feedback 字段(第 3 段)
|
|
<type> <field_name>
|
|
```
|
|
|
|
示例: `action/MoveArm.action`
|
|
|
|
```
|
|
geometry_msgs/PoseStamped target_pose
|
|
string[] joint_names
|
|
float32 max_velocity_scaling
|
|
float32 max_acceleration_scaling
|
|
---
|
|
bool success
|
|
string error_message
|
|
float64 total_time_sec
|
|
---
|
|
float32 progress
|
|
string current_state
|
|
```
|
|
|
|
## 5. rosidl_generate_interfaces
|
|
|
|
ROS2 用 `rosidl` 自动从 .msg/.srv/.action 生成 C++ / Python 代码:
|
|
|
|
```cmake
|
|
find_package(rosidl_default_generators REQUIRED)
|
|
|
|
rosidl_generate_interfaces(${PROJECT_NAME}
|
|
"msg/SensorReading.msg"
|
|
"srv/GetCalibration.srv"
|
|
"action/MoveArm.action"
|
|
DEPENDENCIES std_msgs geometry_msgs
|
|
ADD_LINTER_TESTS
|
|
)
|
|
```
|
|
|
|
生成位置:
|
|
- C++ 头文件:`install/<pkg>/include/<pkg>/msg/<msg_name>.hpp`
|
|
- Python 模块:`install/<pkg>/lib/python3.10/site-packages/<pkg>/msg/<msg_name>.py`
|
|
|
|
## 6. CMakeLists.txt + package.xml 配置
|
|
|
|
**`package.xml` 必备**:
|
|
|
|
```xml
|
|
<buildtool_depend>rosidl_default_generators</buildtool_depend>
|
|
<exec_depend>rosidl_default_runtime</exec_depend>
|
|
<member_of_group>rosidl_interface_packages</member_of_group>
|
|
```
|
|
|
|
**`CMakeLists.txt` 顺序**:
|
|
|
|
```cmake
|
|
find_package(ament_cmake REQUIRED)
|
|
find_package(rosidl_default_generators REQUIRED)
|
|
find_package(std_msgs REQUIRED)
|
|
find_package(geometry_msgs REQUIRED)
|
|
|
|
rosidl_generate_interfaces(${PROJECT_NAME} ...)
|
|
|
|
ament_target_dependencies(${PROJECT_NAME}_core rclcpp "rosidl_typesupport_cpp")
|
|
rosidl_target_interfaces(${PROJECT_NAME}_core ${PROJECT_NAME} "rosidl_typesupport_cpp")
|
|
```
|
|
|
|
## 7. C++ / Python 使用
|
|
|
|
### C++
|
|
|
|
```cpp
|
|
#include "cpp_custom_interface/msg/sensor_reading.hpp"
|
|
|
|
auto msg = cpp_custom_interface::msg::SensorReading();
|
|
msg.sensor_id = "imu_0";
|
|
msg.value = 1.234;
|
|
publisher_->publish(msg);
|
|
```
|
|
|
|
### Python
|
|
|
|
```python
|
|
from cpp_custom_interface.msg import SensorReading
|
|
|
|
msg = SensorReading()
|
|
msg.sensor_id = 'imu_0'
|
|
msg.value = 1.234
|
|
publisher.publish(msg)
|
|
```
|
|
|
|
## 8. 设计原则
|
|
|
|
| 原则 | 说明 |
|
|
|---|---|
|
|
| **优先标准接口** | 90% 场景用 `std_msgs` / `sensor_msgs` |
|
|
| **字段 snake_case + 单位后缀** | `velocity_mps` 而不是 `v` |
|
|
| **Header 必备** | 任何有"时间戳"的消息都加 `std_msgs/Header` |
|
|
| **数组 vs 单值** | `float64[]` 用于多维数据,单值用 `float64` |
|
|
| **不要嵌指针** | 用 ID (`string object_id`) 而非 `string&` |
|
|
| **字段命名清晰** | `target_pose` 不是 `tp`,`max_velocity_scaling` 不是 `v_max` |
|
|
| **Result 加 success + error_message** | client 知道成功还是失败 |
|
|
|
|
## 9. 推荐阅读
|
|
|
|
- [ROS2 自定义接口官方教程](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Custom-ROS2-Interfaces.html)
|
|
- [REP-127: ROS Message 标准](https://www.ros.org/reps/rep-0127.html)
|
|
- [rosidl 文档](https://design.ros2.org/articles/legacy_interface_definition.html)
|
|
- [`cpp_custom_interface` 包](../src/cpp_custom_interface/README.md) — 本仓库的演示
|
|
|
|
---
|
|
|
|
---
|
|
|
|
## 📖 阅读路径导航
|
|
|
|
> 💡 这是仓库 `doc/` 下所有文档的推荐阅读顺序。[返回 README 总导航](../README.md#-23-篇文档怎么读)
|
|
>
|
|
> ⏱ **本文预计阅读时间**: 40 分钟
|
|
> 📍 **当前位置**: 第 7 / 24 篇
|
|
|
|
- ⏮ **上一篇**: [参数系统深度](../15-params.md)
|
|
- ⏭ **下一篇**: [Lifecycle Node](../17-lifecycle.md)
|