☰
毫米波雷达峰值检测:从RD图到干净点迹的最后一公里
2026/11/3 7:57:06 网站建设 项目流程

简介:面向雷达数据处理与信号分析场景的MATLAB工具包,聚焦DCA1000采集数据的读取与雷达信号峰值检测两大任务。readDCA1000.m负责打开二进制文件、解码IQ采样并分离I/Q分量,将原始数据转换为可处理的时间序列;findAllPeak.m则在预处理基础上执行寻峰算法,通过幅度阈值或导数特征识别目标峰位,输出峰值位置与幅度。两个脚本协同可覆盖从底层数据解析到目标特征提取的完整链路,适合雷达工程初学者快速上手实测数据处理,也为研究人员验证算法提供可直接修改的参考代码。压缩包仅含2个m文件、约2KB,体量轻但功能聚焦;目前已有363人学习下载,适合正在学习雷达信号处理、需要DCA1000数据读取或峰值检测现成实现的读者。

1. findAllPeak 到底解决雷达目标检测里的哪个痛点

做完距离维 FFT 和速度维 FFT 之后,你手里是一张 RD 图,目标在这个二维谱图上表现为一个个凸起的峰。很多刚接触雷达数据处理的工程师会随手写max()拿到全图最大值,结果发现只输出一个目标;可雷达前方明明可能站着两个人、停着两辆车,多目标场景下全被这一个全局最大点吃掉了。findAllPeak 这类峰值检测算法解决的就是这个痛点:它不是让你找“最大”,而是把满足幅值、间距、局部凸起条件的所有峰值全部提取出来,作为后续点迹聚合、目标跟踪的输入。这篇文章面向从“跑通 FFT”走向“真正做雷达数据处理”的工程师,也适合已经用 CFAR 拉出候选点但被重复峰困扰的老手。读完你可以自己实现一个可用的 findAllPeak,并且知道参数怎么调、坑在哪。

2. 雷达数据处理链条里,findAllPeak 是点迹提取的最后一道闸

2.1 从 ADC 到点迹:findAllPeak 在信号处理流程中的位置

常见毫米波雷达数据处理的链路是这样的:ADC 采样得到一串复数 IQ 数据,按 chirp 排成矩阵;先沿着快时间维度做距离维 FFT,把回波时延转成距离;再沿着慢时间维度做多普勒维 FFT,把相位变化转成速度;得到的就是 Range-Doppler 图,也就是 RD 图。如果是 4D 毫米波雷达,会在后续做角度维 FFT,把多个接收天线的相位差转成方位角和俯仰角。之后一般做恒虚警检测(CFAR),把超过自适应门限的单元标成候选目标,最后才是峰值检测与点迹输出。

findAllPeak 在整条链里的位置非常靠后,它工作在 CFAR 之后,或者在没有任何检测算法时直接作用在谱图上。CFAR 解决的问题是“哪里可能超过噪声门限”,findAllPeak 解决的问题是“这个超门限连通域里到底哪个点代表目标”。原因是雷达的点扩展函数有主瓣也有旁瓣,一个目标往往会点亮相邻好几个距离-多普勒单元,CFAR 会把这些单元全部判成过门限,如果不做峰值提取,一个目标会被输出成十几个“点迹”,后端的关联滤波器会直接疯掉。所以峰值检测是把检测结果从“区域”压缩成“点”的最后一道闸。

2.2 峰值检测的数学定义:局部极大值判定

从一个一维频谱讲起。给定一段幅度序列x[i],所谓峰值,第一层含义是局部极大:这个点的幅度同时大于它的左邻点和右邻点,也就是满足x[i] > x[i-1]且x[i] > x[i+1]。如果序列里存在平坦段,就要改成x[i] >= x[i-1]且x[i] > x[i+1],或选用带方向的比较策略,避免在平顶上输出一堆重复点。这是 findAllPeak 最基本的筛选条件。

只靠局部极大显然不够,因为噪声也会产生大量毛刺,每个毛刺都是局部极大。所以第二层含义是幅度门限:峰的幅度必须大于某个绝对高度height。第三层是凸起门限:峰与周围最低点的差值要超过threshold,用来剔除落到噪声基底上的小起伏。第四层是相邻峰最小间距distance:两个峰之间至少隔多少个采样点,不满足的只保留幅度更高的那个。把这几层组合起来,才是一个真正可用的 findAllPeak。严格说它不像 CFAR 那样是统计检测器,而是一个确定性提取器,把已经确认存在的目标从谱峰邻域里“挑”出一个代表点。

