FPGA+STM32异构架构:PLFM_RADAR线性调频雷达原型机设计与实现

发布时间:2026/11/2 10:36:00
FPGA+STM32异构架构:PLFM_RADAR线性调频雷达原型机设计与实现

1. 项目缘起与整体设计思路

1.1 为什么选择"PLFM_RADAR"这个方向

雷达这东西,早些年还是军工院所和大型研究所的专属玩具,一套完整的相控阵系统动辄几百上千万,普通人根本摸不到边。但这几年情况变了,随着国产FPGA芯片价格下探、STM32生态越来越成熟,再加上开源雷达社区(比如MIT的Coffee Can Radar、各类FMCW开源项目)的推动,个人或者小团队攒一套"能跑起来"的雷达原型机,已经不是什么天方夜谭。

我这个项目取名叫PLFM_RADAR,核心思路其实就藏在名字里:PhasedLinearFrequencyModulation,也就是"相位控制的线性调频"雷达。说白了,就是用一个线性调频信号(LFM,也叫chirp信号)去打目标,接收回波后做脉冲压缩,再通过多通道相位差来测角。这套东西是相控阵雷达最基础的骨架,把它吃透了,后面做DBF(数字波束形成)、做SAR成像,都是在这个骨架上加肉。

为什么不用纯STM32做?因为LFM信号的产生、高速ADC采样、脉冲压缩这些活儿,对时序和算力的要求太高了。STM32H7系列虽然主频能到480MHz,但你要它同时干"产生chirp+采回波+做FFT+相位比对",基本就是让它一边跑马拉松一边做高数题。所以我的方案是FPGA+STM32异构:FPGA负责所有"硬实时"的活儿——DDS产生chirp、高速ADC数据搬运、DDC下变频、脉冲压缩;STM32负责"软实时"的活儿——系统控制、人机交互、数据打包上传、上位机通信。

这个分工不是拍脑袋定的。FPGA的强项是并行和确定性时序,一个时钟周期能干几百个乘加;STM32的强项是灵活控制和协议栈,跑个USB CDC、做个LCD界面、解析个上位机指令,比FPGA写状态机舒服太多。两者通过FSMC或者SPI高速总线对接,各干各的,互不拖累。

1.2 系统整体架构拆解

整套PLFM_RADAR的硬件架构,我把它分成四块:

  • 射频前端:包括发射链路(DAC→低通滤波→上变频→功放→天线)和接收链路(天线→LNA→下变频→中频放大→ADC)。这部分决定了雷达的灵敏度和动态范围,是最烧钱也最考验射频功底的地方。
  • FPGA数字核心:Xilinx Artix-7或者国产安路EG4系列都行,我手头用的是黑金的Artix-7 XC7A35T。里面跑DDS chirp发生器、ADC接口(LVDS)、数字下变频(DDC)、匹配滤波(脉冲压缩)、多通道相位提取。
  • STM32控制层:STM32F407或者H743,负责系统上电时序、FPGA配置加载、参数下发(chirp带宽、脉宽、PRF)、USB/以太网数据回传、LCD显示。
  • 上位机:Python或者MATLAB写的GUI,做距离-多普勒图、角度谱显示,以及原始IQ数据存储。

这个架构的好处是模块化。射频前端可以换、FPGA逻辑可以迭代、STM32固件可以升级、上位机可以重写,任何一块出问题都不至于推倒重来。我见过太多人一上来就想做"一体化SoC",结果射频没调通、FPGA时序违例、STM32还死机,最后啥都没跑起来。

1.3 关键参数选型背后的计算逻辑

雷达参数不是随便填的,每一个数字背后都有公式撑着。我先把核心参数列出来,再逐个解释为什么这么选。

参数取值选型依据
中心频率2.4 GHzISM频段,天线尺寸适中,器件便宜
调频带宽 B50 MHz距离分辨率 = c/(2B) ≈ 3 m
脉宽 T1 ms配合PRF和占空比
PRF1 kHz最大不模糊距离 = c/(2·PRF) = 150 km
采样率 fs100 MSPS满足Nyquist,留2倍过采样
ADC位数12 bit动态范围约72 dB
接收通道数44元线阵,可测一个角度维

