跳到正文
Hacker News· khoahpd·· 8 天前AI 评分36

Pose Lab:通过 MCP 在 Blender 中测量人体手臂姿态

Show HN: MCP to measure human arm in Blender

AI 导读

Pose Lab 是一个基于 MCP 的工具,为模型提供 Blender 中第一人称手臂与步枪姿态的量化空间数据,包括命名坐标系中的位置、穿模深度、表面朝向和可见度。

正文 · AI 翻译

Pose Lab

在 Blender 中通过 MCP 为第一人称手臂和步枪摆姿势提供可测量的空间答案。

The built-in sample rig: four Blender views of first-person arms holding a rifle

模型很难从图片判断 3D 空间:步枪的哪一面朝向眼睛、手指是否位于机匣内部、枪的任何转动能否露出抛壳口。Pose Lab 给模型提供数字。它报告在命名坐标系中的位置以及任何物体的穿模深度。它测量表面正对眼睛的程度以及眼睛能看到什么。它的求解器针对目标搜索步枪动作,并报告哪些目标没有任何动作能满足。对于动画,它逐帧对照相同的检查扫描一段剪辑,并修复失败之处。

我在为我的第一人称射击游戏手工制作枪膛检查时构建了它。有一个问题花了我好几次完整的 Blender 运行:转动步枪能否让眼睛看到它的抛壳口?用 Pose Lab 只需一次 solve 调用。在我的游戏的 AK 骨架上,仅靠转动在 60 个样本中有 0 个满足目标。这是几何事实:眼睛沿着枪管看。转动并移动步枪各自能满足每个目标,但 400 个样本中没有一个能同时满足全部四个目标。所以这项检查需要新的手部姿势,而不仅仅是新的步枪位置。在内置示例骨架上,仅靠转动在 60 个样本中也有 0 个满足抛壳口目标,而转动并移动在 9 秒内满足了每个目标。

它给模型提供什么

工具 答案
list_rigs、load_rig、describe 骨架、坐标系、符号规则、命名点、剪辑、可动部件
pose_idle、pose_clip、move_part、snapshot 将骨架摆成某个姿势:一次一段剪辑、枪机后拉、保存的姿势
move_gun 翻滚、摆动、俯仰并移动步枪;双手通过手臂 IK 保持握持
reach 通过手臂 IK 将手腕放到某个点上(尝试肘部极点以避开前臂)
where、distance 在命名坐标系中的位置
clearance 步枪嵌入前臂、手掌或手指的深度,测量到其表面,以及位置
anatomy 每条手臂对照人类手臂的限制:肘部、手腕和每个手指关节,以及姿势违反的每条规则
grip 每只手如何接触步枪:用手掌而非手背;手掌本身是否握持它;每侧的深度
arm_ranges 这些限制背后的研究:每个关节的 AAOS 范围、其功能范围、检查的值以及来源
faces_eye、visible、screen 表面正对眼睛的程度、眼睛能看到它多少、它在屏幕上的位置
solve 针对目标搜索步枪动作;报告每个目标以及任何样本满足它的频率
render 一张联系表:玩家眼睛视图和外部视图,每个图块标注为 Blender 视图
record_clip、load_clip 从关键帧姿势或文件生成剪辑:.pose.json、FBX 或 BVH
scan_clip 逐帧对照检查;最差值、发生时间以及失败的时间段
fix_clip 修复失败之处并报告扫描前后情况,以及任何修复都无法达到的部分
save_clip 将剪辑写为 .pose.json 骨骼数据和供游戏引擎使用的 FBX

坐标系与符号

每个位置都以命名坐标系输入和输出,因此无需猜测轴:

  • gun:枪骨骼的当前状态,Unreal 风格轴,厘米:+X 为枪的左侧,+Y 沿枪管,+Z 向上(默认)
  • arms:手臂空间,Unreal 风格轴,厘米
  • view:从眼睛出发,厘米:+X 向右,+Y 向前,+Z 向上

