项目地址:
yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit
前面几篇里,这个项目已经从最开始的目标检测,一路做到:
Single Image
↓
Batch Images
↓
Video Detection
↓
ByteTrack Tracking
↓
Line Crossing
↓
ROI Analysis
↓
Trajectory Heatmap
↓
Multi-ROI Flow
↓
Speed Estimation
↓
Perspective Calibration
↓
Trajectory Prediction
做到这里以后,系统已经可以回答:
目标是谁?
目标在哪里?
目标走过哪里?
目标速度大概有多快?
目标未来几秒可能往哪里走?
那下一步自然就是:
如果两个目标正在同时运动,它们会不会在短时间内发生明显的轨迹冲突?
所以这一次,我继续在现有 YOLO + ByteTrack + Perspective Calibration 的基础上加入了:
Collision Risk Estimate
也就是基于两个目标当前的相对位置和相对速度,计算它们未来短时间内的最近接近情况。
这里要先说明:
这不是"事故预测"。
它只是一个基于运动学规则的:
短时碰撞风险估计。
一、这次到底分析什么?
对于任意两个正在跟踪的目标,例如:
Track A
Track B
系统会计算:
Current Distance
Relative Velocity
Time To Closest Approach
Minimum Predicted Distance
Risk Level
最终风险等级分为:
Low
Medium
High
所以它不只是看:
"两个目标现在离得近不近"
而是更关注:
它们是不是正在快速靠近,以及未来几秒最接近的时候会有多近。
二、为什么只看当前距离不够?
举个很简单的例子。
两个目标现在距离只有 2 米。
但如果它们正在:
互相远离
那其实当前短时风险并不高。
反过来:
两个目标现在距离 10 米
但是它们正在快速朝同一个交汇点运动,
那未来 1~2 秒反而可能出现更明显的冲突风险。
所以这次不能只看:
Current Distance
还需要同时看:
Relative Position
Relative Velocity
TCA
Minimum Distance
三、目标位置仍然使用 Bottom-center Anchor
为了和前面的:
Line Crossing
ROI
Heatmap
Speed
Trajectory Prediction
保持一致,
这次还是继续使用:
Bottom-center Anchor
计算方式:
anchor_x = (x1 + x2) / 2
anchor_y = y2
对于道路视频来说,这个点比 bbox 中心更接近目标实际所在的地面位置。
所以每一个 Track 都会保存:
Track ID
Class
Position
Velocity
Timestamp
然后再进行目标对分析。
四、Perspective 4-Point 继续参与分析
这次我仍然使用前面已经实现的:
Perspective 4-Point Calibration
也就是先在原图中选择四个道路平面点,再输入这个区域对应的真实尺寸。
这次继续使用:
Width:6 m
Height:12 m

