资讯动态

卡尔曼滤波+匈牙利算法:多目标跟踪工程实践全解析

发布时间:2026/9/15 12:03:07 来源:尧图企业网站定制
多目标跟踪里卡尔曼滤波加匈牙利算法这套组合几乎就是 MOT 领域的“入门必修课”。我做跟踪也踩了不少坑回头看这两个算法被很多人讲得玄乎其实拆开看就是一套“先猜后配”的思路卡尔曼负责猜目标下一帧在哪匈牙利负责把猜测和检测对上号。这篇文章我就用工程落地的方式把这两块掰开揉碎讲清楚顺带把 SORT 那套流程完整过一遍保证你看完能自己写个能跑的 MOT demo。这篇内容适合这几类人看刚接触多目标跟踪、打算复现 SORT/DeepSORT 的算法工程师用 YOLO 等检测器做项目但检测框老是抖、ID 老切换的开发者还有准备面试背八股但想真搞懂底层原理的学生。我会重点讲状态向量怎么设计、噪声协方差怎么调、代价矩阵怎么构建、阈值怎么设置以及为什么你调出来的跟踪器 ID Switch 特别多。1. 多目标跟踪的两个核心问题预测与匹配1.1 为什么检测器跑得好好的还要做跟踪很多人一开始不理解YOLO 不是已经把目标框出来了吗为什么还要多此一举做跟踪这里有个本质区别检测是“单帧独立”的每张图从头算一遍但跟踪要求的是“跨帧关联”。你得知道同一个行人在第 1 帧是框 A第 2 帧是框 C中间隔着框 B另一辆车你不能把 ID 搞混。做检测的都知道再好的检测器也会有漏检、误检和抖动。漏检那一帧没有跟踪器的话目标就断了检测框抖动的话你算出来的速度、轨迹都是毛刺。跟踪器干的事就是用一个运动模型把目标的状态“顺”下去再用一个匹配策略把新检测框“挂”到已有轨迹上。这里面最经典、最省算力的实现就是 SORT 用的那套卡尔曼滤波预测位置匈牙利算法做数据关联。1.2 数据关联问题跟踪的核心本质所谓数据关联说白了就是给每个检测框分配一个轨迹 ID。假设当前有 M 条轨迹、N 个检测框你就得算一个 M×N 的代价矩阵然后找出一个“总代价最小”的方案。这问题在数学上叫线性指派问题匈牙利算法是解决它的经典方法。但有个前提你必须明白匈牙利算的是“全局最优”的匹配不是每个目标单独找最近的检测框。单独找最近的这种贪心策略很容易出现两个轨迹抢同一个检测框的情况或者一个轨迹被硬生生拽到另一个目标身上的情况。全局匹配避免了这个矛盾但代价是需要一个可靠的代价矩阵。MOT 里最常用的代价是 IoU交并比因为它不关心框的绝对大小只关心重叠程度天然适合做同一目标的相似度度量。2. 卡尔曼滤波的工程视角状态设计才是重中之重2.1 MOT 里的状态向量怎么设计卡尔曼滤波的形式大家都熟预测、更新两个公式来回迭代。但落到 MOT 里第一个实际问题就是状态向量选什么。SORT 和 DeepSORT 的做法是直接用检测框的参数建状态不搞图像坐标系的复杂建模。我自己最喜欢用的状态是 7 维或 8 维如果是 2D 检测框用中心坐标 (u, v)、宽高比 γ、高度 h再加上各自的速度构成一个 8 维向量。宽高比通常认为不变所以速度设成 0高度速度单独算。这样设计的好处是你不需要手动区分目标的运动类型是行人还是车辆统统建模成“匀速运动 轻微噪声”模型足够简单算得快而且在短时间间隔内效果不差。有人会问为什么不直接建模 x, y, w, h 四个参数的导数也可以但宽和高变化在像素域不是线性的特别是目标转向或相机抖动时直接对 w、h 做匀速假设容易发散。用“中心坐标 宽高比 高度”这套组合是实作里更稳的选择。2.2 预测与更新方程落地矩阵怎么设卡尔曼滤波最劝退新人的就是那一堆矩阵。我们不用背全部推导但 F、H、Q、R 这四个矩阵的含义必须吃透。F 是状态转移矩阵体现“匀速运动”这个假设。Δt 在逐帧处理中可以视为 1所以 F 就是一个分块矩阵左上角单位阵右上角单位阵乘 Δt左下角全 0右下角单位阵。H 是观测矩阵因为我们的观测值就是检测框 bbox而状态向量里也有 bbox 参数所以 H 就是一个选择矩阵把状态向量中的位置部分挑出来。Q 是过程噪声协方差表示你有多相信“匀速运动”这个模型。设得太小模型太死板目标一加速就跟丢设得太大滤波结果就跟检测框一样抖丧失平滑意义。R 是观测噪声协方差表示你对检测框本身的信任程度。检测器越稳R 可以设得越小。实操中Q 和 R 的比例关系比它们的绝对值重要得多。我一般先把检测框自身的偏差按像素估个大概比如中心点 5 像素、宽高 10 像素作为 R 的初值Q 再按 R 的几十倍去调先让跟踪不丢再慢慢收紧。这个调参过程没有标准答案只能靠 MOTA 和 ID Switch 两个指标反复评估。2.3 卡尔曼在 MOT 里的几个典型误用第一个误用是把它当成单纯平滑器。有些朋友把卡尔曼滤波的输出直接当成最终检测、拿去画框这是浪费了它的预测能力。MOT 里卡尔曼更大的价值是输出“预测框”给匈牙利算法做匹配用同时补上漏检帧的位置。第二个误用是没考虑目标机动性。卡尔曼的“匀速”假设在自行车转弯、车辆刹车时很容易失效。解决办法不是什么花哨的模型而是把 Q 调大一点让滤波器更“信检测”再配合门控阈值控制匹配范围。第三个误用是不知道什么时候重置状态。一旦目标发生了遮挡后重新出现或者 ID 已经切换了旧的状态应该果断清掉不要硬保留。我会在后面的实操部分讲 max_age 的设置很多 ID 切换问题都是因为保留了过多的“僵尸轨迹”。3. 匈牙利算法的工程实现不只是调库3.1 从代价矩阵到指派问题匈牙利算法的输入是一个代价矩阵输出是让总代价最小的行到列的分配。MOT 里这个代价矩阵的构建方式直接决定了跟踪效果比算法本身的优化还重要。最常见的做法是用 IoU 距离。设轨迹的预测框为 A检测框为 BIoU (A∩B) / (A∪B)。IoU 越大代表越可能是同一个目标所以代价可以简单定义为1 - IoU。当两个框完全重叠时代价为 0完全不重叠时代价为 1。也可以用-IoU作为代价但用 1 减更容易理解也方便与其它代价比如外观距离加权融合。需要提醒的是如果在多类别场景下做目标跟踪比如同时跟踪行人和车辆通常会在匹配前先按类别分组只在同类别的轨迹和检测之间算代价矩阵。否则一个行人轨迹匹配到一辆车的检测框代价再小也是错的。3.2 一个最小实现看懂匈牙利算法内部在干什么很多工程同学直接用 scipy.optimize.linear_sum_assignment几行代码就把匈牙利解决了这是好事但至少要知道它内部做的是行归约、列归约、找零元素覆盖的最小直线数这三板斧。我建议自己动手写一个最小实现不需要优化到工程水准但能帮你彻底明白“为什么这个算法能取到全局最优而不是局部最优”。核心思想就是代价矩阵每行减去行最小值、每列减去列最小值不改变最优匹配的位置然后通过找增广路的方式不断调整匹配直到所有行都被分配。伪代码逻辑不复杂先对每一行找最小代价行内做减法再对每一列找最小代价列内做减法然后逐行寻找可行的零元素并标记匹配如果某行没匹配上就通过交替路径扩增匹配数相当于重新洗牌之前的匹配腾出位置。这个“洗牌”过程就是匈牙利算法的精髓也是贪心匹配永远做不到的。3.3 为什么用 IoU 而不是欧氏距离纯坐标上的欧氏距离有个问题大框和小框的像素尺度差异太大了。一个 200×300 的行人框和一个 20×30 的远处行人框中心偏移 10 个像素对前者来说只是轻微抖动对后者来说可能已经跑出框了。IoU 是归一化的天然消除了这个尺度差异。代价算法本身也有几个实际执行细节值得注意。第一门控阈值要先过滤掉不可能匹配的对比如1 - IoU 0.6的直接不要缩小矩阵规模。第二不要拿完整的 M×N 矩阵去算M 和 N 一旦到几百匈牙利算法复杂度 O(n³) 就上来了先把明显离谱的候选对裁掉速度能快好几倍。第三匈牙利算法返回的是一组配对列表不是每个检测框对应的 ID 索引取索引的时候要细心我见过不少人在这里把行列搞反了。4. 完整实操过程跑通一个 SORT 风格的 MOT4.1 跟踪流程的主循环抛开花里胡哨的进阶版一个最小可用的 MOT 系统只需要五步检测、预测、匹配、更新、轨迹管理。我按这个顺序给你捋一遍。第一步对当前帧跑检测器拿到 N 个检测框。第二步对每个已有的轨迹用卡尔曼滤波的预测步骤推算出它在当前帧的预测框。第三步计算 IoU 代价矩阵结合门控阈值用匈牙利算法做匹配。第四步匹配上的轨迹用检测框做卡尔曼更新修正状态。第五步没有匹配上的检测框初始化新轨迹持续多帧都没有匹配的轨迹则删除。这里有一个很多人忽略的细节匹配顺序。不是所有轨迹都一起匹配。SORT 里一般先做一次全部匹配而 DeepSORT 引入了“级联匹配”优先匹配最近更新过的轨迹避免较老的轨迹抢占新目标的机会。如果你发现 ID 切换频繁可以优先试这个改动而不用立刻上外观特征。4.2 卡尔曼预测和更新的代码骨架用 filterpy 库写卡尔曼滤波非常省事但我不建议完全黑盒调库至少要能说出每个参数的维度。状态向量是 8 维中心坐标 u、v宽高比 γ w/h高度 h以及各自速度。观测向量是 4 维u, v, γ, h。预测步骤就两行x_pred F xP_pred F P F.T Q。更新步骤按标准卡尔曼公式走关键是用检测框 z 来算残差 y z - H x_pred再算卡尔曼增益 K最后更新状态和协方差。注意 H 是 4×8 的矩阵只挑出位置部分速度部分是观测不到但可以通过滤波推出来的。4.3 轨迹的出生与死亡min_hits 和 max_age 怎么调轨迹管理是 MOT 里最“工程”的部分也是最影响指标的地方。新检测框不会立刻立为正式轨迹而是要连续命中几帧才会转正这个参数叫 min_hits。设小了误检会变成一条假轨迹导致 FP 涨设大了跟踪响应慢前面几帧 ID 不连续。SORT 里一般设 3 左右。轨迹丢失后并不会马上删除而是进入“待定”状态等待重新出现这个等待帧数叫 max_age。设太大遮挡超过几秒后目标重现系统会认为还是原来的人但中间可能已经 ID 错了设太小短暂遮挡就断轨迹。这个参数没有标准按你的场景来。我建议行人密集场景设 510 帧车辆稀疏场景可以适当放宽到 20 帧左右核心是不要让它跟“重新初始化轨迹”的能力打架。4.4 MOT 指标怎么算脱离指标调参就是盲调调参之前一定要先搞懂指标。MOT 领域的四个核心指标MOTA、IDF1、MT/ML、ID Switch。这个也是很多人搜“yolo 多目标跟踪的指标怎么得到”时最困惑的地方。MOTA 的公式是 1 - (FN FP IDSW) / GT。它综合了漏检、误检和 ID 切换但注意它的上限不是 100% 的准确率你把所有目标都漏掉MOTA 也可以小于零。FP 和 FN 好理解唯一要记牢的是IDSW 计数发生在“一个跟踪轨迹的 ID 突然变成另一个已有的跟踪 ID”的时候或者在标注里目标真实 ID 对应的轨迹被打断后再被接上时算一次。IDF1 则是衡量“ID 保持”的指标它计算的是匹配上的 GT 和轨迹的 F1 分数。和 MOTA 相比IDF1 更关注跟踪的一致性。这两个指标经常打架MOTA 高不代表跟踪不切 ID因为 MOTA 里 IDSW 只罚一次后面只要重检测对了就行IDF1 则惩罚整个跟踪片段的断裂。所以调参时两个都要看不能只看一个。MTMostly Tracked和 MLMostly Lost是按“轨迹被覆盖的帧数占比”来统计的分别表示目标至少 80% 的帧数被跟踪、最多 20% 的帧数被跟踪。这对评估模型的“长时间跟踪能力”很有用。5. 常见问题与排查技巧实录5.1 为什么我的跟踪器 ID 不停切换ID 切换多八成原因不在匈牙利算法而在你的检测质量。检测框抖动太厉害卡尔曼滤波器预测的框来回跳匹配代价变高然后就匹配错了人。排查思路我按优先级排先看检测器单帧效果尤其遮挡和重叠场景再看 max_age 和 min_hits 设得是否合理再调卡尔曼的 Q、R 比例最后才考虑换更复杂的关联代价。很多人一上来就加外观特征DeepSORT但你得明白加了外观特征只是兜底检测不稳一切白搭。有一种典型的 ID Switch 发生在两个目标交叉瞬间。匈牙利算法做的是全局最优交叉瞬间它可能为了“整体代价最小”直接交换了两个 ID。这种情况没什么灵丹妙药只能靠更强的外观信息或者运动模型来缓解或者干脆接受少量 IDSW优先保证不丢目标。5.2 卡尔曼滤波在遮挡时的表现预测漂移怎么处理目标被完全遮挡时卡尔曼滤波没有观察值可以更新只能靠运动模型一直往前“猜”。匀速假设下这个预测框会沿着旧速度方向匀速飞出去速度越快漂移越远。等目标重新出现时预测框可能已经远离检测框匹配失败轨迹只能重建ID 就换了。解决思路有几个一是降低 max_age遮挡时间长就放弃旧轨迹二是给长期未更新的轨迹加大过程噪声 Q让滤波器变得更不确定、预测框范围更大三是加门控时对未更新帧数多的轨迹放宽阈值给重新匹配留点余地。最粗暴也最有效的方法就是遮挡太久了干脆清掉让目标重新初始化一个新轨迹很多场景下指标反而更好。5.3 目标多了性能下降匈牙利不是瓶颈代价计算才是匈牙利算法本身 O(n³)n100 时还是有点压力的百万次操作单帧毫秒级一般不是性能瓶颈。真正的瓶颈是每次匹配前都要计算 M×N 个 IoU矩阵一大就卡。优化思路先用坐标门控粗筛一遍比如中心距离超过最大速度×帧间隔的候选对直接丢掉再按类别分组匹配最后对 IoU 矩阵用向量化计算别在 Python 里一层层循环。如果目标真的上百个可以把匈牙利换成贪心匹配先跑一版实测在很多场景下指标损失不大速度能快一个数量级。5.4 常见问题速查表现象优先排查项参考调整方向ID 切换频繁检测框稳定性、代价矩阵阈值提高检测置信度阈值、调小门控 IoU 阈值轨迹断断续续max_age 过小、R 设得过大增大 max_age、降低观测噪声目标跟丢后漂移Q 过小、运动模型太自信增大 Q、限制最大速度快速目标跟不上检测帧率低、目标机动大提高检测器速度、增大 Q多个目标频繁互换 ID遮挡交叉、纯 IoU 代价不够加外观特征或运动方向约束假轨迹很多检测误检高、min_hits 太小提高检测阈值、增大 min_hitsMOTA 为负FN 和 FP 太高说明跟踪基本废了先单独看检测指标修好检测再说6. 更进一步从 SORT 到 DeepSORT 的演进思路6.1 加了外观特征解决了什么问题SORT 最大的弱点是纯靠 IoU 做匹配一旦目标遮挡、检测失败、或者两个目标互相靠近ID 极容易互换。DeepSORT 的改进思路很直接除了运动信息之外再给每个轨迹和检测框都提取一个外观特征向量通常是 ReID 模型输出用余弦距离计算外观相似度和 IoU 代价加权融合得到最终的代价矩阵。这么做的代价是额外跑一个 ReID 网络推理开销明显上升而且 ReID 特征的好坏直接决定上限。在行人跟踪里DeepSORT 的效果提升通常很明显但车辆跟踪里因为同款车太多外观特征区分度低反而容易带来新的误匹配。所以加不加外观特征要按场景来。6.2 运动模型的替代方案从匀速到恒转率卡尔曼滤波里的“匀速模型”确实太简单。目标转弯时预测框会偏离很多。有人用恒转率模型状态向量增加角速度也有人干脆不做运动模型直接靠高帧率检测和 IoU 匹配。这个选择本质上是在“预测准确性”和“计算复杂度”之间做权衡。我个人做工程任务的经验是室内行人和常规车辆场景匀速模型足够了。真正要换模型的场景一般同时伴随着相机运动车载、无人机视角这时更该做的不是调卡尔曼参数而是先用图像配准或 EGO 运动补偿把帧间背景对齐。6.3 端到端方案与启发式方案的取舍现在很多论文已经不需要卡尔曼和匈牙利这套流程了。Transformer 类的端到端跟踪模型比如 MOTR、TrackFormer直接输出跟踪轨迹不需要显式的关联步骤ByteTrack 则证明了在高阈值检测下用简单关联也能逼近很好的效果OC-SORT 在低帧率、强遮挡场景里改进了噪声补偿和观测中心一致性。这些方法各有强势但卡尔曼匈牙利这套组合依然是领域基石也是理解所有后续方法的“接口”。把这两个算法吃透了再看任何 MOT 论文你都能快速定位它在改哪个环节。最后再说一点实操感受。我见过很多同学跑 SORT 代码改了几个阈值好像效果变好了但并不知道为什么变好后来又改了另一个参数效果又回去了。做 MOT 调试必须养成记录指标的习惯每次改动只动一个变量对比 MOTA、IDF1、IDSW 三个指标的变化而不是肉眼看视频觉得“好像挺准”。这条经验我踩过不少次也希望你能少走这个弯路。先跑通最小闭环再谈改进模型和算法这条路比一上来就背论文里的公式要快得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价