news 2026/10/11 19:29:28

YOLOv5+HRnet人体姿态估计实战:从环境配置到实时骨骼绘制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5+HRnet人体姿态估计实战:从环境配置到实时骨骼绘制

简介:面向需要快速落地YOLOv5姿态估计项目的开发者,这份完整工程文件包整合了YOLOv5目标检测与HRnet/SimDR关键点检测流程,支持对图片、视频及摄像头画面实时输出人体骨骼关键点。压缩包共2000个文件、841.53MB,以Python脚本为主,同时包含C/C++底层扩展文件、文本配置与Markdown说明文档,便于直接复用完整工程与权重配置。目前已有1749人学习下载,特别适合不愿从头配置参数和权重的初学者。工程内容覆盖从PyCharm克隆GitHub项目、搭建环境,到添加权重文件、获取图片边界框、加入SPPF模块,再到调整YAML文件、获取并绘制骨骼关键点的完整流程,并提供照片、视频及实时演示结果;同时整理了路径设置、Matplotlib后端切换、上采样属性异常、中文编码报错等常见问题的分析思路,使用者只需将路径修改为自己的即可运行。

1. 姿态估计项目:YOLOv5 检测人体边界框,HRnet 定位关键点

做人体姿态估计,最直接的落地方式就是把「找人在哪」和「找关节在哪」拆成两段:先用 YOLOv5 把画面里的人框出来,再用 HRnet 在框内回归出 17 个关键点坐标。这套方案不需要自己从头训练模型,YOLOv5 的检测权重和 HRnet 的预训练权重都是公开的,工程上的核心工作在于把两个模型的输入输出串起来,并处理好摄像头实时推理时的帧同步问题。从实际效果看,单人场景下关键点定位精度足够用于健身动作计数、安防姿态告警这类任务。这篇笔记就围绕我实际拆过的原工程文件,把环境配置、权重放置、yaml 修改、骨骼绘制和报错排查整个链路复现一遍,适合刚接触姿态估计、想直接跑通完整流程的开发者。

2. 工程环境与项目准备:克隆仓库、配环境、放权重的先后顺序

2.1 从 GitHub 克隆 YOLOv5 仓库并锁定版本

姿态估计工程里,YOLOv5 部分用到的文件是yolov5-master目录下的完整仓库,包括detect.py、models/、utils/等模块。HRnet 部分则依赖lib/目录下的网络结构和预测逻辑。最省事的做法是在 Pycharm 里直接用git clone把官方仓库拉下来,然后在项目根目录下补齐 HRnet 的源码目录。

git clone https://github.com/ultralytics/yolov5.git cd yolov5 git checkout v5.0

这里锁定 v5.0 版本的原因很实际:HRnet 的推理代码里调用的是models/yolo.py中的Detect层和common.py中的SPPF模块,v5.0 的接口相对稳定,网上大部分姿态估计改造案例也都是基于这个版本做的。如果你直接拉最新 master 分支,后续修改yaml时层定义格式可能会有出入。

克隆完成后,下一步是安装依赖。我的做法是先创建一个干净的虚拟环境,再安装requirements.txt,避免和系统里已有的 PyTorch 版本冲突。

python -m venv pose_env source pose_env/bin/activate # Windows 下用 pose_env\Scripts\activate pip install -r requirements.txt pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

参数说明:requirements.txt里包含 numpy、opencv-python、matplotlib、pandas 等基础库,但里面锁定的 torch 版本可能不是最新的,所以我单独用--index-url指定 CUDA 11.8 的 PyTorch 安装源,保证 GPU 推理可用。如果只有 CPU 环境,把--index-url那行去掉,直接pip install torch torchvision装 CPU 版即可。

2.2 权重文件放置路径:YOLOv5 权重与 HRnet 权重不能放错目录

