☰
ZED双目相机+YOLO检测框测距与三维坐标换算
2026/11/6 7:56:12 网站建设 项目流程

简介:面向使用ZED立体相机与YOLO做实时目标检测和双目测距的开发者,提供一套可运行的工程示例,覆盖相机采集、目标识别、深度信息读取与距离解算等核心环节,适合计算机视觉入门到中级实践、机器人导航与自动驾驶感知原型验证。压缩包共42个文件,约78.97MB,以20个Python脚本为主,配合cfg与weights模型配置权重、TensorRT/CUDA加速源码、少量xml配置与说明文档,兼顾运行与二次开发。已有3016人学习下载。读者可据此理解ZED SDK环境配置、numpy版本兼容处理、YOLOv4/v4-tiny模型接入、相机标定与测距流程,并参考脚本模块划分排查部署问题、调整分辨率帧率和深度模式,快速搭建可复现的双目测距实验。

1. 一台双目相机加一个检测框:zedceju.py 解决的是哪一步

桌面上摆着一台双目相机,屏幕上是一个实时窗口,被检测到的目标外面套着一个绿框,框的左上角跳着一个以米为单位的数字。zedceju.py干的就是这件事:用 zed 双目相机取流,用 yolo 找出画面里的目标,再把目标框范围内的深度值换算成相机坐标系下的 (X, Y, Z)。它不是一个跑完就丢的演示脚本,更像一副骨架——相机接入、模型推理、深度回填、结果绘制四段拆得还算干净,你换成自己的模型或者自己的后处理,剩下的轮子不用重造。适合两类人:手上已经有真机、想快速验证「检测 + 测距」这条链路的;以及做抓取、避障、体积估算这类活,需要一个能跑通的视觉定位起点的。前提得说清楚,这套东西离了双目硬件就跑不出真实距离,纯 RGB 版本只能给你一个估算值,别拿它当尺子用。

2. 链路选型与数据流:ZED 取流、YOLO 定位、深度回填怎么接上

双目测距这件事,方案不止一种。最原始的路子是拿两个普通摄像头自己标定:拍棋盘格、算内外参、做极线校正、跑 SGBM 或者 BM 这类立体匹配算法,最后再手动把视差转成深度。这条路我走过,调参调到怀疑人生——窗口大小、唯一性比值、纹理阈值、斑点过滤,参数之间互相牵制,换个光照就得重来一遍,属于典型的玄学环节。zedceju.py选的是另一条路:直接用相机厂商封装好的深度引擎,脚本只负责「检测目标 + 读深度」这两件事,把立体匹配那堆脏活甩出去。这个取舍决定了后面所有代码的形态,所以值得先讲透。

2.1 为什么用相机自带深度,而不是自己标定双目

先说结论:这套脚本的定位是「给检测框补一个距离值」,不是「做高精度三维重建」。定位不同,方案就不同。自己标定的好处是完全可控,标定板精度够、匹配算法调得细,近距离能压到毫米级;坏处是标定一旦漂了,整条链路全废,而且每次改基线、换镜头、动支架,都得重新走一遍流程。双目相机的深度图由内置的校正参数实时算出来,相机上电就能用,脚本里一行retrieve_measure就把深度矩阵拿到手,对快速验证极其友好。

代价也得摆在明面上。内置深度在弱纹理表面会大片缺失,白墙、玻璃、纯色纸箱这类目标,深度图上直接是空洞;强反光和有镜面反射的物体,深度值会跳到错误的位置;超出有效量程之后,误差随距离平方增长。所以脚本里的深度取值必须带有效性过滤,这一段后面会单独讲。另外,深度模式对算力有要求,质量优先的模式基本得靠独立显卡跑,纯 CPU 会掉到个位数帧率,实时性就没了。

我一般会这么判断:如果目标是「框住的东西大概多远」,用相机自带深度,两小时能出结果;如果目标是「给机械臂提供毫米级抓取位姿」,那就老老实实做标定,外加手眼标定,别想着抄近路。

2.2 一条数据的完整链路:从一帧图像到 (X, Y, Z)

整个脚本的主循环可以压缩成五步,每一步都有明确的输入输出,出问题的时候按这五步切段排查,比盯着一个几百行的文件瞎找高效得多。

