YOLOv12实战:5分钟搞定人体姿态估计(附COCO数据集配置指南)

最近在做一个智能健身镜的项目,需要实时分析用户的健身动作是否标准。团队一开始尝试了OpenPose这类经典方案,但发现在移动端设备上帧率总是不太理想,直到我们把目光投向了YOLO系列的最新成员——YOLOv12。说实话,第一次看到它在COCO关键点数据集上跑出70多FPS还能保持高精度时,确实有点惊讶。对于需要快速落地人体姿态估计功能的开发者,尤其是那些面临课程设计或者竞赛截止日期的同学来说,这种“开箱即用”的效率实在太关键了。这篇文章,我就结合自己踩过的几个坑,带你从零开始,用最短的时间搭建一个可运行的人体关键点检测系统。

1. 环境搭建与数据准备

人体姿态估计听起来高大上,但得益于YOLOv12优秀的工程化封装,入门门槛已经大大降低。不过,万事开头难,一个干净、正确的环境是后续所有工作的基石。我强烈建议使用Miniconda或Anaconda来管理Python环境,这能有效避免不同项目间依赖包版本的冲突。

首先,创建一个独立的Python环境。我习惯用Python 3.9,它在稳定性和新特性支持上比较平衡。

conda create -n yolo12-pose python=3.9 -y
conda activate yolo12-pose

接下来安装PyTorch。这里有个小细节需要注意:务必根据你的CUDA版本选择对应的PyTorch安装命令。你可以通过 nvidia-smi 命令查看CUDA版本。如果使用CPU,则选择CPU版本的PyTorch。以下是以CUDA 11.8为例的命令:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

环境就绪后,安装Ultralytics库,这是官方维护的YOLO系列Python接口库,极大简化了我们的操作。

pip install ultralytics

注意:安装过程中如果遇到网络问题,可以考虑使用国内镜像源,例如 -i https://pypi.tuna.tsinghua.edu.cn/simple

数据是模型的“粮食”。COCO(Common Objects in Context)数据集是计算机视觉领域的基准数据集之一,其关键点子集包含了超过20万张图像和25万个人体实例,每个实例标注了17个关键点(如鼻子、左右肩、左右膝等)。对于快速验证和学术研究,它是绝佳的选择。

你需要从COCO官网下载两个核心文件:

  1. 图像数据train2017.zipval2017.zip
  2. 标注文件annotations_trainval2017.zip,解压后我们需要其中的 person_keypoints_train2017.jsonperson_keypoints_val20177.json

下载后,建议按照以下目录结构进行组织,这符合YOLO系列处理数据的惯例:

coco_pose/
├── images/
│   ├── train2017/
│   │   ├── 000000000009.jpg
│   │   └── ...
│   └── val2017/
│       ├── 000000000139.jpg
│       └── ...
└── labels/
    ├── train2017/
    │   ├── 000000000009.txt
    │   └── ...
    └── val2017/
        ├── 000000000139.txt
        └── ...

原始的COCO标注是JSON格式,YOLO需要的是每张图片对应一个TXT文件的格式。每个TXT文件中,每一行代表一个“人”的实例,格式为: <class_id> <x_center> <y_center> <width> <height> <px1> <py1> <pv1> ... <px17> <py17> <pv17>

其中,class_id 对于人体永远是0;x_center, y_center, width, height 是归一化后的边界框坐标;px, py 是归一化后的关键点坐标,pv 是关键点可见性(2:可见,1:遮挡,0:未标注)。

手动转换非常繁琐。幸运的是,Ultralytics提供了一个便捷的脚本。假设你的原始COCO数据放在 /path/to/coco,你可以运行:

yolo pose data=coco-pose.yaml

但这通常需要你提前准备好符合Ultralytics要求的YAML配置文件。一个更直接的方法是使用社区提供的转换脚本。这里我分享一个经过验证的Python脚本片段,你可以用它来将COCO JSON标注转换为YOLO格式:

import json
import os
from tqdm import tqdm

# 加载COCO标注
with open('person_keypoints_train2017.json', 'r') as f:
    data = json.load(f)

# 创建图像id到文件名的映射
img_id_to_info = {img['id']: img for img in data['images']}

# 处理每个标注
for ann in tqdm(data['annotations']):
    if ann['num_keypoints'] == 0:
        continue # 跳过没有关键点的标注
    image_info = img_id_to_info[ann['image_id']]
    # ... 此处省略详细的坐标转换和文件写入逻辑
    # 核心是计算边界框和17个关键点的归一化坐标

2. YOLOv12模型解析与选择

YOLOv12并非一个官方命名,它通常指代Ultralytics YOLO系列在v11之后社区期待或讨论的演进版本。在实际的Ultralytics框架中,最新的稳定版本可能是YOLOv11,但其架构和训练方式代表了当前YOLO for Pose任务的最高易用性水平。我们理解中的“v12”特性,如更高效的注意力机制,其实在v11的某些变体或社区改进中已有所体现。因此,本文的实战将基于 ultralytics 库中最新支持的YOLO姿态估计模型 进行,其使用方式一脉相承。

