4. 目标检测基础

目标检测是PX4目标跟随项目中最关键的感知环节。与图像分类不同,目标检测不仅要回答"画面里有什么",还要回答"目标在哪里"。在无人机视觉场景中,我们需要从机载摄像头获取的实时视频流中,持续定位目标物体(如行人、车辆或特定标记物),并输出其边界框位置、类别标签和置信度分数。这些信息经过坐标转换后,才能生成飞行控制指令。本章将系统讲解目标检测的核心概念、主流算法原理,以及如何在ROS2环境中集成OpenCV与深度学习模型,为后续的目标跟踪与飞行控制奠定基础。

目标检测在PX4视觉感知链路中的位置与核心要素 机载摄像头 RGB图像流 30~60 FPS 目标检测算法 YOLO / SSD / Faster R-CNN 边界框 + 类别 + 置信度 OpenCV DNN / TensorRT 检测结果 目标类别 (Class) 边界框 (x,y,w,h) 置信度 (0~1) 坐标 转换 与跟踪 边界框 (Bounding Box) • 形式: (x, y, w, h) 或 (x1,y1,x2,y2) • 中心点坐标 + 宽高 • 像素坐标系下的位置描述 • 是后续坐标转换的输入 • 需进行归一化处理 • 与图像尺寸强相关 类别 (Class) • 目标所属语义类别 • 由训练数据集决定 • 常见: person, car, drone • 类别数影响模型输出维度 • 需与任务需求匹配 • 类别错误会导致跟踪失败 置信度 (Confidence) • 模型对检测结果的把握程度 • 范围: 0.0 ~ 1.0 • 阈值过滤低质量检测 • 典型阈值: 0.5 ~ 0.7 • 过高阈值导致漏检 • 过低阈值导致误检

上图展示了目标检测在PX4视觉感知链路中的位置。机载摄像头采集图像后,检测算法输出三类关键信息:边界框、类别和置信度。这些信息随后进入坐标转换模块,最终生成飞行控制指令。理解这三要素的物理意义和数学表达,是正确配置检测参数、调试跟踪效果的前提。

4.1 目标检测核心概念与数学表达

目标检测任务可以形式化地描述为:给定输入图像 I ∈ RH×W×3,输出一组检测结果 D = {d1, d2, ..., dn},其中每个检测 di 包含三个组成部分:边界框 bi、类别标签 ci 和置信度分数 si。边界框通常采用两种表示方式:一种是 (x, y, w, h) 形式,其中 (x, y) 是框左上角坐标,w 和 h 是宽高;另一种是 (cx, cy, w, h) 形式,使用中心点坐标。在PX4项目中,我们更推荐使用中心点表示,因为它与后续的相机坐标系转换公式衔接更自然。

置信度分数 si 是一个介于0和1之间的实数,它综合反映了两个层面的信息:一是该边界框内确实存在目标的概率,二是该目标属于类别 ci 的概率。在YOLO系列算法中,置信度被定义为 Pr(Object) × IoUpredtruth,即目标存在概率与预测框和真实框交并比的乘积。实际使用中,我们通常设置一个置信度阈值(如0.5),低于该阈值的检测结果会被过滤掉,以减少误检。

关键理解:边界框的坐标是像素值,其数值范围取决于图像分辨率。例如,对于640×480的图像,x范围是[0, 640],y范围是[0, 480]。在送入坐标转换模块前,通常需要将像素坐标归一化到[0,1]区间,以消除分辨率差异带来的影响。归一化公式为:xnorm = x / W,ynorm = y / H。

4.2 常用目标检测算法对比与选型

当前主流的目标检测算法可以分为两大流派:两阶段检测器和单阶段检测器。两阶段检测器以Faster R-CNN为代表,先通过区域提议网络(RPN)生成候选区域,再对每个候选区域进行分类和回归。这种方法的优点是精度高,但速度较慢,难以满足无人机实时性要求。单阶段检测器则直接在特征图上预测边界框和类别,代表算法包括YOLO系列和SSD。

算法 速度 (FPS) 精度 (mAP) 模型大小 嵌入式部署难度 适用场景
Faster R-CNN 5~15 高 (75+) 大 (>200MB) 高 离线分析、高精度需求
SSD 30~60 中 (70~75) 中 (100MB) 中 实时检测、嵌入式设备
YOLOv5s 60~100 中高 (72~78) 小 (14MB) 低 无人机实时目标跟随
YOLOv8s 80~120 高 (78~82) 小 (22MB) 低 高性能实时检测
YOLO-NAS 70~100 高 (80+) 中 (40MB) 中 需要精度与速度平衡