2.3 为什么 max 不够用:多目标与漏检

有人会问,既然目标在谱图上是一个峰,那取最大值不就行了吗?问题是雷达场景天然是多目标。同一距离门上可能有两个径向速度不同的目标,距离维 FFT 后它们重叠在一起,但多普勒维会分开,全图取 max 只能留下速度更高的那一个,另一个直接丢了。同一速度上可能有两个不同距离的目标,距离维 FFT 分开后,max 又只能留距离最近那个。在安检、周界、矿山等场景里,这种漏检是致命的。

还有一个更隐蔽的问题:目标的真实回波不一定是全局最大。近处有一个强目标,远处有一个弱目标,弱目标幅度低但依然超过检测门限,max()只能看到强目标;而 findAllPeak 配合 CFAR 门限后,可以把强目标主瓣之外的所有显著峰都列出来。雷达距离方程告诉我们,回波功率与距离的四次方成反比,强弱目标动态范围可以相差几十分贝,所以“全局最大”在雷达数据处理里几乎没有意义。真正有意义的只有一个问题:哪些峰是可信的目标,而不是旁边峰拖出来的尾巴。findAllPeak 的存在,就是把这些尾巴切掉,留下干净的目标位置。

3. 用 Python 写一个 findAllPeak:核心实现与参数调优

3.1 一维峰值检测最小实现(基于 NumPy)

常见做法是先把信号转成浮点数组,然后一次性用 NumPy 的向量化比较找出所有局部极大,再逐个应用门限和去重。下面是一个可以直接拿去跑的版本:

import numpy as np def find_all_peak(x, height=None, distance=1, threshold=None): """ 在一维频谱 x 中找出所有局部极大值点。 参数 ---- x : np.ndarray 输入频谱幅度,可为距离维 FFT 输出或 RD 图某一切片 height : float 峰的最低绝对幅度,低于该值直接丢弃 distance : int 两个峰之间的最小样本数,小于该距离的峰只保留幅值高的 threshold : float 峰相对两侧基线的凸起高度,用于抑制噪声毛刺 返回 ---- peaks : np.ndarray 每个峰在 x 中的索引 peak_heights : np.ndarray 每个峰对应的幅度值 """ x = np.asarray(x, dtype=np.float64) if len(x) < 3: return np.array([], dtype=int), np.array([], dtype=float) # 候选峰:严格大于左右相邻点,边界点不参与 left = np.concatenate(([np.inf], x[:-1])) right = np.concatenate((x[1:], [np.inf])) is_local_max = (x > left) & (x > right) peak_idx = np.nonzero(is_local_max)[0] # 绝对幅度门限 if height is not None: peak_idx = peak_idx[x[peak_idx] >= height] # 凸起高度门限 if threshold is not None: keep = [] for i in peak_idx: # 取左右相邻点的较大值作为基线 base = max(x[i - 1], x[i + 1]) if i not in (0, len(x) - 1) else x[i] if x[i] - base >= threshold: keep.append(i) peak_idx = np.array(keep, dtype=int) # 按幅度降序排列,再按照 distance 做非极大值抑制 if len(peak_idx) == 0: return np.array([], dtype=int), np.array([], dtype=float) order = np.argsort(x[peak_idx])[::-1] sorted_idx = peak_idx[order] sorted_val = x[sorted_idx] selected = [] for idx, val in zip(sorted_idx, sorted_val): if all(abs(idx - s) >= distance for s in selected): selected.append(idx) selected = np.array(sorted(selected), dtype=int) return selected, x[selected]

这段代码的逻辑很直白:先用向量化比较筛出所有严格局部极大,再用height去掉低幅度杂波,用threshold去掉相对周围没有明显凸起的平缓隆起,最后把候选峰按幅度从高到低排序,逐个检查是否与已选峰距离过近,近则丢弃。最后一步非常重要,因为窗函数旁瓣和频谱泄漏会让同一个目标在相隔一两个采样点的地方出现两个假峰,按幅度排序后保留最高者,就能把目标分裂压回去。

distance的单位是采样点,不是米或者米/秒。如果距离维 FFT 的 bin 间隔是 0.15 米,distance=3就表示两个峰至少要相隔 0.45 米才被当成不同目标。这个参数需要根据距离分辨率、过采样率来定,不能拍脑袋。