第一步取帧。相机对象调用grab,成功之后能取到左目图像和对应的深度矩阵。注意这两份数据是同一时刻的,别在两次grab之间混用图像和深度,那样会错位一帧,快速移动的目标距离会明显跳变。

第二步推理。左目图像直接送进检测模型,拿到若干检测框,每个框带类别、置信度和归一化坐标。这一步只跟图像有关,跟深度完全解耦,所以模型可以随时换。

第三步裁剪。按检测框把深度矩阵切出一块矩形区域,得到该目标的深度候选集合。这里要注意边界裁剪,框越界会导致索引报错或者取到错误的行。

第四步统计。对这块区域做有效性过滤,再取中位数作为该目标的代表深度。为什么是中位数而不是均值,后面代码那一节会详细解释,简单说就是抗离群点。

第五步反投影。有了像素坐标和深度,配合相机内参,就能算出相机坐标系下的三维点。同时也就有了距离,直接用 Z 值或者算欧氏距离都行。

这条链路里,第二、三、四步是纯 CPU 逻辑,第一步和深度计算在相机侧完成。想提速的话,优化点通常不在检测模型上,而在深度模式的分辨率和帧率配置上,这个反直觉的结论我踩过一次才认。

2.3 选型边界:这套组合在什么场景会失效

写脚本的时候容易有一种错觉,觉得跑通了就万事大吉。实际部署中失效的场景比想象的多,提前知道边界能省很多时间。

近距离失效。相机有最小测距距离,物体贴得太近,视差超出匹配范围,深度直接给零。做桌面级抓取的时候这个坑特别常见,目标一进工作区边缘,深度就没了。

远距离漂移。距离越远,视差越小,相对误差越大。同一个目标从两米走到八米,读数可能偏差几十厘米。所以别拿它做远距离测距的绝对基准。

多目标互相遮挡。两个目标在画面上重叠,深度图在边界处会混入前景的深度值,导致后方目标被算近。框内取中位数能缓解一部分,但根治不了。

类别与距离的耦合。检测模型只认训练过的类别,没见过的目标不会出框,也就没有距离。这一点经常被忽略——有人拿它测一个模型没学过的物体,发现没有输出,回头怀疑深度模块坏了。

画面剧烈运动。双目深度依赖左右目的一致性匹配,快速平移或者强光闪烁会造成瞬时错误,深度值在相邻帧之间跳动。要稳定的读数,得自己在前端加一层时序平滑。

3. 目录与环境:把 zedceju.py 跑起来需要先摆平的四件事

脚本本身只有一个文件,但能跑起来取决于环境里四样东西是否对得上:相机驱动与 SDK、CUDA 与显卡驱动、Python 绑定、检测模型的权重文件。这四样里任何一样版本错位,报错信息都不会直接告诉你原因,只会抛一个模糊的错误码。所以上手第一件事不是改代码,是把环境按顺序装对。

3.1 目录结构:一个主脚本加几个配角文件

我拿到的这份工程很轻,主干就是zedceju.py,权重和输出目录按常见习惯放在同级。这种结构的好处是拷走就能用,不需要改一堆相对路径。下面是我整理后的目录布局,标了每个位置的职责和能不能动。

路径职责是否可改
zedceju.py主脚本,相机初始化、推理、测距、绘制全在这里核心,改之前先备份
weights/检测模型权重文件可换成自己训练的权重
output/截图、录像、测距日志落盘位置可改路径
requirements.txtPython 侧依赖清单按环境增删
config.yaml分辨率、置信度、深度模式等可调参数建议改这里而不是改代码

config.yaml这个文件很多同类脚本里没有,参数全写死在代码中间,改一个阈值要翻半天。我的习惯是把跟场景强相关的数值——置信度阈值、IOU 阈值、深度有效区间、目标类别白名单——全部外置成配置,代码里只读不改。这样同一份脚本换场景的时候,只动 yaml,不动逻辑,回归测试也好做。

3.2 依赖安装顺序:SDK 必须在 Python 绑定之前

顺序错了会非常难受。相机 SDK 装完之后,Python 绑定是随安装包一起进去的,不在 PyPI 上,pip install拿不到。所以正确的做法是先装驱动和 SDK,确认import pyzed能通,再装检测框架那一套。

