跳到主要内容

快速开始 Lambda Sim

本文从生成第一张图像开始,介绍 Lambda Sim 的场景导入、运行时对象、USD 动画回放和图像导出,再按需体验机器人动力学示例。

1. 本教程的目标与范围​

章节完成后得到什么是否必需
开始前的准备可导入的依赖、明确的运行设备和完整资产是
生成第一张图像一张来自 USD 相机的 PNG是
理解 Python 工作流程理解场景、模型、状态与渲染对象的关系建议
动画帧与多相机图像Franka 动画帧、视频及多相机扩展说明按需
机器人动力学示例FR3 瓶子抓取放置与结果判定按需
单求解器与耦合求解器创建求解器、注册到场景并推进仿真按需
结果验收与常见问题判断输出是否完整、内容是否合理是

本教程直接使用 Lambda Sim,不要求准备 RoboEvolve 训练容器、策略 checkpoint 或八卡集群。RoboEvolve 是上层任务编排和数据采集平台之一;策略训练、任务评测及成功率统计不属于本页内容。

2. 开始前的准备​

开始前,请确认 Lambda Sim 的运行环境(依赖、USD 资产、渲染器、Python 与 GPU)已准备就绪。

3. 快速体验:生成第一张图像​

在已安装 Lambda Sim 的环境中,进入 LambdaSim 仓库根目录,通过 src/lambdasim/lambdasim_main.py 执行渲染。以下命令使用 Linux / Bash:

python src/lambdasim/lambdasim_main.py \
tests/assets/shaderball.usda \
--output-dir outputs/quickstart/static \
--camera-indices 0 \
--render-outputs color \
--width 640 \
--height 480 \
--spp 32 \
--device musa:0
参数含义
tests/assets/shaderball.usda输入 USD 场景,作为位置参数传入,不使用--usd-path
--output-dir图像保存目录,不存在时会创建
--camera-indices 0使用导入后索引为0 的传感器相机
--render-outputs color只生成彩色图像,不生成深度或分割结果
--width、--height输出图像宽度和高度
--spp每像素采样数;先用32 验证流程,再按画质需要提高
--device musa:0请求使用 MUSA 设备0

本例显式选择一个相机和 color 输出,且不传 --sequence,因此生成一张静态彩色图像。输出按相机索引和通道分目录保存,默认文件名为输入 USD 的文件名主体加 .png:

outputs/quickstart/static/
`-- camera_000/
`-- color/
`-- shaderball.png

成功后会输出 Rendered outputs (color) to: .../outputs/quickstart/static。打开 outputs/quickstart/static/camera_000/color/shaderball.png,确认分辨率为 640×480,材质球与地面正常显示。

可以通过 --output-name 修改静态图像的文件名,但文件仍保存在相机和通道子目录中。若省略 --camera-indices,会渲染所有传感器相机;若省略 --render-outputs,会生成 color、depth、segmentation 三类输出,其中深度与分割还会保存 .npy 原始数据,PNG 仅用于预览。

命令会导入 USD 场景中的相机和灯光,并使用相机索引 0 渲染。首次运行可能涉及原生组件初始化或内核编译,不应把首次耗时当作稳定渲染性能。

4. 理解最小 Python 工作流程​

4.1 导入场景并创建运行时对象​

导入 USD 前,应根据用途选择接口:

接口适用场景关键区别
Scene.add_usd()场景展示、图像渲染和已有 USD 动画回放通用导入入口,默认namespace="auto"、rigid_read_only=True。自动导入可区分普通几何与具有时变几何的网格,但不等于自动装配可控制的机器人或配置动力学求解器。
Scene.add_rigid_usd()用于需要mujoco物理求解的刚体和关节机器人使用刚体装配路径,读取刚体、碰撞形状、关节和驱动等信息。通过prim_path 选择导入根节点,并可设置 floating、collapse_fixed_joints、enable_self_collisions 等装配参数。
Scene.add_solid_usd()布料、软体等可变形体的几何与约束导入,用于Alphacore Vera Solver使用粒子和网格数据路径,支持分别提供geometry_usd 和 constraint_usd,并通过 geometry_prim_path、constraint_prim_path 选择几何与约束节点。这里的 solid 指固体力学。

使用 add_solid_usd() 时,默认 constraint_mode="usd",必须提供 constraint_usd;若明确只导入几何、不加载约束,可使用 constraint_mode="none"。当前 constraint_mode="generated" 尚未实现,不能依赖它自动生成约束。

这三个接口负责导入和组织数据,调用后都不会自动开始物理仿真。需要物理运动时,还要在 finalize() 后注册与场景数据匹配的求解器,再调用 scene.step(dt);已有 USD 动画的回放则使用 scene.update_by_frame()。第 6 节的机器人示例采用 add_rigid_usd()。

本节使用材质球场景验证导入和渲染,因此选择 add_usd()。

在仓库根目录对应的 Python 环境中执行以下完整示例:

from pathlib import Path
from lambdasim.scene.scene import Scene

usd_path = Path("tests/assets/shaderball.usda")
scene = Scene()
try:
scene.add_usd(str(usd_path), prim_path="/", namespace="auto")
scene.finalize(device="musa:0", env_num=1)

model = scene.get_model()
simdata = scene.get_simdata()
render_scene = scene.get_render_scene()

print(type(model).__name__)
print(type(simdata).__name__)
print(type(render_scene).__name__)
print("cameras:", len(render_scene.cameras_sensor))
print("lights:", len(render_scene.lights))
finally:
scene.close()

前三项预期分别为 Model、SimData、RenderScene,该入门场景应有一个传感器相机和一个灯光。

对象或调用作用
Scene()创建场景管理对象
add_usd()导入 USD;namespace="auto" 根据场景内容组织导入
finalize()为指定设备和环境数量创建运行时对象
Model保存场景结构及模型参数
SimData保存运行时状态与控制数据
RenderScene保存渲染使用的几何、材质、相机与灯光
close()结束使用后释放求解器和运行时资源

finalize() 不会自动创建动力学求解器,也不会自动推进物体运动。本例仅验证场景导入和运行时对象创建,不生成图像。

4.2 手动创建渲染器并渲染​

下面给出从场景初始化到图像保存的完整示例,按照当前 lambdasim_main.py 的调用方式,手动创建渲染器、设置输出通道、选择相机并渲染当前场景。

本例使用支持 RenderOutput、set_camera_index() 和 enable_taa 的新版 path-tracer,依赖版本应与当前 LambdaSim 匹配:

from pathlib import Path
import imageio.v3 as iio
from path_tracer.config import EnvironmentLightMode, RenderConfig, RenderOutput
from path_tracer.lambda_renderer import LambdaRenderer
from lambdasim.scene.scene import Scene

usd_path = Path("tests/assets/shaderball.usda")
output_path = Path("outputs/quickstart/python_static/shaderball.png")
output_path.parent.mkdir(parents=True, exist_ok=True)

scene = Scene()
try:
scene.add_usd(str(usd_path), prim_path="/", namespace="auto")
scene.finalize(device="musa:0", env_num=1)
render_scene = scene.get_render_scene()

renderer = LambdaRenderer(render_scene, 0, device_id=0)

config = RenderConfig()
config.enable_path_tracing = True
config.environment_light_mode = EnvironmentLightMode.DOME
config.width = 640
config.height = 480
config.spp = 32
config.output_mask = RenderOutput.COLOR
config.use_env_light = True # 开启环境光

# 关闭 ReLAX 降噪和 TAA,启用 OIDN 降噪
config.enable_denoising = False
config.enable_taa = False
config.enable_oidn_denoising = True
renderer.set_render_config(config)

camera_index = 0
light_indices = scene.get_light_list()
renderer.update_scene(render_scene, light_indices)
renderer.set_camera_index(camera_index)
image = renderer.render()["color"]
iio.imwrite(output_path, image)
print("Rendered image to:", output_path)
finally:
scene.close()
调用作用
LambdaRenderer(render_scene, 0, device_id=0)为环境索引0 创建渲染器,使用 GPU 编号 0,与本例的 musa:0 对应
RenderConfig()配置路径追踪、输出通道、环境光、分辨率、采样数和降噪
renderer.set_render_config(config)将配置应用到渲染器
scene.get_light_list()获取场景中的灯光索引
renderer.update_scene(render_scene, light_indices)向渲染器更新当前场景和灯光,不选择相机
renderer.set_camera_index(camera_index)选择本次渲染使用的传感器相机
renderer.render()执行渲染,返回以输出通道名为键的字典
renderer.render()["color"]取得返回结果中的彩色图像数据
iio.imwrite(...)将图像保存为 PNG

config.output_mask 决定启用哪些输出通道。本例只启用 RenderOutput.COLOR,因此取返回字典的 "color" 数据保存,不能将整个字典直接传给 iio.imwrite()。

降噪配置中,enable_denoising 对应 ReLAX,enable_taa 控制 TAA,enable_oidn_denoising 控制 OIDN。本例仅启用 OIDN;不再使用旧的 use_temp_denoiser 字段控制时序处理。使用 OIDN 时还需要渲染器构建包含对应支持。

预期生成 outputs/quickstart/python_static/shaderball.png。这里的文件路径由代码中的 output_path 显式指定,不会自动套用第 3 节命令行入口的相机和通道目录规则。打开图像,确认分辨率为 640×480,材质球与地面正常显示。

后续需要渲染物理推进后的状态时,可在 scene.step(dt) 同步渲染状态后,依次调用 renderer.update_scene()、renderer.set_camera_index() 和 renderer.render()["color"];第 6 节现有机器人示例展示了这种用法。

5. 生成动画帧与多相机图像(可选)​

本节使用 scripts/example_franka_mujoco_musa.py 展示如何把机器人仿真过程渲染成动画帧。机器人运动由求解器推进产生,不是回放 USD 中预先写入的动画。

该脚本当前固定渲染相机索引 0,没有多相机命令行参数。下面先运行现有单视角示例,再说明扩展多相机时需要调整的位置。

5.1 运行 Franka 动画示例​

开始前需要准备匹配的 Franka USD、外部资产及 MuJoCo Warp MUSA 环境,并确认资产引用完整。脚本默认引用的 tests/assets/local_test/robotwin_asset/franka-panda/franka-panda.usda 在本稿核对的本地目录中不存在,下面用参数显式指定资产位置。本节使用的 Franka Panda 与第 6 节的 FR3 资产不同,不要混用。

在 LambdaSim 仓库根目录执行,将资产路径替换为实际位置:

export FRANKA_USD="/path/to/franka-panda/franka-panda.usda"
export FRANKA_PRIM="/World/Embodies/franka_panda"

python scripts/example_franka_mujoco_musa.py \
--usd "$FRANKA_USD" \
--prim-path "$FRANKA_PRIM" \
--device musa:0 \
--env-num 1 \
--steps 23 \
--fps 24 \
--sim-substeps 10 \
--width 640 \
--height 480 \
--spp 32 \
--output-dir outputs/quickstart/franka_animation

脚本先渲染初始帧 frame_0000.png,再在每次外层推进后渲染一帧,因此 --steps 23 对应 24 张图像,编号为 0~23。

参数对动画输出的影响
--steps 23执行 23 次外层推进,加上初始状态共渲染 24 帧
--fps 24视频以 24 fps 编码;未指定--dt 时,也参与计算仿真子步时长
--sim-substeps 10每次外层推进包含 10 个物理子步
--width 640 --height 480每帧图像的分辨率
--spp 32每像素采样数
--output-dir中间帧和最终视频的输出目录

本例没有设置 --dt,物理子步时长为 1 / 24 / 10 秒。仿真总推进时长为 23/24 秒;包含初始帧的视频共 24 帧,播放时长约为 1 秒。

5.2 理解动画帧的生成过程​

脚本中的主要调用关系如下:

函数或操作作用
build_scene(args)使用add_rigid_usd() 导入机器人,配置控制模式、地面和相机,并完成 finalize()
run_simulation(scene, args)注册求解器,更新控制目标,推进物理状态并逐帧调用render_frame()
scene.upload_control()、scene.step(sim_dt)在物理子步内上传控制并推进仿真,同步渲染状态
render_frame(scene, args, frame_name)配置灯光和渲染器,渲染相机索引0,保存一张 PNG
compose_video(...)按文件名顺序读取 PNG,并按args.fps 编码视频
clear_frame_outputs(...)视频生成成功后删除中间 PNG

直接运行第 5.1 节命令,成功后最终保留:

outputs/quickstart/franka_animation/
`-- franka_mujoco_musa.mp4
中间帧会被清理

直接运行脚本时,中间 PNG 写在输出目录根部,不会自动生成 camera_000/ 子目录;视频生成后,脚本会删除该目录中的所有 frame_*.png。

编码会读取输出目录中已有的匹配帧,同名视频会被覆盖。应使用没有旧帧的独立目录,不要在其中存放需要保留的 PNG。

5.3 多相机扩展的位置​

当前 build_scene() 显式添加一个传感器相机,render_frame() 更新场景后通过 renderer.set_camera_index(0) 固定选择相机索引 0,再取 renderer.render()["color"] 保存图像。

第 3 节的 lambdasim_main.py 支持 --camera-indices,但本节的 example_franka_mujoco_musa.py 没有该参数,不能直接给这个示例脚本添加 --camera-indices 0 1。

若要基于这个示例扩展多相机,需要:

  1. 在 build_scene() 的 finalize() 之前添加其他传感器相机,并分别设置视角。
  2. 每个待输出的物理时刻先调用一次 renderer.update_scene(render_scene, light_indices),再遍历相机索引,逐一调用 renderer.set_camera_index(camera_index) 和 renderer.render()["color"]。
  3. 将结果按 camera_000/frame_0000.png、camera_001/frame_0000.png 等路径保存,并保留这些帧供后续拼接。
  4. 调整原有视频编码和清理流程,使其与按相机分目录的结构一致。

同一物理时刻应先完成所有相机的渲染,再继续推进仿真,不能为每个相机分别调用 scene.step()。不同相机应使用相同帧编号和图像尺寸。

6. 运行机器人动力学示例:瓶子抓取放置(可选)​

6.1 入口和额外条件​

本节使用 scripts/example_ik_bottle_pick_place_mujoco_musa.py。示例通过逆运动学(IK)计算 FR3 机械臂的关节目标,配合夹爪控制和 SolverMuJoCoMusa 完成瓶子的侧向抓取、抬升、搬运与放置。

动作由脚本中的任务状态机和 IK 控制器生成,不依赖训练好的策略 checkpoint。与第 5 节主要观察机器人运动不同,本节还包含瓶子与夹爪、桌面的物理交互,以及示例自己的成功判定。

以下命令均在 LambdaSim 仓库根目录执行,使用 Bash;脚本、资产和输出目录均使用相对路径。

运行本节前还需要:

  • 可导入的 mujoco、mujoco_warp、warp,且与 MUSA 运行环境匹配。
  • tests/assets/fr3_franka_hand.usda 机器人资产。
  • tests/assets/local_test/robotwin_asset/001_bottle/0/001_bottle_0.usda 瓶子资产及其外部引用。

检查入口依赖与参数:

python -c "import mujoco, mujoco_warp, warp; from lambdasim.solvers.solver_mujoco import SolverMuJoCoMusa; print('robot imports: OK')"
python scripts/example_ik_bottle_pick_place_mujoco_musa.py --help

6.2 运行瓶子抓取放置​

在 LambdaSim 仓库根目录运行;若资产位于其他目录,请将命令中的资产路径替换为实际相对路径:

python scripts/example_ik_bottle_pick_place_mujoco_musa.py \
--usd tests/assets/fr3_franka_hand.usda \
--bottle-usd tests/assets/local_test/robotwin_asset/001_bottle/0/001_bottle_0.usda \
--device musa:0 \
--steps 2400 \
--fps 60 \
--sim-substeps 5 \
--render-every 4 \
--width 320 \
--height 240 \
--spp 16 \
--output-dir outputs/quickstart/ik_bottle_pick_place
参数含义
--usd、--bottle-usd分别指定 FR3 机器人和瓶子 USD
--steps 2400最多执行 2400 次外层推进;状态机进入最后阶段后会提前结束
--fps 60外层推进的时间基准,每次推进对应1/60 秒仿真时间
--sim-substeps 5每次外层推进包含 5 个物理子步,子步时长为1/300 秒
--render-every 4每 4 次外层推进渲染一次;进入最后阶段时也会渲染
--width 320 --height 240 --spp 16设置输出分辨率与每像素采样数
--output-dir中间帧与视频的保存目录

该命令最多推进 40 秒仿真时间,不代表实际运行只需要 40 秒。

脚本会渲染初始状态。若没有提前结束,本例共输出 601 帧参与编码;提前结束时,帧数取决于实际推进次数,不能按固定帧数判断是否成功。最终视频路径为:

outputs/quickstart/ik_bottle_pick_place/
`-- ik_bottle_pick_place_mujoco_musa.mp4

6.3 理解 IK 控制和仿真流程​

阶段示例中的操作
构建物理场景build_scene() 用 add_rigid_usd() 导入固定基座机器人和可自由运动的瓶子,添加桌面、地面与相机
配置运行时数据finalize() 后配置瓶子质量、接触与摩擦参数
注册求解器run_simulation() 创建 SolverMuJoCoMusa,通过 scene.add_solver() 绑定
构建 IK 模型build_ik_model() 创建只包含机器人的运动学模型
计算控制目标BottlePickPlaceController.update_control() 根据当前任务阶段生成末端目标,通过 iterative_ik_device_into() 求解关节位置,并设置夹爪目标
推进物理状态scene.upload_control() 上传控制,在子步循环中调用 scene.step(sim_dt, sync_render=False)
切换任务阶段controller.advance_tasks() 根据实际末端位姿误差和阶段持续时间判断是否进入下一阶段
按需渲染到达渲染间隔或进入最后阶段时,同步渲染状态,再由render_frame() 生成图像
评估并导出evaluate_success() 检查瓶子最终位姿,输出判定结果并编码视频

任务阶段大致为:调整夹爪朝向、移动到瓶子后方、下降和接近、精调、闭合夹爪、抬升、搬运、下降放置、松开夹爪、撤离和回到初始位置。

阶段切换不是只按固定时间播放动作。当前代码要求阶段持续至少 1 秒,且末端位置误差小于 0.015 米、旋转误差小于 2 度,才进入下一阶段。因此步数不足或目标未到达时,可能停留在中间阶段。

本例使用 scene.step() 进行物理推进,不使用 scene.update_by_frame() 回放已有 USD 动画。IK 只负责计算关节目标,接触、夹持和瓶子运动仍由物理求解器计算。

6.4 检查抓取结果和视频​

先检查终端打印的结果。进入最后阶段且瓶子位姿满足判定时,输出应包含:

task_index=11
bottle_pick_place_success=True

task_index 表示状态机当前阶段,11 对应 HOME。当前 completed() 在进入该阶段时即返回真,不代表机械臂已经回到初始位置。

bottle_pick_place_success 来自 evaluate_success(),要求瓶子同时满足:

  • 水平位置与目标点的距离小于 0.04 米。
  • 高度与目标高度的差值小于 0.04 米。
  • 朝向与目标直立姿态的角度差小于 12 度。

成功判定与状态机阶段不是同一个检查。True 只表示瓶子最终位姿通过上述阈值,不代表完成了通用任务评测或长期稳定性验证。

7. 单求解器与耦合求解器的创建和使用(可选)​

本节以 MuJoCo 刚体求解器、VeraFiber 布料求解器,以及两者的单向耦合为例,说明如何创建求解器、注册到场景并推进仿真。

7.1 选择求解器与接入顺序​

类型类适用场景
单求解器:刚体SolverMuJoCoMusa机器人关节控制、刚体接触和抓取,例如第 6 节的瓶子抓取放置
单求解器:布料SolverVeraFiber布料变形及其与静态碰撞体的接触
耦合求解器:刚体与布料SolverMuJoCoToVeraFiberOneWayMuJoCo 推进刚体,再将刚体运动传给 VeraFiber,驱动布料接触响应

这里的“单求解器”指由一个求解器负责所需的物理计算,不是只能模拟一个物体。耦合求解器则在内部组织多个求解器的推进顺序和状态传递。

三种用法都遵循同一接入顺序:

  1. 构建场景,使用 add_rigid_usd()、add_solid_usd() 或 Builder 准备所需物理数据。
  2. 调用 scene.finalize(device="musa:0", env_num=1)。
  3. 使用 scene.get_model() 创建求解器。
  4. 调用 scene.add_solver(solver),该方法会执行求解器的 initialize(sim_data)。
  5. 按需更新控制目标并调用 scene.upload_control(),再通过 scene.step(dt) 推进。
  6. 渲染所需帧,结束使用后调用 scene.close()。
一个场景只注册一个求解器对象

当前 Scene 只保存一个活动求解器。已经调用过 scene.add_solver() 后,再注册另一个求解器会报错。

耦合时应只注册一个 SolverMuJoCoToVeraFiberOneWay,由它创建并管理内部的 MuJoCo 和 VeraFiber 求解器,不是向同一个场景分别添加两个单求解器。

下列代码是求解器接入片段,放在对应场景的 finalize() 之后、close() 之前执行。各片段针对不同的场景构造方式,不能在同一个 scene 上依次执行,也不能直接接在第 4 节已经关闭的场景之后。

7.2 创建和使用单求解器​

刚体与机器人:SolverMuJoCoMusa​

前提是 scene 已包含按刚体装配路径构建的物体或机器人,并在 musa:0 上完成单环境初始化。需要关节动作时,还应在构建阶段配置对应的控制模式和驱动参数。

from lambdasim.solvers.solver_mujoco import SolverMuJoCoMusa

solver = SolverMuJoCoMusa(
scene.get_model(),
device="musa:0",
env_num=1,
)
scene.add_solver(solver)

dt = 1.0 / 300.0
for _ in range(300):
scene.upload_control()
scene.step(dt)

该片段推进 1 秒仿真时间。若控制器需要逐步修改关节位置、速度或力矩目标,应在 scene.upload_control() 之前更新控制数据;仅创建求解器不会自动生成机器人动作。完整控制流程见第 6 节。

布料与静态碰撞体:SolverVeraFiber​

前提是 scene 已通过 add_solid_usd() 导入匹配的几何与约束,并包含标签为 vera_box_collider 的静态形状。这里的静态形状指构建时 body=-1,不是仅把某个动态刚体的速度设为零。

下面采用 scripts/example_standard_solid_verafiber_musa.py 的 IPC 静态碰撞体接入方式:

from lambdasim.solvers.solver_verafiber import SolverVeraFiber

solver = SolverVeraFiber(
scene.get_model(),
device="musa:0",
env_num=1,
core="ipc",
ipc_abd_collider_mode="fixed",
collider_labels=("vera_box_collider",),
)
scene.add_solver(solver)

dt = 1.0 / 60.0
for _ in range(60):
scene.step(dt)

core 可选 "ipc" 或 "xpbd",本例显式选择 IPC。collider_labels 对应 model.shape_label 中真实且唯一的形状标签,不是任意名称、body 名称或文件路径。

这里布料由 VeraFiber 推进,盒子作为固定碰撞体参与接触,不需要 MuJoCo。SolverVeraFiber.upload_control() 当前不执行操作,因此本例直接调用 scene.step()。

如果碰撞体需要跟随动态刚体运动,应使用耦合求解器。当前单独调用 VeraFiber 的 step() 推进动态碰撞体场景会报错。

7.3 创建和使用耦合求解器​

以 scripts/example_mujoco_verafiber_moving_box_musa.py 为例:场景中同时包含布料和由 MuJoCo 控制的移动盒子。盒子通过滑动关节运动,其形状标签为 vera_box_collider。

前提是这个混合场景已经完成 finalize(),并配置好盒子的关节速度目标。创建并注册耦合求解器:

import ektro as ek
from lambdasim.solvers.solver_mujoco_verafiber_oneway import (
SolverMuJoCoToVeraFiberOneWay,
)

solver = SolverMuJoCoToVeraFiberOneWay(
scene.get_model(),
device="musa:0",
env_num=1,
verafiber_gravity=ek.vec3(0.0, 0.0, -9.81),
mujoco_substeps=1,
verafiber_substeps=4,
verafiber_core="ipc",
collider_labels=("vera_box_collider",),
)
scene.add_solver(solver)

dt = 1.0 / 60.0
for _ in range(60):
scene.upload_control()
scene.step(dt)

verafiber_gravity 必须传入 ek.vec3,不能直接传普通元组。它指定布料侧的重力;移动盒子示例在构建场景时将 MuJoCo 侧重力设为零,再单独为布料指定向下的重力。

每次 scene.step(dt) 内部按以下顺序执行:

  1. 按 mujoco_substeps 推进 MuJoCo,更新刚体状态。
  2. 将刚体状态同步为 VeraFiber 的碰撞体状态或控制目标;本例 IPC 路径更新 ABD 碰撞体目标。
  3. 按 verafiber_substeps 推进 VeraFiber,计算布料变形和接触响应。
  4. 返回 Scene.step(),默认同步可供渲染使用的状态。

两侧子步划分的是同一段时间 dt,不是把两侧时间相加。本例中 MuJoCo 每步为 1/60 秒,VeraFiber 每子步为 1/240 秒;一次外层调用仍只推进 1/60 秒。

scene.upload_control() 在该耦合类中转发给 MuJoCo。不要再手动调用内部两个子求解器的 step(),否则会重复推进或绕过必要的状态同步。

当前示例是单向耦合

OneWay 表示 MuJoCo 的刚体运动影响 VeraFiber 布料。当前实现没有把布料的接触反作用力回传给 MuJoCo,不能将它描述成双向力反馈耦合。

“布料会被移动盒子带动”不等于“布料也会反过来改变盒子的 MuJoCo 运动”。

7.4 使用现有完整示例​

上面的代码只展示求解器接入。需要从场景构建一直运行到渲染输出时,可使用现有脚本。以下命令均在 LambdaSim 仓库根目录执行,使用相对路径。

两个示例默认读取 tests/assets/Towel_0/solid_geometry.usd 和 tests/assets/Towel_0/solid_constraint.usd,需要匹配的 alphacore-ektro、Ektro 和渲染依赖;耦合示例还需要 MuJoCo Warp MUSA 环境。

单 VeraFiber 求解器,布料与静态盒子接触:

python scripts/example_standard_solid_verafiber_musa.py \
--device musa:0 --env-num 1 \
--frames 60 --fps 60 --vera-core ipc \
--output-dir outputs/quickstart/single_verafiber

MuJoCo 与 VeraFiber 单向耦合,移动盒子与布料接触:

python scripts/example_mujoco_verafiber_moving_box_musa.py \
--device musa:0 --env-num 1 \
--frames 60 --fps 60 --vera-core ipc \
--mujoco-substeps 1 --vera-substeps 4 \
--output-dir outputs/quickstart/coupled_mujoco_verafiber

CLI 的 --vera-substeps 对应构造参数 verafiber_substeps,--vera-core 对应单求解器的 core 或耦合求解器的 verafiber_core。

当前 SolverVeraFiber 仅支持 env_num=1、env_index=0,包含它的耦合示例同样应使用一个环境。求解器设备还必须与场景模型的设备一致,不能在 finalize() 后仅修改求解器的设备名来迁移场景。

观察结果时,单求解器示例应关注布料与静态盒子的接触;耦合示例应同时关注盒子的关节运动、碰撞体同步和布料响应。需要渲染时,若使用了 scene.step(dt, sync_render=False),应在渲染前显式调用 scene.sync_render_from_sim_data(),再更新渲染器并生成图像。

8. 结果验收与常见问题​

8.1 验收清单​

验收对象通过标准
模块导入导入成功,包路径属于预期环境
USD 资产文件可解析、外部引用完整、相机灯光可找到
静态渲染PNG 可读取、尺寸正确,人工确认场景内容合理
图像序列每路帧编号完整且无多余帧,图像可读取
机器人示例视频可解码、帧数正确,运动与画面合理
求解器接入场景正确注册求解器,状态更新和接触响应符合预期

8.2 常见问题​

现象或错误优先检查处理方向
ModuleNotFoundError 或入口命令不存在当前 Python、虚拟环境和包路径按当前仓库README.md 补齐对应依赖,避免混用环境
原生库无法加载、后端初始化失败path-tracer 构建后端、驱动和 Toolkit确认与 MUSA 环境匹配,不要直接复用 CUDA 产物
Requested device ... is not available容器设备映射及设备编号检查进程内可见设备,再填写正确设备名
USD 引用缺失或只显示部分物体外部网格、纹理、payload 和 Git LFS 实体文件保留资产目录关系,先通过依赖检查
Scene has no sensor cameras to render.USD 是否包含可导入的相机使用有相机的场景;参数选择相机,不会创建相机
has no authored lights; rendering would be blackUSD 灯光及导入结果准备有效灯光;增加采样数不能解决无灯光问题
相机索引越界实际传感器相机数量入门场景只选0;多相机参数需匹配实际场景
图像全黑或视角错误灯光、相机位姿、材质和资产完整性先用入门资产定位环境问题,再检查自定义场景
帧数正确但画面不动是否存在 USD 时间采样,或是否真的运行求解器区分静态序列、动画回放与物理推进
scene already has a solver是否向同一场景重复注册求解器单求解器只注册一次;耦合场景只注册组合求解器
Dynamic VeraFiber ... colliders must be stepped by a coupled solver是否单独用 VeraFiber 推进动态碰撞体场景改用匹配的耦合求解器,或使用真正的静态碰撞体
SolverVeraFiber currently supports env_num == 1场景和求解器的环境数量VeraFiber 及本节耦合示例均使用单环境
视频无法编码或读取FFmpeg 程序、编码器和输出权限先确认 PNG 正常,再检查imageio-ffmpeg 环境
Franka 示例找不到 USD 或panda_link7机器人资产路径、Prim 路径和结构使用与示例匹配的 Franka 资产,不能随意替换机器人
Franka 视频生成后 PNG 消失是否已完成视频编码这是脚本成功后的清理行为,最终检查 MP4

8.3 源码入口与下一步​

以下路径均相对于 LambdaSim 仓库根目录,便于核对实际实现:

文件本页对应内容
README.md、pyproject.toml安装方式、依赖、Python 版本与命令入口
src/lambdasim/lambdasim_main.py场景渲染入口、相机选择、多通道输出与渲染配置
src/lambdasim/data_synth.py设备选择、静态图像、序列帧和相机参数
src/lambdasim/scene/scene.py导入、初始化、求解器注册、物理推进与动画回放
src/lambdasim/solvers/solver_mujoco.pySolverMuJoCoMusa 求解器
src/lambdasim/solvers/solver_verafiber.pyVeraFiber 单求解器及碰撞体接入约束
src/lambdasim/solvers/solver_mujoco_verafiber_oneway.pyMuJoCo 与 VeraFiber 单向耦合及子步调度
scripts/example_franka_mujoco_musa.pyFranka 控制、渲染、编码与中间帧清理
scripts/example_ik_bottle_pick_place_mujoco_musa.pyFR3 瓶子抓取放置、IK 状态机与成功判定
scripts/example_standard_solid_verafiber_musa.py布料与静态盒子的单求解器示例
scripts/example_mujoco_verafiber_moving_box_musa.py移动盒子与布料的单向耦合示例
tests/assets/shaderball.usda入门场景、相机、灯光及 payload 引用
tests/test_verafiber_solver_contract.pyVeraFiber 与耦合适配器的接口契约测试

后续可查阅 API 参考、术语表 和 常见问题。需要任务编排、策略训练或评测时,再进入 RoboEvolve 对应流程。