3.2 参数设计:height、distance、threshold 的取值依据

参数含义雷达场景推荐值设太大设太小
height峰绝对幅度下限噪声平均幅度的 4~6 倍,或 CFAR 门限值弱目标漏检大量噪声峰混入
distance峰间最小样本间隔2~3 个 FFT bin相邻真实目标被合并目标分裂成多峰
threshold相对两侧基线的凸起量3~6 dB 对应的线性倍数低信噪比目标被丢掉噪声毛刺被当成峰

关于height,很多从实验室走向现场的人会栽跟头:直接用幅度绝对值,换一个雷达芯片、换一版 ADC 增益设置,参数全部失效。我一般建议输入 findAllPeak 之前先把谱图归一化,比如除以整个热图的噪声平均功率,或者让height接收“噪声倍数”而不是原始幅度。雷达距离方程决定回波动态范围很大,近距离强目标可能比噪声高 40 dB,远距离目标可能只高 6 dB,固定绝对门限很难同时兼顾。所以更稳的做法是把height设成一个保守值,比如噪声均值以上 6 dB,宁多勿缺,然后用 CFAR 或后处理去剔除假点。

threshold是很多人容易忽略的参数。它的作用是判断一个点到底是不是真正的峰:强目标主瓣很尖锐,峰顶与两侧平台差很大;噪声毛刺虽然也是局部极大,但凸起量很小。用threshold可以滤掉大部分噪声峰,而不用把height调得很高。我习惯把threshold设在 3 dB 左右,也就是要求峰至少比左右相邻采样点高出 1.41 倍,这个值在绝大多数调频连续波雷达里都能有效压制平台噪声。

3.3 从一维扩展到二维 RD 图:峰值检测不能直接分行找

RD 图本质是一个二维矩阵,目标在距离-多普勒平面是一个二维凸起,不能简单对每一行调用一维 find_all_peak,再对每一列调用一次。那样同一个目标会在行搜索和列搜索里各出现一次,点迹直接翻倍。正确思路是使用二维局部最大值滤波器:如果一个点的幅度在其周围n×n邻域内同时是最大值,才把它当成二维峰。SciPy 提供了现成的maximum_filter:

from scipy.ndimage import maximum_filter def find_all_peak_2d(rd, height=None, min_distance=3, neighborhood=5): """ rd : np.ndarray 距离-多普勒幅度图,axis=0 为多普勒,axis=1 为距离 """ # 邻域内最大值的掩码 local_max = (rd == maximum_filter(rd, size=neighborhood, mode='nearest')) # 去掉边界导致的假极大值 pad = neighborhood // 2 local_max[:pad, :] = False local_max[-pad:, :] = False local_max[:, :pad] = False local_max[:, -pad:] = False # 绝对幅度门限 if height is not None: local_max &= (rd >= height) ys, xs = np.nonzero(local_max) vals = rd[ys, xs] # 按照幅度排序,再按照 min_distance 做非极大值抑制 order = np.argsort(vals)[::-1] ys, xs, vals = ys[order], xs[order], vals[order] selected_rows, selected_cols = [], [] for y, x, v in zip(ys, xs, vals): too_close = False for py, px in zip(selected_rows, selected_cols): if abs(y - py) < min_distance and abs(x - px) < min_distance: too_close = True break if not too_close: selected_rows.append(y) selected_cols.append(x) return np.array(selected_rows), np.array(selected_cols), \ np.array([rd[y, x] for y, x in zip(selected_rows, selected_cols)])

neighborhood的参数取值很关键。它表示一个目标在 RD 图上占据的“点扩展函数”尺寸。比如距离维用了汉宁窗,主瓣会比矩形窗宽,那么目标在距离方向会拖出两三个明显的旁瓣,如果neighborhood太小,旁瓣照样会超过邻域最大值,产生假峰;如果太大,两个靠得很近的真实目标会被当成一个目标。常规做法是先画一张强目标的 RD 图切片,数出主瓣上下左右大概几个格点,再把neighborhood设为主瓣宽度的 1.5 倍左右。我一般先设为 5,跑完看点迹数量,如果发现目标分裂就调到 7 或 9,这个参数本身不敏感,但调差了效果很明显。

4. 把 findAllPeak 接入真实雷达数据链:以毫米波雷达 RD 图为例

