简介:围绕HB100与RCWL0516两款常见微波雷达模块的多普勒信号处理实践资源,面向电子工程、嵌入式开发及雷达技术初学者,帮助理解FMCW与脉冲多普勒雷达的工作机制,并掌握从信号采集、混频解调到滤波分析的完整处理流程。压缩包共9个文件、约28.6MB,包含Arduino驱动/信号采集代码、Python信号处理脚本、演示视频、实测频谱图与说明文档,覆盖混频、解调、数字滤波和FFT变换等关键环节,便于对照代码与实验数据逐步上手。资源以天花板风扇等运动目标为例展示了多普勒频移的实际观测结果,配有STFT时频图及音频文件,直观体现速度信息提取过程;同时针对两款模块的硬件差异和适用场景做了对比说明,有助于快速选型和搭建实验环境。目前已有168人学习下载,适合作为雷达原理验证、课程设计或电子竞赛的参考资料。
1. 用 HB100 和 RCWL-0516 同时抓多普勒信号:这包东西到底能干什么
拿到「使用HB100和RCWL0516雷达模块测试多普勒雷达信号处.zip」这个标题时,我第一反应是:终于有人把这两个模块放到同一套测试流程里了。HB100 是经典的 10.525GHz 多普勒雷达前端,输出的是模拟差频信号,得自己搭放大和比较电路;RCWL-0516 则是把微波多普勒前端和信号调理全集成好的数字模块,直接给高/低电平。两者原理同源,但信号形态和调试思路完全不同。这个压缩包的价值不在代码量,而在于它把「从模拟差分信号到数字触发信号」的完整链路串了起来——适合正在做人体存在检测、运动感应、速度方向判别的人。
我拿到这类资源的第一件事,不是急着解压跑代码,而是先搞清楚它到底覆盖了哪一层:是只给了 Arduino 读 RCWL 数字引脚的例程,还是连 HB100 的中频放大、比较器阈值、FFT 频点计算都做了?从标题判断,它应该包含「测试多普勒雷达信号处理」的完整流程,也就是从 HB100 的模拟中频信号采集、RCWL-0516 的数字触发读取,到两者数据的对比与判定逻辑。适合的读者是:想用雷达做存在检测但被误触发搞崩溃的嵌入式开发者,以及需要快速评估两个模块在同一个场景下谁更靠谱的选型工程师。如果你只是想把 RCWL 当人体感应开关用,那这个包对你来说偏重;如果你想理解多普勒信号从天线到 GPIO 的全过程,这包正中下怀。
2. 多普勒信号处理链路拆解:从 HB100 的模拟中频到 RCWL-0516 的数字触发
2.1 两个模块的本质区别:为什么 HB100 需要外接电路而 RCWL-0516 不用
HB100 和 RCWL-0516 的核心都是 10.525GHz 的微波多普勒雷达前端,但它们的输出形态天差地别。HB100 的 IF 引脚输出的是微弱的模拟差频信号,幅度通常在几十微伏到几毫伏级别,频率与运动速度相关——人慢速走动时大约是几十赫兹,挥手这类快速动作可以到两三百赫兹。这个信号必须经过高增益放大、带通滤波和比较器整形之后,才能被单片机的 ADC 或外部中断识别。RCWL-0516 则把这一切封装在了模块内部,它的 OUT 引脚直接输出高/低电平,高电平表示检测到运动,低电平表示静止,3.3V 逻辑直接兼容 ESP32、STM32 等主流主控。简单说,HB100 给你的是「原材料」,RCWL-0516 给你的是「半成品」。
在选择测试方案时,我倾向于两条腿走路:用 RCWL-0516 验证系统级触发逻辑,用 HB100 做信号级的精细分析。RCWL 适合判断「有没有人动」,HB100 适合分析「动了多远、速度多快、方向如何」。两者的 ADC 采样率需求差异很大——RCWL 你只需要以 10-50Hz 的速率轮询 GPIO,HB100 则至少需要 1kHz 以上的采样率才能捕捉完整的肢体运动频谱。这个压缩包里的测试代码如果只处理 RCWL,那它省事但信息量少;如果连 HB100 的采样都做了,那才是真正意义上的「多普勒雷达信号处理」。
2.2 HB100 中频信号放大与偏置电路:动手搭第一级信号链
HB100 的 IF 输出不能直接进单片机 ADC,原因有两个:一是信号幅度太小,ADC 的分辨率根本不够;二是信号是交流的,有正有负,而大多数单片机的 ADC 只能采集 0-VREF 的单极性信号。所以第一级电路必须做两件事:放大和直流偏置。常见做法是用单电源运放搭一个同相放大电路,把增益设在 60-80dB 之间,同时通过电阻分压把静态工作点抬到电源电压的一半。下面的电路参数是我在模拟项目X里验证过的一组值,适合 3.3V 供电的单片机系统:
// HB100 中频信号调理电路参数(运放供电 3.3V,单电源) // 第一级:同相放大,增益约 47dB(约 220 倍) // Rf = 220kΩ, Rin = 1kΩ, 增益 Av = 1 + Rf/Rin = 221 // C1 耦合电容 10uF,与 Rin 组成高通滤波器,截止频率 f = 1/(2π*10uF*1kΩ) ≈ 16Hz // 用于滤除直流分量和低频呼吸干扰 // 第二级:偏置电路 + 二阶低通滤波 // R1 = 10kΩ, R2 = 10kΩ 分压得到 1.65V 偏置 // C2 = 15nF, R3 = 10kΩ, 构成低通滤波,截止频率约 1kHz // 目的:滤除高频噪声,保留 0.1Hz-300Hz 的有效多普勒频移这段参数说明里的关键逻辑是:第一级放大决定了你能检测的最小运动信号,第二级滤波决定了你能分辨的最高运动速度。如果你发现手挥动很快时信号反而削顶失真,说明增益太高;如果人慢慢走过没反应,说明高通截止频率设太高了。调试时用示波器看第二级输出,静止时应该是一条稳定的 1.65V 直流线,有人走动时波形幅度应该在 0.5V 到 2.8V 之间摆动。超过这个范围就调增益电阻,低于这个范围就增大 Rf。这套电路是整个测试平台的基石,RCWL 那边不需要动电路,但 HB100 这边如果你不调好,后面的 FFT 分析全是白搭。
2.3 RCWL-0516 的三种输出模式与触发逻辑测量
RCWL-0516 看起来简单,但它内部其实有三级处理:微波前端检测多普勒频移、片上运放放大、比较器输出逻辑电平。模块上还有一个 CDS 引脚(光敏电阻输入),可以做成「白天不触发、晚上触发」的节能逻辑。我实测下来,RCWL 的 OUT 引脚默认是低电平,检测到运动时拉高,保持时间由模块上的 C(延时)引脚决定——通过调整外接电阻或电容,延时可以从 2 秒到 200 秒不等。需要注意,这个模块在首次上电时会误触发一次,因为内部比较器有个自校准过程。
如果你想用 RCWL-0516 做精确的存在检测而不是简单的运动检测,那就得放弃自带的延时逻辑,改读模块上 3OUT 引脚——这是未延时的原始输出,信号出现和消失几乎实时。这里我给出一个用 Arduino 读取两种输出的最小测试代码,这个逻辑同样适用于 ESP32:
# 伪代码:读取 RCWL-0516 的 OUT 与 3OUT 引脚,对比延时差异 # 引脚定义:OUT -> GPIO5, 3OUT -> GPIO6 import time import RPi.GPIO as GPIO # 以树莓派为例,Arduino 同理 GPIO.setmode(GPIO.BCM) GPIO.setup(5, GPIO.IN) # OUT 延时输出 GPIO.setup(6, GPIO.IN) # 3OUT 原始输出 last_out = GPIO.input(5) last_3out = GPIO.input(6) while True: out = GPIO.input(5) # 延时后的电平 raw = GPIO.input(6) # 原始触发电平 if out != last_out or raw != last_3out: print(f"OUT={out} 3OUT={raw} 时间戳={time.time():.3f}") last_out, last_3out = out, raw time.sleep(0.01) # 100Hz 轮询,足够捕捉 2 秒以上的保持信号这段代码的逻辑是同时监控两个引脚的变化,通过打印的时间戳你能直接量出模块自带的延时到底有多长。我遇到过一种坑:用 OUT 引脚做串口唤醒,结果人走了串口还在睡——原因就是模块的延时电容设成了 200 秒。换成 3OUT 引脚或调小延时电容就能解决。测试时你在模块前方 3 到 5 米走动,观察两个引脚的跳变时刻差,就能算出当前延时配置的实际值。这个数据对你后续做存在检测算法非常关键,因为你得在软件里补偿这个延时,或者在硬件上直接绕过它。
3. 搭建双模块测试平台:ADC 采样、FFT 频点计算与触发比对
3.1 硬件连接与电源去耦:避免微波模块拉垮 ADC 采样
把 HB100 和 RCWL-0516 同时接到一块主控板上,第一个要处理的问题是电源噪声。微波雷达模块的功耗虽然不大,但它们在发射时会有毫安级的电流波动,如果和 ADC 基准电压共用同一个 LDO,你采到的 HB100 模拟信号里就会出现 10.525GHz 的调制噪声(虽然频谱上离得很远,但谐波会折叠进低频带)。我的做法是:HB100 用独立的 5V 供电,RCWL-0516 也用 5V,但两者之间加一个磁珠隔离;主控板的 ADC 基准源用独立的 3.3V LDO,并且在靠近引脚处放一个 100uF 电解电容加 0.1uF 陶瓷电容。
接线时还有一个容易踩的坑:HB100 的 GND 和 IF 输出共地,但 IF 信号线上不能直接长距离飞线,否则会引入 50Hz 工频干扰。我一般把运放电路板直接插在 HB100 模块的排针上,中间距离控制在 3 厘米以内。RCWL-0516 相对皮实,它的 OUT 输出可以直接走 10 厘米以上的杜邦线,不影响逻辑电平。整体连接顺序是这样的:HB100 的 IF 引脚 -> 第一级放大 -> 第二级滤波 -> 主控 ADC 引脚;RCWL-0516 的 3OUT 引脚 -> 主控 GPIO。两个模块的安装位置至少要间隔 20 厘米,避免微波信号互相串扰产生虚假触发。
3.2 ADC 采样率与缓冲设计:为什么 1kHz 是底线
HB100 的多普勒频移频率计算公式是 fd = 2 * v * f0 / c,其中 v 是目标径向速度,f0 是 10.525GHz,c 是光速。人正常行走速度约 1.4m/s,对应的频移大约是 98Hz;快速挥手能达到 3m/s 的等效径向速度,频移约 210Hz。根据奈奎斯特采样定理,采样率至少是最高频率的两倍,即 420Hz,但实际做 FFT 分析时,我建议采样率设在 1kHz 以上,因为你还得考虑频谱泄漏和窗函数的影响。如果采样率只有 500Hz,200Hz 附近的信号幅度会明显衰减,而且峰值谱线会变宽,影响速度估计的精度。
在代码实现上,ADC 采样不能直接在中断里做 FFT,那会阻塞主循环。常见做法是用 DMA 或定时器中断把采样数据填进环形缓冲区,攒够一个 FFT 帧(比如 1024 点)后再做处理。这里我给出一个用 Arduino Due 或 ESP32 实现的双缓冲采样框架:
// 双缓冲 ADC 采样:采样率 1kHz,FFT 帧长 1024 点(约 1 秒数据) // 使用定时器中断触发 ADC 采样,两个缓冲区交替使用 const int FFT_SIZE = 1024; volatile uint16_t buffer[2][FFT_SIZE]; volatile uint8_t active_buf = 0; volatile uint16_t buf_index = 0; volatile bool frame_ready = false; void setup() { // 配置定时器:1kHz 中断 // ESP32 使用 ledcAttachPin + ledcSetup 实现精确 1ms 定时 ledcSetup(0, 1000, 8); // 频率 1kHz,分辨率为 8 位 ledcAttachPin(26, 0); // 引脚 26 挂钩定时器 // ADC 引脚配置 analogReadResolution(12); // 12 位 ADC 分辨率 pinMode(34, INPUT); // GPIO34 为 ADC1_CH6 } void onTimer() { // 定时器中断服务函数(实际用硬件中断或 ISR) buffer[active_buf][buf_index] = analogRead(34); buf_index++; if (buf_index >= FFT_SIZE) { buf_index = 0; active_buf = (active_buf + 1) % 2; // 切换缓冲区 frame_ready = true; // 置位标志,通知主循环处理 } } void loop() { if (frame_ready) { // 处理非活动缓冲区中的数据 uint16_t* data = buffer[(active_buf + 1) % 2]; // 注意:active_buf 已切换 // 在这里调用 FFT 函数,计算幅度谱 // 例如:applyHammingWindow(data); computeFFT(data); findPeak(); frame_ready = false; } }这段代码里的关键参数是采样率 1000Hz 和帧长 1024 点,对应的频率分辨率为 1000/1024 ≈ 0.98Hz,足够区分 0.5m/s 的速度差。双缓冲的核心好处是:主循环处理一帧数据时,定时器中断还在往另一个缓冲区填数据,不会丢采样。注意active_buf切换的时序——我在代码注释里写了处理的是切换后的非活动缓冲区,这里很容易搞反,一旦处理了正在写入的缓冲区,FFT 结果会出现毛刺。实际调试时,你可以在 buffer 填满后打印第一个和最后一个数据点的值,看是否连续,如果不连续就检查中断优先级和缓冲切换逻辑。
3.3 FFT 频点映射与速度计算:让频谱告诉你目标动了多快
拿到一帧 1024 点的时域数据后,FFT 输出的每个频点对应的实际频率是 bin_index * sample_rate / N,即 bin_index * 1000 / 1024。幅度谱的峰值位置就是目标运动产生的多普勒频移。我之前做过一个模拟项目X,在其中检测目标是静止还是走动、快走还是小跑,本质上就是看峰值频率落在哪个区间。下面这段处理逻辑比较完整,包含窗函数、频谱峰值搜索和速度换算:
import numpy as np # 参数设置 sample_rate = 1000 # Hz fft_size = 1024 # 点数 c = 3e8 # 光速 m/s f0 = 10.525e9 # 雷达频率 Hz def process_frame(data): # 1. 去直流:减去均值,消除偏置电压影响 data = data - np.mean(data) # 2. 加汉明窗:减少频谱泄漏 window = np.hamming(fft_size) data_windowed = data * window # 3. 计算 FFT 幅度谱 spectrum = np.abs(np.fft.rfft(data_windowed, n=fft_size)) # 4. 频率轴映射 freqs = np.fft.rfftfreq(fft_size, d=1.0/sample_rate) # 5. 排除直流附近的低频噪声(< 20Hz),寻找峰值 search_band = freqs >= 20 # 只搜索 20Hz 以上的频段 band_spectrum = spectrum[search_band] band_freqs = freqs[search_band] if band_spectrum.size > 0: peak_idx = np.argmax(band_spectrum) peak_freq = band_freqs[peak_idx] # 6. 换算速度:v = fd * c / (2 * f0) velocity_ms = peak_freq * c / (2 * f0) velocity_kmh = velocity_ms * 3.6 return peak_freq, velocity_ms, velocity_kmh else: return 0, 0, 0这段代码里最容易调错的是search_band = freqs >= 20这一步。为什么排除 20Hz 以下?因为环境中的低频振动、呼吸引起的胸腔起伏、甚至是电源的微小波动都会在 10-30Hz 之间产生伪峰。20Hz 以下一刀切,虽然会漏掉极慢速目标(0.28m/s 以下),但能显著降低误报。如果你需要检测非常慢的移动,可以把阈值降到 10Hz,但要在时域上加高通滤波配合。速度换算公式里有个隐含前提:目标运动方向是径向的,如果目标横着走,实际多普勒频移要乘以 cos(角度),算出来的速度会比真实速度偏小。这个偏差在测试时要记住,否则你会觉得模块测速不准。
3.4 双模块触发比对:用同一时刻的 RCWL 电平验证 HB100 的 FFT 判断
测试平台的核心价值在于把两个模块的判定结果对齐到同一个时间轴上。我一般会在主循环里做这样一件事:每 100ms 记录一次 RCWL-0516 的 3OUT 引脚电平,同时把最近一次 FFT 输出的峰值频率和幅度存进日志。当 RCWL 从低变高时,回看前 500ms 的 HB100 频谱记录,确认 FFT 是否也检测到了相应的运动频移。这一步能帮你找出两个模块的响应速度差和灵敏度差。下面是一个简单的状态记录结构:
// 双模块比对日志结构体 typedef struct { uint32_t timestamp_ms; // 毫秒时间戳 uint8_t rcwl_raw_state; // RCWL-0516 3OUT 引脚电平 0/1 uint8_t rcwl_delayed; // RCWL-0516 OUT 引脚电平 0/1 float hb100_peak_freq; // HB100 FFT 峰值频率 Hz float hb100_peak_amp; // HB100 FFT 峰值幅度 float hb100_velocity; // 换算速度 m/s } sensor_log_t; // 用环形缓冲区保存最近 200 条记录 // 当检测到 RCWL 上升沿时,从缓冲区取前 5 条记录生成事件摘要 // 摘要内容包括:RCWL 触发前 500ms 内 HB100 是否有大于阈值的峰值这个比对逻辑能直接回答标题里的「测试多普勒雷达信号处理」问题——你不再只是看单个模块的原始数据,而是在验证两个模块对同一个物理事件的响应一致性。我遇到不少次这样的情况:RCWL 被触发了,但 HB100 的 FFT 峰值幅度很低,说明目标的雷达散射截面(RCS)很小,比如人背对模块走动;反过来,HB100 检测到很大的频移但 RCWL 没反应,通常是 RCWL 的灵敏度调节旋钮(如果有的话)或内部阈值设置导致。这些问题只有在双模块同时测试时才能暴露。
4. 避坑指南:HB100 与 RCWL-0516 联调时的 5 个高频翻车点
4.1 HB100 输出信号被 50Hz 工频淹没:现象、原因与解决
现象是示波器上看到的波形不是干净的多普勒信号,而是叠加了一个 50Hz 的大幅度正弦波,人站在模块前不动时尤其明显。原因有两种:一是 HB100 的天线靠近电源适配器或市电插座,电磁辐射直接耦合进 IF 引脚;二是运放电路的输入引线过长,形成了一个接收天线。解决方法是把整个模拟前端用铜箔胶带或金属屏蔽罩包起来,屏蔽罩接地;同时把运放电路板和 HB100 模块之间的连线缩短到 1 厘米以内。如果还不行,就在运放输入端加一个 100nF 的并联电容到地,把高频辐射滤掉。这个坑在办公室环境下特别常见,因为显示器、充电器都会辐射。
4.2 RCWL-0516 上电瞬间误触发:现象、原因与解决
现象是每次给模块上电,OUT 引脚都会先输出一个 1 到 3 秒的高电平,然后才恢复低电平。原因是模块内部的比较器需要时间完成自动电平校准,这个过程中阈值不稳定,容易把噪声当成运动信号。解决方法是软件延时忽略:在全部代码初始化时,等 RCWL 稳定 3 秒后再开始读取引脚;或者用 3OUT 引脚配合外部 RC 滤波,但最简单还是延时。如果你用 RCWL 做低功耗唤醒,这个上电误触发会导致系统刚唤醒就误判为有人,解决方法是休眠前记录一个标志位,唤醒后延时 2 秒再读取。
4.3 HB100 与 RCWL-0516 互相干扰导致误触发:现象、原因与解决
现象是 RCWL 没有检测到任何运动时,OUT 引脚偶尔会闪一下高电平,时间非常短。原因是两个微波模块靠得太近,HB100 发射的 10.525GHz 信号泄漏进 RCWL 的接收通道,产生拍频信号。解决方法是把两个模块的朝向错开 90 度,或者拉开至少 30 厘米的间距。如果布局不允许,可以在 RCWL 的供电线上串一个 100Ω 电阻和一个 100uF 电容滤波,降低微波信号的传导干扰。我实测过,两个模块面对面安装时误触发概率极高,背对背安装几乎无干扰。
4.4 FFT 频谱峰值漂移:现象、原因与解决
现象是人明明站在原地不动,FFT 频谱里却总是出现一个 15 到 25Hz 的弱峰,而且频率位置会缓慢漂移。原因是呼吸引起的胸腔起伏确实产生了多普勒频移——人体呼吸时胸腔运动速度约 0.01 到 0.05m/s,对应频移约 0.7 到 3.5Hz,但谐波会抬升到 20Hz 附近。解决方法是提高速度检测阈值,设定峰值幅度必须超过背景噪声的 3 倍才算有效;或者用带通滤波器,只保留 30Hz 以上的频移。如果你想检测静止状态下的人,那就要换方案了——多普勒雷达本质上只能检测运动,无法感知绝对静止。
4.5 采样率不均匀导致 FFT 结果噪声大:现象、原因与解决
现象是同一测试场景下 FFT 频谱的旁瓣电平异常高,峰值附近出现大量毛刺。原因是定时器中断里用了 ADC 的阻塞读取(比如 Arduino 的analogRead在某些板子上是阻塞的),导致采样间隔抖动,等效于引入了一个 PM 调制噪声。解决方法是改用 DMA 采样或硬件定时器直接触发 ADC 转换,把采样间隔固定死。如果你用的是 ESP32,可以用adc_continuous驱动实现硬件级连续采样,采样时钟由 APB 周期决定,抖动在纳秒级,FFT 出来的频谱干净得多。
5. 测试脚本与自动化判定:把原始数据变成可信的触发结论
5.1 构建一个通用的多普勒测试流程:从串口打印到文件记录
做雷达模块测试,最怕的是靠眼睛看串口打印判断结果,那样没法量化对比。我通常会把测试数据按统一格式写入 CSV 文件,包含时间戳、RCWL 原始电平、RCWL 延时电平、HB100 峰值频率和幅度。有了文件,后续可以用任何数据分析工具做聚类或统计。下面是我常用的一段数据记录代码,适配串口输出加 SD 卡或 PC 端上位机:
# 串口读取并记录双模块测试日志到 CSV import serial import csv import time ser = serial.Serial('COM10', 115200, timeout=1) # 根据实际端口修改 csvfile = open(f'radar_test_{int(time.time())}.csv', 'w', newline='') writer = csv.writer(csvfile) writer.writerow(['timestamp_ms', 'rcwl_raw', 'rcwl_delayed', 'hb100_freq', 'hb100_amp']) buffer = '' try: while True: data = ser.read(ser.in_waiting or 1).decode('utf-8', errors='ignore') if data: buffer += data # 按行解析,每行格式: 12345,1,0,42.5,320.1 while '\n' in buffer: line, buffer = buffer.split('\n', 1) line = line.strip() if line.startswith('EVT'): fields = line.split(',') if len(fields) == 5: writer.writerow(fields) print(f"{fields[0]}ms RCWL_raw={fields[1]} RCWL_delayed={fields[2]} " f"HB100={fields[3]}Hz amp={fields[4]}") except KeyboardInterrupt: ser.close() csvfile.close()这里的串口帧格式是自主定义的,关键是让单片机的日志输出做到每条不超过 128 字节,波特率 115200 时每秒最多可输出约 100 条,足够覆盖 100Hz 的 RCWL 轮询频率。CSV 的好处是通用,后续用 Python 的 pandas 读进来做可视化、统计触发延迟、画混淆矩阵都方便。如果你不用串口,也可以直接用 SD 卡写文件,但要注意 SD 卡写入是有延时的,不能在中断里直接写,应该在主循环中做。
5.2 灵敏度与距离测试的标准动作:让数据具备可比性
测试雷达模块最怕的是每次测试姿势不一样,导致数据没法横向对比。我建立了一套固定的测试动作,这里分享给你参考:测试距离从 0.5 米到 6 米,每次递增 0.5 米;每个距离点做三类动作——原地小幅度挥手、原地快速挥手、从侧面正常速度走过。每个动作重复 5 次,记录触发率和 FFT 峰值频率分布。通过这套标准动作,你能得到 RCWL-0516 的有效触发距离和 HB100 的信号幅度衰减曲线,还能发现各自的盲区角度。
测试环境也很讲究:金属物体、墙体拐角、人员穿行都会引入多径反射,导致信号出现不稳定的多峰值。如果你在一个办公区域测试,结果会和在空旷走廊完全不同。我一般会优先选择 5 米见方的空旷房间,四壁无大面积金属。记录数据时要标注环境条件,否则过两周回看数据完全不知道为什么这次灵敏度比上次低。还有一个细节是测试者的衣着——金属拉链、手机、钥匙都会增强雷达反射,测试时保持同一身衣服,或者穿纯棉衣物。
5.3 判定阈值标定:把幅度阈值和频率范围做成可配置参数
测试到最后阶段,你要把「检测到人」这个结论变成代码里的一个判定条件。常见做法是设置两个条件:HB100 频谱峰值频率在 30Hz 到 300Hz 之间,且峰值幅度超过设定的阈值(比如背景噪声均值的 3 倍)。RCWL 这边只需读取电平。但阈值不能拍脑袋设,要通过采集静止状态下的基线数据来确定。下面是一小段标定代码:
# 静态基线采集:计算背景噪声的均值和标准差 import numpy as np # 假设已经采集了 N 帧静止时的峰值幅度 # 数据来源:无人环境下连续采样 10 秒 static_amplitudes = np.array([12.3, 11.8, 15.2, 9.7, 13.5]) # 示例数据 baseline_mean = np.mean(static_amplitudes) baseline_std = np.std(static_amplitudes) threshold = baseline_mean + 3 * baseline_std # 3σ 准则 print(f"基线均值: {baseline_mean:.2f}, 标准差: {baseline_std:.2f}") print(f"建议阈值: {threshold:.2f}")这个 3σ 阈值的好处是:在静态环境下误触发概率极低(理论小于 0.3%),但代价是检测灵敏度偏低,小动作可能触发不了。如果你要检测的是微弱的呼吸信号,这个阈值就不合适;如果你只关心有人走动,把它调成 2σ 也够用。我在实际项目里会把阈值做成可配置项,放在 flash 里让现场工程师通过串口命令调整。标定这一步骤在标题的「测试多普勒雷达信号处理」里容易被忽略,但它恰恰决定了你的测试结论是否可靠。
6. 进阶:把 HB100 模拟信号和 RCWL 数字信号融合成状态机
最后一步,我想讲一个比「分别测两个模块」更实用的技巧:用状态机把两个信号源融合成 4 个状态——无目标静止、有目标微动、有目标运动、目标接近或远离。RCWL-0516 的 3OUT 引脚只给出「动/不动」的二值,HB100 的 FFT 频移则给出「动了多快」的连续量化值。把两者做与逻辑能显著降低误报:只有 RCWL 触发且 HB100 频移超过阈值,才确认有目标;单独一个模块触发则标记为待确认。
在实现上,我维护一个 3 个元素的滑动窗口,记录最近 3 个 100ms 周期的判定结果,只有当连续 2 个周期都判定为「有目标」才切换到运动状态,离开运动状态需要连续 3 个周期判定为「无目标」。这个滞回逻辑的好处是消除边缘抖动。HB100 的信号在这一步还有一个独特的用法:通过 FFT 峰值频率的正负变化判断目标运动方向——正向多普勒频移代表目标靠近,负向代表远离,但要注意模数转换后的信号没有相位信息,所以方向判断需要 IQ 解调——如果只是幅度谱,只能测速度模值,测不了方向。这一点在标题提到的「信号处理」里非常关键,很多初学者误以为幅度谱峰值能判方向,实际上做不到。
下面这段伪代码给出了状态机的核心逻辑,你可以直接改造成自己的主循环:
# 双模块融合状态机:state 0=空闲, 1=待确认, 2=有目标 state = 0 consecutive_detections = 0 def process_cycle(rcwl_triggered, hb100_peak_freq, hb100_threshold): """每个 100ms 周期调用一次""" global state, consecutive_detections hb100_detected = hb100_peak_freq > hb100_threshold both_detected = rcwl_triggered and hb100_detected if state == 0: if both_detected: consecutive_detections += 1 if consecutive_detections >= 2: state = 2 print("进入有目标状态") else: consecutive_detections = 0 elif state == 2: # 需要连续 3 个周期无检测才退出 if not (rcwl_triggered or hb100_detected): consecutive_detections -= 1 if consecutive_detections <= -3: state = 0 print("进入空闲状态") else: consecutive_detections = 0让我老实说,这套融合逻辑我最初是在调试某图像处理Demo的配套雷达模块时总结出来的,当时纯用 RCWL 触发结果误报太多,加了一个 HB100 的 FFT 幅度阈值做二次确认之后,误报率直接降了一个数量级。RCWL 的优势是灵敏度高、响应快、集成度高,但无法区分「人走动」和「窗帘被吹动」;HB100 的优势是你能拿到频移的量化值,虽然模拟电路调试麻烦,但信号信息量丰富。两者配合不是复杂的事,就是在代码里多一个与逻辑和一点延迟容忍,但效果是单独任何一个模块都给不了的。希望这套从模拟前端到状态机的完整链路,能让你在拿到那个压缩包之后少走几圈弯路。
本文还有配套的精品资源,点击获取