计算机视觉:让机器人看见世界

从像素矩阵到3D世界坐标——把相机变成机器人的"眼睛"。机械工程师理解标定和坐标系变换有天然优势。

4-6 周
5 个章节
8 个代码示例
5 个验收实验

本阶段目录

  1. 图像基础与OpenCV
  2. 相机模型与标定
  3. 手眼标定:相机与机器人的空间关系
  4. 目标检测:YOLO实战
  5. 3D感知:点云与深度

1. 图像基础与OpenCV

对照已有知识

图像 = 高×宽×3 的张量,就像有限元网格上每个节点的应力张量。OpenCV 就是处理这种张量的工具箱。

基本操作流水线

import cv2; import numpy as np
img = cv2.imread('robot_view.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hsv  = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
blur = cv2.GaussianBlur(gray, (5,5), 0)
edges = cv2.Canny(gray, 50, 150)  # 边缘检测

# 颜色分割:找到红色物体
lower = np.array([0,100,100]); upper = np.array([10,255,255])
mask = cv2.inRange(hsv, lower, upper)
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
    x,y,w,h = cv2.boundingRect(c)
    cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
🔧 工程连接:颜色阈值分割是最简单也最实用的视觉定位方法。用HSV颜色空间(色调-饱和度-明度)而非RGB,因为HSV对光照变化更鲁棒。

2. 相机模型与标定

$\begin{bmatrix}u\\v\\1\end{bmatrix} = \frac{1}{Z_c}\begin{bmatrix}f_x&0&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix}\begin{bmatrix}X_c\\Y_c\\Z_c\end{bmatrix}$

内参矩阵 K 是你相机的"身份证":$f_x,f_y$ 是焦距(像素),$c_x,c_y$ 是主点。标定一次即可。

# 相机标定
pattern_size = (9, 6)  # 棋盘格内角点数
objp = np.zeros((pattern_size[0]*pattern_size[1], 3), np.float32)
objp[:,:2] = np.mgrid[0:pattern_size[0],0:pattern_size[1]].T.reshape(-1,2)

# 从多张棋盘格照片中标定
ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img.shape[:2])
print(f"内参矩阵:\n{K}")

# 去畸变
img_undist = cv2.undistort(img, K, dist)

3. 手眼标定

相机检测到"物体在相机前方0.5m",你怎么告诉机械臂去哪抓?需要 $\mathbf{T}_{base}^{cam}$——相机在机器人基坐标系中的位姿。

$AX = XB$  (Eye-in-Hand: 机械臂末端运动A,相机观测标定板运动B,X是待求的 $\mathbf{T}_{tool}^{cam}$)
# OpenCV 手眼标定
R_cam2base, t_cam2base = cv2.calibrateHandEye(
    R_gripper2base, t_gripper2base,   # 机械臂每次移动的位姿
    R_target2cam, t_target2cam,        # 标定板在相机中的位姿
    method=cv2.CALIB_HAND_EYE_TSAI
)

常见坑

采集数据时机械臂的运动要覆盖旋转和平移,不能只在一个平面内移动。20组以上数据才有稳定结果。

4. 目标检测:YOLO实战

YOLO 把整张图一次性输入网络,直接输出边界框和类别。不像传统方法分"候选区域→分类"两步,YOLO 是 one-stage detection。

from ultralytics import YOLO
model = YOLO('yolov8n.pt')  # nano版本,适合边缘设备
results = model('workspace.jpg')
for r in results:
    for box in r.boxes:
        cls = model.names[int(box.cls[0])]
        conf = box.conf[0].item()
        x1,y1,x2,y2 = box.xyxy[0].tolist()
        print(f"{cls}: {conf:.2f} @ ({x1:.0f},{y1:.0f})-({x2:.0f},{y2:.0f})")

ArUco标记是快速6D位姿估计的"作弊码":相机看到标记后能瞬间算出标记的3D位置和朝向。

aruco = cv2.aruco.ArucoDetector(
    cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250),
    cv2.aruco.DetectorParameters()
)
corners, ids, _ = aruco.detectMarkers(img)
rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(corners, 0.05, K, dist)
print(f"标记 {ids[0][0]}: 位置={tvecs[0][0]} m")

5. 3D感知:点云与深度

RGB-D 相机(如 Realsense)输出彩色图+深度图。深度图 + 内参 → 3D 点云。点云是机器人抓取和导航的核心数据。

import open3d as o3d
pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd, intrinsics)
pcd = pcd.voxel_down_sample(0.005)  # 降采样
pcd, _ = pcd.remove_statistical_outlier(30, 2.0)  # 去噪
plane, inliers = pcd.segment_plane(0.01, 3, 1000)  # RANSAC平面分割
objects = pcd.select_by_index(inliers, invert=True)  # 物体=非桌面

验收实验

  • 用棋盘格标定相机,重投影误差 < 0.5px
  • 手眼标定:采集20组数据,用AX=XB求解,验证重投影误差 < 2px
  • YOLO 在桌面场景识别5类物体,mAP@0.5 > 0.8
  • ArUco 标记 6D 位姿估计:平移误差 < 5mm,旋转误差 < 5°
  • 用 Open3D 从 RGB-D 图像生成点云,分割桌面和物体,IoU > 0.85

上一阶段:← 机器学习与深度学习  |  下一阶段:NLP与大模型 →

拓展资源:计算机视觉

GitHub 仓库

视频课程

数据集