# cpp_custom_interface — C++ 自定义接口(.msg / .srv / .action)
> 学会自定义 ROS2 接口,跨包/跨语言共享数据结构。
>
> 预计学习时间:2-3 小时。
---
## 这是什么?
ROS2 自带的消息类型(`std_msgs/String`、`geometry_msgs/Twist`...)够用吗?不够。
**做项目一定要自定义接口**,比如:
- 机器人: `/robot_status.msg` (含电量、位置、状态)
- 机械臂: `/MoveArm.action` (含目标位姿 + 反馈进度 + 结果)
- 相机: `/CameraCalibration.srv` (含内外参矩阵)
**本包演示**:
- `.msg` (消息,Topic 用): `SensorReading`(传感器读数)
- `.srv` (服务): `GetCalibration`(获取标定数据)
- `.action` (动作): `MoveArm`(机械臂运动)
---
## 🎯 学完之后你能做什么?
1. ✅ 定义 `.msg` / `.srv` / `.action` 文件
2. ✅ 用 `rosidl_generate_interfaces` 生成 C++ / Python 代码
3. ✅ 在自己的包里 include 生成的 C++ 头文件
4. ✅ 跨包/跨语言用自定义类型通信
---
## 📁 文件结构
```
src/cpp_custom_interface/
├── msg/SensorReading.msg # 消息定义
├── srv/GetCalibration.srv # 服务定义
├── action/MoveArm.action # 动作定义
├── include/cpp_custom_interface/ # 生成的头文件会被装到这里
├── src/
│ ├── sensor_publisher.cpp/hpp # 发布自定义 msg 的 Publisher
│ ├── calibration_server.cpp/hpp # 自定义 srv 的 Server
│ └── move_arm_server.cpp/hpp # 自定义 action 的 Server
├── test/test_custom_interfaces.cpp # 测试生成的接口
├── CMakeLists.txt # ⭐ 关键:rosidl_generate_interfaces
└── package.xml
```
---
## 📖 接口定义文件格式
### .msg(SensorReading.msg)
```
std_msgs/Header header # 用其他包的消息类型
string sensor_id
string unit
float64 value
```
类型:`string`、`int32`、`float64`、`bool`、嵌套其他 `msg/...`
### .srv(GetCalibration.srv)
```
string sensor_id # 请求字段
---
float64[9] intrinsic_matrix # 响应字段
float64[3] bias
string calibration_date
bool valid
```
`---` 上是请求,下面是响应。
### .action(MoveArm.action)
```
# Goal
float64 max_velocity_scaling
---
# Result
bool success
string error_message
float64 total_time_sec
---
# Feedback
float32 progress
string current_state
```
三段分别是 **Goal / Result / Feedback**。
---
## 🚀 跑起来
### 启动自定义 Publisher
```bash
source /opt/ros/humble/setup.bash
source /root/ros2_ws/install/setup.bash
ros2 run cpp_custom_interface sensor_publisher_cpp
```
**预期输出**:
```
[INFO] [sensor_publisher]: SensorPublisher started: topic="/sensor_reading"
```
### 订阅看消息(另开终端)
```bash
ros2 topic echo /sensor_reading --once
```
**预期输出**:
```
header:
stamp:
sec: ...
nanosec: ...
frame_id: imu_frame
sensor_id: imu_0
unit: rad/s
value: 0.0
```
### 启动标定服务(另开终端)
```bash
ros2 run cpp_custom_interface calibration_server_cpp
```
```bash
# 调用服务
ros2 service call /get_calibration cpp_custom_interface/srv/GetCalibration "{sensor_id: 'lidar_front'}"
```
### 启动机械臂 Action(另开终端)
```bash
ros2 run cpp_custom_interface move_arm_server_cpp
```
```bash
# 发 goal
ros2 action send_goal --feedback /move_arm cpp_custom_interface/action/MoveArm "{max_velocity_scaling: 0.5}"
```
---
## 📖 CMakeLists.txt 关键配置
```cmake
# 1) 定义接口文件
set(MSG_FILES "msg/SensorReading.msg")
set(SRV_FILES "srv/GetCalibration.srv")
set(ACTION_FILES "action/MoveArm.action")
# 2) 生成 C++ + Python 代码(关键!)
rosidl_generate_interfaces(${PROJECT_NAME}
${MSG_FILES} ${SRV_FILES} ${ACTION_FILES}
DEPENDENCIES std_msgs geometry_msgs
)
# 3) 链接生成的 typesupport 库
rosidl_get_typesupport_target(cpp_typesupport
${PROJECT_NAME} "rosidl_typesupport_cpp")
target_link_libraries(${LIBRARY_NAME} ${cpp_typesupport})
# 4) 包必须 rosidl_interface_packages
```
**package.xml 必须加**:
```xml
rosidl_interface_packages
rosidl_default_generators
rosidl_default_runtime
```
---
## 📖 在自己的代码里使用生成的接口
### C++
```cpp
#include "cpp_custom_interface/msg/sensor_reading.hpp"
#include "cpp_custom_interface/srv/get_calibration.hpp"
#include "cpp_custom_interface/action/move_arm.hpp"
// 使用消息类型
auto msg = cpp_custom_interface::msg::SensorReading();
msg.sensor_id = "imu_0";
msg.value = 1.23;
// Publisher
auto pub = create_publisher("topic", 10);
```
### Python
```python
from cpp_custom_interface.msg import SensorReading
from cpp_custom_interface.srv import GetCalibration
from cpp_custom_interface.action import MoveArm
msg = SensorReading()
msg.sensor_id = 'imu_0'
```
**注意**:Python 包名是 `cpp_custom_interface.msg` 而不是 `cpp_custom_interface/msg`。
---
## 🧪 跑测试
```bash
colcon test --packages-select cpp_custom_interface
colcon test-result --all --verbose
```
**预期**:`cpp_custom_interface: gtest 3/3 ✓` 全部通过。
---
## 🔧 自己定义接口
1. 在 `msg/`、`srv/`、`action/` 下新建 `.msg`/`.srv`/`.action` 文件
2. 改 `CMakeLists.txt` 的 `MSG_FILES`/`SRV_FILES`/`ACTION_FILES` 列表
3. `colcon build`
4. 生成的代码在 `install/cpp_custom_interface/include/`(C++)或 `install/cpp_custom_interface/lib/python3.10/site-packages/`(Python)
---
## 📚 深入学习
- [doc/16-custom-interfaces.md](../../doc/16-custom-interfaces.md) — 自定义接口深度(嵌套 / 数组 / 常量)
---
## ⏭️ 下一个包
继续学 **[py_vision_demo](../py_vision_demo/README.md)** — 图像话题(cv_bridge + OpenCV)。
---
## 📍 学习路径导航
| ⏮ 上一个 | 🏠 当前位置 | ⏭ 下一个 |
|---|---|---|
| [py_params — Python Parameter](../py_params/README.md) | **cpp_custom_interface — C++ 自定义接口** | [py_vision_demo — Python 图像](../py_vision_demo/README.md) |
📍 完整 12 包学习顺序见 [主 README](../../README.md#-12-包推荐学习顺序)