3、目标检测基础:目标检测任务定义、边界框、类别、置信度、IoU、NMS等核心术语

在进入ROS2目标检测节点的代码实现之前,必须先建立一套统一的“语言体系”。目标检测(Object Detection)与图像分类(Image Classification)的本质区别在于:分类只回答“图像里有什么”,而检测需要回答“图像里有什么、在哪里、有多大、有多确定”。这四个问题的答案分别对应类别(Class)、边界框(Bounding Box)、置信度(Confidence)和后续的筛选策略(IoU与NMS)。本章将系统拆解这些核心术语的数学定义、物理意义以及在ROS2节点中的实际映射关系,为后续编写YOLO/TFLite推理节点打下坚实的理论基础。

目标检测核心术语关系图 输入图像 (640×640×3) 检测模型 (YOLO/TFLite) 原始输出 (边界框+类别+置信度) 后处理 (IoU + NMS) 最终检测结果 (Detection消息) 边界框 (Bounding Box) 表示方式: xyxy / xywh / cxcywh 归一化坐标 类别 (Class) COCO 80类 自定义数据集 类别ID + 标签名 置信度 (Confidence) 0.0 ~ 1.0 分类概率 × 目标存在概率 阈值过滤 IoU (交并比) IoU = 交集面积 / 并集面积 衡量两个框的重叠程度 NMS (非极大值抑制) 按置信度排序 抑制重叠框 虚线箭头:后处理依赖关系 实线箭头:数据流方向

3.1 目标检测任务定义:从分类到定位的跨越

目标检测的任务可以形式化地描述为:给定一张输入图像 \( I \),算法需要输出一组检测结果 \( D = \{d_1, d_2, ..., d_n\} \),其中每个检测结果 \( d_i \) 包含三个核心要素:类别标签 \( c_i \in \{1, 2, ..., C\} \)、边界框坐标 \( b_i = (x_1, y_1, x_2, y_2) \) 以及置信度分数 \( s_i \in [0, 1] \)。这里的 \( C \) 是预先定义好的类别总数,例如COCO数据集的80类或自定义数据集的特定类别集合。

与图像分类相比,目标检测的难点在于:图像中可能同时存在多个不同类别的目标,目标可能以任意尺度、任意长宽比出现在任意位置,且目标之间可能存在相互遮挡。这就要求检测模型不仅要具备强大的特征提取能力,还要能够对空间位置进行精确定位。在ROS2的机器人感知场景中,检测结果通常需要转换为机器人可执行的空间坐标,因此边界框的表示方式直接影响到后续的坐标变换和抓取规划。

核心要点:目标检测是“分类 + 定位”的联合任务。分类分支输出每个候选区域的类别概率分布,定位分支输出边界框的坐标偏移量。两者通过多任务损失函数联合优化,这也是YOLO系列模型的基本设计思想。

3.2 边界框:四种表示方式与坐标归一化

边界框是目标检测中最基础的几何概念,它用一个矩形框来近似目标的包围区域。在工程实践中,根据不同的算法和框架,边界框有四种常见的表示方式:

表示方式 参数定义 典型应用场景 ROS2中的映射
xyxy 左上角 (x1, y1) 和右下角 (x2, y2) OpenCV绘图、PASCAL VOC格式 vision_msgs/BoundingBox2D
xywh 左上角 (x, y) + 宽度 w + 高度 h COCO数据集标注、TFLite输出 需转换为xyxy后使用
cxcywh 中心点 (cx, cy) + 宽度 w + 高度 h YOLO系列模型输出 需转换为xyxy后使用
归一化坐标 所有值除以图像宽高,范围[0,1] 模型输入预处理、跨分辨率推理 需乘以图像尺寸还原

在ROS2的YOLO节点中,模型输出的原始坐标通常是归一化的cxcywh格式。例如,YOLOv8输出的张量形状为 \( [1, 84, 8400] \),其中84 = 4(坐标)+ 80(COCO类别数),8400是不同尺度特征图上的锚点总数。坐标值经过sigmoid激活后位于[0,1]区间,表示相对于输入图像尺寸的比例。在发布Detection消息之前,必须将这些归一化坐标乘以原始图像的宽度和高度,转换为像素坐标。

# 坐标转换示例(Python伪代码)
# 假设模型输出为 cx, cy, w, h(归一化)
img_w, img_h = 640, 480
x1 = int((cx - w/2) * img_w)
y1 = int((cy - h/2) * img_h)
x2 = int((cx + w/2) * img_w)
y2 = int((cy + h/2) * img_h)

