[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地

一、写在前面

两个月前我还在全力备战华为OD机考,刷遍了螺旋矩阵、括号匹配、打家劫舍这些经典题。考场上三道题全部零分,原因是新系统换了题型,而我连核心代码模式都没练熟。

考试失利后我决定换个方向:不再为考试而学,而是为解决实际问题而学。我选择了ADAS(高级驾驶辅助系统)作为落脚点。

今天这篇文章,就从我最熟悉的「合并区间」算法开始,看看它在ADAS中是怎么用的。

二、从一个问题开始

假设一辆车上有三个传感器:

  • 前置摄像头:视野范围 -30°, 30°
  • 左前角雷达:视野范围 10°, 90°
  • 右前角雷达:视野范围 -90°, -10°

问:哪些角度范围被至少两个传感器同时覆盖?

这个区域叫做「重叠区」。在重叠区内,多个传感器都能看到同一个目标,可以进行传感器融合,提高检测的置信度。

三、两种不同的需求

拿到这个问题,我第一反应是「合并区间」------把有重叠的区间合并成一个更大的区间。但仔细一想,不对。

合并区间:求所有传感器覆盖的总范围(并集)。

求重叠区:求被至少两个传感器同时覆盖的范围(交集)。

两者完全不同。

用上面的数据举例:

  • 合并区间结果:-90°, 90°(三个传感器合起来覆盖了整个前方半圆)
  • 重叠区结果:-30°, -10°10°, 30°(只有这两个区域被两个传感器同时看到)

四、算法实现:扫描线法

求重叠区最常用的方法是扫描线法,也叫差分计数。

核心思想:用一个计数器记录当前位置被多少个传感器覆盖。从左到右扫描所有区间的起点和终点,计数器增减,当计数 ≥ 2 时记录重叠区间。

复制代码
from dataclasses import dataclass

@dataclass
class SensorFOV:
    """传感器视野定义"""
    name: str
    start_deg: int
    end_deg: int

    def __post_init__(self):
        if self.start_deg >= self.end_deg:
            raise ValueError(f"{self.name}: 起点必须小于终点")
        if not (-180 <= self.start_deg <= 180 and -180 <= self.end_deg <= 180):
            raise ValueError(f"{self.name}: 角度超出范围")


def calc_overlap_zones(sensors: list[SensorFOV]) -> list[tuple[float, float]]:
    """计算被至少两个传感器覆盖的重叠区"""
    events: list[tuple[int, int]] = []
    for sensor in sensors:
        events.append((sensor.start_deg, 1))   # 进入视野
        events.append((sensor.end_deg, -1))    # 离开视野
    events.sort(key=lambda x: x[0])

    count: int = 0
    overlap_start: int | None = None
    result: list[tuple[float, float]] = []

    for pos, delta in events:
        if count >= 2 and overlap_start is not None:
            # 退出重叠区
            result.append((float(overlap_start), float(pos)))
            overlap_start = None
        count += delta
        if count >= 2 and overlap_start is None:
            # 进入重叠区
            overlap_start = pos

    return result

五、调试过程:打印日志理解算法

刚开始看这段代码时,我对 overlap_start 的赋值时机总是搞不清楚。后来我在循环里加了打印语句,一步一步追踪 countoverlap_start 的变化,才真正理解了它的工作原理。

复制代码
count_before:0  overlap_start:None  pos:-90  →  count_after:1  (未进入重叠区)
count_before:1  overlap_start:-30   pos:-30  →  count_after:2  (进入重叠区,记录起点)
count_before:2  overlap_start:None  pos:-10  →  count_after:1  (离开重叠区,输出区间)
count_before:1  overlap_start:10    pos:10   →  count_after:2  (再次进入重叠区)
count_before:2  overlap_start:None  pos:30   →  count_after:1  (离开重叠区,输出区间)
count_before:1  overlap_start:None  pos:90   →  count_after:0  (结束)

最终输出:[(-30.0, -10.0), (10.0, 30.0)]

这个过程就像发动机的起动机:计数从1变成2时「点火」,记录起点;计数从2变回1时「熄火」,输出一个完整的重叠区间。

六、工程化改进

在实际项目中,传感器参数通常不会硬编码在代码里,而是来自配置文件。我把它改成了JSON读取方式:

复制代码
[
    {"name": "front_camera", "start_deg": -30, "end_deg": 30},
    {"name": "left_radar", "start_deg": 10, "end_deg": 90},
    {"name": "right_radar", "start_deg": -90, "end_deg": -10}
]

加载代码:

复制代码
import json

def load_sensors_from_json(filepath: str) -> list[SensorFOV]:
    with open(filepath, 'r', encoding='utf-8') as f:
        data: list[dict] = json.load(f)
    sensors: list[SensorFOV] = []
    for item in data:
        sensors.append(SensorFOV(
            name=item["name"],
            start_deg=item["start_deg"],
            end_deg=item["end_deg"]
        ))
    return sensors

这样做的好处是:传感器布局调整时只需要改JSON文件,不需要改代码。

七、小结

从OD考试的「合并区间」到ADAS的「传感器重叠区计算」,算法本身并不复杂,关键是找到它在实际问题中的映射。

这次经历让我明白两件事:

  1. 算法是工具,不是目的。掌握一个算法最好的方式是在真实场景中使用它。
  2. 调试是最好的学习方式。一行打印日志,胜过十行注释。
相关推荐
Java陈序员1 小时前
轻量运维面板!一款现代化的服务器控制面板工具!
运维·服务器·python·react.js·github
2601_962097361 小时前
1. 使用 C 或 C++ 扩展 Python
python·api·c·引用计数·扩展模块
Yanjun2i2 小时前
Agent学习记录五:Pydantic验证
人工智能·python·学习
繁星蓝雨2 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J2 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪
月光船幽幽2 小时前
跨范式映射的稳定接口设计
人工智能·python·算法
杜大哥2 小时前
python程序:如何查看电脑【电池电量的剩余百分比】 和 【是否插入连接着充电器】?
开发语言·python
wuyk5552 小时前
Python网络爬虫入门到实战 第01章:爬虫到底是什么?原理、流程、合法性、风险全解析(零基础必看)
开发语言·爬虫·python