# 1. 先确认显卡驱动和 CUDA 可用,深度计算依赖 GPU nvidia-smi nvcc --version # 2. 安装相机 SDK(安装包形式,装完 Python 绑定自动就位) # 装完后验证:能在 Python 里导入,且能枚举到设备 python -c "import pyzed.sl as sl; print(sl.Camera.get_device_list())" # 3. 再装检测与图像处理依赖 pip install opencv-python numpy pyyaml # 4. 最后装检测框架(按自己用的主线版本走) pip install ultralytics

每一步验证点不一样:nvidia-smi看的是驱动版本和显卡可见性;get_device_list()返回空列表说明相机没连上或者 USB 带宽不够;import pyzed失败通常是 SDK 版本和 Python 解释器版本对不上。少做一步验证就往后走,最后报的错会指向一个完全无关的地方。

提示:SDK 和显卡驱动的版本有对应关系,两者差距太大时相机能打开但深度模式会静默退化成低质量模式,帧率看着正常,精度掉一大截,非常难查。

3.3 启动命令与参数:第一次跑先降配

第一次运行别直接上高分辨率。1080p 加高质量深度模式,在一台没调优过的机器上很可能几秒才出一帧,你会误以为是代码性能问题。先用 720p、30 帧、中等深度模式跑通,确认链路通了再往上加。

# 最小可运行命令,配置文件用默认值 python zedceju.py --config config.yaml # 调试用:只处理图像不打开窗口,把测距结果打到标准输出 python zedceju.py --config config.yaml --headless --max-frames 100 # 指定权重和输出目录 python zedceju.py --weights weights/model.pt --out output/run01

跑起来之后,日志里会打印相机型号、序列号、分辨率、深度模式、有效量程。这行日志很重要,量程不对的话,后面所有距离值都是错的。看到窗口弹出、框和数字都正常,再开始调参数。

几个关键参数的含义先说清楚:

参数含义调法
depth_mode深度计算模式先中等,精度不够再往高质量调
min_distance最小有效测距按实际工作距离设,别用默认值
conf_thres检测置信度阈值漏检多就降,误检多就升,0.3 到 0.5 之间试
iou_thres非极大值抑制阈值目标密集时调低,避免互相吞框
depth_percentile框内深度取值分位数默认中位数,有遮挡时试 20 分位

调参有个原则:一次只动一个。同时改置信度和深度分位数,出了偏差你分不清是哪一项造成的,白白多花两小时。

4. 核心代码拆解:一个检测框是怎么变成 (X, Y, Z) 的

代码分四段来看比较清楚。每一段都可以单独跑测试:相机段单独验证能不能取到图,推理段单独拿静态图验证框对不对,测距段拿已知距离的目标验证数值,绘制段纯粹是给人看的。这四段解耦得越干净,后面替换任何一段的成本越低。

4.1 相机初始化与内参读取

内参是反投影的前提,必须先拿到。相机对象打开之后,从配置信息里能读到左目相机的焦距和主点,这三个数在整个运行期是不变的,读一次缓存起来就行。

import pyzed.sl as sl def open_camera(cfg): """按配置打开相机,返回相机对象和内参字典""" zed = sl.Camera() init = sl.InitParameters() # 分辨率与帧率:第一次跑建议先用低配置 init.camera_resolution = getattr(sl.RESOLUTION, cfg["resolution"]) # 如 HD720 init.camera_fps = cfg["fps"] # 深度模式与单位:单位必须显式设成米,否则默认是毫米 init.depth_mode = getattr(sl.DEPTH_MODE, cfg["depth_mode"]) # 如 NEURAL init.coordinate_units = sl.UNIT.METER init.depth_minimum_distance = cfg["min_distance"] # 单位米 init.depth_maximum_distance = cfg["max_distance"] err = zed.open(init) if err != sl.ERROR_CODE.SUCCESS: raise RuntimeError(f"相机打开失败: {err}") # 读取左目内参,运行期不变,读一次即可 info = zed.get_camera_information() cam = info.camera_configuration.calibration_parameters.left_cam intr = {"fx": cam.fx, "fy": cam.fy, "cx": cam.cx, "cy": cam.cy} return zed, intr

coordinate_units这一行是整个函数里最容易漏也最要命的一行。漏了它,深度矩阵返回的还是毫米,后面反投影算出来的 X、Y、Z 全部放大一千倍,你会看到一个三米外的杯子报出三千。depth_minimum_distance也不是随便填的,填太小会让近处的无效像素混进来,填太大则会把工作区内的目标直接判成无效。这个值应当等于实际工作距离的下限。