常见陷阱:不同模型对坐标的编码方式不同。YOLOv5使用xywh(左上角+宽高),YOLOv8使用cxcywh(中心点+宽高),而TFLite的SSD模型可能直接输出xyxy。在编写通用节点时,必须通过配置文件明确指定坐标格式,否则会出现边界框偏移或尺寸错误。

3.3 类别与置信度:模型输出的语义解读

类别信息定义了检测到的目标属于哪一类。在COCO数据集中,0号类别是"person",1号是"bicycle",以此类推。模型输出的类别分支是一个概率分布向量,长度为类别总数C。通过softmax或sigmoid激活后,每个元素表示该候选框属于对应类别的概率。最终类别取概率最大的索引,即 \( c = \arg\max(p_0, p_1, ..., p_{C-1}) \)。

置信度分数则是一个综合指标。在YOLO系列中,置信度被定义为:

Confidence = P(Object) × IoU(pred, truth)

其中P(Object)表示该候选框内是否存在目标的概率,IoU(pred, truth)表示预测框与真实框的交并比。在推理阶段,我们无法获得真实框,因此模型直接输出一个综合置信度。这个值同时反映了“框内是否有目标”和“框的定位精度”两个信息。在实际应用中,置信度阈值(通常设为0.25~0.5)用于过滤低质量的检测结果。

工程经验:在ROS2节点中,建议将置信度阈值设为可动态配置的参数(通过ROS2参数服务器),而不是硬编码。因为在不同场景下(如近距离检测 vs 远距离检测),最优阈值差异很大。例如,在自动驾驶场景中,行人检测的置信度阈值通常设为0.4,而在工业质检场景中可能要求0.7以上。

3.4 IoU:量化两个边界框的重叠程度

IoU(Intersection over Union,交并比)是目标检测中最核心的几何度量。给定两个边界框A和B,IoU的计算公式为:

IoU(A, B) = |A ∩ B| / |A ∪ B|

其中|A ∩ B|表示两个框的交集面积,|A ∪ B|表示两个框的并集面积。IoU的取值范围为[0, 1],0表示完全不相交,1表示完全重合。在代码实现中,计算IoU需要以下步骤:

  1. 计算交集矩形的左上角坐标:\( x_{inter1} = \max(x1_A, x1_B) \),\( y_{inter1} = \max(y1_A, y1_B) \)
  2. 计算交集矩形的右下角坐标:\( x_{inter2} = \min(x2_A, x2_B) \),\( y_{inter2} = \min(y2_A, y2_B) \)
  3. 如果 \( x_{inter2} < x_{inter1} \) 或 \( y_{inter2} < y_{inter1} \),则交集面积为0
  4. 交集面积 = \( (x_{inter2} - x_{inter1}) \times (y_{inter2} - y_{inter1}) \)
  5. 并集面积 = 面积A + 面积B - 交集面积
  6. IoU = 交集面积 / 并集面积

IoU在目标检测中有三个主要用途:训练时的正负样本分配(IoU大于0.5的锚点为正样本)、评估指标mAP的计算(预测框与真实框的IoU超过阈值则视为检测正确)、后处理中的NMS筛选。在ROS2节点中,我们主要关注第三个用途。

3.5 NMS:消除冗余检测框的贪心算法

非极大值抑制(Non-Maximum Suppression,NMS)是目标检测后处理中不可或缺的一步。由于检测模型会在同一目标周围产生多个高置信度的候选框,NMS的目标是保留每个目标的最佳检测框,抑制其他冗余框。标准NMS算法的执行流程如下:

步骤 操作 示例(假设5个候选框)
1 将所有候选框按置信度降序排列 框A(0.95), 框B(0.88), 框C(0.75), 框D(0.60), 框E(0.45)
2 选择置信度最高的框,加入最终结果列表 选中框A
3 计算该框与所有剩余框的IoU 计算A与B、C、D、E的IoU
4 删除IoU大于阈值(通常0.45~0.5)的框 假设A与B的IoU=0.7,删除B;A与C的IoU=0.3,保留C
5 重复步骤2-4,直到候选框列表为空 下一轮选中C,继续比较

