# RM Serial Driver 使用文档 ## 功能特性 ✅ **稳定可靠的串口通信** - 自动重连机制,断线后持续尝试重连 - 完善的异常处理,不会因串口异常而崩溃 - CRC16 校验保证数据完整性 ✅ **灵活的协议设计** - 支持多种数据包类型(MCU、裁判系统、AI) - 易于扩展新的数据包类型 - 详细的协议修改指南 ✅ **实时统计监控** - 接收/发送数据包计数 - CRC 错误统计 - 连接状态监控 ✅ **高性能** - 独立接收线程,不阻塞主程序 - 支持多实例,可同时管理多个串口 ## 快速开始 ### 1. 安装依赖 ```bash # 安装 serial 库 git clone https://github.com/ZhaoXiangBox/serial.git cd serial mkdir build && cd build cmake .. make sudo make install ``` ### 2. 编译 ```bash cd ~/MOVE_AI colcon build --packages-select rm_serial_driver source install/setup.bash ``` ### 3. 配置参数 编辑 `config/serial_config.yaml`: ```yaml /rm_serial_driver: ros__parameters: device_name: /dev/ttyACM0 # 串口设备 baud_rate: 115200 # 波特率 reconnect_interval_ms: 1000 # 重连间隔(毫秒) enable_statistics: true # 启用统计信息 topic_prefix: serial # 话题前缀 ``` ### 4. 启动驱动 #### 单个串口 ```bash ros2 run rm_serial_driver rm_serial_driver --ros-args \ -p device_name:=/dev/ttyACM0 \ -p baud_rate:=115200 \ -p topic_prefix:=serial ``` #### 多个串口(使用 launch 文件) ```bash ros2 launch rm_serial_driver rm_serial_driver.launch.py ``` ### 5. 查看话题 ```bash # 列出所有话题 ros2 topic list # 查看 MCU 数据 ros2 topic echo /serial/data_mcu # 查看裁判系统数据 ros2 topic echo /serial/data_ref # 发送 AI 控制命令 ros2 topic pub /serial/data_ai rm_msgs/msg/DataAI "{...}" ``` ## 话题接口 ### 订阅的话题 | 话题名 | 消息类型 | 说明 | |--------|---------|------| | `{prefix}/data_ai` | `rm_msgs/msg/DataAI` | AI 控制命令,发送给单片机 | ### 发布的话题 | 话题名 | 消息类型 | 说明 | |--------|---------|------| | `{prefix}/data_mcu` | `rm_msgs/msg/DataMCU` | 单片机姿态数据(四元数、欧拉角) | | `{prefix}/data_ref` | `rm_msgs/msg/DataRef` | 裁判系统数据(血量、比赛进度) | ## 参数说明 | 参数名 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | `device_name` | string | `/dev/ttyACM0` | 串口设备路径 | | `baud_rate` | int | `115200` | 波特率 | | `reconnect_interval_ms` | int | `1000` | 重连间隔(毫秒) | | `enable_statistics` | bool | `true` | 是否启用统计信息 | | `topic_prefix` | string | `serial` | ROS 话题前缀 | ## 协议格式 ### 数据包结构 所有数据包都遵循以下格式: ``` +----+----------+-------+ | ID | Data | CRC | +----+----------+-------+ | 1B | N | 2B | ``` - **ID**: 1 字节,数据包类型标识 - **Data**: N 字节,实际数据内容 - **CRC**: 2 字节,CRC16-CCITT 校验码 ### 数据包类型 #### 1. MCU 数据包 (ID: 0xC4) 从单片机接收的姿态数据: ```cpp struct DataMCU { struct { float q0, q1, q2, q3; // 四元数 } quat; struct { float yaw, pit, rol; // 欧拉角(度) } gimbal; uint8_t notice; // 控制命令 }; ``` #### 2. 裁判系统数据包 (ID: 0xA8) 从裁判系统接收的比赛数据: ```cpp struct DataReferee { uint16_t remain_hp; // 剩余血量 uint8_t game_progress : 4; // 比赛进度 uint16_t stage_remain_time; // 剩余时间(秒) }; ``` #### 3. AI 控制数据包 发送给单片机的控制命令: ```cpp struct DataAI { struct { float yaw, pit, rol; // 欧拉角增量 } delta_eulr; struct { float vx, vy, wz; // 位置增量 } delta_pos; uint8_t notice; // 控制命令 }; ``` ## 使用示例 ### Python 示例 #### 接收 MCU 数据 ```python #!/usr/bin/env python3 import rclpy from rclpy.node import Node from rm_msgs.msg import DataMCU class MCUListener(Node): def __init__(self): super().__init__('mcu_listener') self.subscription = self.create_subscription( DataMCU, 'serial/data_mcu', self.mcu_callback, 10) def mcu_callback(self, msg): self.get_logger().info( f'姿态: yaw={msg.gimbal.yaw:.2f}, ' f'pitch={msg.gimbal.pit:.2f}, ' f'roll={msg.gimbal.rol:.2f}') def main(): rclpy.init() node = MCUListener() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ``` #### 发送 AI 控制命令 ```python #!/usr/bin/env python3 import rclpy from rclpy.node import Node from rm_msgs.msg import DataAI class AIController(Node): def __init__(self): super().__init__('ai_controller') self.publisher = self.create_publisher(DataAI, 'serial/data_ai', 10) self.timer = self.create_timer(0.1, self.send_command) def send_command(self): msg = DataAI() msg.delta_eulr.yaw = 0.1 msg.delta_eulr.pit = 0.0 msg.delta_eulr.rol = 0.0 msg.delta_pos.vx = 1.0 msg.delta_pos.vy = 0.0 msg.delta_pos.wz = 0.0 msg.notice = 1 self.publisher.publish(msg) def main(): rclpy.init() node = AIController() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ``` ### C++ 示例 ```cpp #include #include "rm_msgs/msg/data_mcu.hpp" #include "rm_msgs/msg/data_ai.hpp" class RMController : public rclcpp::Node { public: RMController() : Node("rm_controller") { // 订阅 MCU 数据 mcu_sub_ = create_subscription( "serial/data_mcu", 10, std::bind(&RMController::mcu_callback, this, std::placeholders::_1)); // 发布 AI 控制命令 ai_pub_ = create_publisher("serial/data_ai", 10); // 定时发送控制命令 timer_ = create_wall_timer( std::chrono::milliseconds(100), std::bind(&RMController::send_command, this)); } private: void mcu_callback(const rm_msgs::msg::DataMCU::SharedPtr msg) { RCLCPP_INFO(get_logger(), "姿态: yaw=%.2f, pitch=%.2f, roll=%.2f", msg->gimbal.yaw, msg->gimbal.pit, msg->gimbal.rol); } void send_command() { auto msg = rm_msgs::msg::DataAI(); msg.delta_eulr.yaw = 0.1; msg.delta_pos.vx = 1.0; msg.notice = 1; ai_pub_->publish(msg); } rclcpp::Subscription::SharedPtr mcu_sub_; rclcpp::Publisher::SharedPtr ai_pub_; rclcpp::TimerBase::SharedPtr timer_; }; int main(int argc, char ** argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared()); rclcpp::shutdown(); return 0; } ``` ## 故障排查 ### 串口无法打开 ```bash # 检查串口设备 ls -l /dev/ttyUSB* /dev/ttyACM* # 添加用户到 dialout 组 sudo usermod -aG dialout $USER # 注销后重新登录 # 检查串口权限 sudo chmod 666 /dev/ttyACM0 ``` ### 查看详细日志 ```bash ros2 run rm_serial_driver rm_serial_driver --ros-args --log-level debug ``` ### 测试串口通信 ```bash # 使用 minicom sudo apt install minicom minicom -D /dev/ttyACM0 -b 115200 # 或使用 screen screen /dev/ttyACM0 115200 ``` ### 常见错误 #### 1. "串口打开失败" - 检查设备路径是否正确 - 检查串口权限 - 确认设备已连接 #### 2. "CRC 校验失败" - 确认单片机和上位机使用相同的 CRC 算法 - 检查数据包结构是否匹配 - 验证字节序是否一致 #### 3. "串口意外关闭" - 检查 USB 线缆连接 - 查看系统日志:`dmesg | tail` - 确认单片机程序正常运行 ## 性能优化 ### 1. 调整接收缓冲区 在 `receive_data()` 中: ```cpp // 增大缓冲区限制 if (serial_buffer_.size() > 8192) // 默认 4096 { serial_buffer_.clear(); } ``` ### 2. 调整统计间隔 ```cpp // 减少统计频率 statistics_timer_ = this->create_wall_timer( std::chrono::seconds(10), // 默认 5 秒 std::bind(&RMSerialDriver::statistics_timer_callback, this)); ``` ### 3. 禁用调试日志 生产环境中使用 INFO 级别: ```bash ros2 run rm_serial_driver rm_serial_driver --ros-args --log-level info ``` ## 更多文档 - [协议修改指南](PROTOCOL_GUIDE.md) - 详细说明如何修改通信协议 - [单片机示例代码](../examples/) - Arduino/STM32 示例代码 ## 许可证 Apache License 2.0