前两篇把工具链和 BLE 基础打通了,这一篇进入正题:Channel Sounding。
1. CS 到底在测什么
蓝牙信道探测(Channel Sounding,CS)是 Bluetooth 6.0 引入的测距机制。它不靠 RSSI 这种"信号强弱"的粗略估算,而是直接测量无线电波在两个设备之间飞行的时间和相位。
两种核心方法:
| PBR(Phase-Based Ranging) | 在多个频点测量载波相位,相位随频率变化的斜率正比于距离 | 精度可达厘米级 | 受多径影响大,需要频率分集 |
| RTT(Round-Trip Time) | 测量信号往返时间,乘以光速 | 抗多径较好 | 精度受时钟与处理延迟限制 |
CS 把两者结合:在 2.4 GHz 频段上跳频,依次发送一串"音调"(tones),每个频点都测一次相位和往返时间,最后用统计方法解算距离。
1.1 Mode 0/1/2/3
| Mode 0 | 仅频率补偿 | 不测距,作为其他模式的辅助 |
| Mode 1 | RTT | 飞行时间测距 |
| Mode 2 | PBR | 相位测距 |
| Mode 3 | PBR + RTT | 两者都测,精度最高、开销最大 |
Nordic 示例默认是 Main Mode 2 + Sub Mode 1:主模式做相位测量,子模式补一次 RTT。这样既拿到相位斜率,也拿到飞行时间,后面三种距离算法(IFFT / Phase Slope / RTT)都有数据。
1.2 一次 CS 过程的结构
Connection Event └── CS Procedure └── CS Event └── CS Subevent └── CS Step(每个 step 发送若干 tone)
Procedure:一次完整的测量流程,可配置间隔和次数
Subevent:一组 step,示例里长度 16000 µs
Step:在一个频点上的一对收发动作
【图片1:一次 CS 测量流程】
2. 两个角色与 RAS
| Initiator | 发起 CS 过程,控制配置,收集结果 |
| Reflector | 响应并反射音调 |
测量结果通过 RAS(Ranging Service) 暴露:
Reflector 侧提供 Ranging Responder(RRSP)
Initiator 侧作为 Ranging Requestor(RREQ)
数据分两类:Ranging Data(历史缓存)和 Real-time Ranging Data(实时子事件数据)
实时数据里除了最终距离,还能拿到每个 step 的 PCT(相位校正项) 和 RTT 原始量。这一点很关键——第 4 篇的"环境感知"就是靠这些原始量,而不是靠距离读数。
3. 硬件准备
| Initiator | nRF54LM20-DK | nrf54lm20dk/nrf54lm20b/cpuapp |
| Reflector | XIAO nRF54L15 Sense | xiao_nrf54l15/nrf54l15/cpuapp |
注意:XIAO 的板级定义来自 Zephyr 上游,NCS 的示例 sample.yaml 里没有把它列进 platform_allow,但手动 west build -b 是可以编的。
LM20 接电脑(J-Link + 串口),XIAO 用 USB 充电头独立供电。
【图片2:双板布置(LM20 接电脑,XIAO 独立供电)】
【图片3:XIAO 用 USB 充电头独立供电】
4. 编译与烧录
4.1 Reflector(XIAO)
cd ws ../venv/bin/west build -b xiao_nrf54l15/nrf54l15/cpuapp \ nrf/samples/bluetooth/channel_sounding/ras_reflector \ -d build/reflector
产物:build/reflector/ras_reflector/zephyr/zephyr.hex。
4.2 Initiator(LM20-DK)
../venv/bin/west build -b nrf54lm20dk/nrf54lm20b/cpuapp \ nrf/samples/bluetooth/channel_sounding/ras_initiator \ -d build/initiator
4.3 烧录与观察
两个节点分别烧录后,先给 Reflector 上电,它会以 Nordic CS Reflector 广播;再给 Initiator 上电,Initiator 会自动扫描并连接。
Initiator 串口(115200)应看到类似输出:
I: Starting Channel Sounding Initiator Sample I: Connected to XX:XX:XX:XX:XX:XX (random) (err 0x00) I: CS capability exchange completed. I: CS config creation complete. ID: 0 I: CS security enabled. I: CS procedures enabled I: Distance estimates on antenna path 0: ifft: 1.039173, phase_slope: 1.581897, rtt: 3.075647
三个数字分别是三种算法给出的距离(单位:米)。正常情况下它们应该在真值附近,但会有可见的离散。
4.4 编译结果与广播验证
两块板的 Reflector 都已经实际编译通过(NCS v3.4.1 + Zephyr SDK 1.0.1):
| nrf54lm20dk/nrf54lm20b/cpuapp | 245624 B / 2036 KB(11.78%) | 53766 B / 511 KB(10.28%) |
| xiao_nrf54l15/nrf54l15/cpuapp | 268208 B / 1524 KB(17.19%) | 100433 B / 160 KB(61.30%) |
XIAO 的数字来自修复工程 code/xiao_reflector(含 IMU 采样、看门狗与断连自恢复);官方示例的原始构建为 251340 B,RAM 53790 B(按 256 KB 计)。
LM20-DK 烧录后,用 BLE 扫描可以确认广播已经起来:
$ .venv/bin/python tools/scan_ble.py --filter "Nordic CS" 6526E504-F6B2-A4B2-20ED-933CE5300800 RSSI=-44 Nordic CS Reflector
这说明 Reflector 侧的固件与射频链路都正常,只差 Initiator 对端到位。
【图片4:双板同时工作的串口输出】
4.5 XIAO 板级踩坑与修复
XIAO nRF54L15 在 NCS 3.4.1 下不能直接跑官方反射端,过程中踩了四个坑:
| 启动即崩,SPSEL 断言 | 板级把应用放在 slot0 @ 0x10000,0x0 留给 MCUboot;只烧应用没有引导 | 删除 slot 分区,boot_partition 扩到 0x0/1524 KB,chosen 指向它 |
| 栈落进保留区 | 默认 SRAM 256 KB,FLPR 保留顶部 96 KB | cpuapp_sram 收窄到 160 KB |
| Failed to allocate buffer | RAS 默认每连接只有 1 个测距缓冲 | BT_RAS_RRSP_RD_BUFFERS_PER_CONN=8 |
| CMSIS-DAP 烧录失败 | HID 后端在 Seeed 板上响应截断 | 用 nrf_ocd 的 libusb bulk 后端 |
另外,原厂反射端在断连时执行 sys_reboot,没有 USB 数据线时冷启动起不来。改为延迟 200 ms 在工作队列里重新广播,并加 10 秒硬件看门狗(wdt31)兜底;实测拔掉对端后数秒内自动恢复广播。
这些改动都在 code/xiao_reflector/ 里,编译目标仍是 xiao_nrf54l15/nrf54l15/cpuapp。
5. 测距精度实测
5.1 测试方法
节点放在同一桌面高度,天线朝向一致
每个距离点连续记录 ≥ 30 次测量
分别测 LOS(视距)和人体遮挡(NLOS)
用卷尺/激光测距仪记录真值
5.2 首次联调记录
两块板子第一次连通时,连续采集 30 秒,共得到 273 个距离估计(约 9 Hz):
| IFFT | 2.007 m | 0.152 m | 1.460 m | 2.290 m |
| Phase Slope | 2.410 m | 0.024 m | 2.340 m | 2.470 m |
| RTT | 6.778 m | 0.265 m | 6.140 m | 7.380 m |
几个观察:
Phase Slope 最稳,标准差只有 2.4 cm,适合做精细的距离变化检测。
IFFT 有约 0.3 m 的量化台阶,读数在 1.46 / 1.76 / 2.05 之间跳。
RTT 绝对值明显偏大,因为包含设备处理延迟;它的价值在趋势而不是绝对值。
此时的板间真实距离尚未用卷尺标定,表中数值只用于横向比较,不能当作绝对精度结论。
备注:首次联调时 XIAO 还存在启动崩溃,后来通过 4.5 节的板级修复与 RAS 缓冲调整解决。
5.3 结果记录表
LOS 视距(2026-10-27 实测)
| 0.3 | 209 | 1.191 ± 0.181 | 1.239 ± 0.058 | 5.657 ± 0.634 | 0.891 / 0.909 | 0.939 / 0.941 | 5.357 / 5.395 |
| 0.5 | 231 | 1.930 ± 0.408 | 3.046 ± 0.141 | 6.781 ± 0.736 | 1.438 / 1.487 | 2.546 / 2.550 | 6.281 / 6.324 |
| 1.0 | 155 | 2.983 ± 0.419 | 3.981 ± 0.274 | 8.863 ± 0.842 | 1.999 / 2.027 | 2.981 / 2.993 | 7.863 / 7.908 |
| 2.0 | 54 | 3.697 ± 0.289 | 5.874 ± 0.213 | 8.385 ± 0.881 | 1.698 / 1.722 | 3.874 / 3.880 | 6.385 / 6.445 |
5 m 点因场地长度不足未测。测试环境是普通桌面,两侧有显示器、键盘和人体反射,属于强多径场景。绝对误差偏大,但三种算法随距离的变化趋势是一致的:IFFT 最接近线性,Phase Slope 在 0.5 m 后出现跳变,RTT 带约 5 m 的固定偏移。
【图片5:距离矩阵:实测与真值】
人体遮挡(NLOS,2.0 m)
| 2.0 | 41 | 3.511 | 5.227 | 8.865 | -0.186 / -0.647 / +0.480 m |
结论:人体遮挡后,IFFT 与 Phase 读数略降,RTT 略升,均值变化都不大;但 IFFT 的标准差从 0.289 升到 0.556,抖动明显增加。这正是第 4 篇用「抖动」而不是「距离」做判据的原因。
5.4 误差曲线
误差随距离的变化见上图的误差棒与理想线(y = x):
IFFT 的斜率最接近 1,是三种算法里最稳的;
Phase Slope 在 0.5 m 后明显偏离,近距离多径对相位斜率的影响很大;
RTT 整体带有约 5 m 的固定偏移,主要来自设备处理延迟,适合看变化趋势而不是绝对值。
6. 实测中会遇到的几个现象
近距离时 IFFT 与 Phase Slope 差异明显。 相位法在近距离对多径更敏感,IFFT 的鲁棒性通常更好。
RTT 的绝对值偏大。 因为包含了设备处理延迟,通常需要做零点标定;它的价值更多在于变化趋势而不是绝对距离。
人体遮挡会让读数跳变。 这不是缺陷——第 4 篇会把它变成特性。
天线朝向影响很大。 两个节点尽量保持极化一致,否则误差会明显增大。
7. 小结
CS 用 PBR + RTT 在 79 个信道上测相位与飞行时间
Initiator / Reflector 通过 RAS 交换配置与结果
三种距离算法各有优劣,实测时要一起记录
遮挡会显著扰动读数,这为下一篇的"环境感知"提供了物理基础
下一篇是重头戏:不追求把距离测准,而是利用"测不准"——用链路抖动感知环境。
我要赚赏金
