毫米波雷达数据回放与复盘实战课程 - 第2节
一、原始ADC数据格式——从天线到数字的“第一手资料”
做雷达数据回放,第一步就是搞懂原始ADC数据长什么样。说白了,ADC数据就是雷达芯片把天线收到的模拟信号“翻译”成数字后的结果。我刚开始接触毫米波雷达时,拿到一包数据直接懵了——全是16进制数,完全不知道哪个是哪个。
TI的IWR系列芯片,比如IWR6843、IWR1642,输出的原始ADC数据通常是复数形式。为什么呢?因为雷达需要同时知道信号的幅度和相位。幅度决定目标有多“亮”,相位决定目标在哪个方向。
1.1 复数IQ数据的含义
IQ数据,I是In-phase(同相分量),Q是Quadrature(正交分量)。你可以把I想象成信号的“实部”,Q是“虚部”。
一个采样点就是一个复数:sample = I + j*Q
我在项目中遇到过一个问题:某次采集的数据回放时,目标距离总是不对。排查了半天,发现是I和Q的顺序搞反了。嗯,这里要注意——不同芯片的IQ顺序可能不同,TI的芯片通常是I在前、Q在后。
1.2 数据包结构:Header + Payload
雷达数据不是一股脑儿往外扔的。它像快递一样,有“包裹单”(Header)和“货物”(Payload)。
典型的TI毫米波雷达数据包结构如下:
| 字段 | 大小(字节) | 说明 |
|---|---|---|
| Magic Word | 8 | 固定值,用于帧同步,比如0x0102 0304 0506 0708 |
| Version | 4 | 协议版本号 |
| Total Packet Length | 4 | 整个数据包长度(含Header) |
| Platform | 4 | 芯片型号标识 |
| Frame Number | 4 | 当前帧序号 |
| Subframe Number | 4 | 子帧序号(多帧模式用) |
| Chirp Margin | 4 | Chirp起始余量 |
| Num Samples | 4 | 每个Chirp的采样点数 |
| Num Chirps | 4 | 每帧的Chirp数 |
| Num Rx Antennas | 4 | 接收天线数量 |
| Num Tx Antennas | 4 | 发射天线数量 |
| Payload | 可变 | 原始ADC数据(IQ数据) |
Header之后紧跟着的就是Payload,里面按顺序排列着所有天线的IQ数据。我习惯先解析Header,确认帧号和数据长度,再读取Payload。这样做的好处是——万一数据丢包了,你能立刻发现。
二、常见雷达芯片的数据输出协议
2.1 TI IWR系列芯片
TI的IWR系列,比如IWR6843、IWR1642,最常用的数据输出接口是LVDS(低压差分信号)。LVDS的好处是速度快、抗干扰强。实际项目中,LVDS数据通过DMM(Debug Message Manager)模块打包成上面说的Header+Payload结构。
TI还提供了DCA1000数据采集卡,专门用来抓取原始ADC数据。DCA1000通过以太网把数据传到PC,数据格式就是标准的LVDS协议包。
2.2 其他常见芯片
除了TI,还有Infineon、NXP等厂商的毫米波雷达芯片。它们的输出协议大同小异,但细节上有些差异:
- Infineon:常用SPI或LVDS输出,数据包结构类似,但Magic Word不同
- NXP:部分芯片支持MIPI CSI-2接口,数据格式更接近摄像头
- Acconeer:采用UDP直接输出,数据包更轻量
你想想看,不同芯片的协议差异,说白了就是Header里的字段定义不同。只要掌握了TI的协议,其他芯片的协议你花半天就能看懂。
三、数据格式解析实战——手把手教你拆包
光说不练假把式。我们拿一段真实的TI雷达数据来拆解一下。
假设收到一包数据,开头8个字节是:01 02 03 04 05 06 07 08——这就是Magic Word,用来找帧头。
接下来4个字节是Version,比如01 00 00 00表示版本1.0。
再往后4个字节是Total Packet Length,比如00 10 00 00(小端模式,实际长度0x1000=4096字节)。
嗯,这里要注意:TI的数据包是小端模式(Little Endian),低字节在前。我刚开始做解析时,直接按大端读,结果帧号全乱了。
解析Payload时,假设有4个接收天线,每个Chirp采样256点,每帧有128个Chirp。那么一帧的Payload大小就是:
4(天线数) × 256(采样点/Chirp) × 128(Chirp/帧) × 4(字节/复数采样点) = 524,288 字节
数据在Payload中的排列顺序是:
- 先按Chirp排列:Chirp 0, Chirp 1, ..., Chirp 127
- 每个Chirp内按天线排列:Rx0, Rx1, Rx2, Rx3
- 每个天线内按采样点排列:Sample 0, Sample 1, ..., Sample 255
我习惯用Python的struct模块来解析,代码大概长这样:
import struct
def parse_ti_packet(data):
# 解析Header
magic = struct.unpack('<Q', data[0:8])[0]
version = struct.unpack('<I', data[8:12])[0]
total_len = struct.unpack('<I', data[12:16])[0]
frame_num = struct.unpack('<I', data[16:20])[0]
# 解析配置信息
num_samples = struct.unpack('<I', data[32:36])[0]
num_chirps = struct.unpack('<I', data[36:40])[0]
num_rx = struct.unpack('<I', data[40:44])[0]
# 解析Payload(IQ数据)
payload = data[48:] # Header固定48字节
iq_data = []
for i in range(0, len(payload), 4):
i = struct.unpack('<h', payload[i:i+2])[0]
q = struct.unpack('<h', payload[i+2:i+4])[0]
iq_data.append(complex(i, q))
return frame_num, iq_data, num_samples, num_chirps, num_rx
四、知识体系总览
下面这张图,是我自己总结的本章知识结构。你看一眼,心里就有谱了。
这张图把本章的核心内容串起来了。从原始ADC数据格式,到数据包结构,再到不同芯片的协议差异。你跟着这个脉络走,就不会迷路。
可可精品资料库