在PX4目标跟随项目中,我们优先推荐YOLOv5s或YOLOv8s。原因有三:第一,模型体积小,适合部署在Jetson Nano、树莓派等嵌入式平台;第二,推理速度快,能够满足30FPS以上的实时处理需求;第三,PyTorch生态完善,可以方便地导出为ONNX格式,再通过OpenCV DNN模块或TensorRT进行加速推理。SSD虽然也是单阶段算法,但其在小型目标上的检测效果不如YOLO系列,且模型转换工具链相对复杂。

4.3 在ROS2中集成OpenCV与深度学习模型

ROS2(Robot Operating System 2)是PX4无人机常用的中间件框架。在ROS2中集成目标检测功能,需要完成三个层面的工作:图像采集与发布、检测节点实现、结果发布与可视化。下面给出一个完整的集成流程。

4.3.1 图像采集与话题通信

首先,我们需要从相机驱动节点获取图像话题。在PX4无人机中,通常使用 gscam 或 v4l2_camera 驱动USB摄像头,发布 sensor_msgs/msg/Image 类型的消息。对于树莓派CSI摄像头,可以使用 raspicam2 节点。图像话题名称一般为 /camera/image_raw。在检测节点中,我们订阅该话题,并将ROS图像消息转换为OpenCV的 cv::Mat 格式。

// ROS2 C++ 图像订阅与转换示例
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/image.hpp>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>

class DetectionNode : public rclcpp::Node {
public:
  DetectionNode() : Node("detection_node") {
    // 订阅图像话题
    sub_ = this->create_subscription<sensor_msgs::msg::Image>(
      "/camera/image_raw", 10,
      std::bind(&DetectionNode::imageCallback, this, std::placeholders::_1));
  }

private:
  void imageCallback(const sensor_msgs::msg::Image::SharedPtr msg) {
    // 将ROS图像消息转换为cv::Mat
    cv::Mat frame = cv_bridge::toCvCopy(msg, "bgr8")->image;
    // 在此处调用目标检测函数
    detectObjects(frame);
  }
  
  void detectObjects(const cv::Mat& frame) {
    // 检测逻辑将在下文实现
  }
  
  rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr sub_;
};

4.3.2 加载ONNX模型并执行推理

OpenCV 4.x及以上版本提供了 cv::dnn::Net 类,可以直接加载ONNX格式的深度学习模型。我们需要先将PyTorch训练的YOLO模型导出为ONNX格式。导出命令如下:

# 在YOLOv5仓库目录下执行
python export.py --weights yolov5s.pt --include onnx --img-size 640 640

# 导出后的文件为 yolov5s.onnx

在ROS2节点中加载ONNX模型并进行推理的代码如下:

#include <opencv2/dnn.hpp>

// 在类中添加成员变量
cv::dnn::Net net_;

// 构造函数中加载模型
DetectionNode() : Node("detection_node") {
  // 加载ONNX模型
  net_ = cv::dnn::readNetFromONNX("yolov5s.onnx");
  // 设置计算后端,优先使用CUDA
  net_.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
  net_.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
}

// 推理函数
void detectObjects(const cv::Mat& frame) {
  // 预处理:缩放至640x640,归一化
  cv::Mat blob = cv::dnn::blobFromImage(frame, 1/255.0, 
                                          cv::Size(640, 640), 
                                          cv::Scalar(0,0,0), true, false);
  net_.setInput(blob);
  
  // 前向推理
  std::vector<cv::Mat> outputs;
  net_.forward(outputs, net_.getUnconnectedOutLayersNames());
  
  // 后处理:解析边界框、类别和置信度
  // 具体解析逻辑取决于模型输出格式
  // YOLOv5输出形状为 [1, 25200, 85]
  // 其中85 = 5 (x,y,w,h,obj_conf) + 80 (COCO类别概率)
}

4.3.3 检测结果后处理与话题发布

模型输出的原始数据需要经过后处理才能得到可用的检测结果。后处理包括:置信度过滤、非极大值抑制(NMS)和坐标转换。处理后的结果可以封装为自定义消息或使用 vision_msgs/msg/Detection2DArray 标准消息发布。下面给出一个简化的后处理流程:

  1. 置信度过滤:遍历所有预测框,将目标置信度低于0.5的框剔除。
  2. 类别筛选:根据任务需求,只保留特定类别的目标(如person或car)。
  3. NMS处理:对同一类别的重叠框进行抑制,保留得分最高的框。
  4. 坐标转换:将640×640网络输入尺寸下的坐标映射回原始图像尺寸。
  5. 发布结果:将检测结果发布到 /detection/result 话题,供后续跟踪节点订阅。
