本协议用于STM32H7割草机MCU与Python控制程序之间的双向通信。
传输介质: UART5, 921600 bps, 8N1
传输方式: DMA发送和接收
校验方式: 16位累加和 (Checksum_u16)
所有数据包采用统一帧格式:
┌──────┬──────┬──────┬────────┬─────────┬──────────┬──────┬──────┐
│ AA │ 55 │ TYPE │ LEN(L) │ LEN(H) │ PAYLOAD │ CRC │ 0D0A │
├──────┼──────┼──────┼────────┼─────────┼──────────┼──────┼──────┤
│ 帧头1 │ 帧头2 │ 类型 │ 长度低 │ 长度高 │ 数据负载 │ 校验 │ 帧尾 │
│ 1B │ 1B │ 1B │ 1B │ 1B │ N Bytes │ 2B │ 2B │
└──────┴──────┴──────┴────────┴─────────┴──────────┴──────┴──────┘
字段说明:
- 帧头 (2B): 固定为 0xAA 0x55
- 类型 (1B): 数据包类型标识
- 0x01 - GPS数据 (STM32→Python)
- 0x02 - IMU数据 (STM32→Python)
- 0x10 - 控制命令 (Python→STM32)
- 长度 (2B): PAYLOAD字段的字节数 (小端序)
- PAYLOAD (NB): 实际数据内容,结构体packed,小端序
- 校验和 (2B): 从帧头到PAYLOAD结尾的16位累加和 (小端序)
- 帧尾 (2B): 固定为 0x0D 0x0A (CR LF)
校验和计算:python checksum = sum(frame_bytes) & 0xFFFF # 16位累加和
方向: STM32 → Python
频率: 10 Hz
负载长度: 44 字节
C结构体定义:c typedef struct __attribute__((packed)) { double m_dLatitude; // 纬度(°), 正数为北纬 (8B) double m_dLongitude; // 经度(°), 正数为东经 (8B) float m_fHeadingAngle; // 航向角(°), 0~360, 北为0顺时针 (4B) float m_fEastVelocity; // 东方向速度(m/s) (4B) float m_fNorthVelocity; // 北方向速度(m/s) (4B) float m_fUpVelocity; // 天顶方向速度(m/s) (4B) float m_fAltitude; // 高程(m) (4B) uint32_t m_u32UTCTime; // UTC时间, hhmmss格式 (例: 123045) (4B) uint8_t m_u8PositionQuality; // 定位质量: 0=无效,1=单点,2=差分,4=固定,5=浮点 (1B) uint8_t m_u8SatelliteCount; // 卫星数量 (1B) uint8_t m_u8Reserved[2]; // 保留字节(对齐到4字节边界) (2B) } ST_PythonLink_GPS; // 总计44字节
Python解析结构:python GPS_STRUCT_FMT = '<dd7fIBB2x' # 小端序 # dd: double (8B) x2 = 16B # 7f: float (4B) x7 = 28B # I: uint32 (4B) x1 = 4B # BB: uint8 (1B) x2 = 2B # 2x: padding (2B) = 2B # 总计: 16+28+4+2+2 = 52B (实际44B,7f应为5f)
注意: 实际结构为 2个double + 5个float + 1个uint32 + 2个uint8 + 2字节对齐 = 44字节
字段说明:
- m_dLatitude/Longitude: WGS84坐标系,精度约0.00000001°
- m_fHeadingAngle: 基于东北速度计算,北为0°顺时针增加
- m_fEastVelocity/NorthVelocity: 地面速度分量,ENU坐标系
- m_u32UTCTime: 示例: 123045 表示 12:30:45 UTC
- m_u8PositionQuality:
- 0: 无效定位
- 1: 单点定位 (GPS)
- 2: 差分定位 (DGPS)
- 4: RTK固定解 (最高精度)
- 5: RTK浮点解
完整帧示例 (十六进制): AA 55 01 38 00 [56 bytes GPS data] [CRC 2B] 0D 0A
方向: STM32 → Python
频率: 100 Hz
负载长度: 32 字节
C结构体定义:c typedef struct __attribute__((packed)) { float m_fAccelX; // X轴加速度(g) float m_fAccelY; // Y轴加速度(g) float m_fAccelZ; // Z轴加速度(g) float m_fGyroX; // X轴角速度(°/s) float m_fGyroY; // Y轴角速度(°/s) float m_fGyroZ; // Z轴角速度(°/s) float m_fTemperature; // 传感器温度(℃) uint32_t m_u32UTCTime; // UTC时间(毫秒), hhmmssmmm格式 } ST_PythonLink_IMU; // 总计32字节
Python解析结构:python IMU_STRUCT_FMT = '<7f I' # 小端序 # f: float (4B) x7 # I: uint32 (4B) x1
字段说明:
- m_fAccelX/Y/Z: 加速度,单位g (1g ≈ 9.8m/s²)
- m_fGyroX/Y/Z: 角速度,单位°/s
- m_fTemperature: IMU芯片温度,用于温度补偿
- m_u32UTCTime: 示例: 123045678 表示 12:30:45.678 UTC
完整帧示例 (十六进制): AA 55 02 20 00 [32 bytes IMU data] [CRC 2B] 0D 0A
方向: Python → STM32
频率: 74 Hz (控制周期)
负载长度: 4 字节
C结构体定义:c typedef struct __attribute__((packed)) { uint16_t m_u16SteeringPWM; // 转向PWM值 (1000~2000us) uint16_t m_u16ThrottlePWM; // 油门PWM值 (1000~2000us) } ST_PythonLink_Control; // 总计4字节
Python打包结构:python CONTROL_STRUCT_FMT = '<HH' # 小端序 # H: uint16 (2B) x2
字段说明:
- m_u16SteeringPWM: 转向舵机PWM脉宽
- 1000us: 最大右转
- 1500us: 中立位置
- 2000us: 最大左转
m_u16ThrottlePWM: 油门电机PWM脉宽完整帧示例 (十六进制): AA 55 10 04 00 DC 05 DC 05 [CRC 2B] 0D 0A ↑ ↑─────↑───── 4B 1500 1500 (中立位置)
Python发送示例:
```python
import struct
def send_control_command(ser, steering_pwm, throttle_pwm):
# 限制范围
steering_pwm = max(1000, min(2000, steering_pwm))
throttle_pwm = max(1000, min(2000, throttle_pwm))
# 构建帧
payload = struct.pack('<HH', steering_pwm, throttle_pwm)
frame_for_checksum = bytes([0xAA, 0x55, 0x10]) + struct.pack('<H', 4) + payload
checksum = sum(frame_for_checksum) & 0xFFFF
packet = frame_for_checksum + struct.pack('<H', checksum) + bytes([0x0D, 0x0A])
ser.write(packet)
ser.flush()
---
## 错误处理
### STM32端
**接收错误处理:**
1. 帧头不匹配 → 丢弃字节,继续搜索
2. 长度异常 → 丢弃该帧,错误计数+1
3. 校验和错误 → 丢弃该帧,错误计数+1
4. 帧尾不匹配 → 丢弃该帧,错误计数+1
**超时保护:**
- 控制命令超时 (1秒) → 恢复中立位置 (1500us)
- SBUS信号超时 (1秒) → Failsafe中立位置
### Python端
**接收错误处理:**
1. 串口超时 → 返回None,等待下一帧
2. 帧头不匹配 → 继续查找下一个帧头
3. 校验和错误 → 打印警告,丢弃该帧
4. 数据类型未知 → 打印警告,丢弃该帧
**统计监控:**
receiver.print_stats() # 每10秒打印接收统计
---
## 性能指标
| 项目 | 指标 |
|------|------|
| 通信速率 | 921600 bps |
| GPS数据频率 | 10 Hz |
| IMU数据频率 | 100 Hz |
| 控制命令频率 | 74 Hz |
| 单帧开销 | 9字节 (帧头+类型+长度+校验+帧尾) |
| GPS带宽占用 | 10 × (56+9) = 650 Bps |
| IMU带宽占用 | 100 × (32+9) = 4100 Bps |
| 控制带宽占用 | 74 × (4+9) = 962 Bps |
| 总带宽占用 | ~5.7 KBps (~6% @ 921600bps) |
| 延迟 | < 10ms (单帧传输时间) |
---
## 调试建议
### 1. 使用串口监视工具
推荐工具: [Serial Port Monitor](https://www.eltima.com/products/serial-port-monitor/) 或 [Termite](https://www.compuphase.com/software_termite.htm)
监视UART5数据流,验证帧格式正确性。
### 2. 打印十六进制帧
**Python端:**
hex_str = ' '.join(f'{b:02X}' for b in packet)
print(f"TX: {hex_str}")
```
STM32端:c for (int i = 0; i < frameLen; i++) { HIDO_Debug2("%02X ", g_au8PythonLinkTxBuf[i]); } HIDO_Debug2("\r\n");
确保两端使用相同的校验算法:c // STM32 uint16_t checksum = 0; for (int i = 0; i < len; i++) { checksum += data[i]; } checksum &= 0xFFFF;
# Python
checksum = sum(data) & 0xFFFF
STM32:c PythonLink_PrintDebugInfo(); // GPS packets sent: 1000 // IMU packets sent: 10000 // Control RX count: 740 // Error count: 2
Python:python receiver.print_stats() # GPS数据包: 1000 # IMU数据包: 10000 # 错误计数: 2
#include "PythonLink.h"
#include "GPS.h"
void GPS_Task(void) {
ST_GPRMI gprmi;
if (GPS_ParseGPRMI(&gprmi) == HIDO_OK) {
// 立即发送到Python
PythonLink_SendGPSData(&gprmi);
}
}
from gps_imu_receiver import GPSIMUReceiver
receiver = GPSIMUReceiver("COM3", 921600)
receiver.connect()
while True:
gps_data, imu_data = receiver.receive_packet()
if gps_data:
print(f"GPS: {gps_data.latitude:.8f}, {gps_data.longitude:.8f}")
if imu_data:
print(f"IMU: Accel=({imu_data.accel_x:.3f}, "
f"{imu_data.accel_y:.3f}, {imu_data.accel_z:.3f})")
from realtime_control import ControlCommandSender
import serial
ser = serial.Serial("COM3", 921600)
sender = ControlCommandSender(ser)
# 发送中立位置
sender.send_control_command(1500, 1500)
# 发送前进+左转
sender.send_control_command(1800, 1600)
维护者: Auto-generated
联系方式: 项目Issue跟踪
最后更新: 2025-11-13