整个工程跑通的前提是两类权重文件各就各位。YOLOv5 的检测权重放在weights/目录下,文件名通常是yolov5s.pt,也可以换成yolov5m.pt或yolov5l.pt换取更高精度。HRnet 的权重则要放在hrnet/目录下,工程里用的是hrnet_w32.pth这个预训练模型,输入分辨率是 192x192。

这里有个容易踩坑的细节:YOLOv5 的权重是 PyTorch 的.pt格式,内部包含模型结构、权重参数和训练配置;HRnet 的.pth文件只包含状态字典,加载时必须配合lib/models/pose_hrnet.py中定义的网络结构才能完成实例化。很多同学把两种权重混用,结果在加载时报unexpected key in state_dict,本质就是结构不匹配。

# 加载 YOLOv5 权重 model = torch.load('weights/yolov5s.pt', map_location=device) model = model['model'].float() # 加载 HRnet 权重 from lib.models.pose_hrnet import get_pose_net hrnet_model = get_pose_net(cfg, is_train=False) hrnet_model.load_state_dict(torch.load('hrnet/hrnet_w32.pth', map_location=device))

逻辑说明:YOLOv5 的权重加载方式比较特殊,torch.load出来的对象是个字典,里面model键对应的才是完整的模型实例,所以需要取出来再转成 float;HRnet 则必须先通过get_pose_net构建出网络图,再用load_state_dict把权重灌进去。两者的加载逻辑完全不同,不能套用同一个模板。

2.3 环境验证:先跑通官方 detect.py 再接入 HRnet

不要一上来就改代码。先把 YOLOv5 官方自带的detect.py跑一遍,确认环境没问题,再往工程里接 HRnet。

python detect.py --weights weights/yolov5s.pt --source data/images/bus.jpg

这条命令成功后,会在runs/detect/下生成标注了边界框的 bus.jpg 输出图。如果这一步就报错,优先检查 PyTorch 版本和 CUDA 是否匹配。我遇到过最常见的情况是torch.cuda.is_available()返回 False,但 nvidia-smi 明明能看到 GPU,这种问题多半是 PyTorch 装成了 CPU 版,重装对应 CUDA 版本的 torch 即可。

验证通过后,再把 HRnet 的推理逻辑写进检测流程里。整体思路是:YOLOv5 输出每个人的边界框坐标,然后对每个框做 crop,缩放到 192x192 输入 HRnet,HRnet 输出 17 个关节点的热图,最后通过 argmax 从热图中还原出原始图像坐标系下的关键点坐标。

3. 目标检测部分:添加 SPPF 模块,抽取边界框坐标

3.1 修改 yaml 文件把 SPP 换成 SPPF

YOLOv5 v5.0 的默认配置里,骨干网络最后一层用的是 SPP 模块,也就是空间金字塔池化。如果 HRnet 工程文件里的models/yolov5s.yaml写的是 SPP,需要改成 SPPF,也就是快速空间金字塔池化。两者的功能类似,但 SPPF 把三个最大池化串行连接,计算量更小,在 v6.0 之后的官方版本里已经是默认配置。

backbone: # [from, number, module, args] [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, C3, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 6, C3, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, C3, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 1, SPPF, [1024, 5]], # 8 [-1, 1, C3, [1024, False]], # 9 ]

参数说明:SPPF [1024, 5]中的1024是输出通道数,5是池化核尺寸。如果你用的权重文件是yolov5s.pt,那么 yaml 文件里的通道数必须匹配;换成yolov5m.pt或yolov5l.pt时,这里也要同步改成对应通道数。改完 yaml 后,还需要同步修改models/yolo.py中解析模型结构时对模块名的匹配逻辑,确保SPPF能被正确识别。

3.2 从 YOLOv5 输出中提取边界框坐标

YOLOv5 的推理输出是一个 1x25200x85 的张量,其中 25200 是三个尺度下预测框的总数,85 是[x, y, w, h, objectness, class_scores...]的组合。直接对这个张量做非极大值抑制很麻烦,工程上通常是调用model(x)[0]之后用官方工具函数处理。