left_cam取的是左目,因为检测用的是左目图像。如果后面改成用右目图做推理,内参也要跟着换成right_cam,混用会导致水平方向系统性偏移,而且偏移量随距离线性增长,看起来很像标定不准,实际上是取错了参数。

4.2 推理与目标框过滤

推理这一段跟普通的目标检测没有区别,区别在于输出要跟像素坐标严格对齐。不同框架的返回格式不一样,有的给归一化坐标,有的给绝对像素,混着用会得到一堆越界的框。

import numpy as np def detect(model, frame_bgr, cfg): """跑一次推理,返回像素坐标的框列表 [(x1,y1,x2,y2,cls,conf), ...]""" h, w = frame_bgr.shape[:2] pred = model.predict( frame_bgr, conf=cfg["conf_thres"], iou=cfg["iou_thres"], verbose=False, )[0] boxes = [] for box in pred.boxes: # 这里取的是绝对像素坐标,不同版本接口字段名不同,注意核对 x1, y1, x2, y2 = box.xyxy[0].tolist() # 裁到图像边界,防止后面切深度矩阵时越界 x1 = int(np.clip(x1, 0, w - 1)) y1 = int(np.clip(y1, 0, h - 1)) x2 = int(np.clip(x2, 0, w - 1)) y2 = int(np.clip(y2, 0, h - 1)) if x2 - x1 < cfg["min_box_px"] or y2 - y1 < cfg["min_box_px"]: continue # 太小的框深度统计不可靠,直接丢 cls = int(box.cls[0]) if cls not in cfg["class_whitelist"]: continue # 只关心白名单里的类别 boxes.append((x1, y1, x2, y2, cls, float(box.conf[0]))) return boxes

三个过滤动作值得单独说。np.clip裁边界看着多余,实际上检测框贴边的概率不低,不裁的话depth[y1:y2, x1:x2]会出现空切片,后面取中位数直接抛异常。min_box_px是对小目标的保护,一个 10×10 像素的框里可能只有十几个有效深度点,中位数的统计意义很弱,报出来的距离波动会很大。class_whitelist是场景约束,做抓取的时候画面里可能有椅子、水杯、人,只保留你要抓的那类,能省掉大量无意义的深度计算和绘制开销。

置信度阈值不要设太高。0.5 以上时,遮挡和逆光场景的漏检会明显增加,而漏检在测距任务里的代价比误检大——误检你能一眼看出来,漏检是静默的,你以为目标不在视野里。

4.3 框内深度取值与三维坐标换算

这一段是整份脚本的核心。深度矩阵和图像是像素对齐的,同一个 (u, v) 索引过去就是该像素的深度。但直接取框中心那一个点的深度非常不可靠,一点噪声就能让读数跳几十厘米,所以要对整个框内的深度做统计。

def box_to_xyz(depth_np, box, intr, cfg): """由检测框和深度矩阵反投影出相机坐标系下的三维点""" x1, y1, x2, y2, cls, conf = box roi = depth_np[y1:y2, x1:x2] # 有效性过滤:去掉零值、无穷、以及超出量程的像素 valid = roi[np.isfinite(roi)] valid = valid[(valid > cfg["min_distance"]) & (valid < cfg["max_distance"])] if valid.size < cfg["min_valid_pixels"]: return None # 有效点太少,这个目标本次不给距离 # 用分位数代替中位数,遮挡场景下取低分位能压住背景干扰 z = float(np.percentile(valid, cfg["depth_percentile"])) # 用框中心的像素坐标反投影,也可以换成框内有效点的质心 u = (x1 + x2) / 2.0 v = (y1 + y2) / 2.0 X = (u - intr["cx"]) * z / intr["fx"] Y = (v - intr["cy"]) * z / intr["fy"] return X, Y, z, cls, conf

为什么用分位数而不是均值,这是血泪经验。均值对离群点毫无抵抗力,框边缘只要混进几行前景深度或者几个噪点,整个读数就被拽偏。中位数(50 分位)稳一些,但在目标被部分遮挡时,框内会同时包含目标本身和它背后的物体,中位数可能落在两者之间,报出一个谁都不属于的距离。这时候把分位数调到 20 甚至 10,取框内偏近的那部分,读数会明显更贴近被遮挡目标的真实距离。代价是当框内混入了更近的干扰物时,会反过来把读数拉近,所以这个参数没有万能值,得按场景定。

min_valid_pixels这个门槛是防止退化。一个远距离的小目标,框内可能只有三五个有效深度点,统计结果完全靠运气。低于门槛直接返回None,让上层决定是跳过还是沿用上一帧的值,比硬报一个错数强。

反投影公式本身是标准的针孔模型,fx、fy是焦距,cx、cy是主点。这里用的是框中心像素,如果你要的是目标的三维中心而不是画面上的框中心,应该改用有效深度点的质心坐标作为u、v,两者的差异在目标偏离画面中心时比较明显。另外脚本里也可以直接调相机的点云接口取三维值,省掉自己算,但那样拿到的单点值同样有噪声问题,还是得自己做统计。

4.4 主循环骨架与结果绘制

主循环把上面几段串起来,同时负责时序平滑和输出。绘制部分纯粹给人看,但字号、位置、单位这些细节直接影响你能不能一眼判断读数对不对。

import cv2 def run(zed, intr, model, cfg): img = sl.Mat() depth = sl.Mat() runtime = sl.RuntimeParameters() history = {} # 类别 -> 最近若干帧的距离,用于时序平滑 while True: if zed.grab(runtime) != sl.ERROR_CODE.SUCCESS: continue # 取左目图和深度图,两者属于同一帧 zed.retrieve_image(img, sl.VIEW.LEFT) zed.retrieve_measure(depth, sl.MEASURE.DEPTH) frame = cv2.cvtColor(img.get_data(), cv2.COLOR_BGRA2BGR) depth_np = depth.get_data() # float32,单位米,无效处为 nan for box in detect(model, frame, cfg): x1, y1, x2, y2, cls, conf = box res = box_to_xyz(depth_np, box, intr, cfg) if res is None: cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, "N/A", (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) continue X, Y, Z, cls, conf = res # 时序平滑:取最近 N 帧的中位数,压住单帧抖动 buf = history.setdefault(cls, []) buf.append(Z) if len(buf) > cfg["smooth_window"]: buf.pop(0) Zs = float(np.median(buf)) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f"Z={Zs:.2f}m XYZ=({X:.2f},{Y:.2f},{Z:.2f})", (x1, max(y1 - 6, 12)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imshow("zed-yolo-range", frame) if cv2.waitKey(1) & 0xFF == 27: break zed.close() cv2.destroyAllWindows()

history这个字典是实用的时序平滑,按类别缓存最近若干帧的距离,取中位数输出。单帧深度抖动在双目上很常见,加了这个之后读数会稳很多,代价是目标突然移动时响应慢几帧。smooth_window设 5 到 9 比较合适,设太大就变成拖影了。

绘制时把 X、Y 和 Z 分开显示很有必要。Z 是沿光轴的距离,X、Y 是横向和纵向偏移,三者一致才能说明反投影是对的。如果 Z 看着合理但 X 或 Y 大得离谱,八成是内参取错或者主点没减,对着画面中心的物体验一下就能确认——画面正中的目标 X 和 Y 应该接近零。

5. 踩坑排查:双目测距跑不通的五个高频原因

环境类和数据类的问题表现完全不同,分开看能省时间。环境类的问题通常一启动就炸,报错信息还算明确;数据类的问题不报错,只是数字不对,这种最费神。

5.1 环境与设备:装不上、连不上、掉帧

第一条,import pyzed失败,提示找不到模块。原因是只装了 Python 侧的依赖,没装相机 SDK,或者 SDK 装的版本跟当前 Python 解释器位数不匹配。解决办法是回到 SDK 安装包重装,装完用python -c "import pyzed.sl as sl"单独验证,不要指望在完整脚本里试出来。另外虚拟环境容易踩这个坑,SDK 装进的是系统解释器,你的 venv 里没有,要么在 venv 里做软链接,要么直接用系统解释器跑。

第二条,相机打开返回失败,或者设备列表是空的。原因是 USB 带宽不够或者线材质量差。双目相机同时传两路图像加深度计算,对带宽要求不低,插在 USB 集线器或者前面板上很容易失败。解决办法是直插主板后置口,换一根带屏蔽的短线,再用get_device_list()确认能枚举到。这一条报错信息很含糊,看起来像驱动问题,实际上九成是物理连接。