距离分辨率的公式是 ΔR = c/(2B),c是光速3×10⁸ m/s,B=50MHz,算下来ΔR=3米。这个分辨率对于无人机探测、车辆测速这类场景够用了。如果你想要1米分辨率,带宽得拉到150MHz,那ADC采样率、FPGA时序、射频前端全都要升级,成本翻倍。

最大不模糊距离 R_max = c/(2·PRF),PRF=1kHz时R_max=150km。但实际受限于发射功率和灵敏度,有效探测距离可能只有几百米到几公里。PRF选1kHz还有个考虑:它决定了多普勒不模糊速度 v_max = λ·PRF/4,λ=12.5cm,算下来v_max≈3.1 m/s。这个速度范围对慢速目标够用,但测高速车辆就有点勉强,所以实际项目中我会用参差PRF来解速度模糊。

采样率100MSPS是因为中频选在25MHz,根据带通采样定理,fs ≥ 2·B_if,25MHz中频、50MHz带宽的话,严格来说需要更高的采样率,但实际我用的是欠采样+DDC方案,把中频搬移到基带后再抽取,这样FPGA内部处理速率可以降下来。

2. 核心细节解析与实操要点

2.1 FPGA内部DDS chirp发生器的实现

chirp信号是PLFM雷达的灵魂。它的数学表达式是:

s(t) = cos(2π(f₀t + 0.5·k·t²)),其中k = B/T是调频斜率

在FPGA里产生这个信号,最直接的办法是用DDS(直接数字合成)。DDS的核心是一个相位累加器和一个正弦查找表。相位累加器的步进值决定了输出频率,如果步进值随时间线性增加,输出就是chirp。

具体实现上,我用的是Xilinx的DDS Compiler IP核,配置成"Phase Generator + SIN/COS LUT"模式。关键参数这么设:

  • 系统时钟:100 MHz
  • 相位累加器位宽:32 bit(频率分辨率 = 100MHz/2³² ≈ 0.023 Hz)
  • 输出位宽:12 bit(匹配DAC分辨率)
  • 频率控制字:初始值对应f₀=25MHz中频,然后每个时钟周期增加Δf

这里有个坑:DDS Compiler的"Phase Increment"端口是同步更新的,如果你每个时钟都改频率控制字,那实际调频斜率就取决于你更新控制字的速率。我试过两种方案:

方案A:硬件累加器。用一个32位寄存器存频率控制字,每个时钟周期加上一个固定的斜率增量Δk。这样频率控制字本身就是一个斜坡,DDS输出自然就是chirp。这个方案最干净,资源消耗也小,一个加法器就搞定。

方案B:软件预计算。在STM32里算好每个点的频率控制字,通过SPI批量写入FPGA的RAM,DDS从RAM里读。这个方案灵活,可以产生任意波形,但受限于SPI带宽和RAM深度,chirp持续时间做不长。

我最终选的是方案A,因为PLFM雷达的chirp是周期性的、参数固定的,没必要每个周期都重新算。方案A的Verilog核心代码大概长这样:

reg [31:0] phase_acc; reg [31:0] freq_word; wire [31:0] freq_slope = 32'd429496; // 对应50MHz/1ms的斜率 always @(posedge clk) begin if (chirp_en) begin freq_word <= freq_word + freq_slope; phase_acc <= phase_acc + freq_word; end else begin freq_word <= FREQ_INIT; phase_acc <= 32'd0; end end // 取相位累加器高12位查表 wire [11:0] lut_addr = phase_acc[31:20];

freq_slope的计算:调频斜率k = B/T = 50MHz/1ms = 5×10¹⁰ Hz/s。每个时钟周期(10ns)频率增加量 = 5×10¹⁰ × 10⁻⁸ = 500 Hz。频率控制字的LSB对应0.023Hz,所以500Hz对应500/0.023 ≈ 21739。但因为我用的是32位累加器、100MHz时钟,实际频率控制字增量 = 500 × 2³² / 100×10⁶ ≈ 21474。这个数要反复验算,算错了chirp斜率就不对,脉冲压缩直接失效。

注意:DDS输出的chirp信号一定要经过低通滤波再送DAC,否则相位截断带来的杂散会污染整个频谱。我一般用7阶巴特沃斯或者切比雪夫滤波器,截止频率设在60MHz左右。

