基于YOLOv5的单目视觉车辆距离检测算法详解
1. 算法概述
本算法基于YOLOv5目标检测框架,结合单目视觉几何原理,实现了对前方车辆距离的精确测量。该方案由KinkangLiu改进并发布新版本,新增了相对速度检测功能(Monocular Distance Velocity Detect),通过整合Deep Sort算法实现对周围车辆距离和相对速度的综合检测。
1.1 技术架构
系统采用双阶段处理流程:
- 目标检测阶段:使用YOLOv5识别视频帧中的车辆目标
- 距离计算阶段:基于单目视觉几何原理计算检测到的车辆与摄像头的实际距离
2. 环境配置与运行指南
2.1 硬件要求
- GPU推荐:NVIDIA 3060及以上显卡
- 显存要求:至少6GB显存以获得流畅运行体验
2.2 软件环境
# 基础环境
Python 3.8
PyTorch 1.9.0 + CUDA 10.2
# 安装命令
conda install pytorch==1.9.0 torchvision==0.10.0 torchaudio==0.9.0 cudatoolkit=10.2 -c pytorch
2.3 运行方式
python estimate_distance.py --source YOUR_PATH\data\images\demo.mp4 --view-img
3. 核心算法原理
3.1 距离测量模型
系统基于单目视觉的几何约束关系建立距离测量模型,关键参数包括:
def __init__(self):
# 相机图像尺寸参数
self.W = 1280 # 图像宽度(像素)
self.H = 720 # 图像高度(像素)
# 物理安装参数
self.camera_height = 0.4 # 相机离地高度(米)
self.angle_a = 0 # 相机俯仰角(度)
3.2 距离计算公式
系统通过以下几何关系计算目标距离:
- 基于目标在图像中的垂直位置计算纵向距离
- 考虑相机高度和俯仰角的几何校正
- 通过透视变换建立像素坐标与世界坐标的映射关系
4. 输出结果解析
算法输出包含丰富的信息维度:
label = label + ' ' + str('%.1f' % d[0]) + 'm ' + str(location)
- 距离信息:d[0]表示目标车辆与摄像头的直线距离(米)
- 位置信息:
- -1:目标车辆位于画面左侧
- 0:目标车辆位于画面中央
- 1:目标车辆位于画面右侧
5. 参数调优指南
5.1 关键参数校准
-
相机高度(H):
- 测量误差直接影响距离计算精度
- 建议使用激光测距仪精确测量安装高度
-
俯仰角(angle_a):
- 零度表示相机水平安装
- 可使用专业水平仪进行校准
- 每1度偏差会导致约1.7%的距离计算误差
5.2 性能优化建议
-
对于高分辨率输入(1080p及以上),建议调整YOLOv5的inference尺寸:
parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='inference size (pixels)')
-
实时性要求高的场景可选用YOLOv5s模型:
parser.add_argument('--weights', type=str, default='yolov5s.pt', help='model path')
6. 新版本功能扩展
6.1 相对速度检测
新版本整合Deep Sort算法实现:
- 多目标跟踪保持ID一致性
- 基于连续帧距离变化计算相对速度
- 运动轨迹预测与滤波
6.2 系统架构升级
-
增加速度估计模块:
def estimate_velocity(self, track_history, fps): # 基于轨迹历史计算速度 pass
-
改进的输出界面:
label = f"{class} {distance:.1f}m {velocity:.1f}m/s {location}"
7. 应用场景与限制
7.1 典型应用
- 前向碰撞预警系统(FCWS)
- 自适应巡航控制(ACC)
- 智能泊车辅助
7.2 技术限制
- 测量精度受相机标定影响显著
- 夜间或低光照环境性能下降
- 极端天气条件(大雨/大雪)可能影响检测效果
8. 后续改进方向
- 多传感器融合:结合雷达数据提升鲁棒性
- 深度学习优化:采用注意力机制改进小目标检测
- 三维姿态估计:扩展至车辆三维空间位置估计
本系统为智能驾驶感知提供了经济高效的解决方案,通过单目摄像头即可实现车辆距离检测,相比多目或雷达方案具有显著的成本优势。开发者可根据实际应用场景调整参数,获得最佳性能表现。
重要代码
class DistanceEstimation:
def __init__(self):
#自己相机的图像尺寸
self.W = 1280
self.H = 720
self.excel_path = r'./camera_parameters.xlsx'
def camera_parameters(self, excel_path):
df_intrinsic = pd.read_excel(excel_path, sheet_name='内参矩阵', header=None)
df_p = pd.read_excel(excel_path, sheet_name='外参矩阵', header=None)
print('外参矩阵形状:', df_p.values.shape)
print('内参矩阵形状:', df_intrinsic.values.shape)
return df_p.values, df_intrinsic.values
def object_point_world_position(self, u, v, w, h, p, k):
u1 = u
v1 = v + h / 2
print('图像坐标系关键点:', u1, v1)
#alpha = -(90 + 0) / (2 * math.pi)
#peta = 0
#gama = -90 / (2 * math.pi)
fx = k[0, 0]
fy = k[1, 1]
#相机高度
#关键参数,不准会导致结果不对
H = 0.4
#相机与水平线夹角, 默认为0 相机镜头正对前方,无倾斜
#关键参数,不准会导致结果不对
angle_a = 0
angle_b = math.atan((v1 - self.H / 2) / fy)
angle_c = angle_b + angle_a
print('angle_b', angle_b)
depth = (H / np.sin(angle_c)) * math.cos(angle_b)
print('depth', depth)
k_inv = np.linalg.inv(k)
p_inv = np.linalg.inv(p)
# print(p_inv)
point_c = np.array([u1, v1, 1])
point_c = np.transpose(point_c)
print('point_c', point_c)
print('k_inv', k_inv)
#相机坐标系下的关键点位置
c_position = np.matmul(k_inv, depth * point_c)
print('相机坐标系c_position', c_position)
#世界坐标系下
c_position = np.append(c_position, 1)
c_position = np.transpose(c_position)
c_position = np.matmul(p_inv, c_position)
d1 = np.array((c_position[0], c_position[1]), dtype=float)
return d1