第三条,帧率远低于预期,画面一卡一卡。原因是深度模式开太高加上分辨率太大,GPU 吃满了。解决办法是先降到 720p、把深度模式从最高档降到中档,看帧率是否恢复。如果还是慢,用nvidia-smi看显存占用和 GPU 利用率,确认深度计算确实跑在 GPU 上而不是退回 CPU。这个降配动作我一开始不愿意做,总觉得分辨率低了精度差,实测下来 720p 在近距离场景的测距精度完全够用,帧率却能翻三倍。

5.2 数据与算法:框对了但距离不对

第四条,检测框位置准确,但读数是 0 或者nan。原因是深度矩阵在该区域全无效值,常见于目标表面是无纹理的白墙、玻璃、纯色纸箱,或者目标距离超出了量程。解决办法是先把目标换成有纹理的物体验证链路是否正常,确认正常之后再针对弱纹理场景处理——降低置信度门槛让框更稳、把深度分位数调高取更靠内的像素、或者干脆在该区域改用目标尺寸估算做兜底。这一步的关键是分清「链路坏了」还是「场景不适合」,两者的处理方式完全不同。

第五条,读数在真值附近大幅跳动,同一个静止目标一会儿报 1.2 米一会儿报 2.8 米。原因是框内混入了背景深度,或者深度图本身在相邻帧之间不稳定。先看框是不是把目标背后的地面也框进去了,把depth_percentile从 50 调到 20 试试;如果还跳,检查是不是弱光或者强反光,这两种情况会让双目匹配在相邻帧之间给出不同结果。最后的手段就是上一节讲的时序平滑,按类别缓存最近若干帧取中位数,能压住大部分抖动,但别指望它解决根本问题。

注意:排查顺序永远是先验证链路、再调参数。拿一个已知距离的物体放在固定位置,跑一百帧把距离值打出来看分布,比在真实场景里反复猜高效得多。

6. 二次开发:把单脚本测距改成能被别的程序调用的能力

zedceju.py作为验证脚本是合格的,但它把所有东西塞在一个while True里,相机对象、模型对象、绘制窗口全耦合在一起。真要拿去用,第一步是把这三样拆开:相机采集做成独立线程,推理和测距做成纯函数,绘制变成可选开关。拆完之后,同一个测距核心既能带窗口跑调试,也能无头跑在后台,被别的程序通过队列或者本地接口消费结果。

改造的时候有几处需要特别小心。相机对象不是线程安全的,多线程取帧必须加锁,或者干脆只让采集线程持有它,其他线程通过队列拿数据。模型推理也建议单独一个线程,因为推理耗时波动大,跟采集放在一起会拖累帧率。测距结果用一个定长的环形缓冲存最近若干帧,消费端按需读取,不要直接共享可变对象。

改造点原实现建议改法
相机采集主循环里同步grab独立线程 + 有界队列,队列满时丢最旧帧
检测推理主循环里同步调用独立线程,批大小为 1,允许跳帧
测距计算直接在循环里算抽成纯函数,输入图像框和深度矩阵,输出结构体
结果输出画在窗口上结构化字段 + 可选绘制开关
参数写在代码里全部外置到配置文件,支持热加载

验证改造是否成功,我一般用三个指标:一是无头模式下跑满 1000 帧不崩、内存不涨;二是把同一个静止目标的不同帧距离值收集起来,看标准差是否在可接受范围;三是断开相机再重连,程序能自己恢复而不是直接退出。第三个指标最容易被忽略,但现场部署时相机掉线是家常便饭,没有重连逻辑就得人工重启。

再往下走一步,可以把测距核心做成一个本地服务,输入一帧图像和一组框,返回每个框的三维坐标。这样上层的机械臂控制、路径规划、上位机界面都不用关心相机型号,换硬件只改底层。接口设计上我建议把「深度有效像素数」和「距离置信度」一起返回,消费端拿到低置信度的结果时可以选择不使用,而不是被一个看起来正常的数字骗过去。

说个自己的教训:最早做这类脚本的时候,我图省事把深度取值和绘制写在一起,结果每次想看数值就得开窗口,跑批量测试的时候窗口一多直接卡死。从那以后我每次写视觉脚本,都强制把「计算」和「呈现」分成两道路径,计算路径不依赖任何 GUI,呈现路径随时可以关掉。这个习惯后来在好几个项目里救过我。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询