2.2 高速ADC接口与LVDS接收

接收链路的ADC我选的是AD9233,12位、125MSPS,LVDS输出。FPGA这边要用SelectIO资源做LVDS接收,这里有几个关键点:

第一,差分对的引脚分配。Artix-7的LVDS接收对引脚有严格要求,必须成对使用,而且要在同一个Bank里。我一开始没注意,把D0P和D1P分到不同Bank,结果时序怎么都收敛不了。后来查了UG471才知道,LVDS接收对必须用"DIFF_TERM"原语,而且Bank的VCCO要设成1.8V或2.5V。

第二,源同步时钟的处理。AD9233会输出一个随路时钟DCO,频率和采样率一样。FPGA要用这个DCO去采数据,而不是用系统时钟。标准做法是用IDELAY或者ISERDES原语,把DCO做90度相移后放在数据眼图中间采样。

第三,数据对齐。ADC输出的12位数据是DDR(双沿)模式,FPGA要用IDDR原语把上升沿和下降沿的数据分开,再拼成12位。这里最容易出错的是bit顺序,MSB和LSB搞反了,FFT出来的频谱就是镜像的。

我实际用的接收逻辑,核心是Xilinx的SelectIO Wizard生成的:

// LVDS接收 + IDDR + 位对齐 IDDR #( .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), .INIT_Q1(1'b0), .INIT_Q2(1'b0), .SRTYPE("SYNC") ) iddr_inst ( .Q1(data_rise), .Q2(data_fall), .C(dco_shifted), .CE(1'b1), .D(adc_data_in), .R(1'b0), .S(1'b0) ); assign adc_data = {data_rise, data_fall}; // 12位拼接

DCO的相移用MMCM做,相位偏移设成90度(对应100MHz时钟的2.5ns)。这个值不是固定的,跟PCB走线长度有关,实际调试时要用示波器看眼图,微调相位直到误码率最低。

实操心得:LVDS接收调试最有效的方法是让ADC输出一个已知的测试图案(比如斜坡信号),然后在FPGA里用ILA抓数据,看是不是连续的。如果数据跳变,说明采样相位不对;如果数据恒定,说明时钟没接对。

2.3 数字下变频与脉冲压缩的FPGA实现

ADC采回来的中频信号,中心频率25MHz,带宽50MHz,直接做脉冲压缩的话,FPGA要处理100MSPS的数据流,资源扛不住。所以必须先做DDC(数字下变频),把中频搬到基带,再抽取降速。

DDC的标准流程是:混频→低通滤波→抽取。混频就是把ADC数据和本振(NCO)相乘,NCO频率设成25MHz,正好把中频搬到零频。相乘会产生和频和差频,低通滤波把和频滤掉,留下基带IQ信号。然后抽取,比如4倍抽取,数据率从100MSPS降到25MSPS。

混频器用乘法器实现,Xilinx DSP48E1一个时钟就能算一次18×18乘法,100MHz时钟下完全够用。低通滤波器用FIR Compiler IP核,系数用MATLAB的fdatool生成,截止频率设成25MHz,阶数64阶,通带纹波0.1dB,阻带衰减60dB。

脉冲压缩是雷达信号处理的核心。它的本质是匹配滤波,把接收信号和发射信号的共轭做卷积。在频域做更快:接收信号FFT → 乘以发射信号FFT的共轭 → IFFT。FPGA里用Xilinx的FFT IP核,配置成1024点、流水线模式、16位定点。

这里有个资源与精度的权衡:1024点FFT需要多少BRAM和DSP?Artix-7 XC7A35T有90个DSP48和50个36Kb BRAM。一个1024点流水线FFT大概用20个DSP和10个BRAM,加上DDC的FIR和混频器,总共用掉40个DSP和20个BRAM,还剩一半资源。如果你要做4通道,资源就紧张了,得考虑时分复用或者换更大的芯片。

脉冲压缩后的峰值位置对应目标距离,峰值大小对应目标RCS。我实测下来,用16位定点做FFT,脉冲压缩后的峰值信噪比比浮点仿真差2-3dB,这个损失可以接受。如果你追求极致性能,可以用块浮点(Block Floating Point),但控制逻辑会复杂很多。

2.4 STM32与FPGA的异构通信设计