与单纯的目标检测不同,姿态估计模型在输出边界框和类别的同时,还会输出每个实例的一组关键点坐标。在YOLO中,这是通过一个额外的“姿态(Pose)”检测头实现的。你可以通过一个简单的参数来调用预训练的姿势估计模型。

YOLO提供了多种不同尺寸的预训练模型,在精度和速度之间提供权衡。对于人体姿态估计,常见的模型变体及其特点对比如下:

模型变体参数量 (M)GFLOPsCOCO Pose AP (val)适用场景
YOLO11n-pose~2.9~7.8~50.2移动端、嵌入式设备,追求极致速度
YOLO11s-pose~9.8~23.5~56.7边缘计算(如Jetson),平衡速度与精度
YOLO11m-pose~21.1~71.8~61.4服务器端通用部署,推荐起点
YOLO11l-pose~27.3~93.5~63.1对精度要求较高的研究或产品
YOLO11x-pose~61.1~208.7~64.8计算资源充足,追求最高精度

对于大多数快速启动和实验的场景,我推荐从 YOLO11s-poseYOLO11m-pose 开始。它们能在保持较好精度的同时,提供更快的训练和推理速度。在我们的健身镜项目中,最终部署选择的就是YOLO11s-pose,在1080p视频流上能达到超过30 FPS的实时分析。

加载和使用预训练模型进行预测简单到令人发指:

from ultralytics import YOLO

# 加载预训练的姿势估计模型
model = YOLO('yolo11s-pose.pt')  # 会自动下载模型

# 对图片进行推理
results = model('path/to/your/image.jpg')

# 可视化结果
results[0].show()  # 显示带有关键点和骨骼连线的图片

results 对象包含了丰富的输出信息,其中 keypoints 属性就是检测到的关键点数据,它是一个形状为 [N, 17, 3] 的数组,其中 N 是检测到的人数,17是关键点数量,3代表 (x, y, 置信度)。

3. 训练你的定制化姿态估计模型

虽然预训练模型很强大,但如果你在自己的特定场景(比如特殊的服装、视角、或需要检测非标准关键点)下效果不佳,就需要进行微调(Fine-tuning)。微调能让你用相对较少的数据和计算成本,获得在该场景下更鲁棒的模型。

首先,你需要按照第一部分所述准备好自己的数据集,并创建一个YAML配置文件来告诉模型数据在哪里。这个配置文件是训练的核心。

创建一个名为 my_pose_data.yaml 的文件,内容如下:

# 数据集路径
path: /path/to/your/coco_pose  # 数据集的根目录
train: images/train2017  # 训练集图像路径(相对于path)
val: images/val2017      # 验证集图像路径(相对于path)

# 关键点配置
kpt_shape: [17, 3]  # 17个关键点,每个点有3个值 (x, y, visibility)
flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15] # 水平翻转时关键点的对应索引

# 类别(对于纯人体姿态估计,通常只有‘person’一类)
names:
  0: person

接下来,编写训练脚本。YOLO的训练API设计得非常简洁。创建一个 train.py 文件:

from ultralytics import YOLO

# 加载一个基础模型。你可以从预训练模型开始,也可以从零开始。
# 从预训练模型开始收敛更快,推荐。
model = YOLO('yolo11s-pose.pt')

# 开始训练
results = model.train(
    data='my_pose_data.yaml',  # 数据配置文件的路径
    epochs=100,                 # 训练轮数,根据数据集大小调整
    imgsz=640,                 # 输入图像尺寸
    batch=16,                  # 批次大小,根据GPU内存调整
    device='0',                # 使用GPU 0,如果是CPU则设为 'cpu'
    workers=8,                 # 数据加载线程数
    project='my_pose_project', # 项目名称,所有输出会保存在此文件夹下
    name='exp1',               # 实验名称
    optimizer='AdamW',         # 优化器, 'SGD', 'Adam', 'AdamW' 等
    lr0=0.01,                  # 初始学习率
    pretrained=True            # 是否使用预训练权重(从yolo11s-pose.pt开始)
)

运行这个脚本,训练就会开始。控制台会输出损失曲线、精度指标等。训练完成后,最佳的模型权重会保存在 my_pose_project/exp1/weights/best.pt

提示:训练过程中最常见的错误是路径问题。请反复检查 my_pose_data.yaml 中的 pathtrainval 路径是否正确,以及图片和标签文件是否一一对应。

训练时,有几个超参数对结果影响很大,值得关注:

  • imgsz:图像尺寸。越大通常精度越高,但训练和推理更慢。640是一个常用的平衡点。
  • batch:批次大小。在GPU内存允许的情况下尽可能设大,有助于训练稳定。
  • lr0:学习率。如果是从头训练,可以尝试0.01;如果是微调,可以设小一点,如0.001。
  • patience:早停耐心值。如果验证集指标在连续 patience 个epoch没有提升,训练会提前停止,防止过拟合。

