NVIDIA Kimodo 运动学动作扩散模型——文本生成 3D 人体动作,本地部署 + Blender 集成全流程。
记录时间:2026-08-25|适用版本:Kimodo (main 分支) + Blender 5.0.1|环境:Windows + GTX 1070 Ti 8GB

一、背景与原理

什么是 Kimodo

Kimodo 是 NVIDIA 发布的运动学动作扩散模型(Kinematic Motion Diffusion),使用 700 小时光学动捕数据训练,核心能力是从文本提示生成 3D 人体动作。

  • 输入:一段英文动作描述(如 “A person walks forward.”)
  • 输出:NPZ(完整动作数据)+ BVH(SOMA 骨架动画文件)
  • 模型:nvidia/Kimodo-SOMA-RP-v1.1(约 1.1GB)

为什么需要量化编码器

Kimodo 官方使用 Meta Llama3-8B 作为文本编码器,该模型在 HuggingFace 上是 gated 模型——需要申请授权并获取 token 才能下载。本教程通过使用社区预量化的 NF4 模型 Aero-Ex/KIMODO-Meta3_llm2vec_NF4(约 4.4GB)替代官方编码器,绕过授权限制,同时大幅降低显存需求。

显存要求

模式 显存需求 适用显卡
GPU 编码(量化后) 约 3GB 编码 + 模型本身 12GB+ 显卡
CPU 编码(本教程) 约 2GB(仅模型) 8GB 显卡即可

本机 GTX 1070 Ti 8GB 显存不足 12GB,通过 TEXT_ENCODER_DEVICE=cpu 将文本编码放到 CPU 上运行,GPU 仅负责扩散模型推理。

二、环境要求

硬件

组件 最低要求 本机配置
GPU NVIDIA 8GB+(CUDA 12.4 兼容) GTX 1070 Ti 8GB
内存 16GB+ 64GB DDR4
磁盘 10GB+ 可用空间 D 盘 148GB
CPU 多核(CPU 编码时影响速度) i7-8700

软件

组件 版本 用途
Python 3.11.x(不要用 3.12+) 运行环境
Git 任意 克隆仓库
CUDA 驱动 525+(支持 CUDA 12.4) GPU 推理
Blender 4.0+(本机 5.0.1) BVH 导入编辑
Node.js 可选(viser Web UI 需要) 前端构建

Python 版本注意:Kimodo 依赖的部分库(如 bitsandbytes)在 Python 3.12+ 上兼容性差,强烈建议使用 Python 3.11。

三、安装步骤

第 1 步:安装 Python 3.11

# 下载 Python 3.11.9
curl -L -o python311-installer.exe "https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe"

# 静默安装(不加入系统 PATH,避免与现有 Python 冲突)
python311-installer.exe /quiet InstallAllUsers=0 PrependPath=0 TargetDir="C:Users<用户名>AppDataLocalProgramsPythonPython311"

第 2 步:克隆 Kimodo 仓库

# 直连 GitHub(国内可能超时)
cd D:
git clone https://github.com/nv-tlabs/kimodo.git

# 国内镜像方案(推荐)
git clone --depth 1 https://ghfast.top/https://github.com/nv-tlabs/kimodo.git

坑 1:GitHub 直连超时 — 国内网络直连 GitHub 经常超时。使用 ghfast.top 镜像或配置 Git 代理。--depth 1 浅克隆只取最新提交,大幅减少下载量。

第 3 步:创建虚拟环境

# 用 Python 3.11 创建虚拟环境
"C:/Users/<用户名>/AppData/Local/Programs/Python/Python311/python.exe" -m venv "D:/kimodo/env"

# 验证
"D:/kimodo/env/Scripts/python.exe" --version
# 应输出: Python 3.11.x

第 4 步:安装 PyTorch(CUDA 12.4)

"D:/kimodo/env/Scripts/pip.exe" install torch --index-url https://download.pytorch.org/whl/cu124

验证 CUDA 可用:

"D:/kimodo/env/Scripts/python.exe" -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_device_name(0))"
# 应输出: 2.6.0+cu124 True NVIDIA GeForce GTX 1070 Ti

坑 2:PyTorch 包约 2.5GB — 下载时间较长(10-30 分钟取决于网速)。如果超时,重试即可,pip 会断点续传。

第 5 步:安装 Kimodo 依赖

# 安装 wheel 和 setuptools(必需)
"D:/kimodo/env/Scripts/pip.exe" install wheel setuptools

# 跳过 MotionCorrection C++ 编译安装基础包
cd D:/kimodo
set SKIP_MOTION_CORRECTION_IN_SETUP=1
"D:/kimodo/env/Scripts/pip.exe" install -e . --no-build-isolation

坑 3:MotionCorrection 编译需要 MSVC + CMake — setup.py 默认编译 MotionCorrection C++ 扩展,需要 Visual Studio 2022 + CMake。本机只有 VS2017,缺少 MSVC,因此用 SKIP_MOTION_CORRECTION_IN_SETUP=1 跳过。后处理功能会被关闭,但不影响核心的动作生成。

坑 4:不要直接 pip install -e ".[all]" — [all] 包含 viser fork 和 SOMA-X 的 git 依赖,会从 GitHub 拉取,国内极易卡死。应分步安装。

第 6 步:安装 bitsandbytes

"D:/kimodo/env/Scripts/pip.exe" install -U "bitsandbytes>=0.46.1"

第 7 步:安装 viser fork(通过镜像)

# 通过镜像安装 viser fork
GIT_SSL_NO_VERIFY=1 "D:/kimodo/env/Scripts/pip.exe" install --no-deps "viser @ git+https://ghfast.top/https://github.com/nv-tlabs/kimodo-viser.git@c60bb86bf8a865272815c4a6be97fd8f9631d910"

安装 viser 依赖:

"D:/kimodo/env/Scripts/pip.exe" install "nodeenv<2.0.0,>=1.9.1" "requests<3.0.0,>=2.0.0" "yourdfpy<1.0.0,>=0.0.53" "rich<15.0.0,>=13.3.3" "trimesh<5.0.0,>=3.21.7" "websockets<16.0.0,>=13.1" msgspec scikit-image matplotlib

坑 5:viser 依赖版本冲突 — SOMA-X 安装后可能升级 trimesh/rich/websockets 到不兼容版本。需重新安装 viser 要求的版本范围。

第 8 步:打 viser Windows 补丁

viser 的 _client_autobuild.py 在 Windows 上调用 npm/npx 时使用 POSIX 路径,会导致 Web UI 启动失败。需要打补丁改用 npm.cmd/npx.cmd。

创建 D:/kimodo/patch_viser.py:

"""
Patch viser/_client_autobuild.py for Windows so it uses npm.cmd / npx.cmd.
Idempotent. No-op on non-Windows.
"""
import re
import sys
from pathlib import Path

PATCH_MARKER = "# VISER_WIN_NPMCMD_PATCHED"

def patch_viser() -> int:
    if sys.platform != "win32":
        print("[patch_viser] Not on Windows, skipping.")
        return 0
    try:
        import viser
    except ImportError:
        print("[patch_viser] viser not installed yet, skipping.")
        return 0

    autobuild_path = Path(viser.__file__).parent / "_client_autobuild.py"
    if not autobuild_path.exists():
        print(f"[patch_viser] File not found: {autobuild_path}")
        return 1

    src = autobuild_path.read_text(encoding="utf-8")
    if PATCH_MARKER in src:
        print("[patch_viser] Already patched.")
        return 0

    new = src
    new, n1 = re.subn(
        r'(node_bin_dir / "npx").exists()',
        '(node_bin_dir / ("npx.cmd" if sys.platform == "win32" else "npx")).exists()',
        new,
    )

    npm_line_re = re.compile(r'^( *)npm_path = node_bin_dir / "npm"s*$', re.MULTILINE)

    def _inject(m: re.Match) -> str:
        indent = m.group(1)
        return (
            f'{indent}npm_path = node_bin_dir / "npm"n'
            f'{indent}if sys.platform == "win32":n'
            f'{indent}    _npm_cmd = node_bin_dir / "npm.cmd"n'
            f'{indent}    _npx_cmd = node_bin_dir / "npx.cmd"n'
            f'{indent}    if _npm_cmd.exists():n'
            f'{indent}        npm_path = _npm_cmdn'
            f'{indent}    if _npx_cmd.exists():n'
            f'{indent}        npx_path = _npx_cmd'
        )

    new, n2 = npm_line_re.subn(_inject, new, count=1)

    if n1 == 0 and n2 == 0:
        print("[patch_viser] No patch targets found.")
        return 0

    new = f"{PATCH_MARKER}n{new}"
    autobuild_path.write_text(new, encoding="utf-8")
    print(f"[patch_viser] Patched: {autobuild_path}")
    return 0

if __name__ == "__main__":
    sys.exit(patch_viser())

运行补丁:

cd D:/kimodo
"D:/kimodo/env/Scripts/python.exe" patch_viser.py

重要:每次重装/升级 viser 后都需要重新运行此补丁。脚本幂等,重复运行无副作用。

第 9 步:安装 SOMA-X(通过镜像)

# 先克隆到本地
cd D:
GIT_SSL_NO_VERIFY=1 git clone --depth 1 https://ghfast.top/https://github.com/NVlabs/SOMA-X.git

# 从本地路径安装
cd D:/SOMA-X
"D:/kimodo/env/Scripts/pip.exe" install -e . --no-build-isolation

坑 6:SOMA-X git 依赖卡死 — 直接 pip install py-soma-x @ git+... 会因 GitHub 连接问题卡死 20+ 分钟。解决方案:先 git clone 到本地,再 pip install -e . 从本地安装。

第 10 步:验证所有导入

"D:/kimodo/env/Scripts/python.exe" -c "import viser; import kimodo; import bitsandbytes; print('All imports OK')"

四、下载模型文件

方案 A:huggingface-cli 下载(推荐,但需绕过沙箱)

set HF_ENDPOINT=https://hf-mirror.com
"D:/kimodo/env/Scripts/huggingface-cli.exe" download nvidia/Kimodo-SOMA-RP-v1.1 --local-dir D:/kimodo/models/Kimodo-SOMA-RP-v1.1
"D:/kimodo/env/Scripts/huggingface-cli.exe" download Aero-Ex/KIMODO-Meta3_llm2vec_NF4 --local-dir D:/kimodo/models/llm2vec-nf4

方案 B:curl 直接下载 LFS 文件(兜底方案)

# 1. 克隆仓库(跳过 LFS)
cd D:/kimodo/models
GIT_LFS_SKIP_SMUDGE=1 git clone https://hf-mirror.com/nvidia/Kimodo-SOMA-RP-v1.1
GIT_LFS_SKIP_SMUDGE=1 git clone https://hf-mirror.com/Aero-Ex/KIMODO-Meta3_llm2vec_NF4 llm2vec-nf4

# 2. 用 curl 下载 LFS 大文件
cd D:/kimodo/models/Kimodo-SOMA-RP-v1.1
curl -L -o model.safetensors "https://hf-mirror.com/nvidia/Kimodo-SOMA-RP-v1.1/resolve/main/model.safetensors"

cd D:/kimodo/models/llm2vec-nf4
curl -L -o model.safetensors "https://hf-mirror.com/Aero-Ex/KIMODO-Meta3_llm2vec_NF4/resolve/main/model.safetensors"
curl -L -o tokenizer.json "https://hf-mirror.com/Aero-Ex/KIMODO-Meta3_llm2vec_NF4/resolve/main/tokenizer.json"

坑 7:HuggingFace 连接超时 — 国内直连 HuggingFace 会超时。必须设置 HF_ENDPOINT=https://hf-mirror.com 使用镜像。

模型目录结构

D:/kimodo/models/
├── Kimodo-SOMA-RP-v1.1/
│   ├── config.yaml
│   ├── model.safetensors          # 约 1.1GB
│   └── stats/
│       └── motion/
│           ├── body/  (mean.npy, std.npy)
│           ├── global_root/  (mean.npy, std.npy)
│           └── local_root/  (mean.npy, std.npy)
└── llm2vec-nf4/
    ├── model.safetensors          # 约 4.4GB
    ├── tokenizer.json             # 约 16MB
    └── ...

坑 9:模型目录名必须匹配 display_name — CHECKPOINT_DIR 环境变量指向模型根目录后,Kimodo 会用模型的 display_name 作为子目录名查找。如果目录名不匹配会报 “Model folder not found”。

五、源码改动(量化编码器支持)

改动 1:kimodo/model/load_model.py

在 TEXT_ENCODER_PRESETS 字典中新增三个量化预设:

TEXT_ENCODER_PRESETS = {
    # 原始预设(需要 Llama3 gated 授权)
    "llm2vec": {
        "target": "kimodo.model.LLM2VecEncoder",
        "kwargs": {
            "base_model_name_or_path": "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp",
            "peft_model_name_or_path": "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised",
            "dtype": "bfloat16", "llm_dim": 4096, "device": "auto", "load_in_4bit": False,
        },
    },
    # === 新增:NF4 预量化编码器(无需授权) ===
    "llm2vec-nf4": {
        "target": "kimodo.model.LLM2VecEncoder",
        "kwargs": {
            "base_model_name_or_path": "llm2vec-nf4",  # 本地目录名
            "peft_model_name_or_path": None,
            "dtype": "bfloat16", "llm_dim": 4096, "device": "auto", "load_in_4bit": False,
        },
    },
    # === 新增:FP16 编码器 ===
    "llm2vec-fp16": {
        "target": "kimodo.model.LLM2VecEncoder",
        "kwargs": {
            "base_model_name_or_path": "Aero-Ex/KIMODO-Meta3_llm2vec_FP16",
            "peft_model_name_or_path": None,
            "dtype": "float16", "llm_dim": 4096, "device": "auto", "load_in_4bit": False,
        },
    },
    # === 新增:bitsandbytes 4bit 量化 ===
    "llm2vec-bnb-4bit": {
        "target": "kimodo.model.LLM2VecEncoder",
        "kwargs": {
            "base_model_name_or_path": "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp",
            "peft_model_name_or_path": "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised",
            "dtype": "bfloat16", "llm_dim": 4096, "device": "auto", "load_in_4bit": True,
        },
    },
}

关键点:llm2vec-nf4 预设中 base_model_name_or_path 的值是 "llm2vec-nf4"(本地目录名),不是 HuggingFace repo ID。因为 TEXT_ENCODERS_DIR 环境变量会将此值拼接为完整路径。

改动 2:kimodo/model/llm2vec/llm2vec_wrapper.py

替换 __init__ 和 to 方法,支持 peft_model_name_or_path=None 和 load_in_4bit 参数:

import os
from typing import Optional  # 新增 import
import numpy as np
import torch
from .llm2vec import LLM2Vec

class LLM2VecEncoder:
    """LLM2Vec text embeddings."""

    def __init__(
        self,
        base_model_name_or_path: str,
        peft_model_name_or_path: Optional[str] = None,  # 改为 Optional
        dtype: str = "bfloat16",
        llm_dim: int = 4096,
        device: str = "auto",
        load_in_4bit: bool = False,  # 新增参数
    ) -> None:
        torch_dtype = getattr(torch, dtype)
        self.llm_dim = llm_dim
        cache_dir = os.environ.get("HUGGINGFACE_CACHE_DIR")

        if "TEXT_ENCODERS_DIR" in os.environ:
            base_model_name_or_path = os.path.join(
                os.environ["TEXT_ENCODERS_DIR"], base_model_name_or_path)
            if peft_model_name_or_path is not None:
                peft_model_name_or_path = os.path.join(
                    os.environ["TEXT_ENCODERS_DIR"], peft_model_name_or_path)

        load_kwargs = {"torch_dtype": torch_dtype, "cache_dir": cache_dir}

        if load_in_4bit:
            from transformers import BitsAndBytesConfig
            load_kwargs["quantization_config"] = BitsAndBytesConfig(
                load_in_4bit=True,
                bnb_4bit_compute_dtype=torch_dtype,
                bnb_4bit_use_double_quant=True,
                bnb_4bit_quant_type="nf4",
            )

        self.model = LLM2Vec.from_pretrained(
            base_model_name_or_path=base_model_name_or_path,
            peft_model_name_or_path=peft_model_name_or_path,
            **load_kwargs,
        )

        env_device = os.environ.get("TEXT_ENCODER_DEVICE")
        if env_device:
            device = env_device
        if device == "auto":
            device = "cuda" if torch.cuda.is_available() else "cpu"
        self._device = device

        is_bnb_quantized = getattr(self.model.model, "is_loaded_in_4bit", False) or getattr(
            self.model.model, "is_loaded_in_8bit", False)
        if device is not None and not is_bnb_quantized:
            self.model = self.model.to(device)

        self.model.eval()
        for p in self.model.parameters():
            p.requires_grad = False

    def to(self, device: torch.device):
        is_bnb_quantized = getattr(self.model.model, "is_loaded_in_4bit", False) or getattr(
            self.model.model, "is_loaded_in_8bit", False)
        if not is_bnb_quantized:
            self.model = self.model.to(device)
        self._device = str(device) if not isinstance(device, str) else device
        return self

改动要点:① peft_model_name_or_path 改为 Optional[str];② 新增 load_in_4bit 参数;③ 检测 bnb 量化状态,量化模型不能调用 .to();④ TEXT_ENCODERS_DIR 拼接时检查 peft_model_name_or_path is not None。

六、关闭后处理(可选)

如果未编译 MotionCorrection C++ 扩展,需要关闭后处理默认设置。编辑 kimodo/demo/config.py:

# 原始值
INIT_POSTPROCESSING = True
# 改为
INIT_POSTPROCESSING = False

七、运行生成

方式 A:CLI 命令行生成

cd D:/kimodo

set CHECKPOINT_DIR=D:/kimodo/models
set TEXT_ENCODERS_DIR=D:/kimodo/models
set TEXT_ENCODER=llm2vec-nf4
set TEXT_ENCODER_MODE=local
set TEXT_ENCODER_DEVICE=cpu
set HF_ENDPOINT=https://hf-mirror.com

"D:/kimodo/env/Scripts/python.exe" -m kimodo.scripts.generate "A person walks forward." --model kimodo-soma-rp --duration 4 --diffusion_steps 50 --num_samples 1 --seed 42 --output D:/kimodo/test_output/walk_forward --bvh --no-postprocess

参数说明:

参数 说明 示例值
--model 模型名称 kimodo-soma-rp
--duration 动作时长(秒) 4
--diffusion_steps 扩散步数(越多质量越好,越慢) 50
--num_samples 生成数量 1
--seed 随机种子 42
--output 输出路径前缀 D:/kimodo/test_output/walk_forward
--bvh 输出 BVH 格式 –
--no-postprocess 跳过后处理 –

方式 B:Web UI 生成

创建一键启动脚本 start_kimodo_web.bat:

@echo off
cd /d "%~dp0"
set "CHECKPOINT_DIR=D:kimodomodels"
set "TEXT_ENCODERS_DIR=D:kimodomodels"
set "TEXT_ENCODER=llm2vec-nf4"
set "TEXT_ENCODER_MODE=local"
set "TEXT_ENCODER_DEVICE=cpu"
set "HF_ENDPOINT=https://hf-mirror.com"
set "SERVER_PORT=7860"
set "KIMODO_MODEL=kimodo-soma-rp"
echo Patching viser ...
"%~dp0envScriptspython.exe" "%~dp0patch_viser.py"
echo Starting Kimodo Web preview ...
echo Open in browser: http://localhost:%SERVER_PORT%
"%~dp0envScriptspython.exe" -m kimodo.demo --model %KIMODO_MODEL%
pause

双击运行后,浏览器打开 http://localhost:7860 即可使用 Web UI。

环境变量速查

变量 作用 本机值
CHECKPOINT_DIR 模型根目录 D:/kimodo/models
TEXT_ENCODERS_DIR 编码器根目录 D:/kimodo/models
TEXT_ENCODER 编码器预设名 llm2vec-nf4
TEXT_ENCODER_MODE 编码器模式 local
TEXT_ENCODER_DEVICE 编码器运行设备 cpu(8GB 显卡用 cpu)
HF_ENDPOINT HuggingFace 镜像 https://hf-mirror.com

八、Blender 集成

BVH 导入脚本

创建 blender_import_bvh.py,用于在 Blender 后台模式导入 BVH 并验证:

"""
Blender script: import BVH file and verify the motion data.
Usage: blender --background --python blender_import_bvh.py -- <bvh_file_path>
"""
import sys
import os
import bpy

def import_and_verify_bvh(bvh_path):
    print(f"[Blender BVH Import] Starting import of: {bvh_path}")
    if not os.path.exists(bvh_path):
        print(f"[ERROR] BVH file not found: {bvh_path}")
        return False

    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

    try:
        bpy.ops.import_anim.bvh(filepath=bvh_path)
        print("[Blender BVH Import] BVH imported successfully.")
    except AttributeError:
        try:
            bpy.ops.import_bvh.bvh(filepath=bvh_path)
        except Exception as e:
            print(f"[ERROR] Failed to import BVH: {e}")
            return False
    except Exception as e:
        print(f"[ERROR] Failed to import BVH: {e}")
        return False

    armatures = [obj for obj in bpy.context.scene.objects if obj.type == 'ARMATURE']
    if not armatures:
        print("[ERROR] No armature found after BVH import.")
        return False

    armature = armatures[0]
    print(f"[Blender BVH Import] Armature name: {armature.name}")
    print(f"[Blender BVH Import] Bone count: {len(armature.data.bones)}")

    if armature.animation_data and armature.animation_data.action:
        action = armature.animation_data.action
        frame_count = int(action.frame_range[1] - action.frame_range[0] + 1)
        print(f"[Blender BVH Import] Total frames: {frame_count}")

    blend_path = bvh_path.replace('.bvh', '_imported.blend')
    bpy.ops.wm.save_as_mainfile(filepath=blend_path)
    print(f"[Blender BVH Import] Saved blend file: {blend_path}")
    print("[Blender BVH Import] VERIFICATION PASSED.")
    return True

if __name__ == "__main__":
    argv = sys.argv
    if "--" in argv:
        argv = argv[argv.index("--") + 1:]
    else:
        argv = []
    if len(argv) < 1:
        sys.exit(1)
    bvh_file = argv[0]
    success = import_and_verify_bvh(bvh_file)
    sys.exit(0 if success else 1)

命令行导入

"K:/Program Files/blender/blender.exe" --background --python "D:/kimodo/blender_import_bvh.py" -- "D:/kimodo/test_output/walk_forward.bvh"

注意:传给 Blender 的 BVH 路径必须用 Windows 风格(D:/... 或 D:...),不能用 Git Bash 的 /d/... 格式。

在 Blender GUI 中手动导入

  1. 打开 Blender → File → Import → Motion Capture (.bvh)
  2. 选择生成的 .bvh 文件
  3. 导入后可看到 78 骨骼的 SOMA 骨架和动画

九、自动化测试

端到端测试脚本

创建 auto_test.py,自动执行 Kimodo 生成 → BVH 导出 → Blender 导入的完整流程:

"""
Automated end-to-end test: Kimodo generate -> BVH export -> Blender import.
"""
import os
import subprocess
import sys
import time

KIMODO_DIR = "D:/kimodo"
PYTHON = "D:/kimodo/env/Scripts/python.exe"
BLENDER = "K:/Program Files/blender/blender.exe"
BLENDER_SCRIPT = "D:/kimodo/blender_import_bvh.py"
OUTPUT_DIR = "D:/kimodo/test_output"

TEST_PROMPTS = [
    "A person walks forward.",
    "A person does a squat.",
    "A person jumps in place.",
]

def run_step(name, cmd, env=None, timeout=300):
    print(f"n{'='*60}")
    print(f"[STEP] {name}")
    start = time.time()
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout,
        env={**os.environ, **(env or {})}, cwd=KIMODO_DIR)
    elapsed = time.time() - start
    if result.returncode == 0:
        print(f"[PASS] {name} ({elapsed:.1f}s)")
        return True
    else:
        print(f"[FAIL] {name} ({elapsed:.1f}s)")
        return False

def check_file(path, min_size=100):
    if not os.path.exists(path):
        return False
    size = os.path.getsize(path)
    return size >= min_size

def main():
    os.makedirs(OUTPUT_DIR, exist_ok=True)
    all_passed = True
    kimodo_env = {
        "CHECKPOINT_DIR": "D:/kimodo/models",
        "TEXT_ENCODERS_DIR": "D:/kimodo/models",
        "TEXT_ENCODER": "llm2vec-nf4",
        "TEXT_ENCODER_MODE": "local",
        "TEXT_ENCODER_DEVICE": "cpu",
        "HF_ENDPOINT": "https://hf-mirror.com",
    }
    for i, prompt in enumerate(TEST_PROMPTS):
        test_name = f"test_{i+1}"
        output_base = os.path.join(OUTPUT_DIR, test_name)
        gen_cmd = [PYTHON, "-m", "kimodo.scripts.generate", prompt,
            "--model", "kimodo-soma-rp", "--duration", "4",
            "--diffusion_steps", "50", "--num_samples", "1",
            "--seed", "42", "--output", output_base, "--bvh", "--no-postprocess"]
        if not run_step(f"Kimodo generate: '{prompt}'", gen_cmd, kimodo_env, timeout=300):
            all_passed = False
            continue
        bvh_path = output_base + ".bvh"
        blend_cmd = [BLENDER, "--background", "--python", BLENDER_SCRIPT, "--", bvh_path]
        if not run_step(f"Blender import: '{prompt}'", blend_cmd, timeout=120):
            all_passed = False
            continue
    return 0 if all_passed else 1

if __name__ == "__main__":
    sys.exit(main())

测试结果(2026-08-25 实测)

测试 Prompt 生成耗时 BVH 大小 骨骼数 帧数 Blender 导入
1 “A person walks forward.” 52.5s 274KB 78 120 ✅
2 “A person does a squat.” 61.4s 275KB 78 120 ✅
3 “A person jumps in place.” 42.6s 274KB 78 120 ✅

全部 3/3 测试通过,总耗时 2 分 45 秒。

十、踩坑汇总

# 问题 原因 解决方案
1 GitHub 直连超时 国内网络 用 ghfast.top 镜像 + --depth 1
2 PyTorch 下载超时 包体 2.5GB 重试,pip 支持断点续传
3 MotionCorrection 编译失败 缺 MSVC + CMake SKIP_MOTION_CORRECTION_IN_SETUP=1
4 pip install -e ".[all]" 卡死 git 依赖从 GitHub 拉取 分步安装
5 viser 依赖版本冲突 SOMA-X 升级了依赖 重新安装 viser 要求的版本
6 SOMA-X git 依赖卡死 GitHub 连接不稳定 先 clone 到本地再安装
7 HuggingFace 连接超时 国内网络 HF_ENDPOINT=https://hf-mirror.com
8 huggingface_hub 下载被阻断 safe-delete shim 用 curl 直接下载
9 “Model folder not found” 目录名不匹配 目录名改为 Kimodo-SOMA-RP-v1.1
10 viser Web UI 启动失败 npm/npx POSIX 路径 运行 patch_viser.py
11 Blender BVH 导入报文件不存在 Git Bash 路径格式 用 Windows 路径

十一、注意事项

  1. BVH 是 SOMA 骨架:Kimodo 输出的 BVH 使用 SOMA 骨架定义(78 骨骼),与 UE/Unity 的标准人形骨架不同。要在游戏引擎中使用,需要额外做 retarget(骨骼重定向)。
  2. 后处理未启用:本教程跳过了 MotionCorrection 编译。如需启用,需安装 Visual Studio 2022 + CMake。
  3. CPU 编码较慢:8GB 显卡用 TEXT_ENCODER_DEVICE=cpu 时,每个动作生成约 40-60 秒。12GB+ 显卡可设为 cuda。
  4. viser 补丁需重复运行:每次重装或升级 viser 后都需要重新运行 patch_viser.py。
  5. Python 版本锁定 3.11:不要升级到 3.12+,bitsandbytes 等依赖兼容性差。
  6. seed 影响结果:相同 seed + 相同 prompt 会生成相同动作。改变 seed 可获得不同变体。

十二、文件清单

文件 路径 用途
Kimodo 仓库 D:kimodo 主程序代码
Python 虚拟环境 D:kimodoenv 隔离运行环境
Kimodo 模型 D:kimodomodelsKimodo-SOMA-RP-v1.1 扩散模型权重 (1.1GB)
NF4 编码器 D:kimodomodelsllm2vec-nf4 量化文本编码器 (4.4GB)
SOMA-X D:SOMA-X 骨架定义库
viser 补丁 D:kimodopatch_viser.py Windows npm 修复
BVH 导入脚本 D:kimodoblender_import_bvh.py Blender BVH 导入验证
自动化测试 D:kimodoauto_test.py 端到端测试脚本
Web UI 启动器 D:kimodostart_kimodo_web.bat 一键启动 Web UI

相关链接