🤖 roboto_origin_03 Wiki
首页 / IMU 子模块 / 串口接口配置指南

本文档面向已完成 环境依赖与安装 的中级开发者,讲解如何通过 UART 串口连接并配置 HiPNUC 系列 IMU 传感器。串口通信相比 CAN 接口具有接线简单、无需配置网络接口的优势,适合快速原型验证与实验室调试场景。阅读本指南后,你将掌握从硬件连接到 Python/C++ 代码调用的完整串口配置链路。

Sources: README_CN.md

串口架构与数据流

roboto_imu 中,串口通信由 IMUSerialPort 独立封装,通过回调机制将原始字节流注入 HiPNUC 私有协议解码器。整个链路遵循“打开设备 → 启动实时接收线程 → 字节级协议解析 → 线程安全数据缓存”四级结构,确保从 /dev/ttyUSB0get_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

接收线程与实时调度

IMUSerialPortinit() 内部创建独立 rx_thread_,并为其设置 SCHED_FIFO 实时调度策略,优先级固定为 80。该线程以 pthread_setname_np 命名为 serial_rx,可通过 htopps -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

下一步

完成串口配置后,建议深入以下章节以全面理解驱动内部机制: