STM32 + UWB + MPU9250 DS-TWR 定位方案技术手册

1. 文档说明

本文档用于说明本项目的方案设计、设备角色、固件运行流程、UWB 测距流程、MPU9250 运动检测流程,以及上位机接收与定位处理流程。阅读对象为客户、项目集成方、现场调试人员和技术支持人员。

本方案由标签、基站和上位机三部分组成:

  • 标签:主动发起 UWB 测距,采集运动检测状态,并周期性汇总上报。
  • 基站:响应标签测距请求,计算 UWB 距离,并将标签汇总数据转发给上位机。
  • 上位机:接收基站串口数据,解析多基站距离和运动状态,完成简单融合与定位显示。

2. 系统组成

2.1 硬件组成

设备 硬件平台 主要功能
UWB 标签 STM32F401 + DW1000/UWB + MPU9250 发起 DS-TWR 测距,检测运动状态,上报多基站距离
UWB 基站 STM32F103 或 STM32F401 + DW1000/UWB 响应测距请求,计算距离,汇聚并转发数据
上位机 Windows PC + Qt 上位机软件 串口/TCP 接收数据,定位计算,界面显示

2.2 软件组成

软件部分 说明
STM32F1 固件 用于 STM32F103 + UWB 设备,可作为标签或基站固件编译
STM32F4 固件 用于 STM32F401 + UWB + MPU9250 设备,支持运动检测
BP-UWB 公共库 封装 DW1000 初始化、UWB 帧结构、DS-TWR 测距应用逻辑
MPU9250 驱动 配置 MPU9250 Wake-on-Motion 运动检测
上位机软件 接收串口/TCP 数据,解析距离,显示标签位置

3. 方案总体设计

本方案采用 DS-TWR 双边双向测距方式。标签依次与多个基站进行 UWB 测距,基站根据 UWB 收发时间戳计算标签到基站的距离。标签完成一轮测距后,将多基站距离和运动检测状态打包发送到汇聚基站,汇聚基站再通过串口发送给上位机。

整体数据链路如下:

  1. 标签按固定周期轮询基站。
  2. 标签向某个基站发送 P 测距请求。
  3. 基站返回 A 应答。
  4. 标签发送 F 结束帧,帧中携带标签侧时间戳。
  5. 基站根据 P/A/F 三帧计算本轮距离。
  6. 标签从基站应答中获得上一轮距离并缓存。
  7. 标签完成多个基站轮询后,发送 M 汇总包给 0x0001 汇聚基站。
  8. 汇聚基站通过 UART 将 M 包数据透传给上位机。
  9. 上位机解析距离、运动状态,并进行定位显示。

4. 设备角色

4.1 标签 Tag

标签是测距主动方,主要职责包括:

  • 按定时器周期启动测距。
  • 依次访问 0x00010x00020x0003 等基站。
  • 向当前目标基站发送 P 帧。
  • 接收基站 A 帧,并发送 F 帧。
  • 缓存各基站返回的距离。
  • 读取 MPU9250 运动检测标志。
  • 将距离和运动状态打成 M 包发送给汇聚基站。

当前默认配置为 3 基站 2D 定位模式。标签的基站轮询顺序为:

Anchor 0x0001 -> Anchor 0x0002 -> Anchor 0x0003 -> Report to Anchor 0x0001 -> repeat

4.2 基站 Anchor

基站是测距响应方,主要职责包括:

  • 长期开启 DW1000 接收模式。
  • 接收标签 P 帧并立即返回 A 帧。
  • 接收标签 F 帧,计算 DS-TWR 距离。
  • 对距离进行 bias 修正和 Kalman 滤波。
  • 作为汇聚基站时,接收标签 M 包并通过 UART 转发给上位机。

4.3 汇聚基站

地址为 0x0001 的基站同时承担汇聚节点作用:

  • 参与普通 UWB 测距。
  • 接收标签发来的 M 汇总数据。
  • M 包中的上位机数据部分通过串口发出。

4.4 上位机

上位机主要职责包括:

  • 打开串口或 TCP 通道。
  • 接收汇聚基站转发的数据。
  • 查找 mri 数据头。
  • 解析标签 ID、帧号、多基站距离和运动标志。
  • 根据基站坐标和距离计算标签位置。
  • 显示标签位置、距离、统计信息和轨迹。

5. 固件启动流程

5.1 STM32F1 固件启动流程

STM32F1 工程主要面向 STM32F103 + UWB 硬件。启动流程如下:

  1. 系统上电复位。
  2. 执行 HAL_Init() 初始化 HAL。
  3. 执行 SystemClock_Config() 配置系统时钟。
  4. 初始化 GPIO、SPI、TIM、UART 外设。
  5. 初始化 UWB 发送帧模板。
  6. 初始化 DW1000:
  7. 复位 DW1000。
  8. 配置 SPI 通信。
  9. 加载 DW1000 微码。
  10. 配置信道、速率、前导码、PANID、短地址。
  11. 配置天线延时和中断源。
  12. 根据编译角色进入标签流程或基站流程。

5.2 STM32F4 固件启动流程

STM32F4 工程主要面向 STM32F401 + UWB + MPU9250 硬件。启动流程如下:

  1. 系统上电复位。
  2. 初始化 HAL、系统时钟和基础外设。
  3. 初始化 UWB 发送帧模板。
  4. 初始化 DW1000。
  5. 如果启用 MPU9250:
  6. 初始化软件 I2C。
  7. 初始化 MPU9250。
  8. 配置 Wake-on-Motion 运动检测。
  9. 根据编译角色进入标签流程或基站流程。

5.3 DW1000 工作参数

当前 UWB 工作参数如下:

参数 当前配置
Channel 2
PRF 64 MHz
Preamble Length 1024
PAC 32
TX/RX Preamble Code 9
SFD Non-standard SFD
Data Rate 110 Kbps
PANID 0xF0F2
短地址 按标签/基站角色配置

6. UWB 空中协议

6.1 帧类型

本方案使用 IEEE 802.15.4 短地址数据帧,应用层通过 messageData[0] 区分业务类型。

帧类型 方向 功能
P 标签 -> 基站 Poll,请求本轮测距
A 基站 -> 标签 Ack,响应 Poll,并携带上一轮距离
F 标签 -> 基站 Final,携带标签侧时间戳
M 标签 -> 汇聚基站 汇总多基站距离和运动状态
C 标签 -> 汇聚基站 控制信息,预留给小车控制等场景

6.2 DS-TWR 三帧测距

一次完整测距由 P/A/F 三个空中帧组成:

Tag                         Anchor
 |                             |
 | ---------- P Poll --------> |
 |                             | 记录 poll_rx_ts
 | <--------- A Ack ---------- |
 | 记录 resp_rx_ts             |
 | ---------- F Final -------> |
 |                             | 记录 final_rx_ts
 |                             | 计算距离

标签侧记录:

  • poll_tx_ts:标签发送 P 的时间戳。
  • resp_rx_ts:标签收到 A 的时间戳。
  • final_tx_ts:标签发送 F 的时间戳。

基站侧记录:

  • poll_rx_ts:基站收到 P 的时间戳。
  • resp_tx_ts:基站发送 A 的时间戳。
  • final_rx_ts:基站收到 F 的时间戳。

基站收到 F 后,根据六个时间戳计算飞行时间,再换算为距离:

Ra = resp_rx_ts - poll_tx_ts
Rb = final_rx_ts - resp_tx_ts
Da = final_tx_ts - resp_rx_ts
Db = resp_tx_ts - poll_rx_ts

tof_dtu = (Ra * Rb - Da * Db) / (Ra + Rb + Da + Db)
distance = tof_dtu * DWT_TIME_UNITS * SPEED_OF_LIGHT

距离计算完成后,基站会进行 DW1000 range bias 修正,并对同一标签的距离结果进行 Kalman 滤波。

6.3 A 帧距离回传方式

基站在收到标签 P 帧后,会立即返回 A 帧。A 帧除了承担测距应答作用,还会携带该标签上一轮已计算完成的距离。

这种设计减少了额外的数据帧,使标签可以在轮询过程中同步缓存每个基站的距离。

6.4 M 汇总帧

标签完成一轮基站测距后,会向 0x0001 汇聚基站发送 M 帧。M 帧中包含上位机需要的定位输入数据:

  • 标签 ID。
  • 帧号。
  • 到多个基站的距离。
  • MPU9250 运动检测状态。

汇聚基站收到 M 帧后,不在本地计算定位结果,而是将数据通过 UART 发送给上位机。

7. 标签端运行流程

标签固件进入 tx_main() 后启动 TIM1 定时器中断,之后主循环保持空转。标签的实际业务由定时器中断和 DW1000 外部中断驱动。

7.1 定时器驱动测距

标签通过 TIM1 周期性启动一次动作。每次定时器到期后,标签根据当前目标地址决定执行哪种业务:

  • 当前目标地址为基站地址:发送 P 帧启动一次测距。
  • 当前目标地址为 0x0000:说明基站轮询完成,发送 M 汇总包。

当前 3 基站配置下,标签一轮完整周期包括:

  1. 0x0001 发送 P
  2. 0x0002 发送 P
  3. 0x0003 发送 P
  4. 0x0001 发送 M 汇总包。

7.2 发送 Poll

标签发送 P 帧时会执行以下步骤:

  1. 设置目标基站短地址。
  2. 设置帧序号。
  3. 设置 messageData[0] = 'P'
  4. 将帧写入 DW1000 TX buffer。
  5. 立即发送。
  6. 记录 poll_tx_ts
  7. 打开接收窗口,等待基站 A 帧。

7.3 接收 Ack 并发送 Final

标签收到基站 A 帧后:

  1. 记录 resp_rx_ts
  2. 设置延迟发送时间 final_tx_ts
  3. 构造 F 帧。
  4. poll_tx_tsresp_rx_tsfinal_tx_ts 写入 F 帧。
  5. 通过 DW1000 delayed TX 发送 F
  6. A 帧中读取该基站上一轮距离。
  7. 将距离缓存到 Final_Distance[]

7.4 发送汇总数据

标签轮询完所有基站后,调用汇总发送流程:

  1. 读取缓存的 Final_Distance[]
  2. 读取并打包 isMpu9250_moved 运动状态。
  3. 构造 M 帧。
  4. M 帧发送到 0x0001 汇聚基站。
  5. 帧号自增。
  6. 清除本轮运动标志,等待下一轮检测。

8. 基站端运行流程

基站固件进入 rx_main() 后,会将 DW1000 配置为常驻接收状态。后续全部由 DW1000 接收中断驱动。

8.1 常驻接收

基站初始化完成后执行:

  1. 使能 DW1000 数据帧过滤。
  2. 设置接收 timeout 为 0,即一直等待接收。
  3. 启动 DW1000 接收机。
  4. 初始化 Kalman 滤波状态。

8.2 响应 Poll

基站收到 P 帧后:

  1. 将回复目标地址设置为标签地址。
  2. 记录帧序号。
  3. 设置 messageData[0] = 'A'
  4. 将上一轮距离写入 messageData[1..2]
  5. 立即发送 A 帧。
  6. 记录 poll_rx_ts

8.3 计算距离

基站收到 F 帧后:

  1. 记录 resp_tx_ts
  2. 记录 final_rx_ts
  3. F 帧中取出标签侧 3 个时间戳。
  4. 使用 DS-TWR 公式计算 TOF。
  5. 将 TOF 换算为距离。
  6. 执行 range bias 修正。
  7. 使用标签 ID 对应的 Kalman 滤波器更新距离。
  8. 保存结果,供下一次 A 帧回传给标签。

8.4 转发汇总数据

汇聚基站收到 M 帧后:

  1. 取出 messageData[1] 起始的数据载荷。
  2. 通过 UWB_USART 发送给上位机。
  3. 重新开启 DW1000 接收。

9. MPU9250 运动检测流程

MPU9250 在本方案中用于判断标签是否发生运动。固件使用 Wake-on-Motion 模式,不进行惯导积分。

9.1 初始化流程

STM32F4 标签启动时执行:

  1. 初始化软件 I2C。
  2. 读取并初始化 MPU9250。
  3. 配置低功耗运动检测模式。
  4. 使能 MPU9250 运动中断输出。

9.2 运动检测配置

运动检测相关配置包括:

  • 选择稳定时钟源。
  • 配置加速度计工作模式。
  • 配置运动检测控制寄存器。
  • 设置 WOM 阈值。
  • 开启运动检测中断。
  • 设置低功耗加速度采样频率。

9.3 运动状态上报

运动状态按如下方式进入定位链路:

  1. 标签运动时,MPU9250 产生中断。
  2. STM32F4 外部中断置位 isMpu9250_moved
  3. 标签发送 M 汇总包时读取该标志。
  4. 如果本周期未检测到运动,写入字符 s
  5. 如果本周期检测到运动,写入字符 m
  6. 上位机根据该字符更新标签运动状态。

10. 串口数据格式

标签发送给上位机的数据不是直接由标签串口发出,而是通过 UWB 发送到汇聚基站,再由汇聚基站串口转发。

当前上位机数据帧格式如下:

字段 长度 含义
m 1 字节 帧头第 1 字节
r 1 字节 帧头第 2 字节
i 1 字节 表示包含 IMU 状态
frame_type 1 字节 0x02 表示标签距离报告
tag_id 1 字节 标签 ID
frame_num 2 字节 帧号,低字节在前
range0 2 字节 标签到基站 0 的距离,单位 cm
range1 2 字节 标签到基站 1 的距离,单位 cm
range2 2 字节 标签到基站 2 的距离,单位 cm
range3 2 字节 第四路距离字段
motion 1 字节 s 表示未运动,m 表示检测到运动
\n\r 2 字节 结束符

距离字段采用小端格式:

range = low_byte | (high_byte << 8)

固件侧距离单位为 cm,上位机内部处理时会换算为 mm 或 m。

11. 上位机处理流程

11.1 数据接收

上位机可以通过串口或 TCP 接收数据。串口模式下:

  1. 打开 COM 口。
  2. 绑定 readyRead() 信号。
  3. 收到数据后调用 newData()
  4. newData() 读取串口缓存。
  5. 调用 raw_data_process() 进入协议解析。

11.2 数据解析

raw_data_process() 使用状态机从字节流中查找帧头:

  1. 等待字符 m
  2. 等待字符 r
  3. 如果第三个字符是 i,进入 UWB + IMU 数据模式。
  4. 收满数据后调用 ProcessData()

ProcessData() 解析字段:

  • frame_type
  • 标签 ID。
  • 帧号。
  • 4 路距离。
  • 运动状态。

11.3 距离处理

上位机解析标签距离后,会更新标签对象中的距离缓存:

  • latestRange[0]:标签到基站 0 的距离。
  • latestRange[1]:标签到基站 1 的距离。
  • latestRange[2]:标签到基站 2 的距离。
  • latestRange[3]:标签到基站 3 的距离字段。

之后上位机会分别触发距离更新信号,用于界面显示每个基站到标签的距离。

11.4 定位计算

上位机定位流程:

  1. 读取配置中的基站坐标。
  2. 读取当前标签的多基站距离。
  3. 根据有效距离数量选择定位模式。
  4. 调用 GetLocation() 计算标签坐标。
  5. 更新标签位置。
  6. 刷新界面显示。

11.5 运动状态使用

上位机会根据 motion 字段更新标签运动状态:

  • s:当前周期未检测到运动。
  • m:当前周期检测到运动。

运动状态可用于辅助上位机融合逻辑,例如静止保持、运动更新、轨迹平滑等。

12. 总体流程图

flowchart TB
    subgraph TAG["标签 Tag"]
        T0["上电启动"]
        T1["初始化 MCU 外设"]
        T2["初始化 DW1000"]
        T3["初始化 MPU9250 运动检测"]
        T4["启动 TIM1 周期调度"]
        T5{"当前目标地址"}
        T6["发送 P Poll 给基站"]
        T7["等待 A Ack"]
        T8["发送 F Final"]
        T9["缓存该基站距离"]
        T10["发送 M 汇总包到 0x0001"]
        T11["读取并清除运动标志"]
    end

    subgraph ANCHOR["基站 Anchor"]
        A0["上电启动"]
        A1["初始化 MCU 外设"]
        A2["初始化 DW1000"]
        A3["进入常驻接收"]
        A4{"收到帧类型"}
        A5["P: 返回 A Ack"]
        A6["F: 计算距离并滤波"]
        A7["M: UART 透传给上位机"]
        A8["重新开启接收"]
    end

    subgraph PC["上位机"]
        P0["串口/TCP 接收"]
        P1["查找 mri 帧头"]
        P2["解析标签ID/帧号/距离/运动状态"]
        P3["更新距离缓存"]
        P4["计算标签坐标"]
        P5["界面显示位置和距离"]
    end

    T0 --> T1 --> T2 --> T3 --> T4 --> T5
    T5 -- "基站地址" --> T6 --> T7 --> T8 --> T9 --> T4
    T5 -- "汇总地址 0x0000" --> T11 --> T10 --> T4

    A0 --> A1 --> A2 --> A3 --> A4
    A4 -- "P" --> A5 --> A8 --> A3
    A4 -- "F" --> A6 --> A8 --> A3
    A4 -- "M" --> A7 --> A8 --> A3

    T6 -- "P" --> A4
    A5 -- "A + 上一轮距离" --> T7
    T8 -- "F + 时间戳" --> A4
    T10 -- "M + 距离 + 运动状态" --> A4
    A7 --> P0 --> P1 --> P2 --> P3 --> P4 --> P5

13. DS-TWR 测距时序图

sequenceDiagram
    participant Tag as 标签
    participant Anchor as 基站
    participant PC as 上位机

    Tag->>Anchor: P Poll
    Anchor->>Anchor: 记录 poll_rx_ts
    Anchor->>Tag: A Ack + 上一轮距离
    Tag->>Tag: 记录 resp_rx_ts
    Tag->>Anchor: F Final + 标签侧时间戳
    Anchor->>Anchor: 记录 final_rx_ts
    Anchor->>Anchor: 计算 TOF 和距离
    Anchor->>Anchor: 距离修正与滤波

    loop 一轮基站轮询完成
        Tag->>Anchor: M 汇总包
        Anchor->>PC: UART 转发 mri 数据
        PC->>PC: 解析距离和运动状态
        PC->>PC: 定位计算与显示
    end

14. 标签状态机

stateDiagram-v2
    [*] --> BOOT
    BOOT --> INIT_UWB
    INIT_UWB --> INIT_IMU
    INIT_IMU --> IDLE
    IDLE --> SEND_POLL: 定时器到期且目标为基站
    SEND_POLL --> WAIT_ACK: P 发送完成
    WAIT_ACK --> SEND_FINAL: 收到 A
    SEND_FINAL --> IDLE: F 发送完成
    WAIT_ACK --> IDLE: 接收超时
    IDLE --> REPORT: 定时器到期且一轮测距完成
    REPORT --> IDLE: M 汇总包发送完成

15. 基站状态机

stateDiagram-v2
    [*] --> BOOT
    BOOT --> INIT_UWB
    INIT_UWB --> RX_WAIT
    RX_WAIT --> SEND_ACK: 收到 P
    SEND_ACK --> RX_WAIT: A 发送完成
    RX_WAIT --> CALC_RANGE: 收到 F
    CALC_RANGE --> RX_WAIT: 距离计算完成
    RX_WAIT --> FORWARD_PC: 收到 M
    FORWARD_PC --> RX_WAIT: UART 转发完成

16. 上位机解析状态机

stateDiagram-v2
    [*] --> WAIT_M
    WAIT_M --> WAIT_R: 收到 m
    WAIT_M --> WAIT_M: 其他字节
    WAIT_R --> WAIT_I: 收到 r
    WAIT_R --> WAIT_M: 其他字节
    WAIT_I --> RECV_FRAME: 收到 i
    WAIT_I --> RECV_UWB_ONLY: 非 i 数据
    RECV_FRAME --> PROCESS: 收满 UWB + IMU 数据
    RECV_UWB_ONLY --> PROCESS: 收满 UWB 数据
    PROCESS --> WAIT_M: 处理完成

17. 典型运行周期说明

以 3 基站、1 标签为例,一个完整定位周期如下:

  1. 标签向基站 0x0001 发起测距。
  2. 基站 0x0001 完成本轮距离计算,并在下一次 A 帧中回传距离。
  3. 标签向基站 0x0002 发起测距。
  4. 基站 0x0002 完成本轮距离计算。
  5. 标签向基站 0x0003 发起测距。
  6. 基站 0x0003 完成本轮距离计算。
  7. 标签发送 M 汇总包给 0x0001
  8. 汇聚基站将 M 包内容通过 UART 发送给上位机。
  9. 上位机解析本轮多基站距离和运动状态。
  10. 上位机计算标签位置并更新界面。

18. 客户集成关注点

18.1 基站地址

基站使用短地址区分,当前 3 基站方案通常使用:

  • Anchor 0:0x0001
  • Anchor 1:0x0002
  • Anchor 2:0x0003

其中 0x0001 作为汇聚基站连接上位机。

18.2 标签地址

标签使用短地址作为标签 ID。上位机解析数据时使用该 ID 区分不同标签。

18.3 基站坐标

上位机定位计算需要预先配置基站坐标。实际部署时,应保证上位机中的基站坐标与现场安装位置一致。

18.4 串口连接

上位机连接汇聚基站串口。汇聚基站将标签汇总数据透传到 PC,因此普通基站无需连接上位机。

18.5 运动检测

MPU9250 输出的是运动状态标志,不输出完整 IMU 姿态或惯导位移。上位机可将该标志用于辅助定位显示或过滤逻辑。

19. 数据流总结

标签 TIM1 周期调度
    -> 依次向基站发送 P
    -> 收到 A 后发送 F
    -> 基站计算距离
    -> 标签缓存上一轮距离
    -> 标签发送 M 汇总包
    -> 汇聚基站 UART 转发
    -> 上位机解析 mri 数据
    -> 上位机计算并显示标签位置

该流程体现了本方案的职责划分:固件侧负责 UWB 测距和运动状态采集,基站侧负责距离计算与数据汇聚,上位机侧负责定位融合与显示。