简介:YOLOv5+单目测距(Python)实现方案面向计算机视觉入门到中阶开发者,也适合在自动驾驶、机器人导航、智能监控等方向做场景验证的工程人员,核心解决单摄像头条件下如何由二维图像推断目标距离的问题:先用 YOLOv5 完成实时目标检测,再结合深度估计网络把检测框映射为三维距离,省去激光雷达等额外硬件成本。压缩包共132个文件、约13.87MB,以34个 py 脚本(训练、推理与测距流程)、40个 yaml 配置(数据集与模型参数)、11个 yml 依赖环境、16个 pyc 编译文件为主,另含 pt 权重、Dockerfile 与 sh 启动脚本、ipynb 教程示例,目录层次分明,便于按模块定位查阅。已有3869人学习。读者可直接拿到可运行的检测与测距联合代码及预训练权重,对照数据增强、Mish 激活、自适应锚框等实现细节复现单目测距流程,并在此基础上替换数据集、调整阈值做二次开发。
1. 一个 USB 摄像头就能报出三维坐标:这套 YOLOv5 单目测距能处理什么
仓库货架横梁上装一颗几十块钱的普通 USB 摄像头,要回答的问题其实很朴素:画面里这个人、这台叉车,离镜头大概几米,偏左还是偏右。不加深度相机、不挂激光雷达,只用 YOLOv5 做检测、再用单目几何把检测框换算成三维测距结果,这就是这套 Python 工程的全部野心。它适合两类人:一类是手上已经有监控或行车记录视频、想加一层“距离告警”的开发者;另一类是刚学完目标检测、卡在“像素怎么变成米”这一步的工程师。反直觉的地方在于,单目测距的精度上限几乎不由检测网络决定,而由那几个相机内参和“目标站在地面上”这条假设决定——检测框画得再准,fy填错一位,距离就能差出两倍。
2. 工程骨架:YOLOv5 推理链路和测距模块是怎么拼起来的
拿到一个叫“YOLOv5+单目测距”的 Python 包,最容易犯的错是直接python xxx.py然后被一堆路径和参数报错劝退。先把骨架看清楚:检测部分是一套标准的 YOLOv5 前向推理,测距部分是独立于网络之外的一段几何计算,两者之间只靠一个东西耦合——检测框的像素坐标。理解这条边界,后面改代码会轻松很多。
2.1 目录结构与每个文件的职责
这类工程常见的组织方式是把“模型推理”和“几何计算”拆成两个文件,配置文件单独抽出来,避免把内参写死在代码里。我拆过的几个版本大同小异,大致是这个形态:
| 文件 / 目录 | 作用 | 跑之前要确认什么 |
|---|---|---|
detect_distance.py | 主入口,负责读图/读视频、推理、调用测距、绘制结果 | 默认参数值,尤其--source和--weights |
distance.py | 测距核心,一个类装下所有几何算法 | 里面的类别高度表是不是你要的类别 |
calibrate.py | 棋盘格标定,产出内参矩阵 | 棋盘格内角点数、格子实际边长 |
camera_config.yaml | 内参、相机安装高度、俯仰角、类别真实尺寸 | 分辨率要和推理时一致 |
models/ | YOLOv5 的网络定义与通用工具 | 有无common.py、experimental.py等依赖 |
weights/ | .pt权重文件 | 权重的类别名和你代码里的类别名对不对得上 |
utils/ | letterbox、NMS、绘图等辅助函数 | scale_boxes的坐标映射是否包含在内 |
data/、output/ | 输入素材与结果输出 | 输出目录不存在时程序会不会自己建 |
真正需要你动的通常只有三个:camera_config.yaml(填你自己相机和场景的参数)、weights/(换你自己的权重)、distance.py里的类别高度字典。其余文件在第一次跑通之前别碰,改了大概率是给自己挖坑。
2.2 一帧图像从进入到输出的完整链路
把数据流在纸上画一遍,出问题时才知道该在哪一环打日志。整条链路是这样的:
cv2.VideoCapture读一帧,得到(h0, w0)的 BGR 原图;- letterbox 等比缩放并补边到 640×640,记录缩放比
gain和填充量pad_x / pad_y; - 转 RGB、转 CHW、归一化,送进模型拿到
(1, 25200, 85)这样的原始输出; - NMS 过滤掉重叠框和低置信度框,留下
N×6的[x1, y1, x2, y2, conf, cls]; - 关键一步:把框的坐标从 640×640 的“网络坐标系”还原回原图坐标系;
- 用还原后的框底边、框高度,配合内参算出
Z(纵深)和X / Y(左右、上下); - 把类别、置信度、距离画回原图并写出视频。
第 5 步是这类工程里最容易出错的地方。很多“距离忽大忽小”的玄学问题,根子不在测距公式,而在于框的坐标还停留在补边后的图上,y2比真实值大了一截,除下来的距离自然对不上。血泪经验:只要坐标系没对齐,后面所有公式都是白算。
2.3 环境搭建与第一次跑通的命令
Python 版本建议 3.8~3.10,太新的版本偶尔会和某些 torch 轮子打架。我一般这么起:
# 建环境,装依赖 conda create -n mono3d python=3.9 -y conda activate mono3d pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python numpy pyyaml tqdm # 先跑一张图,确认检测链路本身是通的 python detect_distance.py \ --source data/images/test.jpg \ --weights weights/best.pt \ --conf-thres 0.4 \ --view-img--conf-thres建议第一次就给到 0.4 以上,低了会冒出一堆半截框,测出来的距离全是噪声。如果只想先看检测效果、暂时不关心距离,可以把camera_config.yaml里的enable_distance关掉,先验证模型本身没问题。
换成视频源只需把--source改成data/videos/test.mp4,或者直接填0用本机摄像头。用本机摄像头时注意一点:cv2.VideoCapture(0)取到的分辨率往往不是你以为的那个,可能默认 640×480,而你的内参是按 1920×1080 标定的,这一错就是成倍的偏差。
2.4 配置文件里的五个参数决定精度
测距模块的参数我习惯全放 yaml,改起来不用重读代码:
# camera_config.yaml camera_matrix: # 标定产出的 3x3 内参,注意分辨率要匹配 fx: 1068.4 fy: 1069.1 cx: 959.2 cy: 541.7 calib_resolution: [1920, 1080] # 标定时用的分辨率 mount_height: 1.85 # 相机光心离地高度,米 pitch_deg: 3.0 # 镜头下倾角度,正数表示朝下 real_heights: # 每个类别的真实物理高度,米 person: 1.70 forklift: 2.10 car: 1.55 default: 1.60 # 兜底,避免字典里没这个类就崩fx / fy是像素单位的焦距,不是毫米,别把标定报告里的毫米数直接抄进来。calib_resolution存在的意义是:当实际输入分辨率和标定分辨率不同时,程序能按比例把fx、cx一起缩放,这一步不做,换分辨率就全乱。mount_height和pitch_deg只在用地平线法时才用到,高度法可以不填,但填了能让日志更可读。real_heights里那个default是个后悔药——新加一个类别忘了登记,至少不会直接KeyError崩在半路。
3. 测距数学:从像素高度到米,三条路线的推导与选型
单目测距没有唯一解,只有“在你的场景里够用”的解。工程里常见的就三条路:相似三角形(像素高度法)、小孔成像 + 内参、地面平面假设。三套公式都很短,难的是知道什么时候该用哪套,以及误差会在哪里爆炸。
3.1 相似三角形法:最省事也最容易失效的一条路
核心假设只有一句:同一类目标的物理高度是固定的。物高H在距离Z处,经焦距f(像素单位)成像,像素高度h满足h = f × H / Z,反解就是:
Z = fy × H / h_px
其中h_px = y2 - y1,也就是检测框的高度。这条公式的好处是不依赖相机的安装姿态,相机歪着装、装在多高都无所谓,只要fy和H是准的。所以手持设备、临时架设的摄像头,我都优先用它。
它的软肋也很明确。第一,它要求目标“直立且完整可见”,人蹲下了、被货架挡掉半截腿,框高度变了,距离立刻跟着错。第二,h_px出现在分母上,误差是非线性的:h_px = 400时抖 2 像素,距离误差 0.5%;h_px = 40(约 20 米外)时抖 2 像素,误差直接 5%,再远就完全没法看。第三,不同类别的H必须实测,靠“大概 1.7 米”估出来的数,在 5 米内也许够用,10 米外就明显偏。
3.2 小孔成像 + 内参:fx 才是真正的尺度来源
把所有假设摊开,三维点到像素的投影关系是这样的:u = fx × X / Z + cx,v = fy × Y / Z + cy。把它反解,就得到从像素加深度回推三维坐标的完整式子:
Z = 由测距方法给出
X = (u - cx) × Z / fx
Y = (v - cy) × Z / fy
这里u一般取框的水平中心(x1+x2)/2,v取框底边y2(代表目标与地面的接触点)。X是横向偏移,单位米,正数在右;Y是相对光轴的高度偏移,配合mount_height就能还原成离地高度。这套反投影是所有单目三维测距的公共出口——不管你前面用哪种方法拿到Z,最后都要过一遍这两个式子才能得到(X, Y, Z)。
要强调的是fx和fy的地位。它们不是“校准用的参数”,而是尺度的定义本身。内参错 10%,所有距离整体错 10%,而且是系统性的、不会被滤波抹掉的那种错。
3.3 地面平面假设:车载与固定机位的主力方案
如果目标是站在地面上的(人、车、叉车),还有一个更强的约束可以用:它们与地面的接触点在一条平面上。相机光心离地Hc,光轴水平时,地面上距离Z处的点在图像中的行号是v = cy + fy × Hc / Z,反解:
Z = fy × Hc / (y2 - cy)
这条式子的性质很特别:y2越靠近cy(地平线),分母越小,Z越趋向无穷大。也就是说远处目标的距离精度极差,近处反而很准,这跟高度法正好相反。实际工程里我会给分母设个下限,比如y2 - cy < 3像素时直接把结果标成inf或“超量程”,而不是输出一个 200 米的荒谬数字。
镜头有俯仰角θ时需要修正,分母变成(y2 - cy) - fy × tan(θ)。这个θ千万别目测填,我一般会让相机对着已知距离的标定点反算:摆一个 5 米外的物体,看它的y2落在哪,倒推θ,比拿量角器量准得多。
3.4 三套方法的误差来源对比与选型
| 方法 | 必需输入 | 适用场景 | 主要误差源 | 近距 / 远距表现 |
|---|---|---|---|---|
| 像素高度法 | fy、类别真实高度H | 手持、临时架设、姿态不定 | 框高度抖动、H不准、目标被遮挡 | 近距好,远距快速恶化 |
| 地平线法 | fy、cy、安装高度、俯仰角 | 车载、固定机位、地面目标 | 俯仰角误差、接触点定位、非地面目标 | 近距极准,地平线附近失效 |
| 逆透视 IPM | 四点对应 + 单应矩阵 | 纯平面区域、固定相机 | 平面不平、单应标定误差 | 全距段均匀,但受平面假设约束 |
选型上我的习惯是:能保证目标踩在地面上、相机固定不动,就走地平线法,精度最好;相机可能被挪动、或者目标不一定在地面(货架上的箱子),走高度法;如果两种都要,就两个都算,在合理区间内取加权平均,差距过大时降级到可信度更高的那一路。逆透视适合做可视化俯视图,直接拿来报距离反而麻烦,因为单应矩阵对四点标定的精度要求高。
4. 核心代码拆解:标定、检测、测距、绘制四段实现
公式看懂了不等于能跑。下面四段代码是这套工程里真正干活的部分,我把每段的参数含义和容易改错的地方都标出来。
4.1 相机标定:把 fx、fy、cx、cy 拿到手
标定是一次性的活,但值得认真做,因为它是所有距离的基准。用棋盘格,拍 20 张以上不同角度的图,代码很短:
import cv2 import numpy as np import glob pattern = (9, 6) # 棋盘格内角点数(横, 纵),不是格子数 square_size = 0.025 # 每格实际边长,单位米,务必拿尺子量 # 构造棋盘格在自身坐标系下的三维点 objp = np.zeros((pattern[0] * pattern[1], 3), np.float32) objp[:, :2] = np.mgrid[0:pattern[0], 0:pattern[1]].T.reshape(-1, 2) * square_size obj_points, img_points = [], [] for path in sorted(glob.glob("calib_images/*.jpg")): gray = cv2.cvtColor(cv2.imread(path), cv2.COLOR_BGR2GRAY) ok, corners = cv2.findChessboardCorners(gray, pattern, None) if not ok: print("角点没找到,跳过:", path) continue # 亚像素精化,能把重投影误差压下去一截 corners = cv2.cornerSubPix( gray, corners, (11, 11), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) obj_points.append(objp) img_points.append(corners) h, w = gray.shape[:2] # 用的是最后一张图的尺寸,务必全部同分辨率 ret, K, dist, _, _ = cv2.calibrateCamera(obj_points, img_points, (w, h), None, None) print(f"重投影误差: {ret:.3f} px") print("内参矩阵:\n", K)pattern填的是内角点数,9×6 的格子实际是 10×7 个方格,填错了角点一个都找不到。square_size用米,输出的K里fx / fy就是像素单位,跟公式对得上;如果填厘米,所有距离就整体差 100 倍。ret是平均重投影误差,0.5 像素以内算好,超过 1.5 像素建议重拍——多角度、多远近、铺满画面边缘,别全在一个距离上拍。
K是个 3×3 矩阵,K[0][0]=fx、K[1][1]=fy、K[0][2]=cx、K[1][2]=cy。填进 yaml 的时候别填反了,cx在K[0][2]、cy在K[1][2],行和列看着很像。
4.2 检测结果还原:letterbox 之后坐标必须映射回原图
YOLOv5 为了保持长宽比会做 letterbox,补边之后的 640×640 和原图之间差着一个缩放比和两个偏移量。这段函数就是把这笔账还回去:
def scale_boxes(img1_shape, boxes, img0_shape): """img1_shape: 网络输入 (h, w);boxes: Nx4 的 xyxy;img0_shape: 原图 (h, w)""" gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) pad_x = (img1_shape[1] - img0_shape[1] * gain) / 2 pad_y = (img1_shape[0] - img0_shape[0] * gain) / 2 boxes[:, [0, 2]] -= pad_x # 先减填充 boxes[:, [1, 3]] -= pad_y boxes[:, :4] /= gain # 再除缩放比 # 裁到图像范围内,防止负数坐标把后面的绘制搞崩 boxes[:, [0, 2]] = boxes[:, [0, 2]].clip(0, img0_shape[1]) boxes[:, [1, 3]] = boxes[:, [1, 3]].clip(0, img0_shape[0]) return boxesgain取长宽两个方向缩放比的最小值,因为 letterbox 是等比缩放后居中补边。pad_x / pad_y是补在两侧的边宽,减掉才是原图坐标。顺序不能反:必须先减 padding 再除 gain,反过来算出来的框会整体偏移。
这一步的验证方法很简单:把还原后的框画在原图上,肉眼看框是不是严丝合缝地套住目标。套不准,说明img1_shape传错了(传成原图尺寸,或者传成(w, h)顺序颠倒)。
4.3 测距类:用单个类封装三种算法
把几何算法收进一个类,调用方只管传框和类别名,这是我觉得最省心的写法:
import math class MonoDistance: def __init__(self, cfg): K = cfg["camera_matrix"] self.fx, self.fy = K["fx"], K["fy"] self.cx, self.cy = K["cx"], K["cy"] self.real_heights = cfg["real_heights"] self.mount_height = cfg["mount_height"] self.pitch = math.radians(cfg["pitch_deg"]) def by_height(self, cls_name, y1, y2): """像素高度法:不依赖相机姿态,手持/临时架设首选""" h_px = max(y2 - y1, 1e-6) # 防除零 H = self.real_heights.get(cls_name, self.real_heights["default"]) return self.fy * H / h_px def by_ground(self, y2): """地平线法:目标必须踩在地面上,远距会趋向无穷""" dy = (y2 - self.cy) - self.fy * math.tan(self.pitch) if dy < 3: # 地平线附近直接判超量程 return float("inf") return self.mount_height * self.fy / dy def to_xyz(self, box, z): """像素 + 深度 -> 三维坐标,单位米""" x1, y1, x2, y2 = box u, v = (x1 + x2) / 2.0, y2 # 底边中点代表接地点 X = (u - self.cx) * z / self.fx Y = (v - self.cy) * z / self.fy return X, Y, zby_height里的max(y2 - y1, 1e-6)是防除零的保险,检测框退化成一个点时不会炸。real_heights用.get()加兜底,新类别没登记也不会抛异常,只是精度差一点,比程序中断好。
by_ground里的dy < 3是我踩过坑之后加的:不设这个阈值,视频里远处会出现几十上百米的读数乱跳,看着像算法坏了,其实只是数学上进了一个奇点。pitch用弧度,yaml 里填的是角度,构造时统一转换,别在每处调用里各转一次,转漏一次就全错。
to_xyz的v取y2而不是框中心,因为要算“接地点”的地面位置。如果你的场景里目标是悬空的(比如挂在墙上的箱子),这里得改成框中心,同时Y的含义从“离地高度”变成“相对光轴高度”,两种语义别混着用。
4.4 融合推理与画面叠加:主循环长什么样
把上面几块串起来,主循环其实很短:
import cv2 import numpy as np import torch import yaml from distance import MonoDistance cfg = yaml.safe_load(open("camera_config.yaml", encoding="utf-8")) dist = MonoDistance(cfg) model = torch.load("weights/best.pt", map_location="cpu")["model"].float().eval() names = model.names cap = cv2.VideoCapture("data/videos/test.mp4") while True: ok, frame = cap.read() if not ok: break h0, w0 = frame.shape[:2] # 1. letterbox 到 640 img = letterbox(frame, 640)[0] tensor = torch.from_numpy( np.ascontiguousarray(img[:, :, ::-1].transpose(2, 0, 1)) ).float().div(255).unsqueeze(0) # 2. 推理 + NMS with torch.no_grad(): pred = non_max_suppression(model(tensor)[0], 0.4, 0.45)[0] if pred is None: cv2.imshow("mono", frame) continue # 3. 坐标还原 -> 测距 -> 绘制 boxes = scale_boxes((640, 640), pred[:, :4].clone(), (h0, w0)) for box, conf, cls in zip(boxes.tolist(), pred[:, 4], pred[:, 5]): label = names[int(cls)] z = dist.by_height(label, box[1], box[3]) if not np.isfinite(z) or z <= 0: continue X, Y, Z = dist.to_xyz(box, z) cv2.rectangle(frame, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 0), 2) cv2.putText(frame, f"{label} Z={Z:.2f}m X={X:+.2f}m", (int(box[0]), int(box[1]) - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 255, 0), 2) cv2.imshow("mono", frame) if cv2.waitKey(1) & 0xFF == 27: # ESC 退出 break几个参数值得盯住:non_max_suppression的第二个 0.45 是 NMS 的 IoU 阈值,目标密集(比如货架上一排箱子)时调高到 0.5~0.6,否则会误删相邻目标;0.4是置信度阈值,跟命令行参数保持一致,改一处忘了另一处是常见的错。
pred[:, :4].clone()里的clone()不能省,scale_boxes是原地修改,直接传切片会把原始预测也改掉,如果你同一帧要算两种测距方法,第二次算的时候框已经被改过了。
5. 避坑与排查:单目测距上线前必查的 5 个细节
测距代码跑起来容易,跑准很难。下面五条都是我在实际项目里遇到过的,按“症状—原因—解决”记下来,遇到问题挨个对一遍,能省不少时间。
5.1 五个真实翻车现场
现象一:距离在 0.3 米和 30 米之间来回跳。原因通常是框高度抖动加上远处误差放大。h_px在分母上,检测框每帧上下浮动 3~5 像素,在近处几乎没影响,到 15 米开外就变成几米的跳变。解决办法分两层:先卡置信度阈值到 0.5 以上,把半截框挤掉;再对同一个 track 的Z做滑动平均或一维卡尔曼,我一般用 α-β 滤波,三行代码就够,比引一个完整的跟踪框架轻量得多。另外给Z加个物理上下限(比如 0.5~50 米),超出的直接丢弃,别让它污染滤波器的状态。
现象二:换了个输入分辨率,所有距离整体差一倍。这是内参没有跟着分辨率走。标定在 1920×1080 做的,实际推理用了 640×640 的输入图,fx还是 1068,那算出来的距离自然全错。解决方式是在配置文件里记下calib_resolution,程序启动时按resize_x = 实际宽 / 标定宽把fx和cx同步缩放:fx' = fx × resize_x,cx' = cx × resize_x,fy和cy按纵轴比例缩。注意是分别按宽、高缩放,输入长宽比和标定不一致时两个比例是不等的。
现象三:只有某一类目标距离离谱,其他都对。十有八九是real_heights里那一项填错了。我见过把车的“宽度”当高度填进去的,结果所有车都算成 1.5 倍距离。排查方法很土但有效:拿卷尺量,把目标摆在 3 米、5 米、8 米处,打印h_px和Z,反推H = Z × h_px / fy,看反推出来的数和你填的差多少。差得多的那一项就是要改的。
现象四:人站在台阶上、货箱放在桌上,距离明显偏大。这是地面假设失效。地平线法假定所有目标接地点在同一平面,目标抬高 1 米,算出来的距离就会虚高,抬高越多错得越离谱。处理办法是分场景:地面目标走地平线法,非地面目标走高度法,判断依据可以用类别,也可以用框底边y2和当前场景的地平线位置的相对关系。别指望一个公式吃下所有情况。
现象五:视频里的距离总比画面慢半拍,人已经走过去了才报 3 米。这是结果和帧对不上,常见于加了异步推理或者多线程读帧的改法。检测在一个线程、绘制在主线程,中间没有帧号绑定,结果就贴到了错误的帧上。解决很简单,给每帧带一个自增的frame_id或者时间戳,结果队列里回传frame_id,绘制时只接受匹配当前帧的结果,对不上就跳过不画。
5.2 一套能复用的自检打印
上面这些问题,凭肉眼看看画面是判断不出来的,需要在关键位置打日志。我习惯在测距调用处插一段诊断输出,出问题时打开、平时关掉:
DEBUG = True def debug_log(frame_id, label, box, dist, z): if not DEBUG: return h_px = box[3] - box[1] horizon = dist.cy + dist.fy * math.tan(dist.pitch) print(f"[{frame_id}] cls={label:<10} h_px={h_px:6.1f} " f"y2={box[3]:7.1f} 地平线={horizon:7.1f} " f"dy={box[3] - horizon:7.1f} Z={z:6.2f}m")重点关注三个数:h_px是不是稳定(抖动大说明检测不稳)、y2和地平线的距离dy(小于 5 像素的目标本来就不该信)、Z的变化曲线(突变说明换类别或者框跳了)。日志里带上frame_id,出问题时能直接跳到那一帧复现,比从头看视频快得多。
6. 二次开发:从“能显示距离”改到“能报警、能平滑、能对外发数据”
跑通之后想做产品化,改动集中在三处。第一处是把类别高度表外置成 csv 或者从配置中心拉,同时加上距离分级:Z < 3标红报警、3~8标黄预警、> 8保持绿色,逻辑写在绘制之前,别塞进测距类里。第二处是平滑,前面提的 α-β 滤波足够用,α取 0.6、β取 0.05 是我试出来的经验值,α大了跟随快但抖,小了平滑但滞后,具体要按你的帧率调。第三处是出口,把(frame_id, cls, X, Y, Z, conf)打包成 JSON 往 MQTT 或者本地 socket 推,主循环里只做put_nowait,别在绘制线程里等网络。
def alpha_beta(prev, z, alpha=0.6, beta=0.05, dt=1/30): """一维平滑,prev=(位置, 速度),track 断开时要重置""" p, v = prev pred = p + v * dt residual = z - pred return (pred + alpha * residual, v + beta * residual / dt)验证方式我建议老老实实做一张实测表:把目标摆在 1、2、3、5、8、12 米,每档记 10 帧取平均,算出绝对误差和相对误差。我的经验是 5 米内高度法能做到 ±0.2 米,超过 10 米就基本只能定性判断“远还是近”,这时候别再纠结小数点后两位,改成区间告警反而实用。如果是车载场景改用地平线法,近距离精度会好很多,但要把地面平整度和俯仰角标定做扎实。
踩过最贵的一次坑,是在一个现场把标定图的尺寸和实际推流尺寸搞混了,整套系统报了三天“差不多但不对”的距离,我一度怀疑是模型权重的问题,重训了两轮才发现配置文件里那一个数字。从那以后我每次上新现场,都会先拿卷尺摆一个 5 米的参照物,跑一帧把h_px、y2、Z三个数打出来核对一遍,对得上再往下做。希望帮到你。
本文还有配套的精品资源,点击获取