整个空间转换过程就是:
原始视频坐标
↓
Homography
↓
Bird's-eye Plane
↓
米制坐标
这样两个目标之间的距离就可以在鸟瞰平面中计算。
如果没有 Perspective Calibration,也可以进行 Pixel-space Risk Analysis。
但这种情况下,UI 会明确显示:
Pixel-space risk only
不会把像素距离伪装成真实米制距离。
五、两个目标是怎么配对的?
假设当前有两个目标:
Track A
Track B
它们在鸟瞰平面中的位置分别是:
PA
PB
对应速度分别是:
VA
VB
那么相对位置就是:
relative_position = PB - PA
相对速度:
relative_velocity = VB - VA
当前距离:
distance = ||relative_position||
这一步其实就是把问题从:
"两个独立运动的目标"
转换成:
"B 相对于 A 在怎么运动"。
六、TCA:多久之后它们最接近?
这次最核心的指标之一就是:
TCA
也就是:
Time To Closest Approach
可以理解为:
按照当前速度继续运动,两个目标大约多久之后会达到最近位置?
计算方式:
t_closest = - dot(relative_position, relative_velocity) / ||relative_velocity||²
如果两个目标当前正在互相靠近,
那么 t_closest 通常会是一个正数。
例如:
TCA = 1.5 s
可以理解成:
如果当前运动趋势不变,大约 1.5 秒后两个目标会达到最近接近状态。
七、哪些情况不应该判高风险?
这里有几个很重要的边界情况。
如果:
relative velocity 很小
说明两个目标相对运动不明显。
或者:
t_closest <= 0
说明最近接近时刻其实已经过去了,两个目标现在更可能正在远离。
再或者:
t_closest > Risk Horizon
比如只分析未来 3 秒,但最近接近时间是 8 秒以后,
那就不应该把它判成当前短时高风险。
所以:
不是距离近就危险,也不是两个目标同时出现就危险。
八、Minimum Predicted Distance
得到 TCA 以后,还需要继续计算:
两个目标在最近接近时,究竟会离多近?
公式是:
closest_relative = relative_position + relative_velocity × t_closest
然后:
minimum_distance = ||closest_relative||
这个指标比 Current Distance 更重要。
因为它回答的是:
如果保持当前运动状态,未来最近的时候,两者预计还剩多少距离?
九、Risk Level 怎么判?
这次使用的是一套可解释的启发式规则。
默认规则:
High:
TCA <= 2 s
并且
Minimum Distance <= 1.5 m
Medium:
TCA <= 3 s
并且
Minimum Distance <= 3 m
其他情况:
Low
也就是说:
时间越短
距离越小
风险等级越高
但这里一定要强调:
Risk Level 不是事故概率。
High 并不代表:
"90% 会发生碰撞"。
它只是表示:
按照当前运动状态继续发展,两个目标在短时间内存在更明显的空间冲突趋势。
十、真实测试参数
这次还是继续使用之前一直测试的同一段道路视频。
视频信息:
Resolution:1920×1080
FPS:29.97
Total Frames:962
Duration:约 32 秒
Perspective Calibration:
Width:6 m
Height:12 m
风险设置主要包括:
Confidence Threshold
Frame Interval
Risk Horizon
Minimum Track History
Minimum Current Distance
Maximum Analysis Distance
High TCA Threshold
High Minimum Distance
Medium TCA Threshold
Medium Minimum Distance
Class Pair Filter

这次 Class Pair Filter 使用:
All
也就是让系统分析所有满足条件的目标对。
十一、962 帧全部处理完成
最终:
962 / 962 Frames
100%
真实结果:
Unique Tracks:37
Pairs Analyzed:122
Risk Events:236
High Risk Events:123
Medium Risk Events:113
Processing Time:约 61.2 s
Calibration Mode:
perspective

这说明整条链路已经实际跑通:
YOLO
↓
ByteTrack
↓
Bottom-center
↓
Perspective Homography
↓
Pair Analysis
↓
Relative Position
↓
Relative Velocity
↓
TCA
↓
Minimum Distance
↓
Risk Level
十二、122 个 Pairs Analyzed 是什么意思?
这次一共检测并跟踪到:
37 个 Unique Tracks
理论上如果全部两两组合,会有很多 pair。
但系统不会无条件分析所有组合。
它还会受到:
Minimum Track History
Maximum Analysis Distance
Class Pair Filter
Track Active State
等条件影响。
最终真正进入风险计算的是:
122 个目标对
这个数字比直接做全组合更合理,也能减少大量无意义的远距离 pair。
十三、236 个 Risk Events 不等于 236 次"险情"
这次结果中:
Risk Events:236
其中:
High:123
Medium:113
刚好:
123 + 113 = 236
说明事件统计是对得上的。
但是这里必须特别说明:
236 个 Risk Events 不等于发生了 236 次真实危险事故。
更准确的说法应该是:
系统在整段视频中生成了 236 条基于运动学规则的短时冲突风险事件,其中 123 条达到 High,113 条达到 Medium。
这些只是:
Collision Risk Estimates
不是现实事故次数。
十四、为什么需要 Debounce?
如果一个 pair 连续几十帧都保持 High,
那如果每一帧都生成一个 event,
最终日志会变成:
High
High
High
High
High
......
完全没有意义。
所以这次加入了:
Debounce / Cooldown
规则是:
同一个 pair 如果持续处于高风险状态,
只创建一次 event。
只有当:
风险解除
然后后面再次进入风险,
才会创建新的 event。
十五、Pair Key 为什么要顺序无关?
例如:
Track 12 和 Track 18
系统必须认为:
(12,18)
和:
(18,12)
是同一个目标对。
否则会出现:
A→B 算一次
B→A 又算一次
造成重复统计。
所以 pair key 会做顺序归一化。
这也是事件去重的基础。
十六、Result Video 里会显示什么?
最终 Result Video 中会保留:
Bounding Box
Track ID
Class
同时对于存在风险的目标对,还会显示:
Pair Connection
Risk Level
TCA
Minimum Distance

