本文档面向已完成 环境依赖与安装 的中级开发者,讲解如何通过 UART 串口连接并配置 HiPNUC 系列 IMU 传感器。串口通信相比 CAN 接口具有接线简单、无需配置网络接口的优势,适合快速原型验证与实验室调试场景。阅读本指南后,你将掌握从硬件连接到 Python/C++ 代码调用的完整串口配置链路。
Sources: README_CN.md
串口架构与数据流
在 roboto_imu 中,串口通信由 IMUSerialPort 独立封装,通过回调机制将原始字节流注入 HiPNUC 私有协议解码器。整个链路遵循“打开设备 → 启动实时接收线程 → 字节级协议解析 → 线程安全数据缓存”四级结构,确保从 /dev/ttyUSB0 到 get_quat() 的端到端延迟最小化。
graph LR
A[物理串口<br/>ttyUSB0] -->|termios| B[IMUSerialPort<br/>SCHED_FIFO 线程]
B -->|回调| C[hipnuc_input<br/>逐字节解析]
C -->|帧同步+CRC| D[hi91_t 数据包]
D -->|shared_mutex| E[HipnucIMUDriver<br/>数据缓存]
E -->|读锁| F[get_quat / get_ang_vel]
串口协议栈采用 8N1 固定帧格式(8 数据位、无校验、1 停止位),波特率由用户在构造时显式指定。IMUSerialPort 内部使用 select() 实现 1ms 超时轮询,兼顾实时响应与 CPU 占用控制。
Sources: serial_port.hpp, serial_port.cpp
前置条件与硬件连接
接线要求
HiPNUC 串口模块通常引出 TTL 电平 UART 引脚,典型接线如下:
| 主机侧 | IMU 侧 | 说明 |
|---|---|---|
| TX | RX | 主机发送,IMU 接收 |
| RX | TX | 主机接收,IMU 发送 |
| GND | GND | 共地,必须连接 |
注意:若使用 USB 转串口模块(如 FT232/CH340),请确保模块地线与 IMU 地线相连;差分地线会导致帧头同步失败或 CRC 校验错误。
用户权限
Linux 下访问 /dev/ttyUSB* 或 /dev/ttyACM* 需要当前用户属于 dialout 组。若未加入,运行时会报 Failed to open serial port 异常。通过以下命令检查并修复:
# 检查当前用户是否在 dialout 组
groups $USER
# 若输出不含 dialout,执行添加
sudo usermod -aG dialout $USER
# 重新登录或执行 newgrp dialout 使权限生效
newgrp dialout
Sources: serial_port.cpp
波特率与串口参数
IMUSerialPort::init() 在打开设备后,通过 termios 结构体完成参数下发。下表列出了驱动层显式支持的波特率;传入其他值将回退到 115200。
| 波特率 | termios 宏 | 典型应用场景 |
|---|---|---|
| 9600 | B9600 |
低速调试、长距离传输 |
| 19200 | B19200 |
兼容旧固件 |
| 38400 | B38400 |
中低速采集 |
| 57600 | B57600 |
平衡速率与稳定性 |
| 115200 | B115200 |
默认推荐 |
| 230400 | B230400 |
高帧率场景 |
| 460800 | B460800 |
大数据量输出 |
| 921600 | B921600 |
极限吞吐测试 |
固定参数一览:
| 参数 | 设定值 | 说明 |
|---|---|---|
| 数据位 | CS8 |
8 位 |
| 校验 | 无 (~PARENB) |
无校验位 |
| 停止位 | 1 (~CSTOPB) |
1 位停止位 |
| 流控 | 无 (~CRTSCTS) |
禁用硬件流控 |
| 模式 | 原始模式 | 禁用回显、规范模式、信号字符 |
Sources: serial_port.cpp
配置流程:从代码到数据
以下流程图展示了在应用程序中启用串口 IMU 的完整步骤。无论是 C++ 还是 Python,接口参数与调用顺序完全一致。
flowchart TD
A[确定串口设备<br/>如 /dev/ttyUSB0] --> B[确认波特率<br/>与 IMU 固件匹配]
B --> C[调用 create_imu<br/>interface_type='serial']
C --> D[驱动内部自动打开串口<br/>启动 RX 线程]
D --> E[设置回调<br/>hipnuc_input 逐字节解码]
E --> F[通过 get_quat / get_ang_vel 读取]
F --> G[程序退出时<br/>析构函数自动 close]
Python SDK 配置示例
from imu_py import IMUDriver
imu = IMUDriver.create_imu(
imu_id=0x01, # 串口场景下作为标识,不影响总线仲裁
interface_type="serial", # 固定为 serial
interface="/dev/ttyUSB0", # 根据实际设备名调整
imu_type="HIPNUC",
baudrate=115200 # 必须与 IMU 输出波特率一致
)
# 读取传感器数据
quat = imu.get_quat() # [w, x, y, z]
gyro = imu.get_ang_vel() # [x, y, z] rad/s
accel = imu.get_lin_acc() # [x, y, z] m/s²
temp = imu.get_temperature() # °C
C++ SDK 配置示例
#include "imu_driver.hpp"
auto imu = IMUDriver::create_imu(
0x01, // imu_id
"serial", // interface_type
"/dev/ttyUSB0", // interface
"HIPNUC", // imu_type
115200 // baudrate
);
auto quat = imu->get_quat(); // {w, x, y, z}
auto gyro = imu->get_ang_vel(); // {x, y, z} rad/s
auto accel = imu->get_lin_acc(); // {x, y, z} m/s²
float temp = imu->get_temperature(); // °C
上述两种语言在底层共享同一套 HipnucIMUDriver 实现:当 interface_type_ == "serial" 时,驱动会调用 IMUSerialPort::open() 生成串口实例,并通过 std::bind 将成员函数 serial_rx_cbk 注册为接收回调。
Sources: hipnuc_imu_driver.cpp, pybind_module.cpp
接收线程与实时调度
IMUSerialPort 在 init() 内部创建独立 rx_thread_,并为其设置 SCHED_FIFO 实时调度策略,优先级固定为 80。该线程以 pthread_setname_np 命名为 serial_rx,可通过 htop 或 ps -L 直接观察。
线程主循环采用 select() 监听文件描述符,超时时间 1ms。当检测到可读事件时,一次性读取最多 1024 字节(BUF_SIZE)到本地缓冲区,随后立即触发用户回调。这种设计避免了逐字节 read() 带来的系统调用开销,同时保证 1ms 级响应延迟。
Sources: serial_port.cpp
协议解析:从字节流到结构化数据
串口字节流进入 HipnucIMUDriver::serial_rx_cbk 后,被逐字节喂入 hipnuc_input()。解码器采用状态机方式寻找帧头 0x5A 0xA5,随后按固定 6 字节头部解析长度字段与 CRC16,最终调用 parse_data() 填充 hi91_t 结构体。
sequenceDiagram
participant S as IMUSerialPort
participant D as HipnucIMUDriver
participant Dec as hipnuc_dec
participant Buf as sensor_data_
S->>S: select() 超时 1ms
S->>S: read(fd, buf, 1024)
loop 逐字节
S->>D: callback_(buf, n)
D->>Dec: hipnuc_input(raw, data[i])
alt 帧头 5A A5 匹配
Dec->>Dec: 累积 CH_HDR_SIZE 字节
Dec->>Dec: CRC 校验通过
Dec->>Buf: 更新 acc/gyr/quat/temp
end
end
Note over Buf: shared_mutex 写锁保护
对于当前 HiPNUC 串口输出,最常用的是 HI91 数据包(0x91),包含 float 类型的加速度(m/s²)、陀螺仪(°/s,驱动内转为 rad/s)、四元数、温度及气压。解码成功后,get_quat() 等方法即可通过 std::shared_lock 安全读取缓存。
Sources: hipnuc_imu_driver.cpp, hipnuc_dec.c, hipnuc_dec.h
故障排查指南
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
Failed to open serial port: /dev/ttyUSB0 |
设备不存在或权限不足 | 确认 ls /dev/ttyUSB* 可见;执行 groups 检查 dialout |
打开成功但 get_quat() 始终返回 [0,0,0,0] |
波特率不匹配 | 核对 IMU 固件波特率与代码传入值;使用 stty -F /dev/ttyUSB0 查看 |
| 数据偶发跳变或 CRC 错误 | 接线松动或 TTL 电平不兼容 | 检查 GND 是否共地;缩短杜邦线长度;降低波特率测试 |
程序退出后 /dev/ttyUSB0 仍被占用 |
未正常析构或异常退出 | 确保 IMUDriver 对象生命周期受控;手动 lsof /dev/ttyUSB0 确认 |
| 高负载下丢包 | 用户线程优先级低于串口 RX 线程,但解码线程阻塞 | 避免在回调内执行耗时操作;hipnuc_input 仅为纯计算,通常无此问题 |
Sources: serial_port.cpp, hipnuc_imu_driver.hpp
下一步
完成串口配置后,建议深入以下章节以全面理解驱动内部机制:
- 若需对比 CAN 与串口的架构差异,请参阅 CAN 接口配置指南。
- 若对
IMUSerialPort的select()轮询与SCHED_FIFO实时线程感兴趣,请参阅 串口通信与 UART 接收线程。 - 若需了解协议解码器如何解析
0x5A 0xA5帧头与 HI91 载荷,请参阅 超核私有协议解码。 - 若关注多线程下的数据一致性,请参阅 传感器数据访问与线程安全。