Unity 插件#
引言#
MuJoCo Unity 插件允许 Unity 编辑器和运行时使用 MuJoCo 物理引擎。用户可以在编辑器中导入 MJCF 文件并编辑模型。该插件在大多数方面(资产、游戏逻辑、模拟时间)依赖于 Unity,但使用 MuJoCo 来决定对象的移动方式,从而让设计者能够访问 MuJoCo 的完整 API。
一个使用 MuJoCo Unity 插件的示例项目以及一套入门教程也作为一个独立的仓库提供。
安装说明#
插件目录包含一个package.json
文件。Unity 的包管理器会识别此文件,并将插件的 C# 代码库导入到您的项目中。此外,Unity 还需要本地 MuJoCo 库,该库可以在相应的平台归档中找到。如果您只想使用插件而不进行开发,应使用仓库中由 git 标签标识的特定版本稳定提交。使用 git 检出克隆仓库的相关版本(git checkout 3.X.Y
,其中 X 和 Y 指定引擎版本)。简单地使用仓库的main
分支可能与 MuJoCo 的最新发行二进制文件不兼容。
在 Unity 2020.2 及更高版本上,导入包时,包管理器会查找本地库文件并将其复制到包目录。或者,您可以手动将本地库复制到包目录并重命名,请参阅下面的平台特定说明。该库也可以复制到您项目 Assets 目录下的任何位置。
MacOS#
在使用本地库之前,需要至少运行一次 MuJoCo 应用程序,以便将该库注册为受信任的二进制文件。然后,将动态库文件从/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.3.2.dylib
(可以通过浏览MuJoCo.app
的内容找到)复制并重命名为mujoco.dylib
。
Linux#
将tar.gz
归档文件解压到~/.mujoco
。然后将动态库从~/.mujoco/mujoco-3.3.2/lib/libmujoco.so.3.3.2
复制并重命名为libmujoco.so
。
Windows#
将zip
归档文件解压到用户目录中名为MuJoCo
的目录,然后复制文件MuJoCo\bin\mujoco.dll
。
使用插件#
导入器#
导入器通过编辑器的Asset(资源)菜单调用:点击“Import MuJoCo Scene”(导入 MuJoCo 场景)并选择包含模型 MJCF 规范的 XML 文件。
鼠标弹簧#
当选定的游戏对象具有MjBody
组件时,可以在 Scene 视图中通过按下 Control 键并左键拖动操作,向鼠标光标方向对该物体施加弹簧力。弹簧力原点的 3D 位置是通过将鼠标位置投影到由相机 X 方向和世界 Y 方向定义的平面上找到的。按下 Shift 键会改变投影平面,使其平行于世界的 X 轴和 Z 轴。
给 Unity 用户的提示#
如果遇到任何编译或运行时错误,系统的状态是未定义的。因此,我们建议在控制台窗口中开启“Error Pause”(错误暂停)。
在 PhysX 中,每个Rigidbody都是一个“自由体”。相比之下,MuJoCo 需要明确指定关节以实现移动性。为了方便起见,我们提供了一个上下文菜单,通过添加父级
MjBody
和同级MjFreeJoint
来“解放”一个世界 geom(即没有任何MjBody
祖先的MjGeom
组件)。该插件不支持没有物理存在的碰撞检测,因此没有内置的触发器碰撞体概念。可以通过添加一个触摸传感器并读取其
SensorReading
值来判断是否存在接触力(该值对应于法向力,请参阅触摸传感器文档 <sensor-touch>)。
设计原则#
插件设计提供了 MJCF 元素与 Unity 组件之间的一对一映射。为了使用 MuJoCo 模拟 Unity 场景(例如,当用户在编辑器中点击“播放”按钮时),该插件会
扫描场景中的 GameObject 层级结构以查找 MuJoCo 组件。
创建 MJCF 描述并将其传递给 MuJoCo 的编译器。
通过 MuJoCo 数据结构中对应的索引将每个组件绑定到 MuJoCo 运行时。此索引用于在模拟期间更新 Unity 的变换。
此设计原则有几个含义
Unity 组件的大多数字段直接对应于 MJCF 属性。因此,用户可以参考 MuJoCo 文档以获取不同值语义的详细信息。
MuJoCo 组件在 GameObject 层级结构中的布局决定了最终 MuJoCo 模型的布局。因此,我们采用一个设计规则:每个游戏对象最多只能有一个 MuJoCo 组件。
我们依赖 Unity 进行空间配置,这要求矢量分量进行坐标轴交换,因为 Unity 使用左手坐标系且 Y 轴朝上,而 MuJoCo 使用右手坐标系且 Z 轴朝上。
Unity 的变换缩放会影响整个游戏对象子树的位置、方向和缩放。然而,MuJoCo 不支持斜切圆柱体和胶囊体的碰撞(斜切球体通过椭球基元支持)。geom 和 site 的 gizmo 会忽略这种斜切(类似于 PhysX 碰撞体),并且总是显示原始形状在物理中的表现。
在运行时,更改组件字段的值不会触发场景重建,因此不会立即影响物理。但是,新值将在下次场景重建时加载。
在可能的情况下,我们遵循 Unity 的方式:重力从 Unity 的物理设置中读取,模拟步长从 Unity 的时间管理器中的Fixed Timestep读取。所有外观方面的设置(例如,网格、材质和纹理)都由 Unity 的 Asset Manager 处理,而 RGBA 规范则使用材质资源完成。
实现说明#
导入器工作流程#
当用户选择 MJCF 文件时,导入器首先在 MuJoCo 中加载该文件,将其保存到临时位置,然后处理生成的保存文件。这有几个效果
它验证 MJCF - 我们保证保存的 MJCF 符合模式。
它验证资产(材质、网格、纹理)并将这些资产导入 Unity,同时为 geom 的 RGBA 规范创建新的材质资产。
它允许导入器处理<include>元素,而无需复制 MuJoCo 的文件系统工作流程。
在 Unity 中,没有等同于 MJCF 的“级联”<default>子句。因此,Unity 中的组件反映了应用所有相关默认类后的对应元素状态,并且原始 MJCF 中的类结构被丢弃。
MuJoCo 场景#
创建 MuJoCo 场景时,MjScene
组件首先扫描场景中所有MjComponent
实例。每个组件使用 Unity 场景的空间结构创建自己的 MJCF 元素,以描述模型的初始参考姿势(在 MuJoCo 中称为qpos0
)。MjScene
根据相应游戏对象的层级结构组合这些 XML 元素,并创建物理模型的单一 MJCF 描述。然后,它创建运行时结构体mjModel
和mjData
,并通过识别其唯一索引将每个组件绑定到运行时。
在运行时,MjScene.FixedUpdate()
调用mj_step,然后根据绑定时确定的索引MjComponent.MujocoId
同步每个游戏对象的状态。当应用程序启动时(例如,当用户点击“播放”时),仅当场景包含任何 MuJoCo 组件时,才会自动添加MjScene
组件。如果您的应用程序初始化阶段涉及在添加游戏对象和组件时推进物理模拟,您可以在初始化阶段结束后调用MjScene.CreateScene()
。
场景重建以下列方式保持物理和状态的连续性
关节的位置和速度被缓存。
MuJoCo 的状态被重置(到
qpos0
),并且 Unity 变换被同步。生成一个新的 XML,创建一个对于那些持续存在的关节具有与之前模型相同
qpos0
的模型。MuJoCo 状态(对于那些持续存在的关节)从缓存中设置,并且 Unity 变换被同步。
MuJoCo 具有动态场景编辑功能(通过mjSpec),但是,Unity 插件尚不支持此功能。因此,添加和移除 MuJoCo 组件会导致完整的场景重建。这对于大型模型或频繁发生的情况可能开销很大。我们计划在插件的未来版本中解除这一性能限制。
全局设置#
一对一组件的例外是全局设置组件。此组件负责 MJCF 中固定大小、单例、全局元素包含的所有配置选项。目前它包含与<option>和<size>元素对应的信息,未来如果其中字段与 Unity 插件相关,它也将用于<compiler>元素。
在应用程序运行时调用导入器#
导入器由类MjImporterWithAssets
实现,它是MjcfImporter
的子类。该父类接受一个 MJCF 字符串并生成组件的层级结构。它可以在播放时调用(不涉及编辑器功能),并且不调用 MuJoCo 库的任何函数。当 MuJoCo 模型是程序化生成的(例如,通过某种进化过程)和/或仅导入 MJCF 以进行转换时(例如,转换为 PhysX 或 URDF),此功能非常有用。由于它无法与 Unity 的AssetManager
(这是编辑器的一项功能)交互,此类的功能受到限制。具体来说
它忽略所有资产(包括碰撞网格)。
它忽略视觉效果(包括 RGBA 规范)。
MuJoCo 传感器组件#
MuJoCo 定义了许多传感器,我们担心为每个传感器创建单独的MjComponent
类会导致大量的代码重复。因此,我们根据被测量属性的对象类型(actuator / body / geom / joint / site)和测量数据的类型(scalar / vector / quaternion)创建了类。
这是一个将类型映射到传感器的表格
Mujoco 对象类型 |
数据类型 |
传感器名称 |
执行器 |
标量 |
|
主体 |
向量 |
|
主体 |
四元数 |
|
Geom |
向量 |
|
Geom |
四元数 |
|
关节 |
标量 |
|
Site |
标量 |
|
Site |
向量 |
|
Site |
四元数 |
|
这是反向的表格,将传感器映射到类
传感器名称 |
插件类 |
---|---|
|
SiteVector |
|
ActuatorScalar |
|
ActuatorScalar |
|
ActuatorScalar |
|
SiteVector |
|
*向量 (取决于坐标系类型) |
|
*向量 (取决于坐标系类型) |
|
*向量 (取决于坐标系类型) |
|
*向量 (取决于坐标系类型) |
|
*向量 (取决于坐标系类型) |
|
*四元数 (取决于坐标系类型) |
|
*向量 (取决于坐标系类型) |
|
*向量 (取决于坐标系类型) |
|
*向量 (取决于坐标系类型) |
|
SiteVector |
|
JointScalar |
|
JointScalar |
|
JointScalar |
|
JointScalar |
|
JointScalar |
|
SiteVector |
|
BodyVector |
|
BodyVector |
|
BodyVector |
|
SiteVector |
|
SiteScalar |
|
SiteVector |
以下传感器尚未实现
tendonpos
tendonvel
ballquat
ballangvel
tendonlimitpos
tendonlimitvel
tendonlimitfrc
user
网格形状#
该插件允许使用任意 Unity 网格进行 MuJoCo 碰撞。在模型编译时,MuJoCo 调用qhull为网格创建凸包,并使用该凸包进行碰撞。目前计算出的凸包在 Unity 中不可见,但我们计划在未来版本中公开它。
高度场#
MuJoCo 高度场通过 Unity 的地形游戏对象表示。这允许使用 Unity 中提供的地形编辑工具来生成与 MuJoCo 碰撞的形状。在 Unity geom 组件中选择高度场类型时,右键上下文菜单提供了一个实用工具,用于将相应的 Unity 地形添加到场景中。地形数据与模拟动态保持同步。
MuJoCo 插件#
当前版本的 Unity 包不支持加载使用MuJoCo 插件(如弹性插件)的 MJCF 场景。添加实现此功能的基本功能将作为即将发布版本的一部分。
与外部进程的交互#
Roboti 的用于 Unity 的 MuJoCo 插件在外部 Python 进程中进行模拟,并且仅使用 Unity 进行渲染。相比之下,我们的插件依赖 Unity 进行模拟。应该可以在外部进程“驱动”模拟时使用我们的插件,例如通过设置qpos
、调用mj_kinematics
、同步变换,然后使用 Unity 进行渲染或计算游戏逻辑。为了与外部进程建立通信,您可以使用 Unity 的ML-Agents包。