这样可以直接从视频中看到:
哪些目标正在形成风险 pair。
相比只看表格,这种可视化会更直观。
十七、Class Pair Summary
这次还增加了:
Class Pair Summary
也就是按类别组合统计。
例如:
car-car
car-person
car-bicycle
person-bicycle

这样后面就可以进一步分析:
哪种类别组合产生的风险事件更多?
例如:
车-车
车-人
车-自行车
这比单纯统计一个总 Risk Events 更有分析意义。
十八、Risk Table
Risk Table 中会展示每一个目标对的详细信息。
包括:
Track A
Class A
Track B
Class B
Current Distance
Relative Speed
TCA
Minimum Distance
Risk Level

这张表其实是整个功能最关键的分析区域之一。
因为它可以直接看到:
当前距离到底多远
相对速度多大
多久后最近
最近预计还剩多少距离
最终为什么被判成 High / Medium / Low
十九、Risk Events
除了实时 Pair Table 以外,
系统还会保存:
Risk Events

也就是说:
某个 pair 在什么时间进入风险状态
风险等级是什么
TCA 是多少
Minimum Distance 是多少
这些数据后面都可以导出做进一步分析。
二十、Minimum TCA = 0.00 s 怎么理解?
这次结果里:
Minimum TCA:0.00 s
看起来会很夸张。
但这并不一定代表系统出错。
因为实际内部值经过显示格式化以后,可能是:
非常接近 0 的正数
最后显示成:
0.00 s
这意味着某些目标对在某个时刻:
几乎正处于最近接近状态。
但是这个结果仍然需要结合:
检测框位置
目标类别
Homography
道路尺寸标定
一起判断。
不能单独看到 0.00 s 就说:
"马上发生碰撞"。
二十一、Minimum Predicted Distance = 0.01 m 也不能直接当真实距离
这次最小预测距离是:
0.01 m
也就是 1 cm。
对于普通街景来说,这个数字显然非常小。
它可能来自:
两个 Bottom-center 在鸟瞰平面中非常接近
Bounding Box 抖动
目标遮挡
Homography 放大误差
人工 6m × 12m 标定尺度不完全准确
所以:
0.01 m
应该理解成:
当前模型和标定条件下得到的最小预测间距估计值。
不能直接宣传成:
"真实两个目标只差 1 cm"。
二十二、这也是为什么 Perspective Calibration 很重要
如果完全在原始像素空间中做分析,
例如:
Current Distance = 80 px
这个数其实很难解释。
但经过 Perspective Calibration 后,
至少可以把问题转到:
Bird's-eye Plane
中。
这样:
Distance
Relative Velocity
Minimum Distance
就可以按米制平面进行解释。
虽然依然是估计值,
但比单纯像素分析更有实际意义。
二十三、无标定时也能运行
这一版并不是强制必须 Perspective。
如果不做四点标定,
系统仍然可以运行:
Pixel-space Risk Analysis
但这时候页面会明确告诉用户:
Pixel-space risk only
这时:
Current Distance
Minimum Distance
只能解释为像素距离,
不能写成:
m
m/s
这也是为了避免"单位看起来很真实,但实际上没标定"的问题。
二十四、过滤机制很重要
Collision Risk 最大的问题之一就是:
目标一多,pair 数量会快速增加。
所以这次加入了多种过滤条件:
Minimum Track History
Minimum Current Distance
Maximum Analysis Distance
Class Pair Filter
例如:
Maximum Analysis Distance = 30 m
那么两个目标如果已经相距很远,
就没必要继续做短时碰撞风险分析。
这样可以减少很多无意义的 pair。
二十五、为什么不直接用 Trajectory Prediction 判断?
上一篇已经做了:
Trajectory Prediction
理论上也可以看两条未来轨迹是不是交叉。
但这次选择 TCA + Minimum Distance,
有一个很大的优点:
更容易解释。
因为最终可以明确回答:
多久后最近?
最近距离多少?
当前是否正在接近?
而不是只看两条线有没有视觉交叉。
所以这次其实是:
Trajectory Prediction
进一步走向:
Relative Motion Analysis
二十六、这次测试增加到 601 个
Collision Risk 完成后:
601 pytest passed
前一版:
511
新增:
90 个后端测试
前端:
348 assertions passed
前一版:
308
新增:
40 个前端断言
测试覆盖了:
Relative Position
Relative Velocity
Current Distance
Approaching Pair
Separating Pair
Parallel Motion
Stationary Pair
TCA
Minimum Distance
Risk Horizon
Low / Medium / High
Threshold Boundary
Pair Key
Debounce
Cooldown
Zero Tracks
One Track
Perspective Coordinate
Pixel Fallback
Invalid Calibration
NaN Protection
Class Pair Filter
Export
Stale Reset
Detector Failure
之前所有模式也全部保留。
二十七、开发分支
这次使用:
Branch:
feat/collision-risk
Commit:
b965204a537cd9e1b6c3155e59bc08fe44a4d68e
主要新增:
app/collision_risk.py
static/collision.js
tests/test_collision.py
同时修改:
app/main.py
app/schemas.py
static/index.html
static/app.js
tests/test_frontend_logic.js
二十八、现在项目已经从"看见目标"走到了"分析关系"
做到这一篇以后,整个项目路线已经变成:
Detection
↓
Tracking
↓
Trajectory
↓
Speed
↓
Prediction
↓
Relative Motion
↓
Collision Risk
也就是说,系统分析的不再只是:
单个目标
而开始进入:
目标与目标之间的关系分析。
总结
这次真实实验最终得到:
962 Frames
37 Unique Tracks
122 Pairs Analyzed
236 Risk Events
123 High Risk Events
113 Medium Risk Events
Minimum TCA:0.00 s
Minimum Predicted Distance:0.01 m
Calibration Mode:perspective
Processing Time:约 61.2 s
完整技术链路:
YOLO
↓
ByteTrack
↓
Bottom-center Anchor
↓
Perspective Homography
↓
Track Position / Velocity
↓
Pair Matching
↓
Relative Position
↓
Relative Velocity
↓
TCA
↓
Minimum Predicted Distance
↓
Risk Level
↓
Risk Events
这一次系统已经不只是回答:
目标现在在哪里?
也不只是:
目标未来可能往哪里走?
而是进一步尝试回答:
两个目标按照当前运动趋势继续下去,会不会在短时间内形成明显的空间冲突?
当然,这仍然是:
Heuristic Collision Risk Estimate
不是事故概率,也不是自动驾驶级安全决策系统。
但对于一个从 YOLO 检测逐步扩展出来的视频分析项目来说,这一步已经让系统从:
Single-object Analysis
继续走向:
Multi-object Interaction Analysis
项目地址:
yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit
下一步如果继续做,我准备在现有 Collision Risk 基础上加入:
Near-Miss Detection
也就是不只看每一帧的瞬时 Risk Level,
而是进一步判断:
一次完整的"险些发生冲突"事件到底是怎么形成的。