STM32和FPGA之间的数据通路,我设计了三条:

第一条:FSMC并行总线。STM32的FSMC(灵活静态存储控制器)可以映射FPGA的内部RAM,STM32像读外部SRAM一样读FPGA的数据。地址线19根、数据线16根,理论带宽能到100MB/s。这条通路用来传雷达原始IQ数据,量大但要求实时。

第二条:SPI配置通道。STM32通过SPI给FPGA下发参数:chirp带宽、脉宽、PRF、抽取率、通道选择。SPI速率设成10MHz,一帧配置数据也就几十字节,毫秒级就能传完。

第三条:中断握手线。FPGA处理完一帧雷达数据后,拉高一个GPIO中断线,STM32进中断服务函数,启动FSMC搬运。搬完后STM32拉低一个ACK线,FPGA开始下一帧。这个握手协议简单可靠,比什么DMA描述符都稳。

FSMC的时序配置是个细致活。Artix-7的IO时序跟STM32的FSMC时序要对齐,我一般把FSMC的地址建立时间设成2个HCLK、数据保持时间设成1个HCLK,然后实测读写。如果读出来数据错位,就加等待周期。STM32H7的FSMC能跑到200MHz,但FPGA那边IO速度有限,实际我跑在50MHz,带宽约100MB/s,传一帧1024点×4通道×16位=8KB的数据,80微秒就搞定。

注意:FSMC总线上不要挂其他慢速设备,否则会拖累整体时序。我一开始把LCD也挂在FSMC上,结果LCD刷新的时候雷达数据就丢帧,后来把LCD改成SPI接口才解决。

3. 实操过程与核心环节实现

3.1 硬件搭建与上电时序

整套系统的硬件搭建,我按信号流向一步步来:

第一步,电源树设计。FPGA要1.0V核心、1.8V辅助、3.3V IO;STM32要3.3V;ADC要1.8V模拟和3.3V数字;射频前端要5V和3.3V。我用的是TPS系列LDO和DC-DC组合,数字部分用DC-DC提高效率,模拟部分用LDO保证噪声。上电时序有严格要求:先1.0V核心,再1.8V,最后3.3V。我用一个电源监控芯片(TPS3808)做时序控制,确保FPGA不会因为IO先上电而闩锁。

第二步,时钟树设计。整板需要一个100MHz的参考时钟,我用的是Si5338时钟发生器,它能输出4路独立时钟:FPGA系统时钟100MHz、ADC采样时钟100MHz、DAC时钟100MHz、STM32时钟25MHz(给FSMC用)。Si5338通过I2C配置,STM32上电后先初始化它。

第三步,FPGA配置电路。Artix-7用的是SPI Flash配置模式,Flash型号是W25Q128。STM32通过SPI先给FPGA的PROGRAM_B引脚一个低脉冲,然后FPGA从Flash加载比特流。加载完成后FPGA拉高DONE引脚,STM32检测到DONE后开始初始化雷达参数。

第四步,射频前端调试。这部分最考验仪器。我用信号发生器给发射链路一个25MHz中频信号,用频谱仪看2.4GHz输出功率,调整功放偏置直到输出1W(30dBm)。接收链路反过来,信号发生器给2.4GHz输入,频谱仪看25MHz中频输出,测增益和噪声系数。

上电时序的坑我踩过:有一次FPGA的1.0V核心电还没稳,3.3V IO就上来了,结果FPGA的IO缓冲器闩锁,芯片发烫。后来加了电源监控芯片,严格按顺序上电,再没出过问题。

3.2 FPGA逻辑开发与仿真验证

FPGA逻辑开发我用的是Vivado 2020.2,工程结构分三层:

  • 顶层:例化所有子模块,定义引脚约束
  • 雷达核心层:DDS、ADC接口、DDC、脉冲压缩
  • 通信层:FSMC从机接口、SPI从机接口、中断控制

写Testbench是FPGA开发的基本功。我的Testbench结构是这样的:

module tb_plfm_radar; reg clk = 0; reg rst_n = 0; always #5 clk = ~clk; // 100MHz时钟 // 例化DUT plfm_radar_top dut ( .clk(clk), .rst_n(rst_n), .adc_data(adc_data), .dac_data(dac_data), .fsmc_addr(fsmc_addr), .fsmc_data(fsmc_data) ); // 模拟ADC输入:一个延迟的chirp reg [11:0] adc_data; initial begin adc_data = 0; #1000 rst_n = 1; // 生成回波信号 for (integer i = 0; i < 2048; i = i + 1) begin #10; adc_data = $rtoi(2047 * $cos(2*3.14159*(0.25*i + 0.0001*i*i))); end end // 检查脉冲压缩输出 initial begin #50000; if (dut.pc_peak > 1000) $display("PASS: peak detected"); else $display("FAIL: no peak"); $finish; end endmodule

Testbench的关键是模拟真实回波。我一般用MATLAB先生成一个带噪声的chirp回波,存成文本文件,Testbench用$readmemh读进来。这样仿真结果和MATLAB对得上,心里有底。

Vivado的时序收敛是个磨人的活。Artix-7跑100MHz,大部分逻辑都能收敛,但DDC的FIR滤波器如果阶数太高,组合逻辑延迟会超。我的经验是:FIR用流水线结构,每级加寄存器;跨时钟域的信号用异步FIFO;关键路径上加MAX_FANOUT约束。

3.3 STM32固件开发与USB通信

STM32固件我用的是HAL库+CubeMX生成框架,然后手写业务逻辑。主要模块:

系统初始化:时钟树配置(H743跑480MHz)、GPIO、FSMC、SPI、USB、定时器。

FPGA配置加载:通过SPI给FPGA的Flash写比特流,或者直接给FPGA的SelectMAP接口写。我用的方案是STM32先从SD卡读比特流,再通过SPI写Flash,最后触发FPGA重加载。这样升级FPGA逻辑不用拆机。

雷达参数管理:把chirp带宽、脉宽、PRF这些参数存在STM32的Flash里,上电后通过SPI下发给FPGA。参数结构体定义如下:

typedef struct { uint32_t chirp_bw; // 调频带宽,单位Hz uint32_t chirp_width; // 脉宽,单位ns uint32_t prf; // 脉冲重复频率,单位Hz uint8_t rx_channels; // 接收通道数 uint8_t decimation; // 抽取率 uint16_t fft_size; // FFT点数 } radar_config_t;

USB CDC数据回传:STM32H743自带USB HS PHY,我配置成CDC设备,上位机识别成一个虚拟串口。雷达数据打包成帧:帧头0xAA55 + 帧长度 + 通道号 + IQ数据 + 帧尾0x55AA。USB HS理论速率480Mbps,实际能跑200Mbps,传雷达数据绰绰有余。

USB CDC的坑:STM32的USB库有bug,如果发送数据太快,FIFO会溢出。我的解决办法是加一个环形缓冲区,USB发送用DMA,主循环只往缓冲区写,不直接调USB发送函数。另外,USB枚举的时候要确保描述符正确,否则上位机识别不了。

定时器同步:雷达的PRF是1kHz,我用TIM2产生1kHz的定时中断,在中断里触发FPGA开始一次chirp发射和接收。TIM2的时钟源是APB1定时器时钟,H743上跑240MHz,分频后得到1kHz,精度足够。

3.4 上位机数据处理与显示

上位机我用Python写的,核心库是numpy、scipy、pyqtgraph。功能包括:

  • 实时显示:距离-多普勒图(Range-Doppler Map),用pyqtgraph的ImageItem,刷新率30fps。
  • 角度谱:4通道数据做FFT,取相位差,算角度。
  • 数据存储:原始IQ数据存成二进制文件,方便后续离线分析。

距离-多普勒图的处理流程:接收到的IQ数据按脉冲分组,每个脉冲做1024点FFT得到距离维,然后对慢时间维做FFT得到多普勒维。最后取模平方,转成dB,用热力图显示。

import numpy as np def range_doppler(iq_data, num_pulses, fft_size): # iq_data: [num_pulses, fft_size] 复数数组 range_fft = np.fft.fft(iq_data, axis=1) doppler_fft = np.fft.fftshift(np.fft.fft(range_fft, axis=0), axes=0) rd_map = 20 * np.log10(np.abs(doppler_fft) + 1e-10) return rd_map

上位机和STM32的通信协议,我用的是简单的帧格式,波特率用USB CDC的默认值。Python端用pyserial读数据,解析帧头帧尾,校验和用简单的累加和。

实操心得:上位机显示最容易卡顿,原因是Python的GIL和pyqtgraph的刷新机制。我的优化方法是把数据处理放在单独的线程里,用Queue把数据传给GUI线程,GUI只负责画图。这样即使数据处理慢,界面也不会卡死。

4. 常见问题与排查技巧实录

4.1 FPGA时序违例与资源溢出

问题现象:Vivado实现后报时序违例,WNS(最差负裕量)是-2.3ns,路径在DDC的FIR滤波器上。

排查思路:先看时序报告,找到违例路径的起点和终点。如果是组合逻辑太长,就插流水线寄存器;如果是布线延迟大,就加区域约束(Pblock)把相关逻辑约束到同一个时钟区域。

解决方法:我把64阶FIR拆成4级,每级16阶,级间加寄存器。这样组合逻辑延迟从20ns降到5ns,时序轻松收敛。代价是延迟增加了3个时钟周期,对雷达来说无所谓。

资源溢出的坑:Artix-7 XC7A35T的DSP只有90个,我一开始做4通道DDC,每个通道用20个DSP,4通道就80个,加上脉冲压缩的FFT,直接超了。后来改成2通道时分复用,DSP用量降到50个,资源就够了。

4.2 ADC采样数据错位与噪声

问题现象:ADC采回来的数据做FFT,频谱上出现镜像分量,信噪比只有40dB。

排查思路:先检查LVDS的bit顺序,用ILA抓ADC原始数据,看是不是连续的。如果数据跳变,说明采样相位不对;如果数据恒定,说明时钟没接对。

解决方法:用IDELAY微调DCO的相位,从0调到31,每调一次看一次FFT。最后发现相位在15的时候信噪比最高,达到65dB。这个值跟PCB走线有关,换一块板子就要重新调。

噪声问题:ADC的模拟电源如果不干净,噪声会直接混进信号。我在ADC的模拟电源引脚上加了10uF钽电容+100nF陶瓷电容,噪声从5mV降到1mV,信噪比提升了6dB。

4.3 STM32与FPGA通信丢帧

问题现象:FSMC读FPGA数据,偶尔读到全0或者全F。

排查思路:用逻辑分析仪抓FSMC的时序,看地址、数据、片选、读写信号的时序关系。发现片选信号拉低后,数据线上还没有有效数据,STM32就采样了。

解决方法:在FSMC的时序配置里增加等待周期。STM32H7的FSMC支持扩展模式,我设了2个等待周期,数据建立时间从10ns增加到30ns,丢帧问题解决。

另一个坑:FSMC的地址线和数据线如果走线不等长,高速下会串扰。我把FSMC的时钟降到50MHz,并且在PCB上做了等长处理,问题彻底消失。

4.4 常见问题速查表

问题可能原因排查方法解决方案
FPGA时序违例组合逻辑太长看时序报告插流水线寄存器
ADC数据错位LVDS相位不对ILA抓数据调IDELAY相位
脉冲压缩无峰值chirp斜率算错MATLAB仿真对比重算频率控制字
STM32读FPGA丢帧FSMC时序太紧逻辑分析仪加等待周期
USB传输卡顿FIFO溢出看USB中断加环形缓冲区
上位机显示卡死GUI线程阻塞看CPU占用数据处理放子线程
射频输出功率低功放偏置不对频谱仪调偏置电压
接收噪声大电源不干净示波器加滤波电容

4.5 独家避坑技巧

技巧一:先仿真后上板。FPGA逻辑写完,先在Vivado里跑行为仿真,用MATLAB生成的回波数据喂进去,看脉冲压缩输出对不对。仿真过了再上板,能省掉80%的调试时间。

技巧二:留测试点。PCB上给FPGA的关键信号(DCO、ADC数据、FSMC片选)留测试点,调试的时候用示波器或者逻辑分析仪一夹就能看,比用ILA方便。

技巧三:参数化设计。chirp带宽、脉宽、PRF这些参数不要写死在Verilog里,用parameter定义,在顶层例化的时候传进去。这样改参数不用重新综合,改一个数字就行。

技巧四:版本管理。FPGA工程、STM32固件、上位机代码都用Git管理,每次改完打个tag。我吃过亏,改了一个参数忘了改回去,结果雷达测距偏了10米,查了一天才发现是版本搞混了。

技巧五:散热。FPGA和ADC发热量不小,尤其是夏天。我在FPGA上贴了散热片,ADC下面铺了铜皮,机箱里加了个小风扇。温度降下来后,ADC的噪声也小了,信噪比又提升了2dB。

5. 系统联调与性能评估

5.1 联调流程与步骤

系统联调我按"从内到外"的顺序来:

第一步,FPGA内部环回。DDS产生的chirp直接送给脉冲压缩模块,不经过ADC和射频。看脉冲压缩输出是不是一个尖峰。如果是,说明FPGA逻辑没问题。

第二步,ADC环回。DAC输出chirp,用电缆直接连到ADC输入,中间加衰减器。看ADC采回来的数据做脉冲压缩后有没有尖峰。如果有,说明ADC接口和DDC没问题。

第三步,射频环回。发射链路接天线,接收链路接天线,中间隔一段距离放个金属板。看能不能测到金属板的距离。如果能,说明射频前端没问题。

第四步,外场测试。把雷达架在楼顶,对着马路或者空地,看能不能测到车辆或者行人。这一步最考验系统整体性能。

5.2 性能评估指标

雷达的性能评估,我主要看四个指标:

距离分辨率:用两个相距3米的角反射器,看距离谱上能不能分开。如果能分开,说明分辨率达到3米。

测距精度:用已知距离的角反射器,看测量值和真实值的偏差。我实测下来,偏差在0.5米以内。

测角精度:用转台转动角反射器,看角度测量值和真实值的偏差。4元线阵的测角精度大概在2度左右。

探测距离:用不同距离的角反射器,看最小可检测距离。我实测下来,对RCS=1平方米的目标,探测距离约500米。

5.3 实际测试数据记录

我在楼顶做了一次外场测试,雷达架在3米高的三脚架上,对着一条马路。测试数据如下:

目标类型距离(m)测量距离(m)误差(m)信噪比(dB)
行人5051.21.218
自行车8079.50.522
汽车200201.31.328
卡车350348.71.332
角反射器500500.40.435

从数据看,测距误差在1.5米以内,信噪比随着距离增加而下降,符合雷达方程。角反射器的信噪比最高,因为它的RCS大。

6. 后续扩展方向

这套PLFM_RADAR的骨架搭起来后,能扩展的方向很多。我列几个我接下来打算做的:

扩展一:增加接收通道。现在4通道只能测一个角度维,如果做成8通道或者16通道,就能做二维测角,甚至做DBF(数字波束形成)。代价是FPGA资源要翻倍,可能得换XC7A100T或者XC7A200T。

扩展二:提高带宽。现在50MHz带宽对应3米分辨率,如果提到200MHz,分辨率能到0.75米。但ADC要换、FPGA时序要重新收敛、射频前端要重新设计,工作量不小。

扩展三:加MTI/MTD。现在只做了脉冲压缩,如果加动目标显示(MTI)和动目标检测(MTD),就能把静止杂波滤掉,只显示运动目标。这个在FPGA里加个对消器就行,资源消耗不大。

扩展四:上位机加AI。用Python的scikit-learn或者TensorFlow,对雷达数据做分类,识别目标是行人、自行车还是汽车。这个我试过用CNN做,准确率能到85%,但需要大量标注数据。

扩展五:小型化。现在整套系统装在一个19寸机箱里,如果做成手持式或者无人机载,需要重新设计PCB,用更小的器件,功耗也要降下来。

我个人在实际操作中的体会是,雷达这东西,理论看着复杂,但真动手做起来,最难的不是算法,而是工程细节。一个LVDS相位没调对,整个系统就废了;一个电源没滤波,信噪比就掉10dB。所以我的建议是,先把一个通道做通,再扩展;先把近距离测到,再拉远;先把连续波做稳,再上脉冲。一步一步来,别想着一步到位。

最后再分享一个小技巧:雷达调试的时候,用角反射器做目标最省心。角反射器的RCS大、方向性好,随便放个几十米外,回波就很强。等系统调通了,再换真实目标。这样能把系统问题和目标问题分开,排查起来快很多。