摄像头测距方案

Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.


该应用是一个基于 Quectel Pi M2 智能主控板,通过双目摄像头采集画面,在预览界面点击目标区域,测量摄像头到该物体的真实世界距离的项目。

该项目通过调用双目摄像头同时采集左右目画面,利用双目视差原理计算目标距离,并提供了摄像头标定、参数调节等辅助功能,可作为双目测距、立体视觉及机器人视觉导航类应用的参考示例。

../../../_images/image_XbhObVjAdo6fvvxVX4CcnCebnSc.webp

开发资源汇总

开发配件清单


配件名称


数量


规格参数

Quectel Pi M2智能主控板


1

Quectel Pi M2智能主控板

USB摄像头模块


1

绿联高清USB摄像头带麦克风(2K 400万高清自动对焦)

USB-C电源适配器


1

27W USB Type-C接口充电器1.2米线长中规电源PD电源适用于树莓派5代

CPU散热风扇(可选)


1

树莓派5代官方原装散热器带导热贴

HDMI A 型对公线


1

HDMI 2.0;线长1 m;接口:HDMI-A(公头)-HDMI-A(公头)

网线


1

线长:1 m;传输速率:千兆

显示屏


1

24英寸HDMI显示器

USB双目摄像头


1

推荐分辨率:2560×720(左右各1280×720);输出格式:YUYV/MJPG

配件实物参考

../../../_images/image_CPdtbMsvEoKcGFx6MiNc4G3onnk.webp

双目摄像头:

../../../_images/image_OF2tbL7hUoUT9JxEBCmckTbLnge.webp

快速上手

开发准备

Quectel Pi M2智能主控板 出厂预装 Debian 13 系统镜像,因此无需重新烧录系统镜像,按照以下步骤操作即可。

硬件连接

按照以下步骤完成所有硬件连接(步骤编号与接口示意图中的标注一致):

../../../_images/image_DxTnbox5aoy5yBxvYpwc4nTGngc.webp
  1. 显示连接:将HDMI线的一端连接到智能主控板的HDMI接口,另一端连接到显示器的HDMI接口。

  2. 网络连接:确保路由器已连接互联网,然后将网线的一端连接到智能主控板的千兆以太网端口,另一端连接到路由器的以太网端口。

  3. 输入设备连接:将USB键盘、鼠标和USB双目摄像头等输入设备连接到智能主控板的USB接口。如果使用无线输入设备,将无线接收器连接到扩展坞的USB接口即可。

  4. 供电连接:将USB-A电源线的USB-A端连接到电源适配器的输出端,将USB-C端连接到智能主控板的电源输入接口(通常标注为 POWER IN)。

项目实现

安装前置

确认有网络连接后,打开终端输入命令:

sudo apt update && sudo apt install -y v4l-utils python3-pip

上述命令将更新软件源并安装项目运行所需的一些库,具体包括:

  • v4l-utils:提供 v4l2-ctl 等摄像头工具,用于读取和设置摄像头参数;

  • python3-pip:Python 包管理器,用于安装项目依赖。

获取代码

下载代码

代码解压到设备

安装 Python 依赖

pip install -r requirements.txt

依赖包说明:

  • PySide6:Qt6 的 Python 绑定,用于构建图形用户界面;

  • opencv-python:OpenCV 图像处理库,用于摄像头采集、图像处理和立体视觉计算;

  • numpy:数值计算库,用于矩阵运算和标定参数处理。

采集标定图像

运行标定图像采集工具:

python3 tools/capture_calib_images.py

程序会自动检测双目摄像头并显示预览画面,按照提示操作:

  1. 将棋盘格标定板放置在摄像头前方不同位置和角度

  2. 按 s 键保存当前图像对(建议采集 15-20 对)

  3. 按 q 键退出采集程序

棋盘标定板:

../../../_images/image_BL7nbrBsMoIgmBxFWRYcTsDqn9f.webp

生成标定参数

采集完成后,运行标定参数生成工具:

python3 tools/generate_calib_params.py

程序会自动读取标定图像并计算双目标定参数,生成 tools/stereo_calib_params.npz 文件。

运行测距应用

完成标定后,运行主程序:

cd src
python3 main.py

程序启动后将显示图形界面,提供以下功能:

../../../_images/image_CcwNbp7r4oJkXqxoeJhcugiqnqd.webp

功能按钮说明

[电子表格]

日志显示区域

