简介:IEEE Std 802.15.4z-2020 标准原文面向低速率无线网络、UWB 测距与实时定位系统(RTLS)方向的研发人员、标准工程师及高校研究者,可用于查阅增强型超宽带物理层与相关测距技术的规范条文,为协议实现、射频设计与测距精度评估提供依据。压缩包内为 1 个 PDF 文件,大小约 3.52MB,保留标准正文、摘要、关键词及版权声明,便于检索 HRP/LRP UWB PHY、ERDEV、SS-TWR/DS-TWR、TOF 完整性等内容。标准在 802.15.4 基础上增加编码选项,改进测距测量的一致性与准确性,并增强 MAC 对飞行时间测距流程的控制及测距设备间的信息交换能力,对多节点测距、低功耗设计和物联网定位产品落地具有参考价值。目前已有 1737 人学习,适合作为 UWB 协议开发、芯片验证和学术研究的案头标准资料。
1. 拿到这份 802.15.4z-2020 的 PDF,先别急着翻第 4 章
做 UWB 测距的团队,最容易出问题的地方不是射频前端选错,而是被问一句“你这套 TWR 是单边还是双边、STS 开没开、时间戳刻度是多少”时答不上来。这份 802.15.4z-2020 的 PDF 就是这几个问题的原始出处:它是在低速率无线网络基础标准之上的一份修正案,2020 年 6 月批准、8 月出版,专门给 UWB 物理层补编码选项和测距完整性,同时把 MAC 层对 TOF 测距流程的控制、测距信息的交换补上。换句话说,市面上 HRP UWB 测距芯片那一堆“STS 模式”“双边测距”“多节点调度”的配置项,命名的源头基本都在这儿。适合三类人:正在选 UWB 方案、要写驱动或固件配置、被距离欺骗和精度抖动折磨过的从业者。它不是教程,得带着具体问题去翻。
2. 拆骨架:这份修正案在 UWB PHY 上到底加了哪三块
第一遍通读别做笔记,先把结构过一遍。这份文档不是一本独立标准,它是基础标准的修正案,所以正文里大量出现“修改某节”“替换某段”“新增某子节”的写法,直接从头读到尾会很痛苦。我的习惯是先看摘要和关键词,再把新增内容归到三类:物理层的编码与帧结构、测距流程本身、MAC 层的控制与信息交换。归完类你才知道自己要的那张表大概在哪个区间。
2.1 修正案的阅读顺序:先目录再关键词索引
修正案类文档有个特点,目录里大量条目是“对第 X 章的修改”,看不出内容。真正高效的入口是文档末尾的关键词索引(Keywords),它把 SS-TWR、DS-TWR、ERDEV、HRP-ERDEV、LRP-ERDEV、TOF integrity、multi-node ranging、RTLS 这些术语对应的页码列出来了。我第一次翻这份 PDF 时,是在关键词表里逐个划出自己要的词,再回到正文定位的,比顺着目录翻快得多。
建议的阅读路线是这样:
| 你手上的任务 | 优先翻的部分 | 要拿到的信息 |
|---|---|---|
| 选芯片、评估方案 | 范围与目的、UWB PHY 概述 | HRP 与 LRP 的定位差异、支持的设备类型 |
| 配物理层参数 | UWB PHY 的帧格式与参数表 | 前导码、SFD、STS、数据段的组合关系 |
| 写测距流程 | 测距相关章节 | SS-TWR、DS-TWR 的报文时序与时间戳字段 |
| 做安全测距 | STS 相关章节 | STS 的生成、长度档位、包结构配置 |
| 做多节点 / RTLS | MAC 增强部分 | 测距回合控制、测距信息元素 |
| 排精度问题 | 与 TOF 完整性相关的内容 | 误差来源、时间戳精度要求 |
这张表不是让你一次全看,而是让你在遇到具体问题时知道该往哪跳。实际项目里九成的查阅都是“带着一个字段名去找定义”,而不是通读。
2.2 HRP 与 LRP 两条路线:选错了后面全白做
文档把 UWB 物理层分成两条路线,HRP(高脉冲重复频率)和 LRP(低脉冲重复频率)。这两个词在参数表里反复出现,很多人第一遍看会以为是速率高低之分,其实差别在定位:HRP 走的是高带宽、高时间分辨率的路子,测距精度能做到厘米级,代价是功耗和射频复杂度;LRP 走的是低功耗、低成本,适合标签类、电池供电、对精度要求没那么极端的场景。
对应到设备侧,文档里用 RDEV 和 ERDEV 来区分测距设备,ERDEV 是增强型测距设备,再细分 HRP-ERDEV 和 LRP-ERDEV。你在配固件时会看到类似“设备能力字段”的东西,填的就是这套分类。选型时的判断很直接:要厘米级、要抗多径、要做安全测距,走 HRP;要几年电池寿命、米级够用、成本压到最低,走 LRP。别两头都想要,物理层参数是绑死的。
这里有个容易忽略的点:HRP 和 LRP 的很多参数表是分开列的,你在文档里搜一个参数名,可能先命中 LRP 的表,照着配到 HRP 设备上,现象是根本发不出包。我一般会在参数表标题上先确认是哪个 PHY,再抄数值。
2.3 编码选项与 TOF 完整性:新增内容的真正价值
摘要里那句“additional coding options and improvements to increase the integrity and accuracy of ranging measurements”,翻成工程语言就是两件事:一是给物理层加了新的编码组合,让同样的发射功率下链路更抗噪、时间戳判决更稳;二是把测距结果的“可信度”这件事写进了标准,也就是 TOF 完整性。
编码这块,HRP UWB 原本就是前导码 + SFD + 物理层头 + 数据段的组合,数据段用卷积码保护,修正案在此基础上增加了可选的编码方案,配合不同的码率档位。这些组合直接决定了你的灵敏度和作用距离。很多人调距离时只想到加发射功率,其实换个编码组合、把前导码长度从短档换到长档,接收灵敏度能改善好几个 dB,而且不违反任何发射限制。文档里那些标着“可选”的编码行,不是摆设,是给你在链路预算吃紧时留的调整空间。
TOF 完整性则是另一个维度。它的意思是:测出来的这个距离,能不能证明它没被中间人篡改或重放。这背后靠的就是 STS,也就是加扰时间戳序列,第四章会展开。这里先记住结论:不开 STS 的测距,精度可能很好,但安全性等于零,一个简单的延迟转发就能把你的距离骗走几十米。
一帧 HRP UWB 测距包,大致是按“前导码(SYNC)→ SFD → 可选 STS → 物理层头(PHR)→ 数据(PSDU)”这个顺序拼的,STS 插在哪、插几段,取决于你选的包结构配置。这份顺序关系是后面所有调试的基础,建议抄在纸上。
3. 测距时间戳的账:SS-TWR 与 DS-TWR 的误差从米级压到厘米级
物理层参数配好,接下来就是测距流程。这部分是文档里被查得最多的内容,也是最容易“照着公式写,结果对不上”的地方。核心就两个方法:单边双向测距 SS-TWR 和双边双向测距 DS-TWR。名字听着对称,误差特性差了一个数量级。
3.1 SS-TWR 的公式、误差放大与适用边界
SS-TWR 的流程很短:A 发一个 poll,B 收到后等一段固定的回复时间,回一个 response,A 收到后,用自己的收发时间差减去 B 的回复时间,除以二,就是单向飞行时间。
写成式子就是:
TOF = (T_round − T_reply) / 2
问题出在时钟上。A 和 B 用的是各自晶振,标称 20 ppm 的晶振,实际偏差可能在 ±20 ppm 内飘。设 A 的偏差是 eA,B 是 eB,那么测出来的 TOF 会带一个误差项,近似等于:
ΔTOF ≈ T_reply × (eB − eA) / 2
注意这个误差和回复时间成正比。这就是 SS-TWR 的关键问题:回复时间越长,误差越大。算一笔账,T_reply 取 1 ms,两端晶振偏差之差取 40 ppm,误差就是 1 ms × 40e-6 / 2 = 20 ns,乘光速约 6 米。这已经不能用“精度不够”来形容了,是彻底不可用。
所以 SS-TWR 的适用边界很窄:要么两端用同一个时钟源(有线同步或者共用参考),要么回复时间压到几十微秒级并且接受分米级误差。实际产品里,SS-TWR 更多出现在对成本极度敏感、精度要求宽松的场景。
我用一段脚本把这段误差关系算清楚,选型时直接看曲线比拍脑袋靠谱:
# SS-TWR 误差估算:回复时间与晶振偏差对测距误差的影响 C = 299_792_458.0 # 光速 m/s def ss_twr_error(reply_s, ppm_a, ppm_b): """返回 (时间误差 ns, 距离误差 m) reply_s: 对端回复时间,单位秒 ppm_a/ppm_b: 两端时钟偏差,单位 ppm """ drift = (ppm_b - ppm_a) * 1e-6 # 两端相对漂移 dt = reply_s * drift / 2.0 # 误差项,单位秒 return dt * 1e9, dt * C # 扫一遍典型回复时间,看误差怎么涨 for reply_us in (50, 200, 500, 1000, 2000): ns, m = ss_twr_error(reply_us * 1e-6, -20, 20) print(f"T_reply={reply_us:5d} us -> {ns:8.2f} ns, {m:7.3f} m")跑出来的结果会很直观:50 微秒时误差在 0.3 米量级,2 毫秒时直接飙到 12 米。代码里的 drift 用的是两端偏差之差而不是绝对值之和,因为同批次晶振往往同向漂移,实际误差可能比最坏情况小一半,但做设计必须按最坏情况留余量。参数上唯一能动的就是 reply_s,这也是为什么固件里那个“回复延迟”寄存器不能随便往大了配。
3.2 DS-TWR 的三段消息与时钟漂移抵消
DS-TWR 多一次往返:A 发 poll,B 回 response,A 再发一个 final。这样两轮下来,两端的收发时间差各有两组,交叉相乘之后,一阶的时钟漂移项被抵消掉了。
标准的计算式是:
TOF = (T_round1 × T_round2 − T_reply1 × T_reply2) / (T_round1 + T_round2 + T_reply1 + T_reply2)
这个式子里 T_round1 是 A 侧测的第一轮往返,T_reply1 是 B 侧的回复时间,T_round2 是 B 侧测的第二轮往返,T_reply2 是 A 侧的回复时间。四个量全部来自本地时钟,所以每个量都带着自己那一端的漂移,但代进去之后,一阶项互相抵消,剩下的误差是二阶小量,量级在皮秒到几十皮秒,换算成距离是毫米级。
我把这个式子也写进脚本,顺便加一个“单位换算”的坑位提醒:
C = 299_792_458.0 # 时间戳刻度:标准中时间戳字段按 1/(128 * 499.2 MHz) 计数 TICK_S = 1.0 / (128 * 499.2e6) # 约 15.65 ps def ds_twr_tof(round1, reply1, round2, reply2, unit='tick'): """四段时间戳输入,返回 TOF(秒) 与距离(米) unit='tick' 表示输入是原始计数值,'ns' 表示已换算为纳秒 """ scale = TICK_S if unit == 'tick' else 1e-9 t_r1, t_p1, t_r2, t_p2 = [x * scale for x in (round1, reply1, round2, reply2)] tof = (t_r1 * t_r2 - t_p1 * t_p2) / (t_r1 + t_r2 + t_p1 + t_p2) return tof, tof * C # 一次典型近距离测量(数值仅作演示,实际从寄存器读) tof, dist = ds_twr_tof(3_200_000, 2_000_000, 3_180_000, 1_980_000) print(f"TOF={tof*1e9:.3f} ns, 距离={dist:.3f} m")这段代码有两个关键点。第一是TICK_S,标准里时间戳字段的计数刻度是 1/(128×499.2 MHz),约 15.65 ps,不是 1 ns,也不是 1/499.2 MHz。直接把寄存器原始值当纳秒用,距离会差几十上百倍,这是新手最常见的翻车点。第二是四个时间戳必须来自同一次测距回合,中途丢了 final 帧或者重传了,这组数据就得作废,不能用上一次的缓存凑。
DS-TWR 的代价是 airtime 变长、功耗变高、状态机复杂。如果设备是电池供电的标签,一轮 DS-TWR 的功耗可能是 SS-TWR 的两倍多。实际产品里常见的折中是:正常测距走 SS-TWR 做粗测,关键节点或安全要求高的场景切 DS-TWR 精测。
3.3 多节点测距的调度:谁先发、时隙怎么切
单对单调通之后,下一个问题就是多节点。文档里 dedicated 的 multi-node ranging 不是让你随便发,它规定了测距回合的控制方式:由某个设备发起并组织整个回合,其他设备按分配的时间窗口响应。
落地时的做法通常是“轮询式调度”:主设备维护一张设备表,按顺序给每个从设备分配一个测距时隙,时隙内完成一次 TWR,时隙外静默。这里有两个参数必须算:一是单个时隙长度,要覆盖 poll + response + final 的最坏 airtime 加上处理延迟;二是保护间隔,要大于最大时钟漂移累积造成的窗口偏移。
我一般会先按最坏情况算时隙:假设单次测距 airtime 是 2 ms,固件处理延迟 1 ms,那么时隙至少给 3.5 ms,再加 10% 余量。10 个节点轮一圈 40 ms,更新率 25 Hz,这个量级对多数 RTLS 场景够用。要提更新率,只能减节点数或者降精度走 SS-TWR。
多节点还有个隐性问题:不同设备的时钟不同步,测距回合的起始时间必须由主设备广播的时间基准来对齐,不能各自用自己的定时器开窗。这一点文档里在 MAC 增强部分有对应机制,配的时候注意别把“本地定时器触发”当成标准流程用。
4. STS 与 MAC 控制:把“能测距”做成“测距结果可信”
测距能跑通,只是及格线。真正让 UWB 在门禁、支付、汽车钥匙这类场景立住脚的,是第四章这套东西:STS 加扰时间戳序列,加上 MAC 层对测距过程的控制。这一章的内容密度最高,参数表也最密,建议是边看边对照芯片手册,把两边的字段名对上。
4.1 STS 是怎么防伪的:一段加密的伪随机序列
STS 的全称是 Scrambled Timestamp Sequence,直译是加扰时间戳序列。它的实现方式是在物理层帧里插入一段由密钥驱动的伪随机序列,这段序列的生成依赖 AES-128 之类的加密运算,密钥由上层协商并同步。接收端必须知道同一把密钥,才能生成同样的序列并与收到的信号做相关;相关峰出现的位置,就是精确的时间戳判决点。
这就带来两个效果。第一,精度提升:相关峰的锐度比普通前导码高,时间戳判决更准。第二,安全性:攻击者不知道密钥,无法伪造一段能被相关上的序列,也就没法构造“提前到达”的假信号来做距离欺骗。延迟转发这种攻击,转发出去的信号已经错过了合法的 STS 窗口,接收端直接判定无效。
文档里 STS 的落点是包结构配置,常见的几档用 SP0 到 SP3 表示:
| 配置 | STS 的位置 | 典型用途 |
|---|---|---|
| SP0 | 不带 STS | 兼容旧设备、纯测距不要求安全 |
| SP1 | 前导码与 SFD 之后、物理层头之前 | 时间戳精度优先 |
| SP2 | 数据段之后 | 兼容性较好的安全测距 |
| SP3 | 物理层头之后、数据段之前 | 需要早期判决的场景 |
具体取值和字段顺序以文档正文的配置表为准,不同芯片厂商对这几档的支持程度不一样,选型时要先确认芯片支持哪几档。这也是我踩过的坑:方案定完才发现模组只支持 SP0 和 SP2,SP1 的精度的优势用不上,只能改设计。
STS 还有静态和动态之分。静态 STS 的密钥在配对时写死,动态 STS 每个测距回合更新一次。动态的更新流程涉及密钥交换和同步窗口,配置项多,第一次调通要花不少时间,但对重放攻击的抵抗力强得多。我的建议是:室内固定设备可以用静态,涉及人身或资金的场景一律动态。
4.2 ERDEV 与 MAC 层控制字段:测距回合的组织方式
MAC 层的增强集中在两件事:控制 TOF 测距过程,以及在参与设备之间交换测距相关信息。落到实现上,就是在原有的帧结构里增加了携带测距参数的字段,包括测距回合的时序、参与设备的能力、设备角色分配这些。
ERDEV 这个概念在这里出现,指的是支持增强测距的设备。设备能力字段里会标明自己是 HRP-ERDEV 还是 LRP-ERDEV,以及支持哪些测距配置。抓包时最先看的就是这个字段:两端能力不匹配,后面的流程根本走不通。
实际调试里,我会写个小脚本把两端配置逐字段比对,比人眼对表快得多:
# 两端 UWB 配置比对:抓的是最容易配错的那几项 local = { "phy_mode": "HRP", # HRP / LRP,必须一致 "device_role": "ERDEV", # RDEV / ERDEV "sts_config": "SP2", # SP0~SP3,必须一致 "sts_mode": "dynamic", # static / dynamic "preamble_len": 128, # 前导码符号数档位 "sfd_len": 8, # SFD 长度档位 "data_rate": "6.81M", # 数据速率档 "ranging": "DS-TWR", # SS-TWR / DS-TWR } remote = {**local, "sts_config": "SP0", "ranging": "SS-TWR"} for k in local: if local[k] != remote.get(k): print(f"[MISMATCH] {k}: local={local[k]} remote={remote.get(k)}")这段脚本的意义在于建立一个检查习惯:任何“测距失败、超时、结果跳变”的问题,第一步都是先比对配置,而不是去怀疑射频。脚本里的字段名按你实际使用的接口命名调整,重点是那几个必须两端一致的项——PHY 模式、设备角色、STS 配置、STS 模式、前导码与 SFD 长度、数据速率、测距方式。其中任何一项不一致,现象都是“能收到包但测不出距离”,非常容易误判成硬件问题。
4.3 加密测距的落地检查清单
STS 调通的标志不是“能出距离”,而是“在正确密钥下稳定出距离,在错误密钥下稳定不出距离”。后者才是验证安全性的方法。我在项目里会强制走一遍下面这几项:
| 检查项 | 做法 | 期望结果 |
|---|---|---|
| 密钥同步 | 两端写入相同密钥,跑 1000 次测距 | 成功率 > 99% |
| 密钥隔离 | 一端换成错误密钥 | 全部失败或全部被标记为无效 |
| STS 长度档位 | 在 32 / 64 / 128 符号档间切换 | 精度随长度增加而改善,airtime 同步增加 |
| 重放测试 | 录制一段合法响应并延迟重放 | 接收端拒绝,不产生有效距离 |
| 边界距离 | 在标称最大距离的 90% 处反复测 | 无跳变、无虚假近距输出 |
第三项容易被忽略:STS 段越长,相关增益越高,精度越好,但每帧的 airtime 也线性增加。有的项目为了追精度把 STS 拉到最长,结果功耗预算爆掉。我的经验是按场景取中档,靠前导码长度和编码组合去补灵敏度,而不是无脑堆 STS。
5. 避坑与排查:五个把测距精度和链路吃掉的现场问题
这一章是我这几年攒下来的错误清单,每一条都真实出现过,而且都不是文档里能直接查到的。格式统一成“现象、原因、解决”,你对照自己手上的现象往上套就行。
5.1 时间戳与时钟类问题
现象一:距离恒定为某个固定值,不管实际怎么移动。
原因是时间戳单位混淆。寄存器读出来的原始计数值,刻度是 1/(128×499.2 MHz) ≈ 15.65 ps,有人按纳秒处理,算出来的 TOF 始终是个很小的数,乘光速后正好落在某个固定距离附近。解决方法是把单位换算封成一个函数,全工程只允许通过这个函数把原始值转成秒,禁止在任何地方手写乘除系数。我现在连调试打印都只打换算后的纳秒值,从源头断掉混用的可能。
现象二:同一对设备,距离在真值上下跳几十厘米,重复性极差。
先排除多径,再看时钟。常见原因是 SS-TWR 的回复时间被固件里的调度延迟拉长了,回复时间不稳定,误差项就跟着漂。用第 3 章那段脚本把实际回复时间代进去算一下,如果算出来的误差量级和你观察到的抖动对得上,那就不是射频问题,是调度问题。解决办法是把回复时间做成硬实时,用硬件定时器触发发送,别走软件任务队列。
现象三:DS-TWR 偶尔出现负的 TOF 或者极小的距离。
四个时间戳里混进了不同回合的数据。丢帧重传时,固件把上一次缓存的 final 时间戳拿来补,交叉相乘之后就出现负值。解决方法是给每个测距回合打一个序号,四个时间戳必须同序号才参与计算,否则整组丢弃。这条我吃过一次亏,排查了两天才定位到是重传逻辑。
5.2 配置与射频类问题
现象四:开了 STS 之后完全测不出距离,关掉就正常。
三个可能:STS 配置档位两端不一致、密钥没同步、前导码长度与 STS 长度组合超出芯片支持范围。按 4.2 节的脚本逐字段比一遍,先排除配置,再查密钥同步流程的日志。特别注意动态 STS 的更新时机,如果一端的更新发生在测距回合中间,这个回合必然失败,要在回合开始的边界做更新。
现象五:实验室里能测 30 米,现场超过 8 米就丢包。
别急着加功率,先算链路预算。UWB 的接收噪声带宽很宽,HRP 信道带宽在 500 MHz 量级,噪声底本身就比窄带系统高二十多 dB,灵敏度对带宽极其敏感。现场如果有金属货架、人体遮挡,多径和衰落会让有效距离打对折。解决办法是把前导码长度换到更长的档位、编码组合换成更低码率,用处理增益换距离,通常能找回 3 到 6 dB,比调发射功率安全。
6. 链路预算反推:拿标准里的参数表核对一遍再动手
最后一章说个具体技巧:在动手写驱动之前,先用文档里的参数表把链路预算算一遍。这一步花不了半小时,但能提前排掉“选完型才发现距离不够”这种返工。
算法不复杂,自由空间路径损耗按 20log10(d) + 20log10(f) + 常数算,接收端噪声功率按 −174 + 10log10(带宽) + 噪声系数估。UWB 的带宽取 500 MHz 时,10log10(5×10⁸) 约 87 dB,噪声底就在 −87 dBm 附近,加 5 dB 噪声系数是 −82 dBm。这个数字比很多人直觉里的低得多,也是 UWB 距离上不去的主要原因。下面这段脚本把常用几个档位都算出来:
import math def link_budget(tx_dbm, bw_hz, nf_db, gain_db=0.0, margin_db=6.0, freq_hz=6.5e9): """返回最大可用路径损耗(dB)与对应自由空间距离(m)""" noise_dbm = -174 + 10 * math.log10(bw_hz) + nf_db # 热噪声底 # 灵敏度近似:噪声底 + 所需信噪比(由编码/前导码决定,这里给保守值) snr_req = 10.0 sens = noise_dbm + snr_req max_pl = tx_dbm + gain_db - sens - margin_db # 允许的最大路径损耗 # 自由空间:PL = 20log10(4*pi*d*f/c) d = (10 ** (max_pl / 20)) * 3e8 / (4 * math.pi * freq_hz) return max_pl, d for bw in (499.2e6, 1.08e9): pl, d = link_budget(tx_dbm=-14, bw_hz=bw, nf_db=5.0) print(f"BW={bw/1e6:.0f}MHz 最大路径损耗={pl:.1f}dB 视距估算={d:.1f}m")这里的几个参数要按你的实际方案替换:tx_dbm是等效全向发射功率,UWB 受法规限制通常很低,−14 dBm 是个保守起点;bw_hz是信道带宽,HRP 常见的是 499.2 MHz 和 1081.6 MHz 两档;nf_db是接收机噪声系数;margin_db是衰落余量,室内多径环境我一般留 6 到 10 dB;snr_req是解调所需信噪比,这个值取决于你选的编码组合和前导码长度,文档里的参数表能查到对应关系,我上面给的 10 dB 只是占位。
算出来的视距估算距离通常比厂商标称值保守,这是正常的,因为标称值往往在理想视距、最优编码组合下测得。如果你算出来只有标称的三分之一,先别怀疑公式,去核snr_req和带宽这两个输入,八成是照抄了别的 PHY 的参数。
做完这一步,再回头看文档里的编码选项表和 STS 长度表,你就知道每个“可选”项对应的是多少米、多少毫瓦。选型讨论也从“这个芯片行不行”变成“在 500 MHz 带宽、6 dB 余量下,需要多少处理增益才能覆盖 15 米”,讨论质量完全不一样。
从那以后我每次拿到一份标准或芯片手册,第一件事都是先把链路预算和关键参数表抄成一个脚本,而不是急着连线调试。很多后来被叫做“玄学”的问题,其实在纸面上就已经有答案了。希望帮到你。
本文还有配套的精品资源,点击获取