4.1 数据从哪里来:AWR2243 读取与 RD 图生成

以最常见的毫米波雷达数据处理为例,很多板子和芯片(比如 AWR2243)通过以太网或 LVDS 输出原始 ADC 数据,格式是多个发射天线、多个接收天线的复数矩阵。拿到数据后第一步是先把它按照 chirp 序号和采样点序号排成二维矩阵raw_data = [n_chirp, n_sample]。这里是复数数据,每一行代表一个 chirp 内的快时间采样,同一行内的多个采样点按时间顺序排列。

距离维 FFT 在快时间方向做,速度维 FFT 在慢时间方向做。常见做法是先沿 axis=1 做距离维 FFT,再去掉距离门零频附近不需要的部分,然后沿 axis=0 做多普勒 FFT,最后用fftshift把零多普勒移到中间:

import numpy as np # raw_data 形状: (n_chirp, n_sample),复数矩阵 range_fft = np.fft.fft(raw_data, axis=1)[:, :n_sample] # 距离维 # 可选:对每根天线各自做完距离维后再做速度维 doppler_fft = np.fft.fftshift( np.fft.fft(range_fft, axis=0), axes=0 ) rd_map = np.abs(doppler_fft) # 幅度 RD 图

这段代码里,np.fft.fft(range_fft, axis=0)是对每一列距离门做多普勒维 FFT。由于多普勒维有正负频率,fftshift把负多普勒放到左边,零多普勒放中间,正多普勒放右边。后面做峰值检测时,一个速度等于 0 的静止目标会出现在中间行附近,这符合直觉。做完之后rd_map的形状是(n_chirp, n_sample),其中每一格代表一个距离-多普勒单元,幅度代表该单元的回波强度。这个rd_map就是 findAllPeak 的输入。

4.2 先 CFAR 后 findAllPeak 还是先峰后 CFAR?

这个问题的答案是:必须先 CFAR 后 findAllPeak,或者至少把 CFAR 门限作为height传入 find_all_peak_2d。原因是雷达背景噪声不平稳,近距地面杂波强,远距噪声低,固定height在全图上效果很差。CFAR 在每个检测单元周围划定参考窗口,当下背景噪声强就把门限抬高,噪声弱就把门限降下来,保证虚警率恒定。

CFAR 的输出是一个与 RD 图同形的布尔矩阵,每个True表示该单元超过局部门限。直接把rd_map中cfar_mask为 False 的位置清零,再送进find_all_peak_2d,能显著减少峰数量,而且不会漏掉弱目标。示例:

def extract_peaks_with_cfar(rd_map, cfar_mask, min_distance=3): # 只保留过门限位置的幅度,其余置零 masked = np.where(cfar_mask, rd_map, 0.0) # 二维局部最大,避免一个目标输出多个峰 local_max = (masked == maximum_filter(masked, size=5, mode='nearest')) ys, xs = np.nonzero(local_max & cfar_mask) # 按幅度排序 + 最小距离去重 vals = masked[ys, xs] order = np.argsort(vals)[::-1] ys, xs, vals = ys[order], xs[order], vals[order] selected = [] for y, x, v in zip(ys, xs, vals): if all(abs(y - py) >= min_distance or abs(x - px) >= min_distance for py, px in selected): selected.append((y, x)) return selected

这里用了一个小技巧:maximum_filter只会在局部极大位置保留原值,其他位置会被邻域内更大的值替代,然后和cfar_mask取交集,这样即使 CFAR 标记出一个复杂连通域,每个连通域也只输出一个峰。需要注意的是,min_distance在二维里用“或”而不是“与”,也就是多普勒方向或者距离方向只要有一个足够远,就算不同目标;如果同时很近才合并。你可以在实际项目里调整这个逻辑,比如 4D 毫米波雷达还要把角度维一并纳入距离判断。

4.3 从峰值坐标换算距离、速度、角度

峰在 RD 图上的索引本身不算目标状态,要换算成物理量。假设距离维 FFT 点数为N_range,雷达带宽为B,则距离分辨率是c / (2B),距离单元r_idx对应的距离是r_idx * c / (2B)。注意这里的r_idx是从 0 开始的,而且距离维窗口可能因为滤波器延迟有一个固定偏置,通常需要校准。