右侧上方区域可输出应用运行过程中的日志信息

../../../_images/image_V8jAbJUgDoPe1cxRpPEc4L4mnPc.webp

摄像头参数调节

程序右侧提供摄像头参数调节面板,可调节以下参数(也可根据具体摄像头可调节参数设置范围):

[电子表格]

进行测距

  1. 点击 “Start Ranging Mode” 进入测距模式

  2. 在预览画面上点击目标位置

  3. 等待距离计算结果显示在顶部提示栏

应用演示

[嵌入内容]

摄像头测距注意事项及常见问题解决方案

代码问题修改

设备缺少 pip

Debian 13 默认不预装 pip 与 venv,需要手动安装并配置虚拟环境

adb shell "apt-get install -y python3-pip python3-venv"

int('-camera0') 导致摄像头枚举崩溃

现象:

ValueError: invalid literal for int() with base 10: '-camera0'

这是因为RK3576平台的设备上存在一个符号链接,需要修改代码

def _video_index(path):
    """从 /dev/videoN 提取整数索引,非法名称返回 None"""
    suffix = os.path.basename(path).replace("video", "")
    return int(suffix) if suffix.isdigit() else None# 跳过符号链接(如 /dev/video-camera0 -> video51),只取真实节点
devices = [d for d in glob.glob("/dev/video*") if not os.path.islink(d)]
devices = [(i, d) for i, d in ((_video_index(d), d) for d in devices) if i is not None]
devices.sort(key=lambda x: x[0])

摄像头选错:rkisp 虚拟节点被误判为双目摄像头

问题在于 rkisp虚拟节点会"接受"任意分辨率设置并原样回报, 但实际 read() 读不出任何数据

解决

检测时 必须真正读一帧 才能确认节点可用:

# 对 UVC 双目摄像头,MJPG 才能跑满高分辨率/高帧率
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 4096)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 2160)
...
# 关键:只有真正读到一帧,才说明这个节点是可用的采集设备
ok, frame = cap.read()
can_capture = bool(ok and frame is not None and getattr(frame, "size", 0) > 0)
...
if is_stereo and can_capture:      # 两个条件都满足才作为候选
    fh, fw = frame.shape[:2]        # 用实际帧尺寸修正上报值
    candidates.append((dev, fw, fh))

标定图像采集注意事项

图像质量

要求:

  • 清晰度保证:确保棋盘格在左右两个摄像头中完全可见,避免运动模糊

  • 光照条件:避免强光直射和严重阴影,保证均匀光照

  • 避免反光:如果使用手机屏幕显示棋盘格,注意避免屏幕反光导致角点检测失败

采集策略:

  • 不同距离:在摄像头前0.3m-2m范围内变化距离

  • 不同角度:左右倾斜±30°,上下倾斜±20°,旋转±15°

  • 不同位置:棋盘格应覆盖画面的左上、右上、左下、右下、中心等不同区域

  • 数量建议:至少15对有效图像,建议20对以上以提高标定精度

注意事项:

  • 内角点数量:CHESSBOARD_SIZE 表示内角点数量,不是方格数量。例如9×6的内角点对应10×7的方格

  • 物理尺寸准确性:SQUARE_SIZE 必须与实际方格边长一致,直接影响基线距离和测距精度

  • 打印建议:

    • 打印纸质棋盘格时,确保平整无皱褶

    • 使用手机屏幕显示时,注意屏幕尺寸测量准确

    • 推荐方格边长:8-12mm

提高精度的方法:

  • 增加标定图像数量(建议20对以上)

  • 确保图像覆盖更多角度和位置

  • 提高图像清晰度,避免模糊

  • 检查棋盘格物理尺寸设置是否正确

验证要点:

  • 基线距离:baseline 应与实际双目摄像头物理间距接近(通常40-120mm)

  • 焦距一致性:左右相机焦距应相近,差异过大说明标定有问题

  • 畸变系数:dist_l 和 dist_r 数值应在合理范围内(通常绝对值<1)

双目测距过程注意事项

测距原理与公式

# 测距公式:Z = (f × B) / d# 其中:# - f: 焦距(像素)# - B: 基线距离(米)# - d: 视差(像素)
distance = (f * self._baseline) / disparity

影响测距精度的关键因素:

  • 视差计算准确性:受立体匹配算法和图像质量影响

  • 标定参数准确性:基线距离和焦距参数

  • 目标点特征:纹理丰富度影响视差计算

