十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

单目摄像头YOLOv5+C++实时测距实现方法

单目摄像头YOLOv5+C++实时测距实现方法 简介本资源是一套基于YOLOv5实现的C单目摄像头实时测距系统面向人工智能、自动化、电子信息等专业的在校学生、教师及工程技术人员适用于毕业设计、课程设计、项目立项演示及算法工程化学习。项目已通过导师指导与答辩评审得分95分代码经实测可稳定运行支持RK3588平台多线程推理与RKNN模型部署涵盖目标检测、深度估计、距离换算全流程。压缩包共47个文件含25个头文件h/hpp定义核心模块接口4个so动态库与4个rknn模型文件支撑硬件加速2个Shell脚本实现一键编译与性能测试另有CMakeLists.txt、LICENSE、README.md及COCO标签列表等关键工程配置与说明文件整体大小27.05MB。目前已有61人下载学习提供完整源码、详细文档、多线程架构设计与跨平台适配实践是理解YOLOv5模型C部署、单目测距原理及嵌入式AI落地的高价值参考项目。1. 单目摄像头YOLOv5C 实现实时测距不是靠双目或激光——它靠的是几何标定与深度回归的协同落地很多刚接触视觉测距的同学会下意识认为“单目没法测距必须双目或ToF”。但实际工业场景中80%以上的边缘端部署需求如智能仓储AGV避障、产线工件定位、校园安防距离预警受限于成本与结构空间只能用单目。这时真正起作用的不是“能不能”而是如何把YOLOv5检测框映射到真实世界坐标系中。本项目正是围绕这一核心矛盾展开它不依赖额外传感器不引入深度学习模型做端到端深度估计而是用C在x86或RK3399/RK3566等嵌入式平台基于传统计算机视觉标定YOLOv5目标检测结果完成毫米级精度的单目测距闭环。适合正在做毕业设计、嵌入式AI落地或需要快速验证算法可行性的工程师——你不需要懂SLAM也不用训练DepthNet只要掌握相机内参标定、像素-世界坐标转换、以及YOLOv5输出的归一化bbox如何反解物理尺寸就能跑通整条链路。2. 为什么选YOLOv5而非YOLOv8或YOLOv10C部署时的模型结构与推理引擎适配逻辑2.1 YOLOv5作为单目测距基座的不可替代性轻量、可导出、结构透明YOLOv5虽非最新架构但在C端侧部署中仍具显著优势其BackboneCSPDarknet53与NeckPANet结构清晰无动态shape、无复杂control flow便于手动解析ONNX或TorchScript图相比YOLOv8的Ultralytics封装、YOLOv10的多分支注意力YOLOv5的head输出为固定三尺度80×80/40×40/20×20每个grid仅输出xywhconfcls共85维COCO为80类便于C层直接memcpy解析。更重要的是官方PyTorch实现支持torch.onnx.export(..., opset_version11)导出的ONNX模型无自定义op可被OpenCV DNN、ONNX Runtime、RKNN Toolkit2等主流后端无缝加载——这正是本项目选择YOLOv5 v6.2非7.0版本的根本原因v6.2的models/yolov5s.yaml中anchor配置稳定且export.py脚本对--include onnx支持成熟避免v7.0中因引入nn.SiLU导致某些旧版OpenCV DNN无法识别激活函数的问题。提示若使用RKNN平台务必确认YOLOv5导出ONNX时设置--dynamic为False否则RKNN Toolkit2在rknn_model.convert()阶段会报错“Unsupported dynamic shape in input”。2.2 C推理引擎选型OpenCV DNN vs ONNX Runtime vs RKNN按硬件分层决策平台类型推荐引擎关键参数说明测距适配要点x86桌面Ubuntu/Win10OpenCV 4.8.0 DNN模块cv::dnn::Net net cv::dnn::readNetFromONNX(yolov5s.onnx); net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);后端设为OPENCV最稳若需GPU加速改用DNN_BACKEND_CUDADNN_TARGET_CUDA但需编译OpenCV时启用CUDAARM嵌入式RK3399/RK3566RKNN Toolkit2 v1.6.0rknn_init(ctx, yolov5s.rknn, 0); rknn_input inputs[1] {0}; inputs[0].index 0; inputs[0].type RKNN_TENSOR_UINT8; inputs[0].fmt RKNN_TENSOR_NCHW;必须量化int8量化后精度下降是常见问题但本项目通过rknn_toolkit2/examples/yolov5/quantize.py中关闭--advanced-optimize、保留--pre-process中的归一化mean[0.485,0.456,0.406], std[0.229,0.224,0.225]可使mAP下降控制在1.2%以内通用跨平台需调试便利性ONNX Runtime 1.16.3Ort::Env env{ORT_LOGGING_LEVEL_WARNING}; Ort::Session session{env, Lyolov5s.onnx, session_options};Windows下需安装Microsoft Visual C Redistributable for Visual Studio 2015–2022否则LoadLibrary失败Linux需链接libonnxruntime.so并确保LD_LIBRARY_PATH包含路径2.2.1 OpenCV DNN在x86上跑YOLOv5的最小可运行代码片段#include opencv2/opencv.hpp #include opencv2/dnn.hpp #include vector #include iostream int main() { cv::dnn::Net net cv::dnn::readNetFromONNX(yolov5s.onnx); net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV); net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU); cv::Mat frame cv::imread(test.jpg); cv::Mat blob; cv::dnn::blobFromImage(frame, blob, 1/255.0, cv::Size(640,640), cv::Scalar(0,0,0), true, false); net.setInput(blob); std::vectorcv::Mat outputs; net.forward(outputs, net.getUnconnectedOutLayersNames()); // outputs[0] is (1,25200,85) // 解析outputs[0]: shape(1,25200,85)每行[x,y,w,h,conf,cls0...cls79] float* data outputs[0].ptrfloat(); for (int i 0; i 25200; i) { float conf data[i*85 4]; if (conf 0.5f) { float x data[i*85 0] * frame.cols; float y data[i*85 1] * frame.rows; float w data[i*85 2] * frame.cols; float h data[i*85 3] * frame.rows; cv::rectangle(frame, cv::Point(x-w/2,y-h/2), cv::Point(xw/2,yh/2), cv::Scalar(0,255,0), 2); } } cv::imshow(detected, frame); cv::waitKey(0); }注意blobFromImage中swapRBtrue是因为YOLOv5训练时用BGR输入OpenCV默认而mean/std必须与训练时一致即PyTorch torchvision.transforms.Normalize。若此处参数错配检测框将大面积漂移直接影响后续测距精度。3. 单目测距的核心公式与C实现从像素坐标到真实距离的三步映射3.1 测距原理拆解为什么单目能测距关键不在模型而在标定与相似三角形单目测距的本质是已知物体真实尺寸 → 反推其到相机距离。其数学基础是小孔成像模型下的相似三角形关系$$ \frac{Z}{f} \frac{W}{w} \quad \Rightarrow \quad Z f \cdot \frac{W}{w} $$其中$Z$物体到相机光心的实际距离单位mm$f$相机焦距单位pixel由标定得出$W$物体真实宽度单位mm需预先设定如车牌宽440mm、A4纸宽210mm$w$检测框在图像中的像素宽度该公式成立的前提是物体平面与像平面平行。实践中我们通过相机标定获得内参矩阵 $K \begin{bmatrix} f_x 0 c_x \ 0 f_y c_y \ 0 0 1 \end{bmatrix}$其中 $f_x, f_y$ 即x/y方向焦距pixel$c_x, c_y$ 为主点坐标。当目标为矩形且姿态接近正对时取bbox宽高中的较大值参与计算可提升鲁棒性。3.2 相机标定实操用OpenCV C完成内参获取与畸变矫正本项目提供calibration.cpp核心流程为打印棋盘格8×6方格边长25mm拍摄≥15张不同角度图像自动提取角点cv::findChessboardCorners(img, boardSize, corners, CALIB_CB_ADAPTIVE_THRESH)亚像素优化cv::cornerSubPix(gray, corners, Size(11,11), Size(-1,-1), criteria)标定cv::calibrateCamera(objectPoints, imagePoints, imgSize, cameraMatrix, distCoeffs, rvecs, tvecs)标定后得到cameraMatrix3×3和distCoeffs1×5含k1,k2,p1,p2,k3存为calib_params.yml%YAML:1.0 camera_matrix: !!opencv-matrix rows: 3 cols: 3 dt: d data: [ 6.1234567890123456e02, 0., 3.2109876543210987e02, 0., 6.1234567890123456e02, 2.4567890123456789e02, 0., 0., 1. ] dist_coeff: !!opencv-matrix rows: 1 cols: 5 dt: d data: [ -2.3456789012345678e-01, 1.2345678901234567e-01, 1.2345678901234567e-03, -5.6789012345678901e-04, 0. ]3.2.1 C中加载标定参数并矫正图像cv::FileStorage fs(calib_params.yml, cv::FileStorage::READ); cv::Mat cameraMatrix, distCoeffs; fs[camera_matrix] cameraMatrix; fs[dist_coeff] distCoeffs; fs.release(); cv::Mat undistorted; cv::undistort(frame, undistorted, cameraMatrix, distCoeffs); // 后续所有检测与测距均基于undistorted图像进行提示未做畸变矫正直接测距当目标位于图像边缘时误差可达±15%。本项目文档强调“标定必须覆盖全画面”尤其要包含四角区域图像。3.3 像素距离→真实距离C函数封装与误差补偿策略// 输入检测框中心x,y宽w高h像素物体真实宽W_mm、高H_mm // 输出估算距离Z_mm及置信度基于宽高比一致性 float estimateDistance(const cv::Mat cameraMatrix, float bbox_w_px, float bbox_h_px, float real_w_mm, float real_h_mm, float confidence) { float fx cameraMatrix.atdouble(0,0); // f_x in pixels float fy cameraMatrix.atdouble(1,1); // f_y in pixels // 取宽高中的较大者降低姿态倾斜影响 float max_dim_px std::max(bbox_w_px, bbox_h_px); float real_max_mm std::max(real_w_mm, real_h_mm); // 基础距离估算 float Z fx * real_max_mm / max_dim_px; // 宽高比补偿理想宽高比应≈real_w_mm/real_h_mm float detected_ratio bbox_w_px / bbox_h_px; float real_ratio real_w_mm / real_h_mm; float ratio_error std::abs(detected_ratio - real_ratio) / real_ratio; confidence std::max(0.0f, 1.0f - ratio_error * 2.0f); // error0.5则置信度归零 return Z; } // 调用示例 float distance_mm estimateDistance(cameraMatrix, 128.0f, 85.0f, 440.0f, 140.0f, conf); std::cout Estimated distance: distance_mm mm (conf conf ) std::endl;4. RKNN平台部署关键ONNX转RKNN全流程与int8量化精度保全技巧4.1 RKNN Toolkit2环境搭建与模型转换命令链在Ubuntu 20.04x86主机上部署RKNN需严格按顺序执行# 1. 安装依赖注意必须用Python3.8RKNN Toolkit2不兼容3.10 sudo apt install python3.8-venv python3.8-dev python3.8 -m venv rknn_env source rknn_env/bin/activate pip install -U pip pip install rknn-toolkit21.6.0 # 2. 准备ONNX模型确保opset11无dynamic axes python export.py --weights yolov5s.pt --include onnx --imgsz 640 # 3. 转换为RKNN关键参数--target_platform rk3399 python convert_rknn.py \ --model yolov5s.onnx \ --input_size_list [[1,3,640,640]] \ --output yolov5s.rknn \ --target_platform rk3399 \ --device_id RK3399convert_rknn.py核心逻辑from rknn.api import RKNN rknn RKNN() rknn.config(mean_values[[0,0,0]], std_values[[255,255,255]], quantized_dtypeasymmetric_affine, quantized_methodchannel_wise) rknn.load_onnx(modelyolov5s.onnx, inputs[images], input_size_list[[1,3,640,640]]) rknn.build(do_quantizationTrue, dataset./dataset.txt) # dataset.txt含50张标定图路径 rknn.export_rknn(./yolov5s.rknn)注意dataset.txt必须是未归一化的原始BGR图像即cv::imread直接读取且尺寸为640×640。若用归一化后的图RKNN量化校准将失效导致int8后数值不动、检测框全消失。4.2 int8量化后精度下降的三大修复手段问题现象根本原因修复方案验证方式检测框数量锐减、conf普遍0.1量化校准图分布与实际场景偏差大在dataset.txt中混入20%实际部署场景图如仓库光照、模糊运动帧转换后用rknn.eval_perf()对比int8与fp16的mAP0.5bbox坐标偏移、宽高比例失真YOLOv5 head输出未做dequantize处理在RKNN推理后对outputs[0]25200×85手动反量化int8_val (float_val - zero_point) / scale用cv::Mat::convertScaleAbs()将int8输出转回float再解析距离估算跳变、同一目标多次测量差异50mm焦距f_x/f_y未随量化缩放同步调整在C加载RKNN模型后将cameraMatrix中f_x/f_y乘以量化scale因子通常为0.00392对静止目标连续测距100帧标准差应3mm4.2.1 RKNN C推理中反量化关键代码// 假设rknn_outputs[0]为int8指针scale0.003921568627, zero_point0 const int output_size 25200 * 85; std::vectorfloat float_output(output_size); for (int i 0; i output_size; i) { int8_t val ((int8_t*)rknn_outputs[0])[i]; float_output[i] (val - 0) * 0.003921568627f; // 反量化 } // 后续解析同OpenCV DNN但输入数据为float_output.data()5. 高分毕设落地技巧如何让单目测距项目在答辩中体现工程深度与鲁棒性5.1 距离误差分析与可视化用OpenCV绘制误差热力图在main.cpp中加入误差统计模块对每个检测目标记录真实距离用激光测距仪实测估算距离绝对误差|Z_true - Z_est|相对误差|Z_true - Z_est| / Z_true然后生成热力图cv::Mat heatmap cv::Mat::zeros(480, 640, CV_8UC3); for (auto det : detections) { float err_ratio std::abs(det.true_dist - det.est_dist) / det.true_dist; cv::Scalar color err_ratio 0.03 ? cv::Scalar(0,255,0) // green 3% : err_ratio 0.08 ? cv::Scalar(0,255,255) // yellow 8% : cv::Scalar(0,0,255); // red 8% cv::rectangle(heatmap, det.bbox, color, -1, cv::LINE_AA); } cv::addWeighted(frame, 0.7, heatmap, 0.3, 0, frame);这一图表在答辩PPT中直接展示“系统在不同距离段的稳定性”比单纯说“平均误差5.2cm”更具说服力。5.2 多目标测距冲突消解当画面中出现多个同类目标时的距离排序策略YOLOv5输出多个bbox但用户通常只关心“最近目标”或“中心区域目标”。本项目采用三级过滤空间优先计算每个bbox中心到图像中心的欧氏距离取最近3个置信度加权score conf × (1.0 / (1.0 0.1 × distance_to_center))距离可信度筛选剔除confidence 0.6或Z 300mm过近易受畸变影响的结果最终输出JSON格式{ timestamp: 1712345678901, targets: [ {class: person, distance_mm: 1245, confidence: 0.87, bbox: [320,210,85,192]}, {class: car, distance_mm: 3420, confidence: 0.72, bbox: [120,340,210,145]} ] }此结构可直接对接ROS topic或MQTT满足毕设“系统集成”要求。5.3 文档撰写重点让评审老师一眼看到你的技术纵深项目文档目录建议按此结构组织对应.zip中docs/文件夹01_标定操作手册.md含棋盘格打印尺寸、拍摄角度示意图、OpenCV标定命令逐行注释02_RKNN部署排错指南.md列出rknn.init()返回-3device not found、rknn.inference()返回-5input shape mismatch等错误码对应解决方案03_测距精度测试报告.pdf含10组实测数据表距离500~5000mm、误差曲线图、与超声波测距模块的对比实验04_C工程结构说明.md解释src/下detector.cppYOLO推理、distance_estimator.cpp几何计算、rknn_wrapper.cppRKNN API封装的职责边界最后一句在CMakeLists.txt中显式声明set(CMAKE_CXX_STANDARD 17)并添加-O2 -DNDEBUG避免Debug模式下测距延迟超过200ms——这是答辩现场演示流畅性的底线。本文还有配套的精品资源点击获取
返回列表