速度维换算稍微复杂一点。调频连续波雷达一个 chirp 周期为T_chirp,波长λ,雷达最大不模糊速度是λ / (4 * T_chirp),多普勒维点数N_doppler对应速度分辨率λ / (2 * T_total),其中T_total是所有 chirp 的总时间。做完fftshift之后,速度索引v_idx到速度的换算要先把索引减去N_doppler // 2,再乘以速度分辨率。下面是一个换算示例:

量公式说明
距离r = r_idx * c / (2*B)距离分辨率不受 FFT 点数影响
多普勒速度v = (v_idx - N_doppler//2) * λ / (2*T_total)fftshift 后 v_idx 以 0 多普勒为中心
角度theta = arcsin(angle_fft_bin * λ / (2*pi*d))多个 RX 天线对距离-多普勒谱做角度 FFT

峰值检测输出的坐标如果能提前换算成距离、速度,再送进跟踪模块,后端的处理会轻松很多。我习惯让 findAllPeak 的输出带上range、doppler、snr这三个字段,而不是光给一个浮点索引。SNR 可以直接用峰的幅度减去同距离门噪声平均幅度,单位 dB,这个值后面做目标丢弃和质量评估很管用。

5. findAllPeak 在雷达数据处理中的五个典型翻车现场与排查

5.1 同一个目标被输出成两个相邻点迹

现象:RD 图上一辆车只有一片亮斑,但点迹输出时同一个距离-多普勒位置出现两个相距 1 到 2 个 bin 的目标点迹。

原因:目标点扩展函数不是单点,主瓣有一定宽度,加上距离维 FFT 可能用了较少的点数,频谱泄漏让主瓣顶部出现两个局部极大值;CFAR 检测时这两个极大值都能过门限,findAllPeak 若没有按距离去重,就会当成两个峰输出。

解决:在 findAllPeak 的非极大值抑制里加大distance参数。常见做法是先看强度剖面图,数出主瓣半宽在几个 bin,再把distance设为主瓣宽度的 1.2 到 1.5 倍。条件允许的话,最好在峰附近做质心聚合,把主瓣内所有超过 3 dB 的格点合成一个峰,避免误分裂。

5.2 强目标旁瓣淹没了邻近弱目标

现象:一个强目标周围出现几个整齐的小峰,幅度比真实目标低 10 dB 左右,位置对称分布在强目标两侧,且与强目标保持固定的间隔。

原因:这是窗函数旁瓣的直接体现。矩形窗第一旁瓣只有约 13 dB 抑制,汉宁窗旁瓣抑制约 32 dB,但旁瓣位置相对主瓣宽度是固定的;强目标回波功率过大时,旁瓣电平和弱目标真实回波相当,findAllPeak 会把旁瓣峰值也选出来。

解决:优先在距离维和多普勒维加窗,比如距离维加汉宁窗,多普勒维再加汉宁窗,把旁瓣压下去。然后调高threshold参数,要求峰与两侧基线差值大于 6 dB,这样旁瓣这种周围本来就有强主瓣背景的峰会被过滤掉。特别注意不要把height调太高,那会连弱目标一起丢掉。

5.3 静态杂波造成零多普勒附近的虚假峰

现象:RD 图中间行特别亮,findAllPeak 在速度 0 附近输出几十个目标点迹,真实运动目标反而淹没在里面。

原因:地面、天线直耦、静止建筑物反射的杂波集中在零多普勒附近,幅度远高于运动目标回波。固定height和threshold拦不住这堵“直流墙”。

解决:在峰值检测之前先做一次多普勒高速滤波或 MTI 对消。最简单的两脉冲对消就是把多普勒维相邻两行相减,doppler_fft[1:] = doppler_fft[1:] - doppler_fft[:-1],这会直接削掉零频率附近的静止分量。做完之后再看 RD 图,运动目标保持,静止杂波被大幅压制,这时 findAllPeak 的检测数量会恢复正常。如果对消后噪声抬升,建议把height重新标定到对消后噪声均值上。

5.4 二维搜索时行列分别找峰造成重复输出

现象:点迹数量突然翻倍,每个目标有两个点迹,一个距离坐标相同、多普勒坐标相差几个 bin,或者反过来。

原因:这是最常见的 findAllPeak 使用错误——分别对每一行和每一列调用一维峰值搜索。行列交叉搜索会把同一个二维峰识别成两个,一个来自行方向上的极大,另一个来自列方向上的极大。

解决:改用二维局部最大值滤波器,如上一章的find_all_peak_2d,让一个峰只在其邻域内是最大值才被选中。如果工程上是 C 代码,可以用 3×3 邻域遍历暴力实现:对每个超过 CFAR 门限的格点,检查它在上下左右和两个对角线方向是否全部大于邻居。这个 3×3 的最简版本其实已经能解决大多数重复点迹问题。

5.5 阈值单位不一致导致参数不可移植

现象:同一套 findAllPeak 参数,在 A+B 雷达板上跑得好好的,换了一台雷达 ADC 位数不同或模拟增益不同,点迹数量暴涨或全部看不见。

原因:height使用了原始 ADC 幅度作为绝对值,不同雷达的噪声电平、FFT 增益、窗函数增益都不一样,绝对阈值没有物理意义。CFAR 门限是自适应的,所以不受影响,但 findAllPeak 里手写的绝对阈值就报废了。

解决:把 findAllPeak 的输入全部归一化到噪声功率。典型做法是先取 RD 图边缘区域(无目标区域)计算平均幅度noise_mean,让rd_map = rd_map / noise_mean,再设置height为 6~13 dB 对应的倍数值。或者直接把 CFAR 输出的门限矩阵传给 findAllPeak,用它替代height,这样每个峰的检测标准都是“相对当下局部背景超多少 dB”,换平台不需要改参数。

6. 进阶:用质心细化峰位,输出点迹直连跟踪滤波

6.1 质心细化:突破频域栅格分辨率

RD 图的 bin 间隔决定了距离/速度量化精度,但目标真实参数并不刚好落在整数 bin 上。FFT 谱峰在离散采样后会呈现近似 sinc 形状,峰顶附近的幅度加权中心靠近真实位置。可以在 findAllPeak 选出的峰周围取一个 3×3 邻域,用幅度作为权值计算亚 bin 坐标:

def refine_peak(rd, y, x, half_win=1): sub = rd[y-half_win:y+half_win+1, x-half_win:x+half_win+1] w = sub / np.sum(sub) ys = np.arange(-half_win, half_win + 1).reshape(-1, 1) xs = np.arange(-half_win, half_win + 1).reshape(1, -1) dy = np.sum(ys * w) dx = np.sum(xs * w) return y + dy, x + dx

质心细化后,距离坐标y + dy不再是整数 bin。如果距离分辨率是 0.15 米,质心偏移 0.3 bin 就对应 4.5 厘米的修正量,在近距离测距应用里这个很重要。注意质心窗口不要开太大,一般取峰左右各 1 个 bin 就够了,窗口太大反而会把噪声旁瓣拉进来。

6.2 输出结构化点迹,直接接后端跟踪

峰值检测的结果如果只输出一组(y, x)数组,跟踪端还需要再去查原始数据,这是设计缺陷。我习惯在峰值检测函数内部直接组装点迹结构体:

from dataclasses import dataclass @dataclass class PointCloudTarget: range: float doppler: float azimuth: float snr: float peak_value: float

每个目标携带从峰位换算出的物理距离、多普勒速度、角度、信噪比。这样做的好处是后端卡尔曼滤波不需要关心 FFT bin 和分辨率这些细节。4D 毫米波雷达只需要再多一步角度维 FFT,然后把峰值对应的角度也填入azimuth字段,整个方案从检测到跟踪就打通了。输出点迹时,建议把 SNR 低于预置质量阈值的目标直接丢弃,这比在峰值检测里硬调height更科学,因为 SNR 在不同目标间已经归一化。

6.3 性能预算:峰值搜索的取舍

最后说一个我在实际项目中养成的习惯:永远不要让 findAllPeak 跑在全图每个点上。二维局部最大值滤波在超大 RD 图上代价不小,尤其是 4D 毫米波雷达有多个接收天线,数据量成倍增加。正确顺序是先跑一个轻量的 CFAR 或幅度门限筛出候选点,再用小邻域做峰值判定,这样计算量集中在少数候选区域。做嵌入式移植的时候,还可以把峰值检测做成滑动窗比较器,每来一行新数据就输出一次,把延迟控制在单帧内。峰值搜索效果的好坏,不取决于算法有多炫,而在于参数是否能跟着场景走、输出的坐标是否能被后端直接用。这套思路帮我从“看波形毛刺”走到了“稳定输出空口点迹”,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询