1. 系统架构 (System Architecture)
整个系统由三部分组成:**RflySim 仿真端**、**转发桥接节点 (Bridge Node)**、**ROS 控制端**。
graph TD
subgraph Windows ["RflySim Windows"]
Sim["CopterSim/Simulink DLL"]
Sim -->|"(UDP 30101+)"| Bridge
Bridge -->|"(UDP 30100+)"| Sim
end
subgraph Linux ["WSL / Linux"]
Bridge["Bridge Node (C++/Python)"]
Bridge -->|"Pub: /mavros/..."| ROS["ROS Control Algo"]
ROS -->|"Sub: /mavros/..."| Bridge
end
关键组件 (Key Components)
- RflySim 综合模型 (DLL): 运行在 CopterSim 平台,负责无人机动力学解算。如果不使用 PX4 SITL,模型直接通过 UDP 在端口 30100 (Recv) / 30101 (Send) 上通信。
- UDP Bridge (转发节点): 本例程的核心。它充当中间人,将 RflySim 的 UDP 协议转换为 ROS 消息(Topics/Services)。
- ROS 算法: 标准的 ROS 节点(如 Ros12MultiUav.py),通过 mavros 兼容的话题控制无人机。
2. 通信协议细节 (Communication Protocol Details)
2.1 端口分配 (Port Mapping)
系统默认支持多机,通过 CopterID 区分端口。
| 通信方向 (Direction) | 源端口 (Source) | 目标端口 (Dest) | 描述 (Description) |
| RflySim -> Bridge (上行) | Random | 30100 + ID*2 - 1 | 仿真状态数据 (State Data)
ID=1 -> 30101
ID=2 -> 30103 |
| Bridge -> RflySim (下行) | Random | 30100 + (ID-1)*2 | 控制指令数据 (Control Cmd)
ID=1 -> 30100
ID=2 -> 30102 |
2.2 数据结构 (Data Structures)
上行数据包 (SOut2Simulator) - 168 Bytes
用于传输飞机状态到 ROS。
| 字段 (Field) | 类型 (Type) | 偏移 (Offset) | 说明 (Note) |
| Checksum | int32 | 0 | 校验位 (123456789) |
| CopterID | int32 | 4 | 飞机 ID |
| VehicleType | int32 | 8 | 机型 |
| VelE | float[3] | 16 | 地球坐标系速度 (NED) |
| AngEuler | float[3] | 28 | 欧拉角 |
| AngQuatern | float[4] | 40 | 四元数 |
| PosE | double[3] | 112 | 地球坐标系位置 (NED) |
| PosGPS | double[3] | 136 | GPS (Lat, Lon, Alt) |
下行数据包 (SILIntFloat) - 120 Bytes
用于从 ROS 发送控制指令到仿真。
| 字段 (Field) | 类型 (Type) | 偏移 (Offset) | 说明 (Note) |
| Checksum | int32 | 0 | 校验位 (1234567897) |
| CopterID | int32 | 4 | 飞机 ID |
| InInts | int32[8] | 8 | 控制标志位 (Flags, Modes) |
| InFloats | float[20] | 40 | 控制量 (Pos, Vel, Yaw...) |
- 控制标志位 (InInts):
- ICmd: 启用标志,如 CmdArmed | CmdPosition (解锁+位置控制)。
- IOffboard: 模式标志,如 HasPos | HasYaw (位置+偏航有效) 或 HasVel | FRD (机体速度有效)。
3. 调用流程 (Calling Workflow)
3.1 启动阶段 (Startup)
- 启动仿真: 运行 MulticopterNOpx4.bat,CopterSim 加载 DLL 模型,开始监听 30100 等端口。
- 启动 Bridge:
- C++: build_bridge.sh 编译并运行 udp_ros_bridge_node。
- Python: start_all.sh 运行 SimulinkAndDllMavrosStart.py,内部实例化 UdpRosBridge 类。
- Bridge 绑定 UDP 端口 30101, 30103... 并注册 ROS 节点。
3.2 运行阶段 (Run Loop)
A. 状态更新 (Sim -> ROS)
- RflySim 每毫秒发送 UDP 包。
- Bridge 的接收线程 (recvLoop) 收到数据。
- 解析 168 字节数据包 (parseSOut2Simulator)。
- 填充 ROS 消息:
- geometry_msgs::PoseStamped -> /mavros/local_position/pose
- geometry_msgs::TwistStamped -> /mavros/local_position/velocity_local
- sensor_msgs::NavSatFix -> /mavros/global_position/global
- 发布消息 (Publish)。
B. 控制指令 (ROS -> Sim)
- 用户脚本 (Ros12MultiUav.py) 计算期望位置/速度。
- 用户脚本发布消息 mavros_msgs/PositionTarget 到 /mavros/setpoint_raw/local。
- Bridge 的订阅回调 (handleSetpoint) 被触发。
- Bridge 根据 type_mask 判断控制模式:
- 位置控制: 提取 xyz,设置 HasPos。
- 速度控制: 提取 vx,vy,vz,设置 HasVel。
- 封装 120 字节 SILIntFloat 结构体。
- 通过 UDP 发送到 30100 (ID1) 或 30102 (ID2)。
- RflySim 接收指令并驱动模型运动。
4. C++ 与 Python 实现对比 (Comparison)
| 特性 (Feature) | C++ Demo | Python Demo |
| 性能 (Performance) | 高,适合高频率闭环控制 | 中,受限于 GIL,适合中低频控制 |
| 并发 (Concurrency) | 多线程 + select IO 复用 | 多线程 (threading) |
| ROS 兼容性 | 源码级兼容 ROS1/ROS2 (通过宏切换) | 依赖 rospy 或 rclpy 库 |
| 部署 (Deployment) | 需要编译 (colcon/catkin) | 直接运行,无需编译 |
| 代码位置 | udp_ros_bridge.cpp | DllSimCtrlAPIROS.py |
5. 总结 (Summary)
两个例程本质上实现了**相同的协议转换逻辑**。用户可以根据开发习惯选择:
- 如果需要修改底层通信协议或追求极致性能,建议基于 C++ Demo 修改。
- 如果只是进行简单的算法验证或利用 Python 丰富的生态(如 PyTorch/TensorFlow),建议使用 **Python Demo**。 无论哪种方式,其对外暴露的 ROS 接口(Topic/Service)是一致的,因此上层控制算法(如 Ros12MultiUav.py)可以通用。