转动使用玩家的说法:roll + 将枪的右侧向上转,swing + 将枪口向左转,pitch + 将枪口向上转。移动(right、forward、up)在视图中。

手握住握把时已经触及待机姿势中的步枪(一根手指扣在扳机上,手指环绕 护木)。在 pose_idle 处调用 clearance 获取该基准,并用 ignore 通配符将这些片段排除。 在内置示例中,待机时左拇指位于护木内侧 2.7 厘米处。clearance 测量到步枪 表面(按射线奇偶性判断在内或在外),因此它看到的是网格角点之间平面内的身体部位。

手臂规则(anatomy)

一个姿势可以避开步枪,却仍然不是人类手臂能做出的姿势。anatomy 会对照以下限制检查每条手臂:

关节 规则
肩 手操作枪时,肘部保持在肩部下方至少 2 厘米处
肘 铰链关节,弯曲 5 到 150 度:绝不锁直
前臂 它承载手的旋转(桡骨绕尺骨转动),因此手部旋转不是手腕弯曲
腕 自身不扭转:手绕前臂轴线的扭转,相对于待机握把,最多 30 度
腕 在前臂轴线 30 度以内;在其关节活动范围内:屈曲 80,伸展 70,桡偏 20,尺偏 30
手指 每个关节只向掌心弯曲:掌指关节 -30 到 100,中节指间关节 -5 到 110,远节指间关节 -10 到 90
手指 每根手指保持在自己的平面内:中节指间关节偏离其铰链最多 15 度,远节指间关节偏离中节指间关节平面最多 25 度
拇指 掌指关节弯曲 -10 到 60,远节指间关节 -15 到 90,横跨掌心;基部与食指掌骨张开最多 80 度,且位于掌心后方最多 20 度

腕和肘的范围是 AAOS 正常值,来自 AAOS 1965 年表格,转载于 Greene 和 Heckman 1994。其他表格有所不同,手指和拇指相差 10 到 25 度。30 度腕线、5 度肘 最小值以及肘部位于肩下,是本游戏手放在枪上外观的工作规则,而非关节限制。 腕部规则比日常任务更严格:Ryu 1991 发现日常任务使用最多 60 度伸展和 40 度 尺偏。手指限制在掌指关节和中节指间关节处比 AAOS 多允许 10 度屈曲; docs/ANATOMY.md 说明了默认值为何保持如此。它给出了每个关节的标准和功能性 范围、所检查的值以及来源(AAOS;Eaton;Morrey 1981;Palmer 1985;Ryu 1991;Hume 1990;Bain 2015; Soucie 2011)。arm_ranges 将相同数据返回给模型。

anatomy 回复会为每条手臂列出:肘部在肩下的高度、肘部弯曲度、腕部弯曲拆分为 屈曲和桡偏、腕部扭转、每个手指关节的弯曲和扭转,以及 bad:姿势 违反的每条规则。每条规则按报告值读取:角度精确到度,肘部高度精确到 0.1 厘米。绑定中的 limits、finger_limits 和 thumb_limits 条目在 rigs.json 中会覆盖任何值。solve 将 {"type": "anatomy"} 作为目标。

move_gun、reach 和 fix_clip 通过旋转来放置手。它们将手的扭转移入前臂,因此腕 关节只弯曲:步枪旋转 60 度而手保持不动时,读数为无腕部扭转。

通过移动步枪、握把或肘部极向来修复违反的规则。不要进一步弯曲关节。

示例绑定的待机握把违反了腕部规则:左手腕偏离前臂弯曲 44 度,右手 58 度。 使用 anatomy 检查的 fix_clip 将左侧降至 29。它让右侧保持在 58:任何能修复它的旋转都会 让步枪进入手掌背面,因此那只手需要新的握把或肘部,而不是腕部旋转。

手部接触(grip)

一只手用掌心及手指的掌侧握住步枪。clearance 通常在运行时会忽略手掌和手指,因为握持处会接触步枪。这也会隐藏位于错误一侧的手。grip 会检查每只手的接触本身:

规则 默认值
握在步枪上的手以掌心而非手背朝向步枪(palm_faces_rifle 高于 0) 在 hold_within_cm 1.0 以内
握住步枪的手是掌心贴在枪上,而不仅仅是指尖(palm_gap_cm) palm_within_cm 1.5
步枪不接触手背和每根手指的背面 back_max_cm 0.1
掌侧可以稍微压入:胶囊体比手掌更圆 palm_max_cm 1.0

每根手指节段的掌侧就是它弯曲所朝向的那一侧。拇指的指腹朝向侧面,因此其接触只计入掌侧的深度。回复给出每只手的 touching(hold_within_cm 内的某部分)、holding(接触,掌心朝向步枪且其表面在 palm_within_cm 以内)、palm_gap_cm、palm_faces_rifle、palm_contact_cm、back_contact_cm、背面接触的位置以及 bad。当 hold 为真时,未握持的手会违反规则。solve 以 {"type": "grip"} 为目标,以 scan_clip 为检查。在示例装配上,一只手绕其前臂旋转 180 度会使其失败。闲置握持也会如此:左拇指位于护木内侧 2.8 厘米处,右掌压入握把 1.2 厘米,扣扳机手指的背面位于扳机护圈内侧 0.8 厘米处。示例保持原样,因此基准测试的数值保持其含义。

运动:扫描并修复片段

scan_clip 逐帧播放片段并对每一帧运行检查。这些检查是求解器的目标(clearance、anatomy、grip、faces_eye、visible、on_screen、barrel)以及另外三项:

  • contact:手在其标记点上的一个点,例如指尖在拉机柄上(a、b、max_cm)
  • hold:手在步枪上相对于其在 ref_s 处握持位置的漂移距离(side、max_cm)
  • pop:突然跳动,即帧间最快的骨骼速度(bones、max_cm_per_s)

任何检查都接受 during: [from_s, to_s]。然后 fix_clip 修复片段的副本:

  • 跳变:它根据周围的好帧重新混合跳变的帧
  • 保持:它通过手臂 IK 将手放回其握持位置
  • 接触:它移动手腕直到该点接触其标记
  • 间隙:它绕肩到腕的连线以最小角度摆动每个肘部以避开,保持手腕不动,并将该摆动在相邻帧上缓动
  • 解剖:它以相同方式摆动肘部,直到肘部位于肩部下方且在其弯曲范围内。然后它将手腕转回其限制范围内,并将每个手指关节转回其活动范围内。它绝不会将手腕转到步枪进入手背的程度;无法以这种方式修复的手腕保持原样,之后的扫描会说明这一点。转动手腕会转动手在其握持位置上的朝向,因此当握持必须保持精确时,添加一个 hold 检查。
  • 握持:不修复;fix_clip 在修复前后报告它。错误一侧的握持需要新的握持。

它报告修复前后的扫描。它还列出那些手必须位于其手臂无法到达的位置的帧,因为只有新的姿势才能修复这些。

examples/motion_test.py 在示例装配上记录了一个带有两个常见故障的片段。步枪滚动 75 度然后返回,仅在其两端设置关键帧,因此手在关键帧之间偏离步枪。有一帧还跳动了 15 厘米。扫描和修复给出了以下数值:

检查 修复前 修复后
左手在步枪上的漂移 2.27 厘米 0.48 厘米
右手在步枪上的漂移 1.67 厘米 0.45 厘米
最快手速(跳变) 450 厘米/秒 33 厘米/秒
间隙 0.93 厘米 0.0 厘米

该修复改动了 43 帧中的 34 帧,之后所有检查都通过了。它还标记出 6 帧,其中左臂比其抓握位置短了 0.46 cm。这仍然通过了 0.5 cm 的限制。(在 0.3.1 之前,间隙读数为 0.0 cm:角点错过了步枪内部的前臂。)

安装

你需要 Blender 和 Python 3.10 或更新版本。我在 Windows 10 上用 Blender 5.2.2 和 Python 3.14 测试过。我还没有测试 macOS、Linux 或更旧的 Blender 版本。

uvx poselab-mcp