性能优化建议:在嵌入式平台上,建议将输入图像尺寸从640×640降至416×416,推理速度可提升约40%,精度损失在可接受范围内。同时,使用TensorRT进行INT8量化,可以将推理时间从30ms降至10ms左右。

4.4 实战案例:基于YOLOv5s的行人检测节点

下面我们通过一个完整的案例,演示如何在ROS2中实现一个行人检测节点。该节点将用于PX4无人机对地面行人的目标跟随任务。

场景背景:无人机在10米高度悬停,需要检测并跟随地面移动的行人。机载平台为Jetson Nano,运行Ubuntu 20.04和ROS2 Foxy。摄像头为USB广角摄像头,分辨率为1280×720,发布频率30FPS。

初始现象:直接使用YOLOv5s原始模型进行推理,发现检测速度仅有12FPS,无法满足实时性要求。同时,由于广角镜头畸变,远处行人的检测框位置存在明显偏移。

分析与解决过程:

问题 原因分析 解决方案 效果
推理速度慢 Jetson Nano GPU算力不足,FP32推理开销大 使用TensorRT进行FP16量化,输入尺寸降至416 推理速度提升至28FPS
检测框偏移 广角镜头畸变未校正 使用OpenCV的 initUndistortRectifyMap 进行畸变校正 检测框位置误差降低60%
远处行人漏检 小目标特征不明显 在检测前进行图像增强,提高对比度 10米外行人检测率从45%提升至78%

关键代码实现:在ROS2节点中集成TensorRT推理,核心代码如下:

// 使用TensorRT加载FP16引擎
cv::dnn::Net net = cv::dnn::readNetFromTensorRT("yolov5s_fp16.engine");
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);

// 畸变校正参数(通过相机标定获得)
cv::Mat cameraMatrix = (cv::Mat_<double>(3,3) << 
  800, 0, 640, 0, 800, 360, 0, 0, 1);
cv::Mat distCoeffs = (cv::Mat_<double>(1,5) << 
  -0.32, 0.12, 0.001, -0.002, 0.05);

// 在图像回调中先校正畸变
cv::Mat undistorted;
cv::undistort(frame, undistorted, cameraMatrix, distCoeffs);

结果对比:优化前,系统延迟约180ms(从图像采集到检测输出),优化后延迟降至65ms。在10米距离下,行人检测的置信度从0.62提升至0.81,边界框中心点误差从35像素降至12像素。这为后续的坐标转换和飞行控制提供了更可靠的数据源。

风险提示:TensorRT引擎与特定的GPU架构绑定。在Jetson Nano上生成的引擎文件不能直接用于Jetson Xavier。部署时需要在目标设备上重新生成引擎。此外,FP16量化可能导致在低光照条件下检测精度下降,建议在夜间任务中切换回FP32模式。

4.5 常见误区与调试经验

在目标检测集成过程中,开发者常遇到以下几类问题,需要特别注意:

  • 误区一:忽略图像预处理一致性。训练时使用RGB格式和特定归一化参数,部署时却使用BGR格式或不同的缩放因子,导致检测精度大幅下降。解决方法是严格保持训练和推理时的预处理一致。
  • 误区二:NMS阈值设置不当。IoU阈值过高(如0.9)会导致同一目标产生多个检测框,过低(如0.3)则会合并相邻的不同目标。建议从0.5开始调试,根据实际效果微调。
  • 误区三:忽视相机延迟。ROS2图像话题的传输延迟加上推理延迟,可能导致检测结果滞后于实际目标位置。在高速运动场景下,需要引入卡尔曼滤波或运动补偿。
  • 误区四:模型类别与任务不匹配。使用COCO预训练模型检测特定目标(如无人机),效果往往不佳。建议使用迁移学习,在自有数据集上微调模型。

调试建议:在开发阶段,建议在检测节点中增加可视化输出,将检测框绘制在图像上并通过 rqt_image_view 实时查看。同时,记录检测结果的置信度分布直方图,帮助判断阈值设置是否合理。当检测速度不达标时,优先检查是否启用了GPU加速,以及输入图像尺寸是否过大。