尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于FFT与数字滤波的信号分离装置设计与实现——电赛H题完整方案

发布时间:2026/9/9 4:30:45

资讯中心
01
ARTICLE

基于FFT与数字滤波的信号分离装置设计与实现——电赛H题完整方案

基于FFT与数字滤波的信号分离装置设计与实现——电赛H题完整方案
简介2023年电赛H题的代码与解题思路已整理为完整工程包面向参赛队伍和嵌入式开发者重点解决两路同频信号叠加后再分离输出的稳定性难题。题目要求对A、B信号求和得到C由单片机对C作FFT分析识别各分量频率再用DDS分别输出A、B。资源围绕这一流程给出可直接编译的完整工程包含信号采集、FFT频谱处理、AD9833驱动和DDS输出并将晶振由25M换为1M以提高频率分辨率同时分享了对温漂的补偿思路和实际调测中的注意事项方便读者快速复现与排查漂移问题。压缩包共136个文件以C、H源码为主包含STM32F4外设驱动、LCD显示、中断定时等模块另有Keil工程配置、hex固件和说明文档整体约11MB。已有685人学习使用适合有STM32基础、正在备赛或做信号处理类项目的读者参考。 2023年电赛H题“信号分离装置”赛后就一直挂在很多实验室群的置顶消息里。直到第二年还经常看到有人翻找它的代码。这道题之所以让人念念不忘是因为它把“测量”和“处理”两件事焊在了一起——你不仅要读出混合信号里两路正弦的频率、幅度还要基于这些参数实时重建两路干净波形。本文就把我从赛题拆解、方案选型、算法设计到代码骨架和踩坑记录的完整思路写出来适合正在备赛的队伍也适合想搞懂FFT、数字滤波和波形重构如何协同工作的人。1. 赛题回顾这道“信号分离装置”到底卡在哪1.1 题目任务拆解以我印象中的H题任务框架为例给定两路频率、幅度在一定范围内不同的正弦信号先经过加法器合成为一路混合信号再由装置接收。装置需要完成两件事——第一把混合信号中两路信号的频率、幅度测量出来并显示第二将分离后的两路信号送出去让双通道示波器可以稳定地看到两路正弦波形。注意题面上“经加法器合成”这个表述其实是出题方在明示输入是纯线性叠加信号没有交调产物、没有谐波干扰。这对选手是有利的意味着常规线性系统分析手段都适用不需要处理复杂的非线性耦合。真正的难点不在这里而是在“频率未知、幅度未知”的前提下系统能不能快速锁定参数并完成分离。赛题任务对应技术难点测量频率频率分辨率、频谱泄漏、抗强信号淹没弱信号测量幅度栅栏效应造成谱线误差、系统增益标定分离输出滤波器设计、滤波器系数自适应更新、输出波形质量双通道显示DAC刷新率、两路输出相位连续性、触发稳定性1.2 “分离”二字的真相先要知道对方在哪才能动手滤很多队伍看到这道题第一反应是做两个模拟带通滤波器中心频率分别对准两路信号后面再加一级放大。这种做法在频率相差比较大的时候确实可行一旦两路信号频率靠近问题立刻暴露两个带通的通带会重叠互相串扰调高Q值能让选择性变好但高Q运放电路很容易自激而且元件误差会让实际中心频率偏移。更关键的是赛题里的信号频率是未知的需要用仪器先测出来再处理。模拟滤波器的中心频率是固定的没法自动对准目标。所以数字域的“先测量、后滤波”才是正路先用FFT之类的算法把两路信号频率、幅度估计出来再根据这些参数动态计算滤波器系数相当于系统自己长了眼睛看到目标在哪再动手。2. 方案选型主控、ADC与模拟前级的搭配逻辑2.1 算力需求估算为什么STM32F407就够用关于主控电赛圈子里最常见的争论就是选单片机还是选FPGA。我的结论很直接这道题用STM32F407完全够用FPGA在这个任务里属于堆料树莓派则压根不合适。做个简单估算。假设信号最高频率50kHz采样率取200kHzFFT点数选1024频率分辨率大约是195Hz。一次1024点实序列FFT在168MHz主频的F407上用CMSIS-DSP库跑耗时在几百微秒级别。就算完成两轮FFT、加上滤波和波形生成整个处理周期也就一毫秒上下远低于显示刷新的需求节奏。树莓派的问题在于官方ADC采样率和精度都不够看外挂高精度ADC要折腾驱动再加上非实时操作系统做混合信号处理任务调度抖动会直接体现在输出波形上。FPGA的优势是高吞吐并行计算但这个赛题采样率只要几百kSPS计算量对F407来说压力不大没必要承担FPGA的开发周期和调试成本。2.2 硬件链路从混合信号输入到双通道输出我的整体信号链设计如下混合信号输入 → 同相比例缩放电路 → STM32F407片上ADC采样 → DMA乒乓缓冲 → FFT频率测量 → 数字分离滤波 → 片内DAC重构波形 → 运放缓冲输出 → 示波器显示前级用一片低噪声运放比如OPA227或者MCP6022做电压跟随和幅度缩放把信号幅度调整到ADC量程的70%到90%。STM32F407片上12位ADC配合DMA多通道扫描采样采样率足够应付这个场景。输出端用片内DAC加一级运放缓冲DAC后面再加一个简单的RC低通滤掉采样时钟引入的高频毛刺。这里有个选型细节很多人忽略F407的DAC输出默认带缓冲输出阻抗比较低直接接示波器没问题但如果要驱动长同轴电缆最好再加一级运放做电流增强否则高频段波形边沿会被电缆分布电容拉垮。2.3 软硬件分工模拟只做调理分离全部交给数字这道题里模拟电路的角色要非常克制。前级运放只干三件事阻抗变换、幅度缩放、抗混叠滤波。混合信号里两路正弦的分离不要试图用模拟带通实现全部放到数字域处理。原因很简单比赛现场最缺的是时间。数字域改滤波器系数就是改两个浮点变量重新烧录固件也不过几秒钟模拟电路调滤波特性要换电阻电容焊接、测量、再焊接一个参数改下来小十分钟。而且数字滤波器没有温漂不会因为板子发热导致中心频率偏移。实测下来所有状态参数尽量做成可调变量再配一个串口打印通道现场调参效率会高很多。3. 核心算法FFT测量、相关法测幅相与陷波分离的完整链路3.1 三种信号分离方案对比开始编码之前我在纸上列了三种可行方案反复权衡后选定了方案B。方案实现思路优点缺点A两个固定IIR带通滤波器各自分离一路信号实现最简单延迟低滤波器系数固定频率接近时互相干扰严重BFFT先测量频率幅度再实时计算陷波器系数分离自适应能力强两路频率靠得近也能处理需要合理安排测量与滤波的计算时序C对整块数据做FFT在频域把不需要的谱线置零再做IFFT还原思路直观无滤波器设计过程块延迟大频谱泄漏严重信号边界有明显失真方案A最容易写但也是最容易在发挥部分翻车的。因为发挥部分通常会设置两路信号频率很接近的工作条件固定带通在这种工况下基本无能为力。方案C看起来很美实际操作中两路信号如果频率差不是FFT分辨率的整数倍矩形窗泄漏会让两个谱峰拖尾重叠置零操作会连目标信号的一部分一起切掉还原出的波形有严重的吉布斯振铃。方案B的本质是“测量引导滤波”先通过FFT得到两路频率的粗略值再用更精确的方法拿到幅度和相位最后动态构造陷波器把通道A中的B分量压下去反过来也一样。这套流程看起来绕但每一步都有明确目的稳定性最高。3.2 频率精估计峰搜索加抛物线插值FFT直接给出的峰值谱线位置只能落在频率分辨率的整数倍上真实频率往往在两条谱线之间。如果直接用峰值索引反算频率误差最大可以达到半个频率分辨率。以200kHz采样率、1024点FFT为例半个分辨率大约是97Hz这个误差对后级陷波器来说不能接受因为中心频率偏了100Hz分离效果会明显恶化。解决办法是抛物线插值。取峰值谱线k以及相邻两条谱线k-1、k1的幅度拟合一条抛物线找到抛物线顶点位置作为真实峰值的偏移量delta 0.5 * (mag[k-1] - mag[k1]) / (mag[k-1] - 2*mag[k] mag[k1])真实频率估计为f_est (k delta) * fs / N这个公式在加汉宁窗后精度很高实测可以把频率误差压到几赫兹以内。记得在搜索峰值时避开直流分量和镜像频段k范围限制在1到N/2-1之间不然插值数组很容易越界。3.3 幅度和相位为什么我不直接读FFT谱线FFT单根谱线的幅度同样受栅栏效应影响直接读出来的幅度和真实值有偏差。更麻烦的是相位信息经过FFT之后受窗函数影响严重汉宁窗会让相位产生偏移修正起来很麻烦。我在代码里用的是I/Q相关法。假设已经通过插值知道了某路信号的频率f0就生成本地参考信号对原始采样序列做互相关I 2/N * sum(x[n] * cos(2PIf0n/fs)) Q 2/N * sum(x[n] * sin(2PIf0n/fs))幅度A sqrt(II QQ)相位φ atan2(Q, I)。这套计算其实就是一次精简的Goertzel算法计算量比完整FFT小得多而且因为没有窗函数影响幅度和相位都更准确。实测下来只要频率估计偏差在几赫兹以内幅度误差可以控制在2%以内。3.4 分离滤波器用陷波器而不是带通确定了每路信号的频率和幅度之后分离滤波我用的是IIR陷波器。对通道A而言另一路信号B就是干扰我只需要在B的频率点挖一个凹坑A信号自身几乎不受影响。二阶IIR陷波器的传递函数需要实时计算系数给定陷波频率f0、采样率fs和品质因数Q先算归一化角频率w0 2PIf0/fs再计算系数。为了工程实现方便我直接用了带通Q值固定为30到50的经验值比赛信号频率差最小也有几百赫兹这个Q值足够在保证陷波深度的同时不伤到目标信号。实际调的时候有个心得陷波器带宽不要太宽。很多队伍喜欢把陷波带宽设得很宽以为这样能把对方信号“罩住”结果把自己的目标信号边缘也切掉了一部分输出波形幅度明显偏小。正确做法是先尽量把频率测准然后把陷波带宽压到刚好覆盖对方信号的频率偏差范围这样对目标信号的损伤最小。4. 代码落地状态机、双缓冲采样与DDS重构的实现骨架4.1 软件框架一个状态机管住所有阶段整个程序我没有用复杂操作系统就是裸机状态机加中断。状态分为SAMPLE采集、MEASURE测量、RECONSTRUCT重构输出、DISPLAY显示刷新。主循环不断轮询状态标志优先保证采样不被阻塞FFT这些耗时计算放在主循环里做。一个关键原则ADC的DMA中断里绝对不要做FFT或者滤波计算只用标志位通知主循环“数据块准备好了”。否则中断处理时间过长会直接导致采样丢失波形出现周期性断裂。4.2 ADC加DMA双缓冲采样双缓冲是保证连续采样的标准做法。我开了两个1024点的缓冲区DMA半传输中断和全传输中断交替触发。下面是简化后的回调逻辑#define BLOCK_SIZE 1024 uint16_t adc_data[2][BLOCK_SIZE]; volatile uint8_t active_buf 0; volatile uint8_t block_ready 0; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc-Instance ADC1) { active_buf ^ 1; block_ready 1; } }主循环检测到block_ready后处理active_buf ^ 1对应的缓冲区也就是刚刚采满的那个块。DMA开启循环模式确保硬件一直在采样软件处理速度跟不上的时候最新的数据块会自动覆盖最旧的数据不影响后续处理。4.3 频率估计与陷波系数更新频率估计函数我封装成独立模块输入是FFT幅度谱输出是精估计频率float estimate_freq(float *mag, int n, float fs) { int k 1; for (int i 2; i n / 2; i) { if (mag[i] mag[k]) k i; } float a mag[k - 1], b mag[k], c mag[k 1]; float delta 0.5f * (a - c) / (a - 2.0f * b c); if (delta 0.5f) delta 0.5f; if (delta -0.5f) delta -0.5f; return (float)(k delta) * fs / n; }插值公式里的分母如果接近零说明峰值附近数据异常必须加一个delta限幅保护否则算出来的频率会飞掉。这套保护在我实测过程中救过好几次信号幅度特别小的时候频谱噪声会让插值结果跳变。算完频率后两路信号频率就确定了分别标记为f1和f2。对通道A需要让f2的陷波器生效对通道B需要让f1的陷波器生效。陷波器系数在每次测量完成后更新一次不需要每个采样点都重算这样可以省掉大量浮点运算。4.4 双路波形重构用DDS思路代替每帧重算分离滤波完成后最稳妥的输出方式其实是直接用测量得到的频率、幅度参数用DDS思路重新合成两路正弦波而不是把滤波后的波形直接送给DAC。这个设计决策很重要。滤波后的波形会保留原始信号的噪声和采样毛刺直接DAC输出后示波器波形不够干净。而用参数重构输出的是理想正弦波示波器上看几乎没有杂散。代价是相位可能和原始信号有偏差但赛题只要求看到两路分离后的正弦波形不要求严格锁定输入相位所以这个代价可以接受。DDS的核心是一个32位相位累加器uint32_t phase_acc 0; uint32_t phase_step_a, phase_step_b; float amp_a, amp_b; // 根据频率计算步进 phase_step_a (uint32_t)(freq_a * 4294967296.0f / DAC_UPDATE_RATE); phase_step_b (uint32_t)(freq_b * 4294967296.0f / DAC_UPDATE_RATE); // 定时器中断里每周期调用一次 void dac_output_callback(void) { phase_acc phase_step_a; float va sinf(2.0f * PI * (float)phase_acc / 4294967296.0f); // 通道B同理使用独立的相位累加器 uint16_t dac_a (uint16_t)(2048 amp_a * 2000.0f * va); HAL_DAC_SetValue(hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_a); }每路输出用独立的相位累加器好处是相位自然连续不会出现每帧从零开始带来的跳变。sinf函数在F407上虽然能跑但中断频率高的时候会比较吃CPU实测可以换成查表加线性插值速度能提升好几倍。5. 实测翻车点与现场调优从频谱泄漏到DAC噪声的排查记录5.1 频谱泄漏把小信号淹没了第一次联调的时候我设置了两路信号一路幅度3V另一路幅度0.3V频率差只有几百赫兹。FFT频谱上小信号那个峰几乎看不到被大信号的泄漏拖尾压得只剩一个鼓包。排查后发现两个问题第一FFT前没有加窗矩形窗的旁瓣衰减只有13dB强信号旁瓣直接盖过了弱信号第二采样数据里混入了直流偏置零频泄漏进一步污染低频段。解决办法是FFT前先做去直流再乘汉宁窗。汉宁窗旁瓣衰减可以到31dB左右虽然频率分辨率会稍微变差但对这个赛题来说完全够用。如果两路信号幅度差超过20dB一轮测量不够我会做两步测量第一轮用FFT找到强信号的精确频率先把强信号陷波掉第二轮再对残余信号做一次FFT这时候弱信号就没有强信号压制了能很干净地测出来。5.2 示波器波形不停滚动的真正原因分离后的波形在示波器上一直跳触发很不稳定看起来像频率在漂。用示波器的频率测量功能看数值确实在变但变化很规律。一开始怀疑是DAC时钟精度问题排查了定时器配置确认时钟没问题。最后定位到问题出在重构波形的相位上。我早期版本是每处理完一块数据就从相位零开始重新生成波形两块数据之间会出现相位跳变示波器触发点每次都不一样波形自然滚动。这个排查过程花了我一个多小时教训很深刻。改成双累加器DDS后相位连续性解决了波形在示波器上稳如老狗。5.3 DAC开关噪声串进ADC的经典地线问题输出波形上出现了一些不随输入变化的固定杂散峰值频率大约是几百kHz。用频谱仪看杂散频率和DAC的更新率有整数倍关系怀疑是DAC切换瞬间的电流冲击通过地线耦合到了ADC输入端。检查电路发现模拟地、数字地、DAC输出地全部汇到一根长面包板地线上地线阻抗把DAC的开关噪声变成了共模干扰。处理方式是重新布线模拟地单点接入主地模拟电源和数字电源分开走运放供电加RC去耦。改动之后杂散峰直接降了20dB以上。这种问题在比赛中很容易被忽视因为你用万用表量地线是通的但高频噪声根本不管直流导通走的是阻抗路径。5.4 现场调参的优先级和几个实用习惯比赛现场时间紧张我最后总结出一套调参顺序先保证频率测量准再优化幅度测量最后才调分离效果。频率测不准的情况下后级滤波器系数全是错的分离效果不可能好。所有关键参数包括采样率、FFT点数、陷波器Q值、窗函数选择、DAC输出幅度全部做成全局变量用一个数组统一管理。配合串口发送命令修改参数现场调参不用反复断电烧录。这个习惯帮了我大忙——性能测试前一分钟发现陷波带宽太宽直接串口发一条命令就改完了不用重新编译工程。再提一个很多人不看重的点第一版程序一定要加看门狗。比赛现场的程序会跑很久如果某个极端输入让程序陷入死循环有看门狗能在几百毫秒内自动复位恢复正常。没有看门狗设备死机一次就要人工断电重启在展示环节非常被动。关于“代码”这件事我的最大体会是H题的核心代码其实不算长真正难的是状态调度和各模块之间的衔接。采样、测量、滤波、重构这四个环节单独拿出来都不复杂但把它们串成一个实时流水线需要考虑缓冲管理、计算耗时、参数更新时机这些工程问题。调试时一定按“采样→测量→分离→重构”的顺序逐级验证每级确认没问题再进下一级翻车的概率会小很多。这套“先测量、再陷波、后重构”的方法论后来我做音频去混叠和传感器信号分离时也一直在用算是这一届电赛留给我的最大收获。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。