第一章 原始数据采集与解析:LVDS数据流格式、C语言解析代码、Python读取bin文件、数据可视化
大家好,我是你们的雷达感知算法讲师。今天咱们正式进入《毫米波雷达点云处理与目标跟踪实战》的第一章。
说实话,很多做雷达算法的朋友,上来就调包、跑模型,结果数据不对,折腾半天才发现是原始数据解析出了问题。我当年刚入行时也踩过这个坑——拿着TI的AWR1843开发板,连上DCA1000采集卡,导出一堆bin文件,愣是不知道怎么读。嗯,今天咱们就把这个坎儿迈过去。
1.1 LVDS数据流格式——雷达和电脑之间的“语言”
先说说LVDS。这玩意儿全称是Low-Voltage Differential Signaling,低压差分信号。说白了,就是雷达芯片和采集卡之间高速传数据的一种方式。
TI的毫米波雷达(比如AWR1243、AWR1843)通过LVDS接口,把ADC采样后的原始数据扔给DCA1000采集卡。采集卡再通过以太网或者USB,把数据打包成bin文件存到电脑上。
那这个bin文件里到底存了啥?我给大家拆开看看:
| 数据块 | 内容 | 字节数 |
|---|---|---|
| 文件头 | Magic Number、版本号、配置参数 | 通常32~64字节 |
| 帧头 | 帧序号、时间戳、 chirp数量等 | 每帧固定大小 |
| chirp数据 | I/Q两路采样点,复数形式 | 每chirp = 采样点数 × 2 × 2字节 |
| 帧尾 | 校验和、填充字节 | 可选 |
这里有个关键点:每个采样点都是16位有符号整数。I路和Q路交替存储。我见过有人把I/Q顺序搞反了,结果点云全跑到负半轴去了——你说冤不冤?
核心公式:
一帧数据总字节数 = 帧头大小 + (chirp数量 × 每chirp采样点数 × 4) + 帧尾大小
其中4 = I(2字节) + Q(2字节)
1.2 C语言解析代码——硬核工程师的选择
我个人习惯用C语言做底层解析。为啥?因为嵌入式平台上跑算法,C是绕不开的。而且C的指针操作,处理这种二进制流简直不要太顺手。
下面是我当年在项目里用的一段解析代码,去掉了业务逻辑,保留了核心结构:
#include <stdio.h>
#include <stdint.h>
#define FRAME_HEADER_SIZE 32
#define CHIRP_NUM 128
#define SAMPLES_PER_CHIRP 256
typedef struct {
uint32_t magic;
uint32_t frame_id;
uint32_t timestamp;
uint8_t reserved[20];
} FrameHeader;
typedef struct {
int16_t i;
int16_t q;
} ComplexSample;
int parse_frame(FILE *fp, ComplexSample *buffer) {
FrameHeader header;
fread(&header, sizeof(FrameHeader), 1, fp);
// 检查Magic Number,我习惯用0xAA55开头
if (header.magic != 0xAA55) {
printf("帧头错误,数据可能不对齐\n");
return -1;
}
// 读取所有chirp数据
for (int c = 0; c < CHIRP_NUM; c++) {
fread(buffer + c * SAMPLES_PER_CHIRP,
sizeof(ComplexSample),
SAMPLES_PER_CHIRP,
fp);
}
return header.frame_id;
}
避坑指南:我曾经在解析时忽略了字节序问题。TI的雷达数据默认是小端序(Little-Endian),但有些采集卡会配置成大端序。如果你发现解析出来的数值大得离谱,先检查一下字节序对不对。
1.3 Python读取bin文件——快速验证的利器
做算法验证的时候,我更喜欢用Python。毕竟numpy一把梭,比C快多了。你想想看,调参的时候每次都要编译C代码,那得多崩溃?
下面这段代码,我几乎每个项目都会复用:
import numpy as np
import struct
def read_ti_bin(file_path, num_chirps=128, num_samples=256):
"""
读取TI毫米波雷达的bin文件
返回形状: (num_chirps, num_samples, 2) -> 最后一维是I/Q
"""
with open(file_path, 'rb') as f:
# 跳过文件头,我一般直接读32字节
f.read(32)
# 计算总采样点数
total_samples = num_chirps * num_samples
# 读取原始数据,每个采样点4字节(I+Q各2字节)
raw_data = np.fromfile(f, dtype=np.int16, count=total_samples * 2)
# 重塑为I/Q对
iq_data = raw_data.reshape(-1, 2)
# 再重塑为(chirp, sample, 2)
iq_data = iq_data.reshape(num_chirps, num_samples, 2)
return iq_data
# 使用示例
data = read_ti_bin('adc_data.bin')
print(f"数据形状: {data.shape}") # (128, 256, 2)
这里有个小技巧:np.fromfile比struct.unpack快得多。我测过,处理1GB的bin文件,前者只要几秒,后者可能要几分钟。做数据预处理时,性能差距就体现出来了。
1.4 数据可视化——看一眼就知道对不对
解析完数据,第一件事不是跑算法,而是可视化。我个人的经验是:先画个图看看数据长啥样。如果波形不对,后面的算法全是白搭。
咱们先画一个chirp的I/Q波形:
import matplotlib.pyplot as plt
def plot_iq_waveform(iq_data, chirp_idx=0):
"""
画单个chirp的I/Q波形
"""
chirp = iq_data[chirp_idx]
samples = np.arange(chirp.shape[0])
plt.figure(figsize=(12, 4))
plt.plot(samples, chirp[:, 0], label='I路', linewidth=1)
plt.plot(samples, chirp[:, 1], label='Q路', linewidth=1, alpha=0.7)
plt.xlabel('采样点')
plt.ylabel('幅度')
plt.title(f'第{chirp_idx}个chirp的I/Q波形')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# 调用
plot_iq_waveform(data, chirp_idx=0)
正常的I/Q波形应该像正弦波,幅度在几百到几千之间。如果你看到一条直线或者全是噪声,那八成是解析参数配错了。
再进阶一点,咱们画个距离-多普勒图(Range-Doppler Map)的雏形:
def plot_range_doppler_heatmap(iq_data):
"""
对每个chirp做FFT,生成距离-多普勒图
"""
# 先对每个chirp做距离维FFT
range_fft = np.fft.fft(iq_data[:, :, 0] + 1j * iq_data[:, :, 1], axis=1)
# 再对chirp维做FFT(多普勒维)
doppler_fft = np.fft.fftshift(np.fft.fft(range_fft, axis=0), axes=0)
# 取幅度
rd_map = np.abs(doppler_fft)
plt.figure(figsize=(10, 8))
plt.imshow(rd_map, aspect='auto', cmap='jet',
extent=[0, rd_map.shape[1], -rd_map.shape[0]//2, rd_map.shape[0]//2])
plt.xlabel('距离单元')
plt.ylabel('多普勒单元')
plt.title('距离-多普勒热力图')
plt.colorbar(label='幅度')
plt.show()
plot_range_doppler_heatmap(data)
注意:上面的代码只是演示数据流是否正确的快速验证。真正的RD谱还需要加窗、做CFAR等处理,咱们后面章节会详细讲。这里你只要看到热力图上有明显的亮斑,就说明数据解析没问题。
本章小结
咱们这一章把原始数据从LVDS到bin文件,再到C和Python的解析,最后可视化验证,整个链路走了一遍。说白了,就是教会你:
- LVDS数据流的结构——帧头、chirp数据、帧尾
- C语言怎么高效解析——指针操作、结构体映射
- Python怎么快速读取——numpy的fromfile
- 可视化验证——画波形、画RD谱
下一章,咱们会深入讲ADC数据预处理,包括直流偏置校正、IQ不平衡补偿这些实战中必用的技巧。到时候我会分享一个我踩过的坑——IQ不平衡没处理好,导致目标角度估计偏了5度,排查了两天才找到原因。
嗯,今天就到这儿。记得动手跑一下代码,光看是学不会的。