4. 推理部署与高级应用

模型训练好后,就到了展示成果的环节。推理不仅仅是跑一张图片看看效果,更要考虑如何集成到实际应用中。

基础推理与可视化

from ultralytics import YOLO
import cv2

# 加载自定义训练的最佳模型
model = YOLO('my_pose_project/exp1/weights/best.pt')

# 图片推理
results = model('test_image.jpg', save=True, conf=0.5)  # conf为置信度阈值

# 视频流推理
cap = cv2.VideoCapture(0)  # 打开摄像头
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    results = model(frame, stream=True, conf=0.5)  # stream模式更高效
    annotated_frame = results[0].plot()  # 绘制检测框和关键点
    cv2.imshow('YOLO Pose Estimation', annotated_frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

关键点数据的获取与利用: 可视化固然直观,但更多时候我们需要的是结构化的数据来做进一步分析。results 对象提供了完整的访问接口。

for result in results:
    boxes = result.boxes.xyxy.cpu().numpy()  # 边界框 [x1, y1, x2, y2]
    keypoints = result.keypoints.xy.cpu().numpy()  # 关键点坐标 [N, 17, 2]
    confidences = result.keypoints.conf.cpu().numpy()  # 关键点置信度 [N, 17]

    for i, (box, kpts) in enumerate(zip(boxes, keypoints)):
        print(f"Person {i}:")
        print(f"  Bounding Box: {box}")
        for j, (x, y) in enumerate(kpts):
            print(f"  Keypoint {j} ({model.names.get(j, j)}): ({x:.2f}, {y:.2f})")

有了这17个关键点的坐标,你就可以解锁很多高级应用:

  • 动作识别:计算关节角度(如肘部、膝部的弯曲角度),结合时序信息判断是“深蹲”还是“跳跃”。
  • 姿态评分:对比用户姿态与标准姿态模板(如瑜伽动作),给出相似度分数。
  • 异常检测:在安防场景,通过分析人体骨架的时空变化,检测跌倒、打架等异常行为。

这里给出一个计算肘关节角度的简单示例,这是健身应用中判断手臂弯曲是否到位的核心:

import numpy as np

def calculate_elbow_angle(shoulder, elbow, wrist):
    """
    计算肘关节角度。
    参数:肩、肘、腕三个关键点的坐标 (x, y)。
    返回:角度(度)。
    """
    # 将点转换为向量
    vec1 = shoulder - elbow
    vec2 = wrist - elbow
    # 计算点积和模长
    dot_product = np.dot(vec1, vec2)
    norm1 = np.linalg.norm(vec1)
    norm2 = np.linalg.norm(vec2)
    # 计算夹角(弧度)并转换为度
    angle_rad = np.arccos(dot_product / (norm1 * norm2 + 1e-8))  # 加小量防止除零
    angle_deg = np.degrees(angle_rad)
    return angle_deg

# 假设 kpts 是单个人的17个关键点数组
# 索引参考:5:左肩,7:左肘,9:左腕
left_shoulder = kpts[5]
left_elbow = kpts[7]
left_wrist = kpts[9]

angle = calculate_elbow_angle(left_shoulder, left_elbow, left_wrist)
print(f"左肘关节角度: {angle:.1f}°")
if angle < 60:
    print("手臂弯曲幅度较大(接近收缩)。")
elif angle > 160:
    print("手臂几乎伸直。")

模型优化与加速: 对于端侧部署,模型大小和速度至关重要。Ultralytics模型支持导出为多种格式:

  • ONNX:跨平台中间格式,兼容性强。
  • TensorRT:NVIDIA GPU上的极致推理加速。
  • CoreML:苹果设备(iOS/macOS)原生支持。
  • OpenVINO:英特尔CPU/GPU加速。

导出命令非常简单:

yolo export model=best.pt format=onnx  # 导出为ONNX
yolo export model=best.pt format=engine  # 导出为TensorRT引擎(需要CUDA环境)

在部署时,你还可以通过调整推理参数来平衡速度和精度:

  • conf:调高(如0.7)可减少误检,调低(如0.3)可提高召回。
  • iou:非极大值抑制的IoU阈值,影响重叠框的合并。
  • imgsz:推理时输入图像的尺寸。减小尺寸能显著提升速度,但会损失精度。

最后,分享一个我们项目中的实际教训:在复杂背景或多人严重遮挡的场景下,关键点检测可能会跳变或丢失。一个实用的技巧是加入简单的轨迹平滑滤波,比如对连续视频帧中同一人的相同关键点坐标进行移动平均滤波,能有效提升视觉上的稳定性和后续动作分析的准确性。这不需要复杂的算法,几行代码就能带来用户体验的显著提升。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