from utils.general import non_max_suppression results = model(img)[0] # 原始输出 pred = non_max_suppression(results, conf_thres=0.4, iou_thres=0.5, classes=[0]) for i, det in enumerate(pred): if det is not None and len(det): # det 每行: x1, y1, x2, y2, confidence, class for *xyxy, conf, cls in reversed(det): x1, y1, x2, y2 = [int(coord) for coord in xyxy] print(f'人体边界框: ({x1}, {y1}) -> ({x2}, {y2}), 置信度: {conf:.2f}')

逻辑说明:non_max_suppression里的classes=[0]表示只保留类别 0,也就是 person 类别,这样就不会把狗、猫、汽车等目标框也送进 HRnet,既省计算量又避免误检。conf_thres控制置信度阈值,静态图片可以设 0.3 以下多召回一些目标,视频流建议 0.4 以上,减少漏检导致的抖动。返回的坐标是原图尺寸下的像素坐标,后续 crop 时直接用这四个值切片就行。

3.3 边界框裁剪与缩放:保持宽高比填充

拿到边界框后,直接把图像切片送进 HRnet 会遇到两个问题:一是框的宽高比不固定,直接 resize 到 192x192 会导致人体拉伸变形,影响关键点定位精度;二是框太小或太大时,边缘信息丢失。我的处理方式是先按宽高比扩展成正方形区域,再统一缩放。

def crop_and_resize(img, box, target_size=192): x1, y1, x2, y2 = box w, h = x2 - x1, y2 - y1 cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 side = max(w, h) * 1.2 # 扩展 20% 边距 # 裁剪正方形区域 nx1 = max(0, int(cx - side / 2)) ny1 = max(0, int(cy - side / 2)) nx2 = min(img.shape[1], int(cx + side / 2)) ny2 = min(img.shape[0], int(cy + side / 2)) crop = img[ny1:ny2, nx1:nx2] # 保持宽高比的缩放 crop_resized = cv2.resize(crop, (target_size, target_size), interpolation=cv2.INTER_LINEAR) return crop_resized, (nx1, ny1, side)

参数说明:side = max(w, h) * 1.2中的 1.2 是扩展系数,把人的头部和脚部边缘留出来,避免关键点正好卡在裁剪边界上。INTER_LINEAR线性插值适合缩放后需要保留平滑轮廓的场景,如果对速度敏感可以换INTER_NEAREST。返回值里带上(nx1, ny1, side)是因为后面把关键点坐标映射回原图时,需要知道裁剪区域在原图中的偏移量。

4. 姿态估计部分:HRnet 关键点回归与骨骼绘制流程

4.1 修改 hrnet.yaml 配置文件:数据路径与输出通道

HRnet 的推理代码里有一个独立的experiments/目录,存放网络结构和训练参数的配置。工程文件里需要重点关注的是hrnet_w32.yaml中MODEL部分的NUM_JOINTS字段,这个值决定了输出关键点的数量。COCO 数据集预训练权重对应的是 17 个关键点,如果你换成 MPII 的权重,这里就要改成 16。

MODEL: NAME: 'pose_hrnet' PRETRAINED: 'hrnet/hrnet_w32.pth' NUM_JOINTS: 17 EXTRA: FINAL_CONV_KERNEL: 1 STAGE2: NUM_CHANNELS: [32, 64] BLOCK: 'BASIC' NUM_MODULES: 1 STAGE3: NUM_CHANNELS: [32, 64, 128] BLOCK: 'BASIC' NUM_MODULES: 4 STAGE4: NUM_CHANNELS: [32, 64, 128, 256] BLOCK: 'BASIC' NUM_MODULES: 3

参数说明:NUM_JOINTS是核心参数,改动后输出层的通道数会跟着变,所以权重文件必须与之一一对应。STAGE2到STAGE4定义的是 HRnet 不同分辨率分支的通道数和模块数,如果不是自己重新训练,这些字段基本不需要动。PRETRAINED路径必须与 2.2 节中torch.load的路径保持一致,否则加载时会去读一个不存在的文件直接报错。