SGBM立体匹配参数调优

stereo = cv2.StereoSGBM_create(
    minDisparity=0,               # 最小视差
    numDisparities=16*12,         # 视差搜索范围(必须是16的倍数)
    blockSize=11,                 # 匹配块大小(奇数,3-21)
    P1=8*3*11*11,                 # 视差平滑惩罚项1
    P2=32*3*11*11,                # 视差平滑惩罚项2
    disp12MaxDiff=1,              # 左右一致性检查最大差异
    uniquenessRatio=10,           # 唯一性比率
    speckleWindowSize=100,        # 散斑过滤窗口大小
    speckleRange=32,              # 散斑过滤范围
    mode=cv2.STEREO_SGBM_MODE_HH  # 高精度模式)

参数调优建议:

[电子表格]

测距精度影响因素

有效测距范围

# 测距范围受视差搜索范围限制
max_disparity = numDisparities  # 192 (16*12)
min_distance = (f * baseline) / max_disparity

示例计算:

  • 焦距 f = 695 像素

  • 基线 B = 0.0735 米

  • 最大视差 = 192

  • 最近测距距离 = (695 × 0.0735) / 192 ≈ 0.27米

视差有效性过滤

# 代码中的视差过滤逻辑for dy in range(-kernel//2, kernel//2 + 1):for dx in range(-kernel//2, kernel//2 + 1):
        d = disparity_map[y, x]if d > 0.5:  # 过滤弱视差噪声
            disparity += d
            valid_count += 1

注意事项:

  • 视差值过小(<0.5)通常表示无效匹配

  • 采用5×5邻域平均减少噪声影响

  • 有效点数过少时应提示用户重新点击

距离合理性检查

# 对计算结果进行合理性验证
z_3d = point_3d[2]if 0.01 < z_3d < 100.0:
    distance = z_3d
else:# Z不合理时用公式计算
    distance = (f * self._baseline) / disparity

测距误差来源:

  • 标定误差:基线距离和焦距参数不准确

  • 视差误差:立体匹配算法精度限制

  • 图像质量:光照、模糊、纹理不足

  • 距离因素:距离越远,视差越小,相对误差越大


常见问题及解决方案

标定相关问题

角点检测失败率高

现象:

Warning: Skip 5th pair - chessboard not detected in left image
Valid image pairs for calibration: 8 (need ≥10)

解决方案:

  • 改善光照条件,避免阴影和反光

  • 确保棋盘格完全在画面内

  • 增加图像对比度

  • 减小棋盘格尺寸或增大拍摄距离

  • 使用更清晰的棋盘格图像源

基线距离异常

现象:

Baseline Length: 0.1234 meters (应该约为0.06米)

解决方案:

  • 重新测量方格实际物理尺寸

  • **检查SQUARE_SIZE**参数设置

  • 确保棋盘格平面平整

  • 重新进行标定

测距相关问题

测距结果为0或无效

现象:

Error: Ranging failed - No valid disparity points

原因分析:

  • 点击区域纹理不足或重复纹理

  • 点击区域过远或过近

  • 图像质量差(模糊、光照不足)

  • 未加载标定参数

解决方案:

# 检查标定参数是否加载if not self._is_calibrated:
    LogManager.append_log("Warning: No calibration loaded!")
  • 确保已加载标定参数文件

  • 点击纹理丰富的区域

  • 改善光照条件

  • 调整SGBM参数增大numDisparities

测距精度低、误差大

现象:
实际距离1米,测量结果0.8米或1.2米

解决方案:

  • 检查标定质量:重投影误差应<1.0

  • 优化SGBM参数:根据测距范围调整 numDisparities 和 blockSize

  • 改善图像质量:

# 增强对比度
clahe = cv2.createCLAHE(clipLimit=6.0, tileGridSize=(8, 8))

视差图质量差

现象:
视差图充满噪点,无法识别物体轮廓

解决方案:

  • 调整预处理参数:

# 增强对比度
clahe = cv2.createCLAHE(clipLimit=6.0, tileGridSize=(8, 8))# 增强降噪
gray_left = cv2.GaussianBlur(gray_left, (5, 5), 0)
gray_left = cv2.medianBlur(gray_left, 5)
  • 调整SGBM参数:

# 增强匹配严格性
uniquenessRatio=15
speckleWindowSize=150
  • 检查标定参数:重新标定以获得更好的校正效果