代码示例#
MuJoCo 附带了几个提供实用功能的代码示例。其中一些非常复杂(特别是 simulate.cc),但我们希望它们能帮助用户学习如何使用该库进行编程。
testspeed#
此代码示例对给定模型的模拟进行计时。计时过程很简单:对被动动力学(带有可选的控制噪声)的模拟进行指定步数的演练,同时收集有关接触点数量、标量约束以及来自内部分析的 CPU 时间的统计信息。结果随后被打印到控制台。若要模拟受控动力学而非被动动力学,可以安装控制回调 mjcb_control,或者修改代码以显式设置控制信号,如以下 仿真循环 章节所述。此命令行工具的运行方式为
testspeed modelfile [nstep nthread ctrlnoise npoolthread]
其中命令行参数为
参数 |
默认值 |
含义 |
|---|---|---|
|
(必需) |
模型路径 |
|
10000 |
每次演练的步数 |
|
1 |
并行运行演练的线程数 |
|
0.01 |
注入执行器的伪随机噪声的缩放比例 |
|
1 |
引擎内部线程池中的线程数 |
注释
当指定
nthread > 1时,代码会分配一个单一的 mjModel 和每个线程对应的 mjData,并并行运行nthread个相同的模拟。这可以在所有核心激活的情况下测试性能,正如在并行收集样本的强化学习场景中一样。最佳的nthread通常等于逻辑核心数。默认情况下,模拟从模型参考配置开始,且速度为零。但是,如果模型中存在名为“test”的关键帧,它将被用作初始状态。
ctrlnoise参数可以防止模型进入静态状态,因为在静态状态下,由于预热(warmstarts),人们可能会测量到人为加速的模拟。当指定
npoolthread > 1时,会创建一个具有指定线程数的引擎内部 mjThreadPool,以加速大型场景的模拟。请注意,虽然可以同时使用nthread和npoolthread,但需要这些不同类型多线程的场景通常是互斥的。为了获得更可重复的性能统计信息,请在 Linux 上使用
performance调频策略(governor),或在 Windows 上使用高性能电源计划,以减少 CPU 缩放带来的干扰。许多现代 CPU 包含“性能”核心和“效率”核心的混合体。为了获得更具解释性的性能统计信息,用户应考虑将进程限制为仅在同一类型的核心上运行。这可以通过 Linux 上的 taskset 命令,或 Windows 上的 start /affinity 命令来实现(macOS 上无法通过文档记录的 API 方式指定处理器亲和性)。
simulate#
此代码示例是一个功能齐全的交互式模拟器。它使用平台无关的 GLFW 库打开一个 OpenGL 窗口,并在其中渲染模拟状态。它内置了帮助、模拟统计信息、分析器和传感器数据绘图功能。模型文件可以作为命令行参数指定,也可以使用拖放功能在运行时加载。此代码示例使用原生 UI 渲染各种控件,并提供了如何使用新 UI 框架的说明。下面是 simulate 运行时的屏幕截图
交互通过鼠标完成;按 F1 键可查看包含可用命令摘要的内置帮助。简而言之,左键双击可选中对象。用户随后可以通过按住 Ctrl 并拖动鼠标对选定对象施加力和力矩。仅拖动鼠标(不按 Ctrl)用于移动相机。有键盘快捷键可用于暂停模拟、重置和重新加载模型文件。后一种功能在 XML 编辑器中编辑模型时非常有用。
代码很长但注释合理,最好直接阅读。我们在此提供一个高层概述。main() 函数初始化 MuJoCo 和 GLFW,打开一个窗口,并安装用于鼠标和键盘处理的 GLFW 回调。请注意,没有渲染回调;GLFW 将控制权交给用户,而不是在后台运行渲染循环。主循环负责处理 UI 事件和渲染。模拟在后台线程中处理,该线程与主线程同步。
鼠标和键盘回调执行任何必要的操作。这些操作中有许多调用了 MuJoCo 的 抽象可视化 机制所提供的功能。事实上,此机制旨在或多或少直接挂钩到鼠标和键盘事件,并提供相机及扰动控制。
分析器和传感器数据绘图说明了 mjr_figure 函数的使用,该函数可以绘制带有网格、注释、轴缩放等的复杂二维图形。分析器中呈现的信息是从 mjData 的诊断字段中提取的。它是调整约束求解器算法参数的一个非常有用的工具。模型中定义的传感器输出被可视化为柱状图。
请注意,分析器显示了使用高分辨率计时器收集的计时信息。在 Windows 上,根据电源设置,操作系统可能会降低 CPU 频率;这是因为 simulate.cc 大部分时间都在睡眠以减慢至实时速度。这会导致计时不准确。为避免此问题,请更改 Windows 电源计划,使最低处理器状态为 100%。
compile#
此代码示例调用了内置的解析器和编译器。它实现了所有可能的从(MJCF, URDF, MJB)格式到(MJCF, MJB, TXT)格式的模型转换。保存为 MJCF 的模型使用我们在 建模 章节中描述的格式的规范子集,因此 MJCF 到 MJCF 的转换通常会产生不同的文件。TXT 格式是模型的人类可读路线图。它不能被 MuJoCo 加载,但在模型开发过程中是一个非常有用的辅助工具。它与编译后的 mjModel 是一一对应的。另请注意,可以使用函数 mj_printData 创建一个与 mjData 一一对应的文本文件,尽管代码示例中没有这样做。
如果输入文件是 MJCF 或 URDF 且输出文件为空,则编译会执行两次,以测量编译器 资产缓存 的影响。每次编译都会打印详细的计时细分,显示总时间、资产处理时间(挂钟时间)以及网格和纹理的各类别 CPU 时间。这些计时通过 mjs_getTimer 从 mjtCTimer 字段中读取,可以在调用 mj_compile 后通过编程方式读取。
basic#
此代码示例是一个极简的交互式模拟器。模型文件必须作为命令行参数提供。它使用平台无关的 GLFW 库打开一个 OpenGL 窗口,并以 60 fps 的速度渲染模拟状态,同时实时推进模拟。按 Backspace 键可重置模拟。鼠标可用于控制相机:左键拖动旋转,右键拖动在垂直平面平移,Shift+右键拖动在水平平面平移,滚动或中键拖动进行缩放。
下方的 可视化 编程指南解释了可视化的工作原理。此代码示例是该指南中概念的极简说明。
record#
此代码示例模拟给定模型的被动动力学,将其离屏渲染,读取颜色和深度像素值,并将它们保存到原始数据文件中,随后可以使用 ffmpeg 等工具将其转换为电影文件。与 simulate.cc 相比,渲染被简化了,因为没有用户交互、可视化选项或计时;相反,我们只是尽可能快地使用默认设置进行渲染。离屏缓冲区的尺寸和多重采样数量在 MuJoCo 模型中通过 visual/global/{offwidth, offheight} 和 visual/quality/offsamples 属性指定,而模拟持续时间、渲染的帧率(通常远低于物理模拟速率)和输出文件名则作为命令行参数指定。
record modelfile duration fps rgbfile [adddepth]
其中命令行参数为
参数 |
默认值 |
含义 |
|---|---|---|
|
(必需) |
模型路径 |
|
(必需) |
录制持续时间(秒) |
|
(必需) |
每秒帧数 |
|
(必需) |
原始录制文件路径 |
|
1 |
在左下角覆盖深度图像(0: 无) |
例如,创建一个 5 秒、每秒 60 帧的动画,命令如下
record humanoid.xml 5 60 rgb.out
默认的 humanoid.xml 模型指定了 2560x1440 分辨率的离屏渲染。有了这些信息,我们可以将(巨大的)原始数据文件压缩为可播放的电影文件
ffmpeg -f rawvideo -pixel_format rgb24 -video_size 2560x1440
-framerate 60 -i rgb.out -vf "vflip,format=yuv420p" video.mp4
请注意,模型的离屏渲染分辨率必须与 ffmpeg 的 video_size 完全一致。
此示例可以通过三种方式编译,区别在于 OpenGL 上下文的创建方式:使用带不可见窗口的 GLFW、使用 OSMesa 或使用 EGL。后两个选项仅在 Linux 上可用,并且在编译 record.cc 时通过定义符号 MJ_OSMESA 或 MJ_EGL 来调用。initOpenGL 和 closeOpenGL 函数根据定义了上述哪个符号,以三种不同的方式创建和关闭 OpenGL 上下文。
请注意,MuJoCo 渲染代码不依赖于 OpenGL 上下文的创建方式。这就是 OpenGL 的美妙之处:它将上下文创建留给平台,而实际的渲染则是标准的,并且在所有平台上以相同的方式工作。回想起来,将上下文创建排除在标准之外的决定,导致了重复技术的不必要激增,这不仅在不同平台之间存在差异,而且在 Linux 平台的内部也存在差异。增加几个额外的函数(例如 OSMesa 提供的那些)本可以避免很多混乱。EGL 是 Khronos 的一项较新标准,旨在解决这个问题,它正变得越来越流行。但我们还不能假设所有用户都已经安装了它。