或 pip install poselab-mcp 并运行 poselab-mcp。

将其添加到 Claude Code:

claude mcp add poselab -- uvx poselab-mcp

或添加到任何 MCP 客户端的配置中:

{
  "mcpServers": {
    "poselab": {
      "command": "uvx",
      "args": ["poselab-mcp"],
      "env": { "POSELAB_BLENDER": "C:/Program Files/Blender Foundation/Blender 5.2/blender.exe" }
    }
  }
}

设置

变量 含义
POSELAB_BLENDER Blender 的可执行文件,如果它不在 PATH 中或不在通常的安装文件夹中
POSELAB_RIGS 一个 rigs.json,描述你自己的绑定(参见 examples/rigs.example.json)
POSELAB_OUT Pose Lab 唯一写入的文件夹(渲染、保存的片段、工作进程的日志);默认 ~/.poselab

绑定

内置示例(load_rig {"rig": "sample"})不需要任何文件。Pose Lab 在 Blender 中从代码构建它:两条带有 Unreal 人形骨骼名称的手臂握着一把 AR 式步枪,其拉机柄可以向后滑动。

你自己的绑定来自 FBX 文件:手臂网格、一个空闲姿势、步枪和片段。在一个 rigs.json 中描述它们(复制 examples/rigs.example.json)并将 POSELAB_RIGS 指向它。片段可以是同一骨骼上的 FBX 动画,或 <clip>.pose.json 骨骼数据:{"fps": 30, "frames": [{"bone": [[x, y, z], [w, x, y, z]], ...}, ...]},即空闲骨架中每根骨骼的局部位置和旋转。(Blender 在重新导入自己导出的 FBX 动画时会误读它;骨骼数据避免了这一点。describe 报告每个 FBX 片段的骨骼匹配情况。)

安全

  • Pose Lab 只读取你的绑定和片段文件。它写入渲染、保存的片段及其日志,且仅在 POSELAB_OUT 内。
  • Blender 工作进程仅监听 127.0.0.1,使用一个空闲端口,并且只应答携带会话随机令牌的请求。它只运行 Pose Lab 自己的命令。

工作原理

  • poselab_mcp/server.py:MCP 服务器(官方 Python SDK,stdio)。
  • poselab_mcp/worker_client.py:在第一次调用时启动一个无头 Blender 并保持绑定加载。
  • poselab_mcp/blender/lab.py:在 Blender 内部:绑定、帧、测量、IK、求解器、渲染。
  • poselab_mcp/blender/sample.py:内置示例绑定。

测试

python examples/selftest.py

它像 MCP 客户端那样启动服务器并加载示例绑定。然后它重放上面的问题:仅转动永远不会显示端口,而转动并移动则会显示。联系表落在 ~/.poselab/renders/sheet.png 中。

python examples/motion_test.py

它记录上述有缺陷的片段,扫描它,修复它,并将 roll_fixed.pose.json 和 roll_fixed.fbx 保存在 ~/.poselab/clips/ 中。

blender -b -P examples/anatomy_faults.py

它在示例上每次放置一个已知缺陷,如果某项检查漏掉它则失败:一根手指向侧面弯折、一只手相对于其前臂扭转、一只手位于一个平面内、仅指尖接触,以及一个带有字节顺序标记的绑定文件。这些来自一个真实绑定的基准测试(见下文)。

基准测试

benchmarks/ 询问一个模型使用 Pose Lab 的测量是否比仅使用渲染更好地为绑定摆姿势。它在示例绑定上运行五个任务,并用 Pose Lab 对它们评分。一个脚本化的 oracle 和一个什么都不做的对照检查评分器。参见 benchmarks/README.md。

benchmarks/runs/2026-10-09-toangtown-m24 在一个真实游戏绑定上测试检查本身:8 个手放在弹匣上的姿势,其中 7 个带有已知缺陷。Pose Lab 0.3.0 捕获了 5 个,并以其深度的 40% 读取穿模;0.3.1 捕获了全部 7 个,且深度精确。

许可证

MIT

来源:Hacker News · github.com