NMS的时间复杂度为 \( O(n^2) \),其中n为候选框数量。在YOLO模型中,原始候选框数量可能达到8400个,直接对所有框执行NMS会非常耗时。因此,工程上通常先通过置信度阈值过滤掉大部分低质量框(例如保留置信度大于0.25的框),将候选框数量降低到几百个,再执行NMS。在ROS2节点中,NMS通常在CPU上执行,对于实时性要求高的场景,可以考虑使用Fast NMS或Soft NMS等改进算法。

风险提示:NMS阈值设置不当会导致两类问题:阈值过高(如0.7)会导致同一目标保留多个重叠框,造成重复检测;阈值过低(如0.3)会导致相邻的同类目标被错误抑制,造成漏检。在密集场景(如人群检测)中,建议适当降低NMS阈值;在稀疏场景中,可以适当提高。

3.6 案例拆解:从模型输出到ROS2 Detection消息

下面通过一个完整的案例,展示如何将YOLOv8的原始输出转换为ROS2的Detection消息。假设我们使用一个在COCO数据集上预训练的YOLOv8n模型,输入图像尺寸为640×640,检测一张包含一个行人和一辆自行车的图片。

步骤1:模型推理输出

# 模型输出张量形状: [1, 84, 8400]
# 其中84 = 4(坐标) + 80(类别概率)
# 经过置信度过滤后,保留2个候选框:
# 候选框1: cx=0.35, cy=0.42, w=0.12, h=0.28, class=0(person), conf=0.92
# 候选框2: cx=0.68, cy=0.55, w=0.20, h=0.15, class=1(bicycle), conf=0.87

步骤2:坐标转换与NMS

将归一化坐标转换为像素坐标(假设原始图像尺寸为1280×720):

# 候选框1转换结果
x1 = int((0.35 - 0.12/2) * 1280) = 371
y1 = int((0.42 - 0.28/2) * 720) = 201
x2 = int((0.35 + 0.12/2) * 1280) = 524
y2 = int((0.42 + 0.28/2) * 720) = 403
# 候选框2转换结果
x1 = int((0.68 - 0.20/2) * 1280) = 742
y1 = int((0.55 - 0.15/2) * 720) = 342
x2 = int((0.68 + 0.20/2) * 1280) = 998
y2 = int((0.55 + 0.15/2) * 720) = 450

由于两个框的类别不同且空间位置相距较远,IoU接近0,NMS不会抑制任何一个框。

步骤3:填充Detection消息

# 使用vision_msgs/Detection2D消息
vision_msgs/Detection2D[] detections
  ├─ header: stamp=now, frame_id="camera_link"
  ├─ results[0]:
  │   ├─ id: 0
  │   ├─ score: 0.92
  │   ├─ bbox: center_x=447.5, center_y=302.0, size_x=153, size_y=202
  │   └─ hypothesis: class_id=0, score=0.92
  └─ results[1]:
      ├─ id: 1
      ├─ score: 0.87
      ├─ bbox: center_x=870.0, center_y=396.0, size_x=256, size_y=108
      └─ hypothesis: class_id=1, score=0.87

步骤4:结果验证与复盘

在RViz2中可视化检测结果,确认边界框正确包围目标。如果发现边界框偏移,需要检查坐标转换是否正确;如果发现重复检测,需要调整NMS阈值;如果发现漏检,需要降低置信度阈值。

复盘经验:在ROS2节点中,建议将坐标转换和NMS封装为独立的工具函数,并编写单元测试。使用已知的输入输出对(如上述案例)验证函数的正确性,可以避免在集成测试阶段才暴露坐标问题。

3.7 常见误区与总结

在目标检测基础概念的理解和应用中,初学者常犯以下错误:

  • 混淆坐标格式:将YOLO输出的cxcywh直接当作xyxy使用,导致边界框偏移。解决方法是始终明确记录模型输出的坐标格式,并在代码注释中标注。
  • 忽略归一化:模型输出的坐标可能是归一化的,直接用于图像绘制或消息发布会导致尺寸错误。必须乘以原始图像尺寸。
  • NMS阈值一刀切:不同场景下最优NMS阈值不同。在ROS2中应通过参数动态调整,而不是固定写死。
  • 置信度与类别概率混淆:YOLO的置信度是综合指标,而类别概率只是分类分支的输出。在输出最终结果时,应使用两者的乘积作为最终分数。
  • 忽略类别ID映射:模型输出的类别ID是整数,需要映射到可读的类别名称(如0→"person")。在ROS2中,建议使用std_msgs/String或自定义消息传递类别名称。