4.2 关键点热图解码:从 17 张热图回归坐标

HRnet 的原始输出不是坐标值,而是 17 张 48x48 的热图,每张热图对应一个关键点的置信度分布。把热图转成坐标的标准做法是取每个通道最大值所在的像素位置,再按热图与原图的缩放比例映射回去。

import torch.nn.functional as F def decode_heatmap(heatmap, crop_offset, crop_side, original_shape): # heatmap: [17, 48, 48] B, C, H, W = heatmap.shape heatmap = F.interpolate(heatmap, size=(original_shape[0], original_shape[1]), mode='bilinear', align_corners=False) keypoints = [] for i in range(C): hmap = heatmap[0, i] max_val, max_idx = torch.max(hmap.view(-1), 0) y, x = divmod(max_idx.item(), W) # 映射回原图坐标 orig_x = crop_offset[0] + (x / W) * crop_side orig_y = crop_offset[1] + (y / H) * crop_side keypoints.append((orig_x, orig_y, max_val.item())) return keypoints

逻辑说明:这里先用interpolate把热图上采样到原图尺寸,再做 argmax,好处是坐标精度比直接在 48x48 热图上取值再缩放更高。divmod(max_idx, W)把一维索引还原成二维坐标,max_val就是这个关键点的置信度,可以用来过滤低置信度的预测结果。crop_offset和crop_side来自 3.3 节的返回值,缺少这个偏移量的话坐标会整体跑偏。

4.3 骨骼绘制:按 COCO 骨架连接关系连线

拿到 17 个关键点坐标后,按 COCO 数据集的骨架定义连线就能画出骨骼。COCO 的 17 个关键点顺序是:鼻子、左眼、右眼、左耳、右耳、左肩、右肩、左肘、右肘、左腕、右腕、左髋、右髋、左膝、右膝、左踝、右踝。

import cv2 COCO_BONES = [ (0, 1), (0, 2), (1, 3), (2, 4), # 头部 (5, 6), (5, 7), (7, 9), (6, 8), (8, 10), # 手臂 (11, 12), (5, 11), (6, 12), # 躯干 (11, 13), (13, 15), (12, 14), (14, 16) # 腿 ] def draw_skeleton(img, keypoints, threshold=0.3): for idx, (x, y, conf) in enumerate(keypoints): if conf > threshold: cv2.circle(img, (int(x), int(y)), 3, (0, 255, 0), -1) for (start, end) in COCO_BONES: x1, y1, c1 = keypoints[start] x2, y2, c2 = keypoints[end] if c1 > threshold and c2 > threshold: cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2) return img

参数说明:threshold=0.3是置信度阈值,低于这个值的关键点不绘制也不连线,防止遮挡或模糊导致的关键点跳动污染画面。cv2.circle的-1参数表示填充整个圆。这套绘制逻辑可以直接套用到视频帧处理中,每帧调用一次即可。

5. 避坑指南:路径、编码、PyTorch 兼容性的五个典型问题

5.1 路径问题:相对路径在 Pycharm 和命令行下表现不一致

现象:同样的代码,在 Pycharm 里运行正常,在终端用 python 直接跑就报FileNotFoundError,提示找不到hrnet/hrnet_w32.pth。

原因:Pycharm 默认把项目根目录设为工作目录,而命令行执行时工作目录是当前终端所在路径。工程代码里用的是相对路径,一旦工作目录切换就失效。

解决:所有涉及权重和配置文件的路径,统一在代码入口处用os.path.dirname(os.path.abspath(__file__))拼接绝对路径。例如:

import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) WEIGHT_PATH = os.path.join(BASE_DIR, 'hrnet/hrnet_w32.pth')

从那以后我在每个工程入口都强制加一段路径锚定逻辑,避免在不同环境下反复踩同一个坑。

