1. 项目缘起与整体设计思路
PLFM_RADAR 这个名字,第一次看到的人大概率会愣一下。PLFM 是 Pulse Linear Frequency Modulation 的缩写,中文叫脉冲线性调频,说白了就是雷达领域里最经典的一种波形体制。加上 RADAR 后缀,整个项目的定位就很清晰了:一套基于脉冲压缩体制的小型雷达信号处理系统。热搜词里同时出现了 phased array、FPGA、STM32,这三个词基本框定了这个项目的技术栈轮廓——相控阵天线前端、FPGA 做高速信号处理、STM32 做系统控制与上位机交互。
我接触这类项目差不多有几年时间了,从最早用分立 ADC 加 FPGA 搭简易测距雷达,到后来做多通道相控阵收发链路,踩过的坑不算少。PLFM_RADAR 这个项目之所以值得拿出来聊,是因为它把雷达系统里几个最核心的工程问题都串起来了:线性调频信号的产生、回波信号的采集、脉冲压缩算法的实现、多通道相位控制、以及整机的时序调度。这几个环节任何一个出问题,整机就出不来正确结果。
先说整体设计思路。雷达系统的本质是“发一个已知的信号出去,接收回来,通过比对收发之间的差异来推断目标信息”。PLFM 体制的核心优势在于,它通过在大带宽信号上做脉冲压缩,同时获得大的作用距离和高的距离分辨率。这两个指标在单频脉冲体制下是矛盾的——脉宽越大,能量越强、作用距离越远,但距离分辨率越差。线性调频通过给脉冲内部加上频率调制,让接收端可以用匹配滤波把长脉冲“压缩”成一个窄尖峰,从而同时兼顾两者。
那为什么选 FPGA 而不是纯 DSP 或者纯 STM32 来做?这里涉及一个很实际的算力账。假设系统带宽 B=10MHz,根据奈奎斯特采样定理,ADC 采样率至少 20MSPS,实际工程中通常取 2.5 到 4 倍过采样,也就是 40 到 80MSPS。在这个采样率下,每一路回波数据流每秒产生几千万到上亿个采样点。脉冲压缩本质上是卷积运算,如果脉压系数长度是 1024 点,那每秒钟的乘加运算量就是几亿到几十亿次。STM32 的 Cortex-M4 内核跑 168MHz,做单精度浮点乘加也就勉强够做控制,拿来做实时脉压完全不现实。FPGA 的并行结构天然适合这种流水线式的乘加运算,一个时钟周期可以并行处理多个抽头,这是架构层面的优势,不是靠提高主频能弥补的。
STM32 在这个项目里的角色是系统调度和接口管理。它负责的事情包括:给 FPGA 下发工作参数(比如脉冲重复频率、发射脉宽、接收增益)、控制收发切换时序、管理 T/R 组件的相位配置、通过串口或网口跟上位机通信、以及做一些低速的状态监测。这些任务的特点是实时性要求相对低、逻辑分支多、需要跟各种外设打交道,正好是 MCU 擅长的领域。FPGA 和 STM32 之间通过 SPI 或者并口通信,STM32 做主,FPGA 做从,这样分工明确,各司其职。
相控阵这个关键词意味着系统不是单通道的。典型的相控阵雷达有 N 个天线单元,每个单元后面接一个 T/R 组件,通过控制每个通道的发射/接收相位差来实现波束的电子扫描。假设是 8 通道的线阵,那 FPGA 需要同时处理 8 路 ADC 数据,在脉压之后还要做数字波束形成,也就是对各通道信号加权求和。这个计算量又是单通道的 8 倍。所以 FPGA 选型的时候,逻辑资源和 DSP Slice 的数量要留足余量。我一般建议至少留 30% 的余量,因为后期调试时经常需要加在线调试逻辑、改算法结构,资源用满了会非常被动。
整个系统的信号链是这样的:STM32 配置好参数后,触发 FPGA 产生线性调频数字波形,经过 DAC 变成模拟信号,上变频到射频后由发射通道放大,通过天线辐射出去。回波信号被天线接收,经过低噪放和下变频,变成中频模拟信号,再由 ADC 采样成数字信号送入 FPGA。FPGA 对每一路做数字下变频、脉冲压缩、波束形成,然后把处理后的结果通过通信接口送给 STM32,STM32 再打包上传给上位机做显示和进一步处理。这个链路里每一个环节的时序都要严格对齐,否则脉压结果就是散的。
2. 核心细节解析与实操要点
2.1 线性调频信号的数字产生
线性调频信号在数字域的产生方式,直接决定了整个系统的性能上限。最朴素的做法是用 DDS 核,但 DDS 产生的 chirp 信号在脉冲边缘会有截断效应,频谱旁瓣比较高。我在实际项目中更倾向于用查找表加相位累加器的方式,把预失真也做进去。
具体来说,假设系统时钟 100MHz,要产生带宽 10MHz、脉宽 100us 的 chirp 信号。调频斜率就是 10MHz/100us = 1e11 Hz/s。相位累加器的位宽选 32 位,频率控制字根据瞬时频率计算。每个时钟周期更新一次频率控制字,累加器溢出一次代表相位走完 2π。查找表存的是正弦值,位宽 12 到 14 位就够了,太宽了浪费 Block RAM,太窄了量化噪声大。
这里有个细节很多人会忽略:chirp 信号的起始频率和终止频率要关于中频对称。比如中频是 30MHz,带宽 10MHz,那 chirp 应该从 25MHz 扫到 35MHz,而不是从 30MHz 扫到 40MHz。这样做的好处是下变频之后基带信号的频谱关于零频对称,后续的滤波和抽取都好处理。如果偏到一边,镜像频率会落进有用带宽里,很难滤干净。
注意:查找表的深度和相位累加器的截断位数要匹配。如果累加器取高 12 位查表,而查找表深度只有 1024,那就会产生周期性相位误差,在频谱上表现为离散杂散。我一般让查找表深度等于 2 的截断位数次方,保证一一对应。
2.2 多通道 ADC 数据采集与对齐
相控阵系统里,多路 ADC 之间的采样对齐是个硬骨头。就算你用同一片时钟芯片给所有 ADC 供时钟,PCB 走线长度的微小差异也会导致采样时刻偏差。在 10MHz 带宽下,1mm 的走线差异大约对应 6ps 的延时,看起来很小,但相控阵对通道间相位一致性要求很高,几个 ps 的偏差在波束形成时就会导致旁瓣抬升。
我的做法是在 FPGA 里给每一路 ADC 数据配一个可编程延时线,用 IDELAY 原语或者自己写的移位寄存器实现。上电校准的时候,用一个已知的测试信号从所有通道同时注入,在 FPGA 里比较各通道过零点的时间差,然后调整延时值直到对齐。这个过程可以做成自动校准状态机,每次上电跑一遍,省得手动调。
数据对齐之后还要做数字下变频。如果 ADC 采样率是 80MSPS,中频 30MHz,那数字本振频率就是 30/80 = 0.375 倍采样率。用 NCO 产生正交本振,跟 ADC 数据相乘,得到 I/Q 两路基带信号。这里乘法器的位宽要算清楚:ADC 12 位,本振 12 位,乘积 24 位,累加之后还会增长。我一般保留 24 位给后续处理,在脉压之前再根据实际情况截位。
2.3 脉冲压缩的 FPGA 实现
脉冲压缩是 PLFM_RADAR 最核心的算法环节。原理上就是回波信号跟发射波形的共轭做卷积,在频域看就是匹配滤波。FPGA 里实现有两种路子:时域卷积和频域 FFT 乘法。
时域卷积的结构简单,就是一个 FIR 滤波器,系数是 chirp 的共轭翻转。假设脉压系数 1024 点,那就需要 1024 个乘法器。如果系统有 8 个通道,那就是 8192 个乘法器。这个规模在中低端 FPGA 上就吃不消了。所以实际工程中常用的是分段卷积或者频域方法。
频域方法就是做 FFT,点乘,再 IFFT。1024 点 FFT 大概需要 log2(1024)=10 级蝶形运算,每级 512 个蝶形,每个蝶形一个复数乘法。算下来资源消耗比直接卷积少很多。但 FFT 的缺点是处理延迟大,而且需要额外的存储来缓存数据。对于脉冲雷达来说,脉冲重复周期内的时间是有限的,如果 FFT 处理延迟太大,可能来不及在下一个脉冲到来之前完成。
我一般会根据系统参数来选。如果脉压系数在 256 点以内,通道数不多,直接时域做,简单可靠。如果系数超过 512 点,或者通道数超过 4 路,就上频域方法。Xilinx 的 FFT IP 核配置起来很方便,但要注意缩放因子(scaling schedule)的设置,设置不对会导致溢出或者精度损失。
实操心得:FFT IP 核的缩放因子不要用自动模式,手动指定每一级的缩放。因为 chirp 信号经过匹配滤波后,峰值会很高,动态范围大。自动缩放有时候会在峰值处截断,导致脉压增益下降。我通常在前几级少缩放,后几级多缩放,把动态范围压到输出位宽能表示的范围内。
2.4 STM32 与 FPGA 的协同调度
STM32 和 FPGA 之间的分工前面说了,但具体怎么协同,这里展开讲。两者之间的物理接口我推荐用 SPI,速率可以跑到 10MHz 以上,对于参数下发和状态回读足够了。如果数据量大,比如要把原始 ADC 数据传给 STM32 做记录,那就用并口或者高速串口。
STM32 端的软件架构我一般这样搭:主循环里跑一个状态机,状态包括空闲、配置、发射、接收、处理、上传。每个状态里做该做的事,做完切到下一个状态。FPGA 那边对应地有一个命令寄存器组,STM32 写命令,FPGA 执行,执行完了置一个完成标志,STM32 轮询或者用中断响应。
时序上最关键的是收发切换。发射的时候接收通道要关掉,不然大功率信号会烧掉低噪放。这个切换信号由 STM32 的定时器产生,精度要求到微秒级。STM32 的定时器可以做到,但要注意中断延迟。如果系统对切换时刻要求特别严,比如要求误差小于 100ns,那就得让 FPGA 来产生这个切换信号,STM32 只给一个粗触发。
注意:STM32 和 FPGA 的 IO 电平要匹配。STM32 一般是 3.3V,FPGA 的 Bank 电压可能是 1.8V 或 2.5V。中间要加电平转换,或者选支持 3.3V 的 FPGA Bank。我见过有人直接连,结果 FPGA 的 IO 被拉坏的情况。
3. 实操过程与核心环节实现
3.1 硬件平台搭建与选型
先列一下我推荐的最小系统配置。FPGA 选 Xilinx Artix-7 系列,比如 XC7A100T,逻辑单元够用,DSP Slice 有 240 个,Block RAM 有 4.8Mb,做 8 通道 1024 点脉压加波束形成绰绰有余。如果预算紧,Spartan-6 也能凑合,但资源会紧张一些。STM32 选 F4 系列,比如 STM32F407,168MHz 主频,带 FPU,做控制和通信足够。
ADC 选双通道或四通道的,采样率 80MSPS 以上,分辨率 12 到 14 位。我常用的是 AD9238 或者 ADS62P49 这类。DAC 用 AD9767 或者 AD9744,更新率 100MSPS 以上。时钟芯片用 LMK 系列或者 AD9516,给 ADC、DAC、FPGA 提供同源时钟,保证相参性。
电源部分要特别注意。ADC 和 DAC 的模拟电源要用低噪声 LDO,数字电源可以用开关电源但纹波要控制好。FPGA 的内核电压和 IO 电压分开供,上电顺序要按手册来,一般是内核先上,IO 后上。我见过因为上电顺序不对导致 FPGA 锁死的案例,折腾了很久才找到原因。
PCB 布局上,模拟区和数字区分开,地平面要完整。ADC 的模拟输入走差分线,等长,阻抗 100 欧姆。时钟线要包地,远离高速数字信号。这些是基本功,但雷达系统对噪声敏感,做不好底噪会抬得很高,小目标就淹没了。
3.2 FPGA 工程结构与关键模块
FPGA 工程我一般分成这几个模块:时钟管理、ADC 接口、数字下变频、脉压、波束形成、DAC 接口、STM32 通信、以及顶层状态机。
时钟管理用 MMCM 或 PLL,产生系统需要的各种时钟。比如 100MHz 系统时钟、80MHz ADC 采样时钟、200MHz 处理时钟。注意跨时钟域的信号要同步,用双触发器或者异步 FIFO。
ADC 接口模块负责把 LVDS 或 CMOS 数据接收进来,做串并转换和位对齐。如果 ADC 是 LVDS 输出,要用 ISERDES 原语。位对齐可以用训练序列,也可以用 FPGA 的 IDELAY 扫描。我一般写一个自动对齐状态机,上电后扫描所有可能的延时值,找到误码率最低的那个。
数字下变频模块包括 NCO、乘法器、CIC 滤波器和半带滤波器。NCO 用 DDS 核或者自己写的相位累加器。CIC 做抽取,半带做进一步滤波和抽取。抽取率根据系统带宽来定,比如 80MSPS 抽到 20MSPS,抽取率 4,CIC 的级数选 4 到 5 级。
脉压模块如果用时域结构,就是一个 FIR 编译器生成的滤波器,系数从 ROM 里读。如果用频域,就是 FFT IP 加复数乘法器加 IFFT IP。复数乘法器的位宽要算好,FFT 输出一般是 16 位,乘完之后 32 位,截位到 16 位送 IFFT。
波束形成模块对 N 个通道的脉压结果做加权求和。权重是复数,包括幅度加权和相位加权。相位加权根据波束指向计算,公式是 2πd sinθ/λ,其中 d 是阵元间距,θ 是波束指向角,λ 是波长。这个计算可以在 STM32 里做,把结果传给 FPGA,也可以在 FPGA 里用 CORDIC 算。我一般放 STM32 里算,因为波束指向变化不频繁,没必要占 FPGA 资源。
3.3 STM32 固件开发要点
STM32 这边用 Keil 或者 STM32CubeIDE 开发都行。我习惯用 CubeMX 生成初始化代码,然后手写业务逻辑。时钟树配置好,168MHz 主频,APB1 跑 42MHz,APB2 跑 84MHz。SPI 配置成主机模式,时钟极性相位根据 FPGA 的要求设。
跟 FPGA 的通信协议我一般定义成简单的寄存器读写。STM32 发一个字节的命令,一个字节的地址,然后读或写数据。命令包括写寄存器、读寄存器、启动采集、停止采集等。FPGA 那边用一个状态机解析。
收发切换的定时器配置很关键。用 TIM1 或 TIM8 这种高级定时器,配置成单脉冲模式,触发一次输出一个固定宽度的脉冲。脉冲宽度就是发射脉宽,比如 100us。定时器的预分频和重装载值根据系统时钟算。比如 168MHz 时钟,要 100us 延时,预分频 168-1,重装载 100-1,这样计数 100 次就是 100us。
实操心得:STM32 的定时器输出要经过 IO 口驱动到 FPGA 或者 T/R 组件的控制端。IO 口的翻转速度有限,如果切换沿要求很陡,加一个高速门电路或者用 FPGA 直接产生。我一般让 STM32 给一个提前量,FPGA 收到后用自己的高速时钟精确对齐切换时刻。
3.4 系统联调与参数标定
硬件和固件都准备好之后,联调是最耗时间的环节。我的步骤一般是:先单独测 FPGA 的 DAC 输出,用示波器看 chirp 波形对不对。然后测 ADC 采集,给一个已知信号,看 FPGA 里读到的数据是否正确。再测脉压,用信号源产生一个延迟的 chirp,看脉压峰值位置是否对应延迟。最后测整机,接天线或者用电缆直连,看目标检测效果。
参数标定包括几个方面。一是通道间幅度和相位一致性,用矢量网络分析仪测每个通道的传输系数,然后在 FPGA 里做补偿。二是脉压系数的标定,实际系统的发射波形可能跟理想 chirp 有偏差,要把实际波形采下来做参考。三是波束指向的标定,用远场源或者暗室测方向图,修正相位加权值。
我印象很深的一次调试,脉压结果总是有很高的旁瓣,查了很久发现是 ADC 的采样时钟抖动太大。换了时钟芯片,旁瓣就下去了。所以雷达系统里时钟质量是生命线,省什么都不能省时钟。
4. 常见问题与排查技巧实录
4.1 脉压结果异常排查
脉压结果不对是最常见的问题,表现有好几种。一种是完全没有峰值,那可能是发射通道没工作,或者接收通道没通,或者脉压系数不对。排查的时候先用示波器看发射波形,再看 ADC 采集到的原始数据,最后看脉压输出。逐级确认,不要跳步。
另一种是峰值位置对但旁瓣高。这通常是加权函数没加或者加错了。矩形加权旁瓣只有 -13dB,加汉明窗可以到 -40dB 以下。但加窗会展宽主瓣,降低分辨率,要根据需求权衡。还有可能是 ADC 量化位数不够,或者脉压系数截位太狠。
还有一种是有多个峰值。这可能是多径反射,也可能是系统内部串扰。如果是固定延迟的假峰,检查一下 PCB 上有没有走线耦合。如果是随机的,可能是电源噪声或者时钟杂散。
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 无峰值 | 发射/接收通道故障 | 逐级测波形 |
| 旁瓣高 | 加权错误或量化不足 | 检查加权系数和位宽 |
| 多峰值 | 多径或串扰 | 改变环境对比 |
| 峰值偏移 | 同步问题或延迟估计错误 | 检查触发时序 |
4.2 多通道不一致问题
相控阵系统里通道不一致会直接恶化波束形成效果。表现是方向图旁瓣抬升、波束指向偏移。排查的时候先测各通道的幅相一致性,用同一个信号源功分到各通道,在 FPGA 里比较幅度和相位。
幅度不一致一般是增益控制字没校准,或者 LNA 的增益有离散性。相位不一致可能是走线长度差异、滤波器群延时差异、或者 ADC 采样时刻偏差。走线差异可以在 PCB 设计时用蛇形线补偿,滤波器差异要选一致性好的器件,采样偏差用前面说的 IDELAY 校准。
我一般会在 FPGA 里给每个通道留一个复数补偿系数,幅度补偿一个实数乘法,相位补偿一个复数乘法。校准的时候把系数算出来写进去,之后就不用动了。
4.3 STM32 与 FPGA 通信故障
通信故障的表现是 STM32 读不到 FPGA 的数据,或者读到的数据不对。先查物理连接,用示波器看 SPI 的时钟和数据线有没有波形。如果没有,查 STM32 的 SPI 配置和 IO 口配置。如果有波形但数据不对,查时序参数,时钟极性相位要匹配。
还有一种情况是通信偶尔出错,跑一段时间就死。这通常是跨时钟域同步没做好,或者 FIFO 溢出。检查一下 FPGA 里 SPI 接收模块的时钟域,STM32 的 SPI 时钟和 FPGA 的系统时钟是异步的,要用同步器。FIFO 的深度要够,STM32 读得慢的时候不能溢出。
注意:STM32 的 SPI 在高速率下容易受干扰,如果通信线比较长,加一个串联电阻匹配,或者降低速率。我一般先用低速调通,再逐步提速,找到稳定的最高速率。
4.4 电源与时钟相关故障
电源问题往往表现得很诡异,比如系统偶尔复位、ADC 数据随机跳变、FPGA 配置失败。排查的时候先用示波器看电源纹波,特别是 ADC 和 FPGA 内核的电源。纹波超过 50mV 就要查了,可能是 LDO 选型不对,或者去耦电容不够。
时钟问题表现为频谱杂散、脉压旁瓣高、通道间相位漂移。用频谱仪看时钟的相位噪声,用示波器看时钟的抖动。时钟芯片的环路滤波器参数要按手册配,配错了相噪会恶化。晶振要选低抖动的,普通晶振和 TCXO 差距很大。
我踩过的一个坑是 FPGA 的配置时钟用了内部振荡器,精度不够,导致配置偶尔失败。后来换成外部晶振,问题就没了。所以关键时钟一定要用好的源。
5. 系统扩展与性能优化方向
PLFM_RADAR 这个框架搭起来之后,可以往几个方向扩展。一是增加通道数,从 8 通道扩到 16 通道甚至 32 通道,波束更窄,增益更高。但 FPGA 资源要跟着涨,可能得换更大的器件或者用多片 FPGA 级联。
二是换波形体制,从 PLFM 扩展到非线性调频或者相位编码。非线性调频可以进一步压低旁瓣,相位编码抗干扰能力更强。FPGA 里的脉压模块要相应改,但整体架构不变。
三是加动目标显示和动目标检测。这需要在脉压之后加多普勒滤波器组,对多个脉冲的回波做 FFT。FPGA 里加一个慢时间维的 FFT 模块就行,但存储量会增大,因为要缓存多个脉冲的数据。
四是提高处理带宽。现在 10MHz 带宽对应 15 米距离分辨率,如果做到 100MHz 带宽,分辨率就到 1.5 米。但 ADC 采样率要提到 400MSPS 以上,FPGA 的处理时钟和接口速率都要跟着提,PCB 设计难度也上一个台阶。
性能优化方面,脉压的截位策略很关键。截早了精度不够,截晚了资源浪费。我一般用仿真确定最优截位点,在 MATLAB 里把定点模型跑一遍,看信噪比损失和资源消耗的平衡点。波束形成的加权系数也可以用类似方法优化,在旁瓣电平和主瓣宽度之间找平衡。
最后分享一个我在实际项目中总结的小技巧:FPGA 工程里一定要留足够的在线调试资源,比如 ILA 核。调试阶段把关键信号引出来看,比盲猜快得多。但 ILA 很占 Block RAM,调试完了记得去掉,不然资源不够用。我一般会做一个调试版本和一个发布版本,用宏定义切换,这样不用改代码结构。