简介:RoboMaster2022思玄机器人队工程机器人视觉系统源码是一份面向机器人竞赛与计算机视觉学习者的Python实战项目,聚焦赛场环境下的目标检测、实时追踪与决策控制。整个压缩包共33个文件,包含16个Python脚本、14张图片、JSON配置与Markdown说明等,总计2.31MB,其中py文件覆盖相机驱动、自动瞄准、姿态校正等核心逻辑,图片素材用于调试与流程示意。已有164人学习这份源码,目录结构清晰,主模块拆分合理,适合希望理解实际机器人视觉工程架构的开发者参考。通过研读源码,可掌握图像预处理、特征识别、卡尔曼滤波追踪、深度学习目标分类以及视觉与运动控制模块的通信方法,为参加RoboMaster等赛事或自研机器人视觉系统提供完整的技术范例。
1. RoboMaster2022 工程机器人视觉源码:一个抢弹任务背后的完整工程
拿到 RoboMaster2022 思玄机器人队 工程机器人视觉系统源码.zip 这类包,先别急着找模型文件。工程机器人在赛场上的差事比步兵杂:补弹区取弹、场地数字识别、自动导航,偶尔还要承担救援动作的视觉辅助。你拆开包以后会发现,真正值钱的不是某一段检测代码,而是从图像采集、目标识别到坐标解算、串口通信这一整条流水线。这套东西能不能在比赛前三天稳住,取决于每一个环节的参数是否被调过、踩过坑、留过后手。这篇文章会用我搭工程机器人视觉系统的顺序,把这条链路拆开讲清楚:改哪里、看哪里、哪些地方最容易翻车。适合刚接手视觉组的新人照着复现,也适合被帧率、误检和坐标抖动折磨过的老队员对一对排查思路。
2. 视觉系统拆成四块:感知、检测、解算、通信的选型逻辑
工程机器人视觉系统和步兵的击杀视觉有本质区别。步兵视觉追求的是“看见人形装甲板并快速给出摩擦轮前置量”,而工程机器人面对的是慢速、静态、强规则约束的目标,比如补给区的弹药瓶、场地的数字标识。所以选型逻辑可以更激进:能用几何方法解决的就不上网络,传统视觉兜底,深度学习做泛化。这个思路也直接决定了源码包里代码的组织方式。
2.1 一块听话的相机和一套固定曝光参数,比模型更值钱
先说图像采集。很多队伍把精力全花在检测算法上,忽视了相机的曝光、白平衡、增益设置,结果就是白天测试一个效果,上场灯光下一个效果。工程机器人常用的相机有工业 USB 相机(海康、大恒)和普通的 USB 摄像头模组。工业相机胜在可调参数多、驱动稳定、支持硬触发,但价格高;普通摄像头便宜,但对自动曝光和白平衡的“自作主张”让人头疼。
我的建议是:选支持手动曝光和手动白平衡的相机,哪怕分辨率低一点都可以。在源码包的相机配置里,通常会有这样的参数文件,核心是下面几个:
# camera_config.yaml camera_index: 0 image_width: 640 image_height: 480 fps: 60 exposure_time: 500 # 单位 us,工程机器人场景我一般用 300-800 gain: 8 # 增益,越低噪点越少,但画面会偏暗 white_balance: 4500 # 固定色温,避免现场灯光变化导致偏色 auto_exposure: false auto_white_balance: false参数说明:exposure_time是曝光时间,单位微秒。RoboMaster 赛场灯光充足,500us 左右能保证画面不拖影、不过曝;如果场地有强光直射数字区域,可以压到 300us,但要注意暗部细节会丢失。gain是增益,我习惯控制在 10 以内,增益过大会让图像噪点激增,直接影响后续的轮廓提取和数字识别。white_balance固定色温非常关键——自动白平衡会在相机扫过不同颜色区域时不断调整,导致同一块数字的颜色特征漂移,传统视觉的颜色阈值就全废了。
提示:源码包如果带有相机标定结果,先检查标定日期和相机型号是否匹配。同一型号的相机也有个体差异,最好自己重新标一遍,别偷懒。
2.2 检测用混合策略:传统视觉兜底,YOLO 扛大梁
2022 赛季的工程机器人视觉任务里,最典型的是两类:识别场地数字(1-9)和识别弹药瓶(静止或旋转)。数字识别用传统视觉完全能打:数字是标准印刷体,对比度高,形状固定,用轮廓检测加七段特征就能稳定识别。但弹药瓶在旋转台上转动时,形状、角度、遮挡都在变,传统视觉容易误判,这时候就需要深度学习模型来扛。
所以源码包里常见的是两条检测链路并存:
| 任务 | 传统视觉方案 | 深度学习方案 | 我的选择 |
|---|---|---|---|
| 场地数字识别 | 灰度化 + 轮廓提取 + 七段码特征 | YOLOv5 检测数字框再分类 | 传统视觉为主 |
| 弹药瓶识别 | 颜色阈值 + 圆形度筛选 | YOLOv5 检测瓶体 + 角度回归 | 深度学习为主 |
| 能量机关激活辅助 | 时序分析 + 扇形区域提取 | 分割网络识别旋转叶片 | 传统视觉为主 |
这样分工的原因很实际:数字识别需要极低的延迟和高帧率,传统视觉在 640x480 分辨率下能做到 5ms 以内的处理;而弹药瓶识别要考虑旋转台的动态模糊和遮挡,传统视觉的阈值在这时候非常脆。两条链路共用同一套相机参数和畸变矫正数据,输出统一到相机坐标系,后端的解算模块不需要关心目标是怎么被找到的。
2.3 坐标解算与通信:云台角度的最后一百米
检测到目标只是第一步,工程机器人要的是“目标在我的哪个方向,偏了多少”。这就涉及到相机的内参、外参和目标在真实世界中的尺寸。常见做法是 PNP 解算:已知目标物体的三维尺寸,加上图像中的二维像素坐标,求解相机与目标之间的相对位姿。
这里有一个很多人忽略的点:工程机器人经常用多个相机,一个朝前看数字,一个朝下看补给区。每个相机都要单独标定外参,而且外参是相对于机器人底盘或云台的。源码包里一般会有一个extrinsic_params目录,记录每个相机相对于云台系的平移和旋转。通信模块则负责把这些角度通过串口发给下位机,协议通常是自己定义的,带校验和帧头。
3. 在源码里跑通第一版:从标定到数字识别的 OpenCV 最小链路
不管你拿到的源码包是什么结构,我建议先跑通一条最朴素的主链路:相机取帧 → 畸变矫正 → 数字识别 → 画框显示。这条链路能跑,再往里面加深度学习、加PNP、加通信。很多队伍上来就训练 YOLO,结果模型还没收敛,连相机图像都是花的。
3.1 相机标定:棋盘格的尺寸、数量与一处常见失误
标定是视觉系统的地基。工程机器人常用 15mm 或 20mm 方格大小的棋盘格标定板,打印后贴在硬板上。标定时要采集 15-20 张不同角度的图像,保证棋盘格出现在画面的各个区域,尤其是边缘。下面是常用的标定脚本:
import cv2 import numpy as np import glob # 棋盘格参数:内角点数,例如 9x6 表示每行 9 个内角点,每列 6 个 CHECKERBOARD = (9, 6) SQUARE_SIZE = 0.02 # 单位:米,20mm 棋盘格 # 准备物体点,格式为 (0,0,0), (1,0,0), ... 的实际物理坐标 objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) objp *= SQUARE_SIZE objpoints = [] # 存储所有图像中的物体点 imgpoints = [] # 存储所有图像中的角点 images = glob.glob('calib_images/*.jpg') for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, None) if ret: objpoints.append(objp) # 亚像素精度的角点位置 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) imgpoints.append(corners2) ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None) print("内参矩阵:\n", mtx) print("畸变系数:\n", dist) # 保存标定结果 np.savez("camera_calib.npz", mtx=mtx, dist=dist)逻辑说明:findChessboardCorners找到棋盘格的内角点,cornerSubPix把角点精度提升到亚像素级,这是标定精度的重要来源。calibrateCamera输出的mtx是内参矩阵,dist是畸变系数。
参数说明:CHECKERBOARD是棋盘格内角点数量,不是格子数量。例如打印的棋盘格是 10x7 的格子,内角点就是 9x6,填错会导致标定失败。SQUARE_SIZE一定要用实际打印尺寸,很多队伍在制图软件里画的是 20mm,实际打印出来是 19.8mm,这个误差会直接进入 PNP 解算的距离结果。
注意:标定采集时,棋盘格不要放在画面正中间,多让棋盘出现在画面边缘,畸变模型才能被充分约束。标定失败的最大原因就是图像太少或角度太单一。
3.2 数字识别:灰度化、二值化、轮廓筛选的打开方式
标定完成后,就可以写数字识别了。我用的方案是:对图像做灰度化 → 高斯模糊 → 自适应阈值或固定阈值 → 轮廓查找 → 按面积、宽高比筛选候选区域 → 用七段特征或模板匹配判定数字。
import cv2 import numpy as np cap = cv2.VideoCapture(0) # 加载标定结果 calib = np.load("camera_calib.npz") mtx, dist = calib["mtx"], calib["dist"] while True: ret, frame = cap.read() if not ret: continue # 畸变矫正,undistort 比较耗时,可以先用 initUndistortRectifyMap 做映射表 h, w = frame.shape[:2] mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, mtx, (w, h), cv2.CV_32FC1) # 实际使用时 mapx/mapy 只需计算一次,不要放在循环里 frame_undist = cv2.remap(frame, mapx, mapy, cv2.INTER_LINEAR) gray = cv2.cvtColor(frame_undist, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (5, 5), 0) # 固定阈值二值化,数字区域是白色,背景是深色 _, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: x, y, w_box, h_box = cv2.boundingRect(cnt) area = w_box * h_box if area < 500 or area > 20000: continue ratio = w_box / h_box if ratio < 0.2 or ratio > 0.8: continue # 在二值图上做数字分类,这里是占位,实际可以用模板匹配或七段码 cv2.rectangle(frame_undist, (x, y), (x + w_box, y + h_box), (0, 255, 0), 2) cv2.imshow("result", frame_undist) if cv2.waitKey(1) & 0xFF == ord('q'): break逻辑说明:initUndistortRectifyMap和remap的组合比每帧调用undistort快不少,尤其在嵌入式平台上差距明显,所以映射表只要标定结果不变就只计算一次。threshold的阈值 200 基于固定曝光下数字与背景的对比度,不同场地灯光需要微调。findContours之后用面积和宽高比过滤掉噪点区域。
参数说明:RETR_EXTERNAL只取最外层轮廓,避免数字内部空洞造成的重复框选;CHAIN_APPROX_SIMPLE压缩轮廓点数量,减少后续计算量。宽高比范围 0.2-0.8 覆盖了数字 0-9 的大致形状,但数字 1 的宽高比会偏小,需要根据场地实际微调。
这个版本的识别率应对干净的场地数字已经够用,但真实赛场的数字可能是倾斜的、带边框的、甚至被灯光照出阴影的。如果发现框选不稳定,下一步就是引入形状上下文或小型的 CNN 分类器,而不是直接上目标检测大模型。
4. 从传统视觉切到 YOLOv5:数据集、训练与 PNP 坐标解算
弹药瓶识别不能靠颜色阈值硬撑,原因很简单:旋转台上的弹药瓶在转动过程中,高光、阴影、遮挡都在变化,固定阈值很容易把瓶身和背景切开。2022 赛季大部分队伍用的是 YOLOv5 这类轻量检测网络,在 Jetson 或妙算上能跑到 30ms 以内一帧。
4.1 数据集制作:自己拍、自己标、负样本一定要够
很多队伍直接拿公开数据集训练,上场必翻车。RoboMaster 场地的光照、颜色、纹理都是特定的,公开数据集里没有。正确的做法是:用自己的相机、自己的曝光参数,在比赛场地上拍 500-1000 张图,然后用 LabelImg 或 labelme 标注。这里有一条血泪经验:负样本一定要够。所谓负样本,就是没有目标物的场地背景图,把它们也作为训练数据丢进去,模型才知道“什么都不是”的情况长什么样。
下面是数据集划分脚本:
import os import random import shutil # 假设原始图片在 dataset/images 下,标注文件在 dataset/labels 下(YOLO 格式) dataset_dir = "dataset" images_dir = os.path.join(dataset_dir, "images") labels_dir = os.path.join(dataset_dir, "labels") # 划分比例:训练 70%,验证 20%,测试 10% train_ratio, val_ratio, test_ratio = 0.7, 0.2, 0.1 img_files = [f for f in os.listdir(images_dir) if f.endswith((".jpg", ".png"))] random.shuffle(img_files) train_files = img_files[:int(len(img_files) * train_ratio)] val_files = img_files[int(len(img_files) * train_ratio):int(len(img_files) * (train_ratio + val_ratio))] test_files = img_files[int(len(img_files) * (train_ratio + val_ratio)):] for split, files in [("train", train_files), ("val", val_files), ("test", test_files)]: split_images = os.path.join(dataset_dir, "images", split) split_labels = os.path.join(dataset_dir, "labels", split) os.makedirs(split_images, exist_ok=True) os.makedirs(split_labels, exist_ok=True) for f in files: shutil.copy(os.path.join(images_dir, f), os.path.join(split_images, f)) label_file = f.rsplit(".", 1)[0] + ".txt" if os.path.exists(os.path.join(labels_dir, label_file)): shutil.copy(os.path.join(labels_dir, label_file), os.path.join(split_labels, label_file))逻辑说明:这个脚本把图片和对应的 YOLO 格式标注文件同步划分到train/val/test三个子目录。划分前一定要random.shuffle,否则连续帧的画面高度相似,训练集和验证集会互相污染。
参数说明:train_ratio设置 0.7 是经验值,样本少的时候可以提高验证集比例到 0.2-0.25,但不要低于 10%,否则验证指标波动很大。
训练命令,我用的是 YOLOv5 官方仓库的train.py:
python train.py --data rm_bottle.yaml --weights yolov5s.pt --img 640 \ --batch-size 16 --epochs 100 --device 0 --project runs/train --name rm_bottle参数说明:--img 640是输入分辨率,工程机器人场景建议用 640 而不是 1280,省下来的帧率对实时控制更有价值。--batch-size视显存而定,6G 显存跑 16 没问题。--weights yolov5s.pt用预训练权重做迁移学习,比从头训练收敛快得多。rm_bottle.yaml这个数据配置文件需要手动写,里面指定train/val路径和类别名。
YOLO 训练的关键参数我一般不动--epochs到 100 以上,反而是--patience早停要打开,防止过拟合。模型选yolov5s就够了,工程机器人的目标大、类别少,没必要上yolov5m或l。
4.2 PNP 解算:把像素坐标变成云台角度
检测网络输出的是目标框的中心坐标和宽高。要把它变成机器人云台需要转动的角度,就得用 PNP。PNP 的核心是:知道目标在真实世界的三维尺寸,知道它对应的图像像素坐标,反推出相机与目标之间的旋转和平移。
import cv2 import numpy as np # 弹药瓶的物理尺寸,单位 mm,需要实测 bottle_real_points = np.array([ [0, 0, 0], # 瓶底中心 [70, 0, 0], # 瓶底右边缘 [70, 70, 0], # 瓶底后右角 [0, 70, 0], # 瓶底后左角 ], dtype=np.float32) # 对应的图像像素坐标,来自检测网络的输出,这里只是示例 bottle_img_points = np.array([ [320, 400], [380, 402], [382, 390], [322, 388], ], dtype=np.float32) # 相机内参,来自标定 mtx = np.array([[600, 0, 320], [0, 600, 240], [0, 0, 1]], dtype=np.float32) dist = np.zeros((4, 1)) # 使用 PNP 求解,SOLVE_PNP_ITERATIVE 适合点数少且共面的情况 ret, rvec, tvec = cv2.solvePnP(bottle_real_points, bottle_img_points, mtx, dist, flags=cv2.SOLVE_PNP_ITERATIVE) R, _ = cv2.Rodrigues(rvec) # 旋转向量转旋转矩阵 # 计算目标在相机系下的方向向量 direction = R @ np.array([[0], [0], [1]], dtype=np.float32) yaw = np.arctan2(direction[0], direction[2]) * 180 / np.pi pitch = np.arcsin(direction[1]) * 180 / np.pi print(f"yaw={yaw:.2f}, pitch={pitch:.2f}")逻辑说明:solvePnP输入两组点:物体坐标系下的三维点和图像坐标系下的二维像素点,输出旋转向量rvec和平移向量tvec。Rodrigues把旋转向量转成旋转矩阵,然后我们取旋转矩阵第三列,也就是相机坐标系的 Z 轴在物体坐标系下的方向,这个方向就是目标中心相对相机的朝向。
参数说明:SOLVE_PNP_ITERATIVE适合共面点,弹药瓶的四个点都在瓶底平面上,用它没问题。如果点不共面,可以用SOLVE_PNP_P3P。tvec里的 Z 值就是目标与相机的距离,单位是 mm,这个值可以用于后续的距离判断。
注意:PNP 的精度极度依赖 3D 点的尺寸是否准确。用尺子量弹药瓶直径时多量几次,取平均值。测量误差 1mm,在 2 米距离上就会造成明显的角度偏差。
5. 工程机器人视觉的 5 个经典翻车现场与排查顺序
把系统搭起来不难,难的是上场前把问题都暴露出来。下面这 5 个坑是从实际调试中总结出来的,按出现频率排序。每条都给出排查思路,能帮你少走几天弯路。
5.1 过曝让白色数字和背景糊成一团,模型和阈值全部失效
现象:比赛场地的顶灯直接打在数字牌上,画面里数字区域一片白,轮廓提取不出来,YOLO 也检测不到。
原因:相机暴露在自动曝光模式下,或者曝光时间太长。比赛场地的光照强度远超实验室,自动曝光会把画面提亮到过曝。
解决:把所有视觉处理相机的自动曝光关闭,固定曝光时间。我一般从 300us 起调,逐步增加,直到数字边缘清晰、背景不过曝为止。同时把增益压在 10 以下,如果画面偏暗,优先加曝光而不是加增益。调试时盯住直方图,让数字区域的灰度值落在 180-230 之间。
5.2 畸变矫正后图像正常,但 PNP 算出的距离偏了半米
现象:相机标定结果看起来没问题,图像矫正后直线都是直的,但solvePnP输出的距离和实际距离差了 30-50cm。
原因:棋盘格标定的SQUARE_SIZE填错了,或者标定后相机安装位置有变动导致外参失效。PNP 的距离计算直接依赖目标的真实尺寸,标定板的格子尺寸如果差 1mm,距离误差会随目标距离线性放大。
解决:重新测量棋盘格的实际打印尺寸,注意有些打印店会按 98% 缩放。标定完成后做一个验证:把阿木狗或标定板放在已知距离处,用solvePnP反推距离,误差超过 5% 就重新标定。另外,标定后不要碰相机镜头或安装座,任何微小的位移都会让外参失效。这也是为什么我建议把相机标定放在所有机械调试完成之后再做。
5.3 YOLO 在测试集上很准,赛场上一帧误检“数字 8”识别成“数字 0”
现象:实验室录的视频回放检测准确率 98%,到赛场随机画面里频繁出现把场地边框或阴影检测成数字的情况。
原因:训练数据里负样本太少,模型没有见过“像数字但不是数字”的干扰。公开数据集和实验室背景不够丰富,赛场的栏杆、反光、地面纹理都可能触发误检。
解决:专门去赛场采集 200-300 张不包含任何目标的背景图,标注成空标签或直接放入训练集但不加标注。训练时在data.yaml里增加background类别或使用 YOLOv5 的--cls损失权重来提高类别置信度阈值。部署的时候把置信度阈值从默认的 0.25 提到 0.6 以上,宁可漏检也不误检。工程机器人的应用场景漏检可以重试,误检会导致云台乱转。
5.4 多线程取帧时程序偶尔卡顿,帧率抖动接近 10ms
现象:用cv2.VideoCapture在一个线程里取帧,主线程做检测,发现帧率不稳定,偶尔一次取帧耗时冲到 30ms。
原因:VideoCapture的内部缓冲区是队列,读取方式不对会造成阻塞。常见误用是在取帧循环里加了imshow,显示刷新会占住主线程导致丢帧。另一个原因是 USB 相机的传输模式在带宽不足时自动降帧。
解决:取帧用独立线程 + 最新帧覆盖策略,队列只保留最新一帧,消费端每次取queue[-1]而不是按顺序取。调试时把imshow注释掉,用cv2.imwrite间隔写帧来验证算法,确认算法本身耗时稳定后再说显示问题。如果怀疑 USB 带宽,降低分辨率到 640x480,或者检查是否有多个相机抢带宽。
5.5 串口发送的坐标偶尔跳变,下位机收到乱码后云台猛转
现象:视觉输出的 yaw 和 pitch 坐标绝大多数时间正常,突然在某几帧出现接近 180 度的跳变,下位机收到后云台猛甩。
原因:PNP 解算在目标被部分遮挡时给出的结果不稳定,或者通信协议没有做校验,一帧被干扰的数据被当成有效数据执行。
解决:在解算端做合法性过滤,tvec的 Z 值超出合理范围(比如 0.3m-5m)直接丢弃;相邻两帧 yaw 角度差超过 15 度也丢弃,等下一帧。通信协议用帧头 + 数据长度 + 累加和校验,解析时用状态机而不是暴力拆分。下面是串口发送的示例:
// 串口数据帧结构:0xAA 0x55 长度 数据 校验 uint8_t buffer[16]; buffer[0] = 0xAA; buffer[1] = 0x55; buffer[2] = 8; // 数据长度 memcpy(&buffer[3], &yaw, 4); memcpy(&buffer[7], &pitch, 4); buffer[11] = crc8(buffer, 11); // CRC 校验 serial_write(buffer, 12);逻辑说明:帧头0xAA 0x55用于找同步位置,数据长度解决粘包问题,CRC8 保证数据完整性。下位机收到后先核对帧头,再校验 CRC,不通过就丢弃整帧。这套逻辑虽然简单,但能避免 90% 的通信乱码问题。
6. 离线回放与帧率余量:让视觉系统上场的最后一个技巧
系统联调完成、所有参数都调好之后,先别急着上车。我的习惯是做一个离线回放工具:用录制的视频或 ROS bag 包,把视觉处理链路完整跑一遍,输出每一帧的检测结果、PNP 角度和耗时,保存成 CSV。这一步能暴露很多只在连续帧上才会出现的问题,比如坐标抖动、误检漂移、帧率波动。
最简单的回放工具可以这样写:读取 bag 或视频文件,按帧调用和实车相同的处理函数,把结果落盘。关键在于“和实车共用同一套代码”,否则回放测了等于白测。
# 用 ROS bag 回放,rate 控制播放速度 rosbag play --rate 1.0 vision_record.bag # 同时运行视觉节点,话题名与实车一致 rosrun rm_vision vision_node _image_topic:=/camera/image_raw回放时重点看三个指标:单帧处理耗时的最大值(不是平均值)、连续 100 帧中 PNP 角度的标准差、以及误检发生的帧号。最大值决定帧率上限,标准差决定云台稳定性。如果标准差超过 1 度,说明 PNP 输入点不稳定,需要检查检测框的抖动。
提示:回放时把检测框和角度叠加在原始图像上,用视频播放器逐帧看,发现问题帧后停下来分析。这一步能帮你省掉大量现场调试时间。
另一个容易被忽略的技巧是给视觉系统留帧率余量。工程机器人不需要像步兵那样满帧率跑,但至少要稳定在 30fps 以上。实际操作中,我会把单帧处理耗时控制在 25ms 以下,留出至少 15% 的余量给系统负载波动。如果发现算法耗时接近 30ms,优先优化预处理步骤而不是换更重的模型。数字识别场景完全可以用缩小图像或 ROI 区域来减少计算量。
回放和帧率验证都通过后,最后就是场地实测。我会提前列一个测试清单:不同距离下的识别成功率、不同角度下的角度误差、灯光变化下的鲁棒性。每项测试都要用真实场地数据,不要在实验室灯光下自我感觉良好。这套视觉系统带给我最大的教训就是:所有参数都要为最差情况准备,而不是最优情况。把过曝、遮挡、干扰当成常态来设计,上场才不会慌。希望这套从拆包到落地的思路能帮到你,让你的工程机器人第一次上场就稳住。
本文还有配套的精品资源,点击获取