5.2 YOLOv5 训练警告:functional.py 报grid_sampler相关错误

现象:训练或推理时终端打出大段警告,定位到torch.nn.functional中的grid_sampler或upsample相关代码,但不影响结果输出。

原因:新版本 PyTorch(1.10+)修改了部分算子实现,YOLOv5 旧代码里的调用方式产生兼容性告警。这个通常不影响推理结果,但如果告警伴随报错中断,就要处理。

解决:把models/yolo.py中Detect层的forward函数里的grid计算方式替换为当前 PyTorch 推荐的写法。具体做法是找到yolo.py底部make_grid的调用位置,将其中的torch.meshgrid加上indexing='ij'参数。

5.3 matplotlib 后端报错:matplotlib.use(Agg)的适用场景

现象:在无显示环境的服务器上运行脚本,调用 matplotlib 绘图时直接崩溃,报错显示无法连接到 display。

原因:默认 matplotlib 后端是TkAgg,需要图形界面支持,服务器上不存在 X server 就必然报错。

解决:代码开头加上import matplotlib; matplotlib.use('Agg')切换到非交互后端。注意use('Agg')必须放在import matplotlib.pyplot之前,否则不生效。这个模式适合把结果图保存到文件,但不支持弹窗预览。

5.4 AttributeError:Upsampleobject has no attributerecompute_scale_factor

现象:运行姿态估计推理时,PyTorch 报AttributeError: 'Upsample' object has no attribute 'recompute_scale_factor',堆栈指向torch.nn.modules.upsampling。

原因:工程里 HRnet 的_upsample逻辑中显式调用了recompute_scale_factor,这个属性在 PyTorch 1.11 之后被移除了。

解决:找到报错文件中的F.interpolate或nn.Upsample调用,删掉recompute_scale_factor=True参数,或干脆用F.interpolate(input, size=(H, W), mode='bilinear', align_corners=False)替代。重装旧版 PyTorch 也能解决,但没必要为这一处降低整个环境版本。

5.5gbkcodec can't decode byte:Windows 下读取文件编码冲突

现象:在 Windows 上读取 YOLOv5 的 yaml 配置文件或数据集的 txt 标注文件时,报UnicodeDecodeError: 'gbk' codec can't decode byte。

原因:yaml 或 txt 文件里含中文注释,Windows 默认用 GBK 编码打开,而文件保存的是 UTF-8。

解决:在读取文件的open()调用中显式指定编码:

with open(yaml_path, 'r', encoding='utf-8') as f: cfg = yaml.safe_load(f)

我之前在 Windows 上跑数据集标注时被这个错误卡了半小时,后来养成了习惯:所有读取 yaml、txt、json 的地方,一律显式传encoding='utf-8'。

6. 摄像头实时推理的帧率优化:跳过不必要的检测分支

把整套流程跑通后,接下来要解决的是实时性问题。摄像头采集的视频流如果每一帧都完整走 YOLOv5 + HRnet 全流程,在普通 GPU 上帧率大概只有 10~15 FPS,体感有明显延迟。我常用的优化手段是检测帧率降频、关键点插帧。

import time cap = cv2.VideoCapture(0) frame_skip = 3 # 每 3 帧做一次完整检测 frame_count = 0 last_keypoints = [] while True: ret, frame = cap.read() if not ret: break frame_count += 1 if frame_count % frame_skip == 0: results = model(frame)[0] pred = non_max_suppression(results, conf_thres=0.4, iou_thres=0.5, classes=[0]) last_keypoints = [] for det in pred[0]: box = det[:4].int().tolist() crop, offset_side = crop_and_resize(frame, box, 192) heatmap = hrnet_model(crop)[0] # [17, 48, 48] keypoints = decode_heatmap(heatmap, offset_side) last_keypoints.append((box, keypoints)) # 中间帧用上一帧的关键点绘制,保证显示连贯 display = frame.copy() for box, keypoints in last_keypoints: draw_skeleton(display, keypoints) cv2.imshow('Pose Estimation', display) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

