BLARM 这个方向最值得关注的一点,是把一段普通视频里学到的运动,迁移到一个独立的 3D 物体上。简单说就是:你不需要给模型做骨骼绑定,也不需要人工一帧一帧摆动作,而是让模型去看视频里的运动,然后把这个运动表达成一组“潜在刚性运动原语”的组合,再驱动目标 3D 物体动起来。对做三维动画、数字人、虚拟拍摄、游戏资源复用的人来说,这类方法解决的是一个很实际的问题:如何低成本让 3D 资产动起来。
第一次看到项目名里的 “Blending Latent Rigid Motion Primitives” 时,我下意识会觉得这是不是又一种“动作迁移”的套壳方案。但真正拆开理解后发现,它想处理的不是一个简单的人体骨骼动作复制,而是更普遍的问题:一段视频里某个物体在动,另一段 3D 资产是静态的,有没有办法让这段 3D 资产学会同样的运动模式。核心难点不在于“能不能动”,而在于“动作是从视频里自动学出来的”,而且最终渲染出来要像同一个 3D 物体在真实运动,而不是贴图晃动或者整体位移。
下面我把这个方向从原理、准备、参数到落地边界完整拆一遍。如果你正准备复现或者评估要不要用类似能力,可以按这个顺序往下读。
1. 先搞清楚 BLARM 到底解决哪类动画问题
1.1 输入输出和常见用法
这个名为 BLARM 的方法,从命名可以判断,它基本要连接两个输入:
- 一个 3D 物体模型或者场景表征
- 一段包含相似物体运动过程的视频
输出是一个动画结果,通常是这个 3D 物体的连续帧渲染,或者一套可以让渲染器播放的运动参数。
有人会把它和骨骼动作捕捉搞混。动作捕捉通常需要标注点、动捕设备或者可靠的姿态估计结果,输出往往是骨架关节旋转。BLARM 这类方法的重点在于“刚性运动原语”,也就是说它希望把复杂视频运动分解成若干小块刚体运动的组合,再用这些组合去驱动目标对象。对非刚性物体,比如布料、动物身体、带弹性的物体,这种拆法比纯骨骼绑定更容易表达局部形变。
我一般会先区分它是用于以下哪一种场景:
- 语义复现:视频里有一只鸟在扇翅膀,目标是另一个造型相近但骨骼结构不同的 3D 鸟模型,希望翅膀有幅度和节奏相近的扇动。
- 风格迁移:视频里一个角色在走路,目标 3D 角色不是人,而是风格化角色,需要保持目标身份,但运动节奏来自视频。
- 素材预演:先用一段手机拍摄的视频验证动作节奏,再决定是否进入精细动画流程。
BLARM 这类方法的定位更接近第一种和第二种的交叉:它不是传统动捕,也不是手工关键帧,而是从视频里学出一个可用于驱动 3D 对象的运动表示。
1.2 常见动画方案做不到什么
传统动画管线里,想做一个 3D 模型运动,一般有三条路:
- 手工 K 帧:质量高,但费人,复杂一点的动作常常一星期都调不稳。
- 骨骼绑定加蒙皮:适合人和四足动物,但遇到软体、流体、变体模型时很麻烦。
- 动作捕捉重定向:硬件和采集成本高,普通做资产展示的人很难用起来。
BLARM 这类方法的吸引力在于,它希望把“运动来源”从动捕设备降级成“普通彩色视频”,同时把目标 3D 模型的形体保持问题交给潜在空间里的分解和混合去解决。这个逻辑在学术上很通顺,实际落地却要面对不少细节问题,后面会挨个讲。
1.3 适合谁看
如果你是做三维视觉研究的,这篇内容可以直接帮你判断这个方法的关键模块是什么、复现难点在哪里。如果你是做 3D 资产生成、游戏物件动态化、电商模型展示、虚拟陈列之类的工作,这类方法值得关注,但别急着上生产。最适合的切入方式是先拿一条短视频和两个简单模型跑通流程,再决定要不要投入更多资源。
2. 核心思路拆解:潜在刚性运动原语到底做了什么
2.1 “刚性运动”怎么理解
先解释一个容易被绕晕的词:刚性运动。
所谓刚性,是指一个物体在运动过程中,内部任意两点之间的距离保持不变。最常见的例子是刚体旋转和平移,比如椅子被抬起来往前放。真实世界里很多动作不是严格刚性的,人的手臂会弯曲,衣服会抖动,动物皮肤会有局部起伏。但把时间切片和空间区域切得非常小之后,很多看似柔性的动作都可以近似看成“一小块区域在做刚体变换”。比如手臂在屈肘时,上臂这段的形变很小,可以近似为刚性;前臂是另一段;每个骨骼主导的区域就是一个运动原语。
BLARM 标题里的 “Rigid Motion Primitives”,就是把这种“局部刚体变换”当作基本单元。多个原语组合起来,就可以表达一个比较复杂的整体动作。这种方法的好处是,不像骨骼动画那样要求你预先定义骨骼层级,也不像逐顶点形变那样容易产生高频抖动。
2.2 为什么要做“原语”混合
单个刚性变化只能表达平移和旋转,能覆盖的动作非常有限。但多个刚性变化原语的组合,就可以表达关节、肢体、衣物等多种形态的连续运动。
混合的关键是权重和时序。什么时间段哪个原语起作用,某个顶点受几个原语影响,原语之间如何平滑过渡,这些直接决定动画像不像。
这里就会遇到一个问题:如果直接在每个 3D 点或者每个渲染像素上做混合,计算量很大,而且容易输出不够自然的动作。所以 BLARM 这类方法会把原语放到“潜在空间”里去处理,而不是在最底层的坐标空间里硬做加减乘除。
2.3 潜在空间里混合有什么好处
潜在空间可以理解成一个特征空间,网络把输入视频和 3D 物体先编码成一组特征,再把刚性运动原语以带权重的方式融合在这些特征里,最后通过解码器还原成连续动画。
相比直接在三维空间里混合,这种设计的实际好处有三个:
- 更容易做运动解耦:视频里哪个部分是自身运动,哪个部分是相机运动,潜在空间可以强制分离。
- 不容易产生穿越和撕裂:坐标空间混合容易出现两个部件的相交,潜在空间混合会更强调全局一致性。
- 便于做泛化:如果原语是在潜在空间里学到的,换一个目标 3D 模型时,可能只需要重新解码,而不需要从头学运动。
当然,“可能”这个词要划重点。真正替换目标模型后是否稳定,取决于训练时目标模型和视频物体是否来自同一类别、视角是否接近、外观差异大不大。
2.4 从视频到动画的典型流程
虽然项目原始材料没有给出完整代码细节,但从标题和通用实现角度看,整个思路通常会这样组织:
- 输入视频后,先做帧间运动估计,找到哪些区域在运动。
- 将这些运动区域聚类成若干刚性运动原语。
- 把 3D 目标物体也编码到同一个潜在空间。
- 在潜在空间里,把视频运动原语的时序信息与目标物体的静态几何信息混合。
- 通过可微渲染或 3D 投影,把混合结果还原成动画帧。
这里的“原语数量”很关键。原语太少,动作细节不足;原语太多,容易过拟合到视频的噪声上。后面参数部分会单独讲。
3. 想复现或试用,先准备什么
3.1 硬件与依赖
这类项目本质是视频理解加 3D 渲染的联合优化,通常避不开深度学习框架。常见的项目配置会涉及 PyTorch、CUDA、第三方渲染库和若干数据处理包。
硬件方面,先别幻想单张普通显卡就能直接跑最复杂配置。最低限度需要有 NVIDIA 显卡,显存至少 8GB 起步比较稳妥。如果项目使用神经辐射场或者 3D 高斯泼溅类渲染,24GB 显存也不是新鲜事。
我刚拿到这类项目时,一般不是先看论文,而是先看两处:
- 项目仓库里写的 requirements 版本
- demo 脚本默认数据的分辨率和帧数
如果 requirements 里要求 CUDA 版本比较高,本地环境又装了老版本驱动,那大概率第一步就会卡在安装依赖上。这里我建议不要追求把环境和版本一次性装到“完美”,而是先创建一个独立环境,再把最新依赖一层层装。
conda create -n blarm python=3.9 conda activate blarm pip install torch torchvision具体 torch 版本要以项目说明为准。上面只是通用流程,不代表官方依赖,千万不要直接照抄后抱怨跑不起来。
3.2 数据准备:视频选择标准
很多初学者拿到这类项目,第一个想法就是找一段高难度视频测试,结果十个里面九个不收敛。
我更建议先准备一段“运动简单、背景干净、目标明确”的视频做冒烟测试。判断标准如下:
- 视频里只有一个主要运动物体,没有多人交叉、遮挡频繁、快速相机移动。
- 物体运动幅度适中,能看到周期性动作最好。
- 背景不要频繁变化,避免网络把背景光流学进运动原语。
- 视频时长不要太长,十几秒到几十秒足够,过长只会拖慢训练和试错速度。
- 分辨率不需要 4K,但画面不能模糊掉帧。
如果手头只有手机随手拍的抖动视频,先做一次镜头稳定和裁剪,能省很多调参时间。
3.3 3D 资产准备:Mesh、NeRF 还是建模文件
BLARM 最终驱动的是一个 3D 对象,所以目标模型的形式会影响处理路径。
目前常见的 3D 输入形式有这么几类:
| 输入类型 | 常见来源 | 处理重点 |
|---|---|---|
| OBJ/GLB/FBX 网格 | 建模软件、扫描设备、3D 素材库 | 需要统一朝向和尺度,检查网格是否封闭 |
| 点云 | 结构光相机、激光扫描 | 需要先做重建或补面,否则渲染困难 |
| NeRF/Gaussian 场 | 多视角图片重建 | 运行资源高,但渲染质量好 |
| 单张图片重建的模型 | 图像转 3D 工具 | 几何不稳定,容易出现运动拉伸 |
我建议第一次测试用最简单的网格模型,最好是单一物体中心位于坐标原点,朝向规范。不要一上来就上高精扫描模型,那个后期会分不清是算法问题还是模型法线问题。
3.4 先跑通最小样例再谈实验
不管项目设计多复杂,拿到后都要先拆成三步:
- 启动:确认训练或推理脚本能跑起来。
- 单条样例:用项目自带的 demo 视频和 demo 模型跑通一次完整流程。
- 批量处理:确定输出格式、命名规则、失败重试机制之后再扩展。
第二步是整个流程最花时间的。如果跑通了但结果不理想,先别急着换算法,请先确认目标模型和视频物体在姿态、类别、视角上是否有足够对齐。这一步没有对齐,后面无论如何调参都白搭。
4. 参数怎么设,结果怎么判断
4.1 核心参数有哪些
虽然 BLARM 具体配置没有公开细节,但这一整类方法通常会包含几组关键参数。你可以把它们当成排查清单,而不是硬编码数值。
| 参数维度 | 作用 | 我的判断思路 |
|---|---|---|
| 原语数量 | 控制动作分解的细粒度 | 先小后大,看动作完整度是否显著提升 |
| 输入视频帧数 | 决定时间建模窗口长度 | 先少帧测试,跑通后再加时长 |
| 图像分辨率 | 影响渲染精度和显存 | 最好固定较低分辨率做代码验证 |
| 训练迭代步数 | 决定网络收敛程度 | 看 loss 是否下降,不要盲目堆步数 |
| 学习率 | 影响稳定性 | 太高会抖动,太低会卡住 |
| 相机参数 | 决定 3D 投影位置 | 先手动核对是否和目标模型匹配 |
| 输出帧率 | 影响动画流畅度 | 按视频原始帧率导出最省事 |
这里最容易犯的错误是“别人推荐的参数直接套到自己数据上”。默认参数只保证官方数据或类似场景可复现,换数据后必须重新验证。先把参数记录成一个配置文件,每次只改一个变量,这样出问题时才能快速回退。
4.2 成功输出长什么样
一个相对成功的结果,至少应该同时满足四点:
- 目标 3D 模型的静态标识没有丢失。最直接的体现是:一帧一帧看,仍能认出是同一个物体。
- 运动是连续的,不会每几帧跳变一次。
- 局部部件没有明显穿模。比如两条腿交叉时不能严重互相穿透。
- 整体运动节奏和输入视频接近。如果视频是慢速起伏,输出却像快速抽搐,说明原语时序学歪了。
如果只控制一个指标,我会优先看“时序连续性”。很多项目单帧渲染出来都很好看,做成视频后就是高频抖动,这个是运动原语没有平滑混合的缘故。
4.3 用学习任务分开验证
实际复现时,我不建议只盯着最终渲染视频看。更实用的做法是把验证拆成两层:
- 运动验证:输入视频换成另一个同类别物体,原动作权重是否能复现。
- 外观验证:输入视频不变,替换目标 3D 模型,运动是否还能保持大部分节奏。
如果第一层失败,问题大概率在运动原语的数量或混合权重上。如果第二层失败,问题大概率在潜在空间里几何特征和运动特征没有解耦。知道是哪一层出问题,调参才有效率。
4.4 常见失败和排查顺序
这里列一份高频排查顺序,按优先级从高到低:
- 现象:训练时 loss 不下降。先看输入视频是否裁剪干净、目标模型是否有 NaN、相机参数是否单位不统一。
- 现象:输出动画扭曲。先看原语数量是否太少,再看目标模型坐标中心是否落在远处。
- 现象:动作和视频完全不像。先看网络是否学成了相机运动,把相机固定后重新训练。
- 现象:单张图正常但视频闪烁。先看相邻帧原语权重是否突变,再看输出是否直接回归到视频帧而忽略了 3D 结构。
- 现象:显存占用过高。先把视频分辨率降到 512 以下,并降低一次输入的视频帧数。
遇到报错不要一上来怀疑模型。先看日志发生的位置,再去检查对应的数据文件路径、依赖版本和输出目录权限。至少有一半的问题,最后都是文件没有生成或者路径写错。
5. 换到自己数据时容易踩的坑
5.1 源视频和目标模型必须“语义接近”但不等于“外观一致”
换数据测试时,最大的坑是认为“只要是同一个动作类别就行”。
比如视频里是猫在走路,但目标 3D 模型是一条长尾蜥蜴。运动原语也许能把蜥蜴身体驱动起来,可四条腿的步态相位、尾巴摆动方式、身体高低起伏都很难一致。因为原语是从视频里拆出来的,它并不知道目标模型的生物结构差异。
我的建议是先按相似度分三档:
- 同物体同类姿态:最容易复现,比如视频是木马动画,目标是简化版木马。
- 同类别不同姿态:需要目标模型提供较好的初始位置,否则网络容易学到一个混乱的中间状态。
- 跨类别语义动作:当作高风险实验,成功则是亮点,失败不要奇怪。
5.2 相机位姿和尺度是隐蔽杀手
这类方法在潜在空间里混合运动,但相机位姿依然是影响成败的重要前置信息。
如果你的视频是用普通手机围着物体拍摄一圈,那相机的运动很复杂。如果目标 3D 模型固定在一个透视角度下,想输出匹配视频的动画,就需要先估计相机的内外参数,否则算法会把“相机旋转”和“物体运动”混在一起。
对于 3D 网格资产,我一般会在预处理时做四件事:
- 把模型中心平移到原点。
- 统一到米或厘米单位。
- 调整朝向,让模型正面朝 Z 轴或 Y 轴。
- 检查网格是否封闭,有没有明显破面。
这几步不起眼,却直接影响投影和裁剪,进而影响运动原语的质量。
5.3 批量任务必须考虑输出命名和失败跳过
写代码的时候,单次调用很容易跑通。真正用于生产时,通常是几十个视频和一堆 3D 模型要批量跑。此时至少要建立四套机制:
- 输入清单:记录每个视频和哪个 3D 模型配对。
- 配置快照:每次运行把参数保存在结果目录里。
- 日志目录:单独输出每一轮 train/eval 日志。
- 断点续跑:某个模型失败后,不阻塞后续任务。
输出命名也建议规范化,例如video01_model01_origNum8_fps24.mp4。这样后续分析哪个参数起了作用,一眼就能看出。
5.4 和渲染引擎对接要注意的格式问题
如果最终要进 three.js、Unity、Unreal 或其他渲染引擎,通常不能只导出图片序列。需要把运动转化成引擎可读取的格式。
此时的常见问题是格式不一致。比如引擎里模型的骨骼名称和节点的轴心方向不同,同样一个位移动画就会出现位置错乱。接口对接时,最保险的方式是先导出一个简单立方体测试动画,确认坐标手柄定义一致后,再替换真实模型。
如果目标 3D 模型本身就是扫描生成的网格而没有拓扑一致性,逐帧形变导出会非常困难。这时候要么把动画烘焙成顶点缓存,要么在建模软件里重新拓扑后使用。
6. 从研究 demo 走向相对稳定使用,还需要考虑什么
6.1 不要高估“自动”二字
我会给所有准备把这方法接入生产管线的朋友一个提醒:工具的自动化不等于数据处理的自动化。
视频质量、单目标程度、光照变化、目标模型的几何质量,这些都不会自动消失。前期数据清洗仍然占整个工作量的很大比例。想直接原始视频拖进去就得到可用动画,目前来看风险很高。
更务实的处理方式是把它放在“预演/方案比对”环节:先快速生成一批候选动画,人选出可行的,再进入绑定细化或高分辨率渲染。
6.2 可能的优化方向
如果后续想深入研究,有几个方向通常值得尝试:
- 增加显式正则项,让相邻原语的刚体变换尽可能平滑,减少高频抖动。
- 引入少量交互标注,比如手动指定哪个原语主要影响哪个部件,让学习更稳。
- 对视频做光流或分割预处理,先把背景和前景分开,避免背景运动污染潜在空间。
- 结合 3D 高斯泼溅等更高效的渲染表示,在保证质量的同时降低计算量。
- 把运动原语先学成一个小型动作字典,再对不同目标模型做检索和重映射,这样复用性更强。
这些都属于基于通用经验的扩展判断,不一定在 BLARM 项目中已经支持。动手前务必先确认项目自身有没有这类模块,别把别人的设计目标强加进去。
6.3 什么时候应该放弃用这个方案
使用边界这件事,比技术本身更重要。
如果视频里的物体是流体、烟雾、颗粒物,或者目标 3D 模型带有大量自由漂浮部件,那用“刚性运动原语”去建模就很不合适。因为这类对象的核心特征是连续变形和拓扑变化,刚体原语组合很难覆盖。
同样,如果视频里目标物体大部分时间被遮挡,只有两三帧能看到完整轮廓,网络几乎没有可靠的运动监督信号。这种情况下,与其硬跑,不如换用人工关键帧或者补拍数据。
资源受限环境下也不要硬上。低显存机器可以跑小分辨率,但批量任务和长时间训练一定要有内存、磁盘和日志监控。直接开最大并发结果资源耗尽,既浪费时间也浪费电。
6.4 长期使用的建议
如果你看好这类“从视频驱动 3D 物体”的方向,我的建议是把它当成一个持续迭代的学习型资产,而不是一次性脚本。
第一阶段用官方 demo 或最接近的样例跑通原理。 第二阶段建立一套自己的数据规范,包括视频时长、模型格式、相机参数、输出帧率。 第三阶段针对最常用的几类资产做小规模批量验证,记录原语数量、分辨率、训练步数对应的效果差异。 第四阶段再把量化结论写进使用文档,方便团队其他人复制。
踩过几次之后我发现,很多问题不是模型能力不够,而是前置环境和输入材料没有处理干净。视频没裁剪、模型坐标偏移、相机参数标反、输出目录没权限,这些都会让一个原本不错的方法看起来像“不能用”。先确认这些可控项,再怀疑模型,是复现这类项目最省时间的方式。
如果只是学习,默认配置通常已经够用。如果要长期使用,日志、输出目录、任务队列和失败重试这些工程习惯,要提早养成。