简介:一份面向雷达与红外传感器融合研究者的 MATLAB 源代码包,聚焦卡尔曼滤波在目标跟踪中的应用,解决多传感器数据同步、状态预测与观测校正等典型问题。压缩包共10个文件,其中9个为 .m 脚本/函数,涵盖雷达与红外异步数据模拟、扩展卡尔曼滤波(EKF)、源信号生成及融合主流程,另有1个程序说明 txt 便于对照运行。资源整体仅14KB,代码轻量易读,适合电子信息、信号处理方向的初学者或开发者快速上手。已有859人学习下载。通过阅读源码可掌握 EKF 的预测与更新步骤、雷达/红外异构数据的融合思路,并可基于示例工程修改参数或扩展至其他传感器场景,提升多传感器跟踪系统的实际设计能力。 先把话放前面:这套“雷达与红外图像跟踪融合”的工程方案,前前后后我改了三个版本,最后跑稳定的是下面这套架构。核心就一句话——用毫米波雷达解决“目标在哪、多快、多远”的问题,用红外热像仪解决“目标是什么、长什么样”的问题,再把两者从时间、空间、置信度三个维度融合起来,输出带ID的连续跟踪轨迹。这套东西可以直接用在安防监控、无人车感知、低空无人机探测这类场景,你手头如果有雷达和红外相机,按这个思路改就能落地;如果只是做算法预研,源码结构和融合策略同样有参考价值。
1. 项目整体设计与融合思路拆解
1.1 为什么是雷达加红外,而不是雷达加可见光
我最早一版用的是雷达加普通可见光摄像头,白天效果确实不错,一到晚上就露馅。可见光相机靠环境光成像,夜间、逆光、雨雾天气下目标对比度很低,检测器基本瘫痪。雷达倒是全天候稳定输出,但它的角度分辨率有限,点云又稀疏,只能告诉你“那里有个东西在动”,说不清是人、车还是树。后来换成红外热像仪,问题迎刃而解:红外是被动接收目标的热辐射,不依赖环境光,夜间照样能看到人和车辆的热轮廓,而且热目标天然和背景有温差,检测逻辑反而更简洁。
雷达和红外的互补关系,是这套融合方案能成立的根本前提。雷达能给出精确的距离、径向速度、方位角,但拿不出目标形态;红外图像能给出目标的像素位置、尺寸、热特征,但没有深度信息。两者一结合,等于一个人同时有了“尺子”和“眼睛”。
1.2 系统架构与数据流向
整个系统分成四层,每层只负责一件事,出了问题能单独定位:
- 传感器接入层:毫米波雷达通过串口或网口输出点云和检测目标,红外热像仪通过USB或GigE接口输出热像图流。这一层要做的基本是驱动适配和数据帧解析。
- 目标检测层:雷达侧做距离多普勒处理、CFAR恒虚警检测、DBSCAN聚类,输出目标列表;红外侧做图像预处理、阈值分割、连通域分析,输出目标检测框。
- 融合对齐层:对雷达目标和红外目标做时间同步与空间投影,把雷达目标的极坐标映射到红外图像的像素坐标系,然后执行目标级匹配与置信度融合。
- 跟踪输出层:利用卡尔曼滤波维护轨迹预测与更新,通过匈牙利算法做帧间数据关联,最终输出带稳定ID的融合轨迹。
分层设计的优势在调试期特别明显。比如融合结果漂移时,你只需要查是检测层的问题还是对齐层的问题,不需要把整个链路翻一遍。
1.3 融合策略选型:为什么我选目标级融合
多传感器融合有数据级、特征级、目标级(也叫决策级)三条路线。数据级融合是把雷达点云和红外像素直接拼到一起处理,信息量最大,但雷达点云几十个点、红外图像上百万像素,数据形态完全不对等,直接拼接会让模型很难训练,工程上也不稳定。特征级融合效果上限高,但需要训练特征提取网络,且依赖GPU算力,适合发论文不适合快速落地。
我最终用的是目标级融合:两个传感器各自独立完成检测,再把检测结果投到同一个坐标系里做匹配和融合。好处很直白——模块解耦、可解释性强、算力要求低,哪个传感器出问题了单独看它的检测结果就能定位。对工程落地来说,目标级融合是最稳妥的选择。
2. 雷达数据链路:从原始回波到目标列表
2.1 距离-多普勒处理与CFAR检测
我手头的雷达是TI的AWR2243毫米波雷达,其他型号的毫米波雷达处理流程基本一致。雷达发射线性调频连续波,目标回波和发射信号混频后产生中频信号,对这个信号做一维FFT,峰值位置对应目标的距离。但是一个距离上可能有多个速度不同的目标,所以还要对一帧内多个chirp的回波沿着慢时间维度再做一次FFT,得到距离-多普勒二维矩阵。这一步做完,矩阵里每一个峰就是一个候选目标点,横轴是距离,纵轴是径向速度。
直接在这个矩阵上取峰值会有一堆虚警,因为环境杂波不是均匀的。我用的CFAR(恒虚警检测)能让检测阈值随背景噪声自适应变化——把每个待检测单元和它周围一圈参考单元的平均功率做比较,目标比背景高出一个设定的系数才判为有效。这个系数就是虚警概率的调节旋钮,调低了漏检多,调高了虚警多,具体数值要靠实测场景去卡。
2.2 雷达参数设计与计算逻辑
雷达参数不是拍脑袋定的,要和探测场景挂钩。下面几个公式是我每次换场景都要重新算一遍的:
- 距离分辨率:ΔR = c / (2B),c是光速,B是雷达射频带宽。带宽B=4GHz时,距离分辨率约3.75cm。带宽越大距离分辨率越高,但系统成本和功耗也上去了。
- 最大不模糊距离:R_max = Fs × c / (2S),Fs是ADC采样率,S是chirp斜率。采样率固定时,chirp斜率越小,最大探测距离越大。
- 速度分辨率:Δv = λ / (2T_f),λ是载波波长,T_f是一帧内所有chirp的总时长。总时长越长,速度分辨率越高,但帧率会降下来。
我实际项目里用带宽4GHz、帧率20Hz的配置,能在30米范围内做到厘米级距离分辨率和0.1m/s左右的速度分辨率,对安防监控场景足够用了。
2.3 点云聚类与目标特征输出
CFAR输出的点云是散乱的,同一辆车可能反射十几个点。直接把这些点全部送进融合模块,匹配计算量爆炸。我先用DBSCAN做聚类,原因是目标点云形状不规则,而DBSCAN不需要预设簇个数,只要设置好邻域半径ε和最少点数min_samples就能适应任意形状。聚类后每个簇对应一个目标,目标特征包括:距离、方位角、径向速度、点云数量和平均信噪比SNR。
点云数量和SNR这两个特征容易被忽略,但它们在融合权重计算里是很有价值的置信度指标——点云越多、SNR越高,说明这个目标回波越可靠。雷达侧目标的几何信息(距离、速度)是后面跟踪的直接输入,一定不要丢。
3. 红外图像处理与目标提取
3.1 红外图像预处理三件套
红外热像仪输出的是温度分布灰度图,理论上温度越高像素越亮,但直接用原始图做检测效果很差。主要问题有三个:电路噪声和坏点、背景热辐射不均匀、目标与背景对比度低。我的预处理流程是固定三件套:
- 中值滤波:替换掉突兀的脉冲噪声点,同时保持目标边缘,比高斯滤波更适合红外图。
- 顶帽变换:先对图像做形态学开运算(先腐蚀后膨胀),得到大面积背景估计,再用原图减去这个背景,把不均匀热背景抑制掉,突出局部亮目标。这一步对室外场景特别关键。
- 直方图均衡化:当场景整体温差小时用来拉伸对比度,注意只做轻度均衡,过度增强会把噪声也放大了。
3.2 目标检测:阈值分割与连通域分析
预处理之后,我用Otsu自适应阈值分割做二值化。Otsu的原理很简单:按像素灰度把图分成前景和背景两类,遍历所有可能阈值,找出让类间方差最大的那个值作为分割阈值。它不需要人工干预,在红外场景下比固定阈值稳得多,因为不同时段、不同天气下目标和背景的温差是变化的。
二值图还有不少零星噪点,目标内部也可能有空洞,我按顺序做一次形态学开运算、一次闭运算。开运算去掉孤立噪点,闭运算填充目标内部的小空洞。然后做连通域分析,把每个连通域当作候选目标,计算面积、外接矩形、质心、平均灰度。面积太小的(噪声块)和面积过大的(整片热源)直接过滤。
这里有一个非常实用的过滤经验:白天室外场景里,车灯、排气管、阳光反射都可能产生热源,单纯按面积滤不干净。我会额外加一个约束——目标应该有“热轮廓”,也就是连通域的边界灰度明显高于周边背景,用这个特征能把很多静态热源滤掉。
4. 时空对齐与门控融合实现
4.1 时间同步:双缓存最近邻对齐
雷达和红外相机的帧率通常不一致,我调试时常用雷达20Hz、红外25Hz的组合。直接把两路数据按各自时间戳丢进融合模块,移动目标在融合结果里会出现重影,因为两帧目标的位置差了十几毫秒到几十毫秒。
我的做法是为每个传感器维护一个最近帧缓存,融合时取两帧时间戳差值最小的一对送入匹配。这种方式对帧率抖动不敏感,实现也简单。如果项目对实时性要求高,比如车载场景,可以走硬件方案:用外部触发信号让雷达和红外相机同时采样,做到严格帧同步,代价是多一组硬件连线,并且红外相机要支持外触发模式,不是所有型号都支持。
4.2 空间投影:从雷达极坐标到图像像素坐标
空间对齐的目的是算出雷达目标在红外图像上的像素位置。雷达目标给出的是极坐标参数:距离r、方位角θ、俯仰角φ,先转成雷达直角坐标:
x_r = r · cosφ · cosθ y_r = r · cosφ · sinθ z_r = r · sinφ
然后用外参矩阵把雷达坐标转到红外相机坐标:
p_c = R · p_r + t
最后通过相机内参K投影到像素平面:
u = f_x · X_c / Z_c + c_x v = f_y · Y_c / Z_c + c_y
这套公式里,R和t是外参标定出来的。我的标定做法是做一个同时能被雷达和红外看到的标定物——金属角反射器外面缠加热带,雷达能看到角反射器的强回波,红外相机能看到发热体。采集多组角反射器在不同位置下的雷达坐标和图像坐标,用PnP算法求解R和t。多采几组点、让点分布覆盖整个视场,标定精度才有保证。
4.3 门控融合:置信度动态决策
目标匹配完成后,融合不是简单地把雷达和红外结果平均,我对置信度用了一个门控加权:
conf_fused = g · conf_radar + (1 - g) · conf_ir
门控系数g = σ(w_r · snr_norm + w_i · ir_score_norm + b),σ是sigmoid函数,snr_norm是雷达目标SNR归一化到0-1,ir_score_norm是红外目标灰度或面积的归一化分数,w_r、w_i、b是权重参数,用一小批标注数据拟合出来。
这个门控的直观含义是:雷达信号强而红外特征弱时,多信雷达;红外特征清晰而雷达SNR低时,多信红外。融合后的目标同时保留雷达的测距和测速信息、红外检测框的像素位置和热特征信息,再送给跟踪器,跟踪的稳定性和准确率比用单一传感器输出高出一个级别。
5. 跟踪器设计:卡尔曼滤波与数据关联
5.1 卡尔曼滤波:匀速模型的预测与更新
融合后的目标列表仍然会有漏检和测量噪声,不能直接拿来做最终轨迹,我引入卡尔曼滤波做平滑和预测。状态向量取[x, y, vx, vy],x和y是目标在笛卡尔坐标系下的位置(由雷达距离和方位角换算),vx和vy是速度。运动模型用匀速模型。
卡尔曼的预测-更新循环是标准流程:先预测当前帧的状态和协方差,再用融合目标的位置作为观测值更新状态。关键参数是过程噪声协方差Q和观测噪声协方差R,Q决定模型对运动突变的适应能力,R决定对观测值的信任程度。我实测的经验是,Q别设太小,否则目标突然加减速时轨迹会被拉成直线,跟不上真目标。
5.2 数据关联:匈牙利算法解匹配问题
多目标场景下,上一帧的轨迹和当前帧的检测需要配对。我把这个问题建模为线性分配问题:构建代价矩阵,矩阵元素是轨迹预测位置和当前检测之间的马氏距离,然后用匈牙利算法求解最优匹配。Python里直接用scipy.optimize.linear_sum_assignment就能解,不需要自己写匈牙利核心逻辑。
关键是设置最大关联距离阈值。超过阈值的轨迹和检测不强行配对,否则会产生大量ID跳变和轨迹断裂。这个阈值可以根据目标最大速度和帧间隔估算,比如目标最快10m/s、帧间隔0.05s,那么帧间位移上限是0.5m,留出两倍余量设成1m比较合适。
5.3 轨迹生命周期管理
给轨迹定义三种状态:待确认、稳定、丢失。
- 新检测连续命中2帧后,状态从待确认转为稳定,才允许对外输出。
- 稳定轨迹连续丢失3帧以上,标记为丢失并从轨迹池删除。
- 待确认轨迹只要丢失1帧就立即删除,防止虚警积累成假轨迹。
这套规则是我调试中逐步调整出来的。最开始我让待确认轨迹也输出,结果一组虚警目标被当成真实轨迹显示;后来改成“两帧确认”策略,虚警大幅减少。稳定轨迹的丢失容忍帧数要结合目标遮挡场景来调,安防场景我一般容忍3-5帧,车载场景因为遮挡更频繁,会放宽到8帧。
6. 源码工程结构与调试实录
6.1 模块化工程目录
放一下工程目录结构,你可以直接对照复用:
radar_ir_fusion/ ├── configs/ │ ├── radar.yaml │ ├── camera.yaml │ └── fusion.yaml ├── data_acquisition/ │ ├── radar_reader.py │ ├── ir_camera_reader.py │ └── synchronizer.py ├── detection/ │ ├── radar_detector.py │ ├── ir_detector.py │ └── result_fusion.py ├── tracking/ │ ├── kalman_filter.py │ ├── data_association.py │ └── track_manager.py ├── calibration/ │ ├── radar_camera_calib.py │ └── project_points.py ├── visualization/ │ ├── draw_tracks.py │ └── video_writer.py └── main.py6.2 核心融合代码:目标级匹配
融合模块的代码逻辑很直白,关键是坐标投影和匹配阈值这两点。核心片段可以这样写:
def fuse_detections(radar_targets, ir_detections, calib, weights): fused_list = [] for rt in radar_targets: # 雷达极坐标 -> 雷达直角坐标 -> 相机像素坐标 px, py = calib.project_radar_point( r=rt.range, az=rt.azimuth, el=rt.elevation ) # 在红外检测框中找最匹配项 best_box = None best_dist = float("inf") for box in ir_detections: center_x = (box.x1 + box.x2) / 2 center_y = (box.y1 + box.y2) / 2 dist = math.hypot(px - center_x, py - center_y) if dist < best_dist: best_dist = dist best_box = box if best_box and best_dist < calib.max_match_pixels: # 置信度门控融合 snr_norm = sigmoid(rt.snr / weights.snr_scale) ir_norm = sigmoid(best_box.gray_mean / weights.gray_scale) g = sigmoid(weights.w_r * snr_norm + weights.w_i * ir_norm + weights.b) conf = g * rt.quality + (1 - g) * best_box.confidence fused_list.append(FusedTarget( range=rt.range, velocity=rt.velocity, bbox=best_box.bbox, confidence=conf, )) return fused_list匹配阈值max_match_pixels是调参重点,设大了会把距离较远的两个目标错误融合为一个,设小了会漏匹配。我一般以目标在图像中的平均尺寸为参考,设成目标框平均宽度的0.5倍左右。
6.3 踩过的坑与排查方案
整理几个我实际调试中遇到的高频问题,给也准备入坑的朋友一份参考:
| 问题现象 | 根因分析 | 解决思路 |
|---|---|---|
| 融合目标重影 | 时间戳未对齐,两传感器帧率不一致 | 改用双缓存最近邻时间同步,必要时硬触发 |
| 雷达目标投影偏出红外框 | 外参标定误差大 | 增加标定点数量并覆盖全视场,用最小二乘优化重算R和t |
| 红外画面开机后持续漂移 | 热像仪未预热,探测器温度未稳定 | 开机后预热5分钟再开始检测,开启非均匀性校正 |
| 轨迹ID频繁跳变 | 关联阈值过小或目标被长时间遮挡 | 调大最大关联距离,提高丢失容忍帧数 |
| 雷达近处目标突然消失 | 毫米波雷达存在近距离盲区 | 融合策略中加入“雷达失效时仅用红外检测”的降级逻辑 |
第二个问题的坑值得单独多说一句。我第一次做外参标定时只放了5个点,而且都在画面中央区域,结果投影误差在边缘区域被放大,目标一出画面边缘投影就偏出去半个框。后来增加到15个点、覆盖整个视场,投影误差明显下降。标定的核心原则是多点、分散、覆盖远近距离。
一些实际的收尾建议
这套系统我前后跑了大半年,最大的感触是:融合系统的上限不取决于某一个传感器有多强,而取决于两个传感器能否在对齐层面配合好。时间对齐和空间标定花掉的时间,比检测算法本身多得多,但这两件事做扎实了,后续融合和跟踪就是水到渠成的事。
最后分享一个调试技巧:每次改完融合参数,别直接看融合结果,先把雷达检测结果和红外检测结果分别录一批视频,叠加时间戳,离线逐帧对比。很多“融合效果差”的问题,最后都定位在单侧检测漏检或者时间戳偏差上,跟融合算法本身没关系。有了这两份独立的检测录像,排查效率会高很多。这套方案接下来我打算加入一个三维可视化前端,把雷达点云和红外图像投影到统一场景里做三维展示,有兴趣的可以一起交流。
本文还有配套的精品资源,点击获取