参数说明:frame_skip=3意味着检测线程每 3 帧跑一次完整推理,中间两帧沿用最近一次的关键点结果。人的动作在 30 FPS 下两帧之间的位移很小,肉眼基本察觉不到关键点滞后。conf_thres=0.4在实时场景下可以适当提高,比如 0.5,减少低质量检测框对 HRnet 的无效调用。

另一种更激进的优化是把 HRnet 的输入分辨率从 192x192 降到 128x128。精度会损失一些,但推理速度提升明显。我的经验是:做健身计数这类对关节位置精度要求不高的场景,128 分辨率完全够用;做康复评估这类需要精确角度的场景,还是老老实实用 192。

如果遇到摄像头读取延迟偏高,优先检查cv2.VideoCapture的缓冲设置,把CAP_PROP_BUFFERSIZE调到 1 能强制拿到最新帧。此外,YOLOv5 的half=True半精度推理在支持 FP16 的 GPU 上能把检测耗时砍掉近一半,但 CPU 环境不要开这个参数,速度反而更慢。整套工程我从配置环境到最后跑通摄像头实时流,中间最耗时的其实是排查各类版本兼容问题,技术链路本身并不复杂。希望这份笔记能帮你少走些弯路。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 19:28:28

Zabbix 7.0 LTS 数据库分区实战:从部署到优化的完整指南

简介:本资源为Zabbix 7.0 LTS部署及数据库分区优化的操作记录文档,面向运维工程师、监控系统管理员及需要处理Zabbix数据库性能瓶颈的技术人员。内容聚焦MySQL/MariaDB环境下历史记录与趋势表的分区方案,针对housekeeper进程繁忙、旧数据删除…

作者头像 李华
网站建设 2026/10/11 19:28:24

Intouch报警数据库配置实战:从Alarm DB Logger到SQL Server稳定落地

简介:Intouch报警数据库配置是一份面向工业自动化工程师、组态软件学习者和考试备考人群的PDF资料,重点梳理Wonderware InTouch报警系统中报警数据库从连接到查询的完整配置流程。文档围绕Alarm DB Logger展开,先说明SQL Server必须设为混合模…

作者头像 李华
网站建设 2026/10/11 19:27:31

SQL数据库图书管理系统课程设计:表结构与建表实战解析

简介:一份完整的 SQL 数据库图书管理系统课程设计文档,面向数据库初学者、高校信息管理相关专业学生,尤其适合正在完成课程设计或毕业设计的读者。资源以图书馆真实管理场景为背景,围绕读者信息、图书信息、操作员信息三大模块&am…

作者头像 李华
网站建设 2026/10/11 19:27:14

Selenium实战指南:从环境搭建到网页自动化应用

我刚接触Selenium那会儿,还在一个做数据运营的团队里,每天重复打开同一个后台系统,把十几个报表页面挨个点开、截图、核对数据。后来我用一个简单的Python脚本把这些手工操作替换成了自动化流程,下班时间从晚上九点提前到六点半。…

作者头像 李华
网站建设 2026/10/11 19:24:58

红外海洋船只检测数据集:8402张VOC+YOLO双格式7类目标实战指南

简介:这份红外海洋船只检测数据集面向从事目标检测算法研究、遥感图像分析及海上智能监控开发的工程师与研究生,提供可直接用于模型训练与验证的标注数据。数据集同时包含Pascal VOC与YOLO两种标注格式,图片与标注一一对应,省去格…

作者头像 李华
网站建设 2026/10/11 19:24:13

SpringBoot众筹平台全栈实战:前后台管理系统从立项到跑通

简介:这是一套基于SpringBoot开发的众筹平台前后台管理系统完整源码,面向计算机相关专业学生与缺乏实战经验的初级开发者,可用于课程设计、毕业设计参考或项目练习。系统功能覆盖前台用户注册登录、发起众筹、支持项目、个人中心,…

作者头像 李华