add chinese
build / setup (compute matrix) (push) Has been cancelled
build / ${{ matrix.label }} (push) Has been cancelled
build / macos-15-arm64-studio (push) Has been cancelled
build / ubuntu-24.04-clang-18-studio (push) Has been cancelled
build / ubuntu-24.04-gcc-14-studio (push) Has been cancelled
build / windows-2025-ninja-studio (push) Has been cancelled
build / ubuntu-24.04-clang-18-wasm (push) Has been cancelled
build / ubuntu-24.04-clang-18-mjx (push) Has been cancelled
build / setup (compute matrix) (push) Has been cancelled
build / ${{ matrix.label }} (push) Has been cancelled
build / macos-15-arm64-studio (push) Has been cancelled
build / ubuntu-24.04-clang-18-studio (push) Has been cancelled
build / ubuntu-24.04-gcc-14-studio (push) Has been cancelled
build / windows-2025-ninja-studio (push) Has been cancelled
build / ubuntu-24.04-clang-18-wasm (push) Has been cancelled
build / ubuntu-24.04-clang-18-mjx (push) Has been cancelled
This commit is contained in:
+139
-275
@@ -1,80 +1,55 @@
|
||||
# MuJoCo JavaScript Bindings
|
||||
# MuJoCo JavaScript 绑定
|
||||
|
||||
These are the canonical JavaScript and TypeScript bindings for the MuJoCo
|
||||
physics engine.
|
||||
这是 [MuJoCo 物理引擎](https://github.com/google-deepmind/mujoco) 的官方规范 JavaScript 和 TypeScript 绑定。
|
||||
|
||||
This package provides a high-level API that allows you to interact with the core
|
||||
MuJoCo engine compiled into a high-performance WebAssembly (WASM) module. These
|
||||
bindings are developed and maintained by Google DeepMind and are always up to
|
||||
date with the latest developments in MuJoCo. For brevity, the documentation
|
||||
below will often refer to “JavaScript” but the concepts apply equally to
|
||||
TypeScript.
|
||||
本软件包提供了一个高级 API,使您能够与编译为高性能 WebAssembly (WASM) 模块的 MuJoCo 核心引擎进行交互。这些绑定由 Google DeepMind 开发和维护,并始终与 MuJoCo 的最新进展保持同步。为简明起见,下文中的文档通常称为“JavaScript”,但相关概念同样完全适用于 TypeScript。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> _These bindings are still a WIP. For details, see the [Future Work](#future-work)
|
||||
> section. Also note that development has primarily taken place on Linux using
|
||||
> Google Chrome. If you're working on a different OS or browser, you may
|
||||
> encounter some rough edges. We have successfully tested the bindings on MacOS
|
||||
> in CI but as of November 13th 2025, Windows support remains experimental
|
||||
> (installation succeeded on one Windows 11 machine but failed on others)._
|
||||
> _这些绑定仍处于开发阶段(WIP)。详情请参阅[未来工作](#未来工作)部分。另请注意,开发主要是在 Linux 上使用 Google Chrome 进行的。如果您在其他操作系统或浏览器上开发,可能会遇到一些粗糙边缘。我们已在 CI 中成功在 macOS 上测试了绑定,但截至 2025 年 11 月 13 日,Windows 支持仍处于实验性阶段(在某台 Windows 11 设备上安装成功,但在其他设备上失败)。_
|
||||
|
||||
## Installation
|
||||
## 安装
|
||||
|
||||
The easiest way to use the MuJoCo JavaScript bindings is to install the
|
||||
`@mujoco/mujoco` package from npm:
|
||||
使用 MuJoCo JavaScript 绑定最简单的方法是从 npm 安装 `@mujoco/mujoco` 软件包:
|
||||
|
||||
```sh
|
||||
npm install @mujoco/mujoco
|
||||
```
|
||||
|
||||
This package is ESM (`type: module`) and includes the pre-compiled WebAssembly
|
||||
module, JavaScript bindings, and TypeScript declarations. Ensure your bundler or
|
||||
dev server serves the `.wasm` asset at runtime.
|
||||
该软件包采用 ESM 模块规范(`type: module`),包含预编译的 WebAssembly 模块、JavaScript 绑定和 TypeScript 类型声明。请确保您的打包工具(bundler)或开发服务器在运行时能够正确提供 `.wasm` 静态资源服务。
|
||||
|
||||
### Threading Models
|
||||
### 线程模型(Threading Models)
|
||||
|
||||
The `@mujoco/mujoco` package includes two distinct builds of the engine to
|
||||
support different browser environments and performance needs.
|
||||
`@mujoco/mujoco` 软件包包含两个不同的引擎构建版本,以支持不同的浏览器环境和性能需求。
|
||||
|
||||
#### 1. Single-Threaded (Default)
|
||||
#### 1. 单线程(默认)
|
||||
|
||||
The standard single-threaded version is located at the root of the package. It
|
||||
is compatible with all modern browsers and does not require special security
|
||||
headers.
|
||||
标准的单线程版本位于软件包的根目录。它兼容所有现代浏览器,无需特殊的安全响应头配置。
|
||||
```typescript
|
||||
import loadMujoco from '@mujoco/mujoco';
|
||||
```
|
||||
|
||||
#### 2. Multi-Threaded (MT)
|
||||
#### 2. 多线程(MT)
|
||||
|
||||
The multi-threaded version is located in the `/mt` subfolder. It utilizes Web
|
||||
Workers and `SharedArrayBuffer` to parallelize physics computations.
|
||||
```typeScript
|
||||
多线程版本位于 `/mt` 子目录中。它利用 Web Workers 和 `SharedArrayBuffer` 来并行化物理计算。
|
||||
```typescript
|
||||
import loadMujoco from '@mujoco/mujoco/mt';
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Due to the use of `SharedArrayBuffer`, browsers require Cross-Origin Isolation
|
||||
> to enable multi-threading. Your web server must send the following HTTP
|
||||
> headers:
|
||||
> 由于使用了 `SharedArrayBuffer`,浏览器需要配置跨域隔离(Cross-Origin Isolation)才能启用多线程。您的 Web 服务器必须发送以下 HTTP 响应头:
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
> If these headers are missing, the module will fail to initialize.
|
||||
> 如果缺少这些响应头,模块将无法完成初始化。
|
||||
|
||||
## Build from source
|
||||
## 从源码构建
|
||||
|
||||
### Prerequisites
|
||||
### 前置依赖
|
||||
|
||||
> [!NOTE]
|
||||
> Run all the commands in this README from the top-level directory.
|
||||
> 请在项目顶级根目录下运行本 README 中的所有命令。
|
||||
|
||||
- To compile the [`bindings.cc`](codegen/generated/bindings.cc) file, which
|
||||
generates the `.wasm` WebAssembly file, `.js` JavaScript import, and `.d.ts`
|
||||
TypeScript declaration file, you will need Emscripten SDK version `4.0.10`.
|
||||
Later versions may work but are untested. To set up the SDK, do the
|
||||
following, you can run this anywhere but the rest of the commands in this
|
||||
README only work in the shell where you source the `emsdk_env.sh` script.
|
||||
- 要编译 [`bindings.cc`](codegen/generated/bindings.cc) 文件(用于生成 `.wasm` WebAssembly 文件、`.js` JavaScript 导入文件和 `.d.ts` TypeScript 声明文件),您需要安装 Emscripten SDK `4.0.10` 版本。更新的版本可能可用但未经充分测试。要配置 SDK,请执行以下命令(您可以在任何位置运行安装,但本 README 中的后续命令仅在执行了 `source ./emsdk/emsdk_env.sh` 的 Shell 中生效):
|
||||
|
||||
```sh
|
||||
git clone https://github.com/emscripten-core/emsdk.git
|
||||
@@ -83,22 +58,14 @@ import loadMujoco from '@mujoco/mujoco/mt';
|
||||
source ./emsdk/emsdk_env.sh
|
||||
```
|
||||
|
||||
- To easily run the JavaScript tests and the demo application, `node` and `npm`
|
||||
are required. We recommend managing these using
|
||||
[nvm](https://github.com/nvm-sh/nvm). There are also various JavaScript
|
||||
dependencies needed for the tests, demo, and bindings build process. These
|
||||
dependencies are expected to be located in the `wasm` folder. To install
|
||||
them and ensure they can be found by later commands, run the following:
|
||||
- 要轻松运行 JavaScript 测试和演示应用,需要安装 `node` 和 `npm`。我们建议使用 [nvm](https://github.com/nvm-sh/nvm) 进行版本管理。此外,测试、演示和绑定构建过程还需要一些 JavaScript 依赖项。这些依赖位于 `wasm` 文件夹中。要安装它们并确保后续命令可以找到相关工具,请运行:
|
||||
|
||||
```sh
|
||||
npm install --prefix ./wasm
|
||||
export PATH="$(pwd)/wasm/node_modules/.bin:$PATH"
|
||||
```
|
||||
|
||||
- To modify the bindings `python3` is required because the [`bindings.cc`](codegen/generated/bindings.cc)
|
||||
file is generated by a Python script. To run the bindings generator tests,
|
||||
`absl` is required and `pytest` will be helpful. Set up a Python environment
|
||||
with these dependencies as follows:
|
||||
- 要修改绑定,需要安装 `python3`,因为 [`bindings.cc`](codegen/generated/bindings.cc) 文件是由 Python 脚本生成的。要运行绑定生成器测试,需要 `absl`,同时 `pytest` 也很有帮助。配置包含这些依赖的 Python 虚拟环境:
|
||||
|
||||
```sh
|
||||
python3 -m venv .venv
|
||||
@@ -107,254 +74,199 @@ import loadMujoco from '@mujoco/mujoco/mt';
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> _Emscripten is well-documented. We recommend reading the sections covering the
|
||||
> [Emscripten Compiler Settings](https://emscripten.org/docs/tools_reference/settings_reference.html),
|
||||
> the [Emscripten SDK](https://emscripten.org/docs/tools_reference/emsdk.html),
|
||||
> and the [Embind](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html)
|
||||
> library. To understand the limitations and caveats related to using the
|
||||
> browser as a platform, see the
|
||||
> [Porting](https://emscripten.org/docs/porting/index.html#porting) section._
|
||||
> _Emscripten 文档非常详尽。建议阅读涵盖 [Emscripten 编译器设置](https://emscripten.org/docs/tools_reference/settings_reference.html)、[Emscripten SDK](https://emscripten.org/docs/tools_reference/emsdk.html) 以及 [Embind](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html) 库的章节。若要了解将浏览器作为运行平台的相关限制和注意事项,请参阅 [Porting(移植)](https://emscripten.org/docs/porting/index.html#porting) 章节。_
|
||||
|
||||
The [`bindings.cc`](codegen/generated/bindings.cc) file is compiled to generate
|
||||
the `.wasm` WebAssembly file, `.js` JavaScript import, and `.d.ts` TypeScript
|
||||
declaration file. These are the files you'll use to call MuJoCo from JavaScript.
|
||||
To generate them ensure the npm and Emscripten SDK prerequisites are set up and
|
||||
then run the following:
|
||||
编译 [`bindings.cc`](codegen/generated/bindings.cc) 文件将生成 `.wasm` WebAssembly 文件、`.js` JavaScript 导入文件以及 `.d.ts` TypeScript 声明文件。这些文件将用于在 JavaScript 中调用 MuJoCo。要生成它们,请确保已配置好 npm 和 Emscripten SDK 前置环境,然后运行以下命令:
|
||||
|
||||
```sh
|
||||
emcmake cmake -B build && cmake --build build
|
||||
```
|
||||
|
||||
This command will generate the following folders under the project root:
|
||||
该命令将在项目根目录下生成以下文件夹:
|
||||
|
||||
- `build`: contains MuJoCo compiled using Emscripten.
|
||||
- `wasm/dist`: contains the WebAssembly module, `.js` and `.d.ts` files.
|
||||
- `build`:包含使用 Emscripten 编译的 MuJoCo。
|
||||
- `wasm/dist`:包含 WebAssembly 模块、`.js` 和 `.d.ts` 文件。
|
||||
|
||||
The assets inside those folders are compiled and prepared to run as
|
||||
single-threaded. If you need to operate with a multi-threaded version of the
|
||||
module make sure to pass the `-DMUJOCO_WASM_THREADS=ON` flag like:
|
||||
这些文件夹内的资源默认编译并配置为单线程运行。如果需要使用多线程版本的模块,请传递 `-DMUJOCO_WASM_THREADS=ON` 标志:
|
||||
|
||||
```sh
|
||||
emcmake cmake -B build -DMUJOCO_WASM_THREADS=ON && cmake --build build
|
||||
```
|
||||
|
||||
### Example Application
|
||||
### 示例应用程序
|
||||
|
||||
After generating the bindings you will be ready to write web applications using
|
||||
MuJoCo. We have provided a basic web application that uses Three.js to render a
|
||||
simple simulation, to try it run this command:
|
||||
生成绑定后,您就可以开始编写使用 MuJoCo 的 Web 应用程序了。我们提供了一个使用 Three.js 渲染简单仿真的基础 Web 应用,运行以下命令体验:
|
||||
|
||||
```sh
|
||||
npm run dev:demo --prefix ./wasm
|
||||
```
|
||||
|
||||
You may prefer to write your entire app in C++ and compile it using Emscripten.
|
||||
If you do this, you won’t need to use these bindings, since you’ll be writing
|
||||
minimal JavaScript, and the granularity of these bindings may be inappropriate
|
||||
(e.g., you might want to call multiple MuJoCo functions in the C++ callback
|
||||
invoked by `requestAnimationFrame`).
|
||||
您也可以选择完全用 C++ 编写整个应用并使用 Emscripten 编译。如果采用这种方式,您将不需要使用这些绑定,因为您只需编写极少量的 JavaScript,而且这些绑定的粒度可能不太合适(例如,您可能希望在 `requestAnimationFrame` 触发的 C++ 回调中调用多个 MuJoCo 函数)。
|
||||
|
||||
We have also found that a hybrid approach can be helpful, as it is often more
|
||||
convenient to work with browser APIs directly in JavaScript. If you choose to
|
||||
write your application in C++ and compile it using Emscripten, you may want to
|
||||
copy a subset of the `EMSCRIPTEN_BINDINGS` from `bindings.cc` into your
|
||||
application’s source file.
|
||||
我们还发现混合架构非常实用,因为直接在 JavaScript 中操作浏览器 API 通常更方便。如果您选择用 C++ 编写应用程序并使用 Emscripten 编译,您可以将 `bindings.cc` 中 `EMSCRIPTEN_BINDINGS` 的子集复制到您的应用程序源文件中。
|
||||
|
||||
## User Guide
|
||||
### Named Access
|
||||
## 用户指南
|
||||
|
||||
The bindings support named access methods, similar to the Python bindings,
|
||||
allowing convenient access to model and data elements by name or index. For
|
||||
example, you can access a geometry by name using `model.geom('mygeom')` or a
|
||||
joint using `data.jnt('myjoint')`.
|
||||
### 按名称访问(Named Access)
|
||||
|
||||
For more details and examples of how to use named access, please refer to the [named access tests](tests/bindings_test.ts#L1876-L2378) and [documentation](https://mujoco.readthedocs.io/en/stable/python.html#named-access).
|
||||
绑定支持按名称访问方法(与 Python 绑定类似),允许通过名称或索引便捷地访问模型和数据元素。例如,可以通过 `model.geom('mygeom')` 按名称访问几何体,或通过 `data.jnt('myjoint')` 访问关节。
|
||||
|
||||
### Memory Management
|
||||
Embind-wrapped C++ object handles created or returned into JavaScript live on
|
||||
the WebAssembly heap and are **not** garbage-collected by the JS runtime.
|
||||
有关如何使用按名称访问的更多详细信息和示例,请参阅[按名称访问测试](tests/bindings_test.ts#L1876-L2378)以及[官方文档](https://mujoco.readthedocs.io/en/stable/python.html#named-access)。
|
||||
|
||||
Any heap-allocated C++ object exposed to JS (e.g. via `new Module.MyClass(...)`
|
||||
or returned as a pointer/reference from a binding) must be explicitly freed
|
||||
when no longer needed to avoid memory leaks.
|
||||
### 内存管理
|
||||
|
||||
Use the generated `.delete()` method on wrapped instances to destroy the
|
||||
underlying C++ object:
|
||||
通过 Embind 封装创建或返回到 JavaScript 中的 C++ 对象句柄保存在 WebAssembly 堆内存中,**不会**被 JS 运行时的垃圾回收器自动回收。
|
||||
|
||||
任何暴露给 JS 的堆分配 C++ 对象(例如通过 `new Module.MyClass(...)` 创建,或作为绑定的指针/引用返回),在不再需要时必须显式释放,以避免内存泄漏。
|
||||
|
||||
在封装实例上调用生成的 `.delete()` 方法来销毁底层 C++ 对象:
|
||||
|
||||
```typescript
|
||||
const obj = new Module.MyClass(...);
|
||||
// ... use obj ...
|
||||
obj.delete(); // free the C++ memory
|
||||
// ... 使用 obj ...
|
||||
obj.delete(); // 释放 C++ 内存
|
||||
```
|
||||
|
||||
Be careful to call `.delete()` exactly once per created object (double-delete
|
||||
is an error). In JS code paths that may throw or return early, ensure
|
||||
deletion happens in finally blocks or wrap lifetime management to avoid leaks.
|
||||
请注意,每个创建的对象只能调用 `.delete()` **一次**(重复释放属于错误)。在可能抛出异常或提前返回的 JS 代码路径中,请确保在 `finally` 块中执行删除操作,或对生命周期管理进行封装以避免内存泄漏。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> _Embind's documentation strongly recommends that JavaScript code explicitly
|
||||
> deletes any C++ object handles it has received._
|
||||
> _Embind 文档强烈建议 JavaScript 代码显式释放其接收到的所有 C++ 对象句柄。_
|
||||
|
||||
### Copy vs. Reference
|
||||
### 拷贝 vs. 引用(Copy vs. Reference)
|
||||
|
||||
When interacting with MuJoCo objects through the WASM bindings, it's important
|
||||
to understand how data is accessed. Properties on objects like `MjModel` and
|
||||
`MjData` can expose data in two ways: by copy or by reference.
|
||||
通过 WASM 绑定与 MuJoCo 对象交互时,理解数据的访问方式非常重要。`MjModel` 和 `MjData` 等对象上的属性可以通过两种方式暴露数据:按值拷贝(Copy)或按引用(Reference)。
|
||||
|
||||
#### 1. By Copy (Value-based access)
|
||||
#### 1. 按值拷贝(基于值的访问)
|
||||
|
||||
Some properties return a copy of the data at the time of access. This is common
|
||||
for complex data structures that need to be marshalled from C++ to JavaScript.
|
||||
某些属性在访问时会返回数据的副本。对于需要从 C++ 封送到 JavaScript 的复杂数据结构,这是常见做法。
|
||||
|
||||
A key example is `MjData.contact`. When you access `data.contact`, you get an
|
||||
object containing a copy of the contacts at that specific moment in the
|
||||
simulation.
|
||||
一个典型的例子是 `MjData.contact`。当访问 `data.contact` 时,您会获得一个包含该仿真时刻接触信息副本的对象。
|
||||
|
||||
If you step the simulation forward, they will not be updated. You must access
|
||||
`data.contact` again to get the new contact information.
|
||||
如果推进仿真步进,这些副本将不会自动更新。您必须重新访问 `data.contact` 才能获取最新的接触信息。
|
||||
|
||||
The object you get is a JavaScript proxy interface generated by [Emscripten’s Embind library](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions) when you expose a `std::vector` using `register_vector<T>`. It is essentially a "bridge" object.
|
||||
您获得的对象是由 [Emscripten Embind 库](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions)在使用 `register_vector<T>` 暴露 `std::vector` 时生成的 JavaScript 代理接口。它本质上是一个“桥接”对象。
|
||||
|
||||
```typescript
|
||||
export interface MjContactVec extends ClassHandle {
|
||||
/** Appends a new element to the end of the vector, increasing its length by one. */
|
||||
/** 向向量末尾追加一个新元素,长度加 1。 */
|
||||
push_back(_0: MjContact): void;
|
||||
/** Resizes the vector to contain the specified number of elements, filling new slots with the provided value. */
|
||||
/** 调整向量大小以包含指定数量的元素,新位置填充提供的值。 */
|
||||
resize(_0: number, _1: MjContact): void;
|
||||
/** Returns the total number of elements currently stored in the vector. */
|
||||
/** 返回向量中当前存储的元素总数。 */
|
||||
size(): number;
|
||||
/** Retrieves the element at the specified index, or returns undefined if the index is out of bounds. */
|
||||
/** 检索指定索引处的元素;如果索引越界则返回 undefined。 */
|
||||
get(_0: number): MjContact | undefined;
|
||||
/** Overwrites the element at the specified index; returns true if successful or false if the index is invalid. */
|
||||
/** 覆盖指定索引处的元素;成功返回 true,索引无效返回 false。 */
|
||||
set(_0: number, _1: MjContact): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
示例:
|
||||
```typescript
|
||||
// Gets contacts at the current time.
|
||||
// 获取当前时刻的接触信息
|
||||
const contacts = data.contact;
|
||||
|
||||
// Step the simulation
|
||||
// 推进仿真单步
|
||||
mujoco.mj_step(model, data);
|
||||
|
||||
// `contacts` is now stale. To get the new contacts, you must access the property again:
|
||||
// `contacts` 现在已过时。要获取新的接触信息,必须重新访问该属性:
|
||||
const newContacts = data.contact;
|
||||
|
||||
// Remember to delete all created objects when they are no longer needed.
|
||||
// 记得在不再需要时删除所有创建的对象
|
||||
contacts.delete();
|
||||
newContacts.delete();
|
||||
```
|
||||
|
||||
#### 2. By Reference (View-based access)
|
||||
#### 2. 按引用(基于视图的访问)
|
||||
|
||||
Many properties, especially large numerical arrays, return a live view directly
|
||||
into the WebAssembly memory. This is highly efficient as it avoids copying large
|
||||
amounts of data.
|
||||
许多属性(特别是大型数值数组)会直接返回指向 WebAssembly 内存的实时视图(Live View)。这种方式效率极高,避免了大量数据的拷贝。
|
||||
|
||||
A key example is `MjData.qpos` (joint positions). When you get a reference to
|
||||
this array, it points directly to the simulation's state data. Any changes in
|
||||
the simulation (e.g., after a call to `mj_step`) will be immediately reflected
|
||||
in this array.
|
||||
一个典型的例子是 `MjData.qpos`(关节位置)。当您获取对该数组的引用时,它直接指向仿真的状态数据。仿真中的任何更改(例如调用 `mj_step` 之后)都将立即反映在该数组中。
|
||||
|
||||
```typescript
|
||||
// `qpos` is a live view into the simulation state.
|
||||
// `qpos` 是指向仿真状态的实时视图
|
||||
const qpos = data.qpos;
|
||||
|
||||
console.log(qpos[0]); // Print initial position
|
||||
console.log(qpos[0]); // 输出初始位置
|
||||
|
||||
// Step the simulation
|
||||
// 推进仿真单步
|
||||
mujoco.mj_step(model, data);
|
||||
|
||||
// `qpos` is automatically updated.
|
||||
console.log(qpos[0]); // Print new position
|
||||
// `qpos` 自动更新
|
||||
console.log(qpos[0]); // 输出新位置
|
||||
|
||||
// Remember to delete all created objects when they are no longer needed.
|
||||
// 记得在不再需要时释放对象
|
||||
data.delete();
|
||||
```
|
||||
|
||||
### Data Layout: Row-Major Matrices
|
||||
### 数据排布:行主序矩阵(Row-Major Matrices)
|
||||
|
||||
When a function from the MuJoCo C API returns a matrix (or needs a matrix as
|
||||
input), these are represented in the JavaScript bindings as flat,
|
||||
one-dimensional `TypedArray`'s. The elements are stored in row-major order.
|
||||
当 MuJoCo C API 函数返回矩阵(或需要矩阵作为输入)时,在 JavaScript 绑定中表示为扁平的一维 `TypedArray`。元素按行主序(Row-Major)存储。
|
||||
|
||||
For example, a 3x10 matrix will be returned as a flat array with 30 elements.
|
||||
The first 10 elements represent the first row, the next 10 represent the second
|
||||
row, and so on.
|
||||
例如,一个 3x10 的矩阵将作为包含 30 个元素的扁平数组返回。前 10 个元素表示第一行,接下来的 10 个元素表示第二行,依此类推。
|
||||
|
||||
Example: Accessing an element at `(row, col)`
|
||||
示例:访问 `(row, col)` 位置的元素
|
||||
```typescript
|
||||
// A 3x10 matrix stored as a flat array.
|
||||
// 存储为扁平数组的 3x10 矩阵
|
||||
const matrix: Float64Array = ...;
|
||||
const nRows = 3;
|
||||
const nCols = 10;
|
||||
|
||||
// To access the element at row `i` and column `j`:
|
||||
// 要访问第 `i` 行、第 `j` 列的元素:
|
||||
const element = matrix[i * nCols + j];
|
||||
```
|
||||
|
||||
### Working with Out Parameters
|
||||
### 处理输出参数(Out Parameters)
|
||||
|
||||
Many functions in the MuJoCo C API use "out parameters" to return data. This
|
||||
means instead of returning a value, they write the result into one of the
|
||||
arguments passed to them by reference (using pointers). In our JavaScript
|
||||
bindings, you'll need to handle these cases specifically.
|
||||
MuJoCo C API 中的许多函数使用“输出参数”(Out Parameters)来返回数据。这意味着它们不是直接返回值,而是将结果写入通过引用(指针)传递给它们的参数中。在 JavaScript 绑定中,您需要针对这些情况做特别处理。
|
||||
|
||||
There are two main scenarios you'll encounter:
|
||||
通常会遇到两种主要场景:
|
||||
|
||||
#### 1. Array-like Out Parameters
|
||||
#### 1. 类数组输出参数
|
||||
|
||||
When a function expects a pointer to a primitive type (like `mjtNum*` or `int*`)
|
||||
to write an array of values, you need to pre-allocate memory for the result on
|
||||
the JavaScript side. We provide helper classes for this: `mujoco.Uint8Buffer`,
|
||||
`mujoco.DoubleBuffer`, `mujoco.FloatBuffer`, and `mujoco.IntBuffer`.
|
||||
当函数期望接收指向基本类型(如 `mjtNum*` 或 `int*`)的指针来写入值数组时,您需要在 JavaScript 端预先分配结果内存。我们为此提供了辅助类:`mujoco.Uint8Buffer`、`mujoco.DoubleBuffer`、`mujoco.FloatBuffer` 和 `mujoco.IntBuffer`。
|
||||
|
||||
Here's how to use them:
|
||||
使用步骤如下:
|
||||
|
||||
1. Create a buffer: Instantiate the appropriate buffer class with an initial array of the correct size (e.g., an array of zeros).
|
||||
2. Call the function: Pass the buffer instance to the function as the out parameter.
|
||||
3. Access the result: Use the `.getView()` method on the buffer to get a `TypedArray` view of the data written by the C++ function.
|
||||
4. Free the memory: When you are done with the buffer, you must call the `.delete()` method to free the underlying memory and prevent memory leaks.
|
||||
1. **创建缓冲区**:使用正确大小的初始数组(例如全零数组)实例化相应的缓冲区类。
|
||||
2. **调用函数**:将缓冲区实例作为输出参数传递给函数。
|
||||
3. **获取结果**:在缓冲区上调用 `.getView()` 方法,获取由 C++ 函数写入的数据的 `TypedArray` 视图。
|
||||
4. **释放内存**:使用完缓冲区后,必须调用 `.delete()` 方法释放底层内存,以防止内存泄漏。
|
||||
|
||||
Example: Rotating a vector
|
||||
示例:旋转向量
|
||||
|
||||
The function `mju_rotVecQuat` rotates a vector `vec` by a quaternion `quat` and
|
||||
stores the result in the `res` out parameter.
|
||||
函数 `mju_rotVecQuat` 使用四元数 `quat` 旋转向量 `vec`,并将结果存储在 `res` 输出参数中。
|
||||
|
||||
```typescript
|
||||
// Create a buffer to hold the 3D vector result.
|
||||
// 创建缓冲区以容纳 3D 向量结果
|
||||
const res = new mujoco.DoubleBuffer([0, 0, 0]);
|
||||
|
||||
const vec = [1, 0, 0];
|
||||
const quat = [0.707, 0, 0, 0.707]; // 90-degree rotation around z-axis
|
||||
const quat = [0.707, 0, 0, 0.707]; // 绕 z 轴旋转 90 度
|
||||
|
||||
try {
|
||||
// Call the function with the buffer as the out parameter.
|
||||
// 调用函数,将缓冲区作为输出参数
|
||||
mujoco.mju_rotVecQuat(res, vec, quat);
|
||||
|
||||
// Get the result as a Float64Array.
|
||||
// 获取 Float64Array 格式的结果视图
|
||||
const resultView = res.getView();
|
||||
console.log(resultView); // Expected: approximately [0, 1, 0]
|
||||
console.log(resultView); // 期望输出:约 [0, 1, 0]
|
||||
|
||||
} finally {
|
||||
// IMPORTANT: Free the memory allocated for the buffer.
|
||||
// 重要:释放为缓冲区分配的内存
|
||||
res.delete();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Struct Out Parameters (e.g., mjvCamera*, mjvScene*)
|
||||
#### 2. 结构体输出参数(例如 mjvCamera*、mjvScene*)
|
||||
|
||||
When a function modifies a struct passed by pointer, you should pass an instance
|
||||
of the corresponding JavaScript wrapper class. The underlying C++ struct will be
|
||||
modified in place.
|
||||
当函数修改通过指针传递的结构体时,应传递相应 JavaScript 包装类的实例。底层 C++ 结构体将被就地(in-place)修改。
|
||||
|
||||
Example: Updating a scene
|
||||
示例:更新场景
|
||||
|
||||
The function `mjv_updateScene` populates an `mjvScene` object with information
|
||||
from `mjModel` and `mjData`.
|
||||
函数 `mjv_updateScene` 使用来自 `mjModel` 和 `mjData` 的信息填充 `mjvScene` 对象。
|
||||
```typescript
|
||||
// Create instances of the necessary structs.
|
||||
// 创建必要结构体的实例
|
||||
const model = mujoco.MjModel.from_xml_string(xmlContent);
|
||||
const data = new mujoco.MjData(model);
|
||||
const scene = new mujoco.MjvScene(model, 1000);
|
||||
@@ -362,9 +274,9 @@ const option = new mujoco.MjvOption();
|
||||
const perturb = new mujoco.MjvPerturb();
|
||||
const camera = new mujoco.MjvCamera();
|
||||
|
||||
// ... (step simulation, etc.)
|
||||
// ... (单步仿真等)
|
||||
|
||||
// Update the scene. The 'scene' object is modified by the function.
|
||||
// 更新场景。'scene' 对象被函数就地修改。
|
||||
mujoco.mjv_updateScene(
|
||||
model,
|
||||
data,
|
||||
@@ -375,9 +287,9 @@ mujoco.mjv_updateScene(
|
||||
scene
|
||||
);
|
||||
|
||||
console.log('Number of geoms in scene:', scene.ngeom);
|
||||
console.log('场景中的 geom 数量:', scene.ngeom);
|
||||
|
||||
// Remember to delete all created objects when they are no longer needed.
|
||||
// 记得在不再需要时删除所有创建的对象
|
||||
scene.delete();
|
||||
camera.delete();
|
||||
perturb.delete();
|
||||
@@ -386,141 +298,93 @@ data.delete();
|
||||
model.delete();
|
||||
```
|
||||
|
||||
As with buffers, you are responsible for managing the memory of these struct
|
||||
instances and must call `.delete()` on them when you are finished.
|
||||
与缓冲区一样,您负责管理这些结构体实例的内存,并在使用完毕后调用 `.delete()`。
|
||||
|
||||
### Enums
|
||||
Access via `.value`:
|
||||
### 枚举类型(Enums)
|
||||
通过 `.value` 访问枚举值:
|
||||
```javascript
|
||||
mujoco.mjtDisableBit.mjDSBL_CLAMPCTRL.value
|
||||
```
|
||||
|
||||
### Constants
|
||||
Scalar constants can be accessed as properties:
|
||||
### 常量(Constants)
|
||||
标量常量可以直接作为属性访问:
|
||||
|
||||
```javascript
|
||||
mujoco.mjNEQDATA
|
||||
```
|
||||
|
||||
Non-scalar constants like `mjFRAMESTRING` are also accessed as properties, and
|
||||
return JavaScript arrays:
|
||||
非标量常量(如 `mjFRAMESTRING`)也可以作为属性访问,并返回 JavaScript 数组:
|
||||
|
||||
```javascript
|
||||
mujoco.mjFRAMESTRING
|
||||
```
|
||||
|
||||
This will return a javascript array representation of the values in MuJoCo
|
||||
`mjFRAMESTRING`.
|
||||
这将返回 MuJoCo 中 `mjFRAMESTRING` 包含的值对应的 JavaScript 数组。
|
||||
|
||||
> [!NOTE]
|
||||
> You will notice constants like `mjFRAMESTRING` are typed as `any`. This is
|
||||
> because they are bound using `emscripten::val::array()` in C++, and Embind
|
||||
> maps `emscripten::val` to `any` in TypeScript definition files. While
|
||||
> `EMSCRIPTEN_DECLARE_VAL_TYPE(StringArray)` could be used to define
|
||||
> `StringArray` as an alias for `emscripten::val` and hint to Embind how to
|
||||
> handle conversions in function signatures or when using `.as()` it does not
|
||||
> change how `emscripten::constant` infers types for properties.
|
||||
> 您会发现像 `mjFRAMESTRING` 这样的常量被类型化为 `any`。这是因为它们在 C++ 中使用 `emscripten::val::array()` 进行绑定,而 Embind 在 TypeScript 定义文件中将 `emscripten::val` 映射为 `any`。虽然可以使用 `EMSCRIPTEN_DECLARE_VAL_TYPE(StringArray)` 将 `StringArray` 定义为 `emscripten::val` 的别名,并提示 Embind 如何在函数签名或使用 `.as()` 时处理类型转换,但这不会改变 `emscripten::constant` 推断属性类型的方式。
|
||||
|
||||
## Development
|
||||
## 开发指南
|
||||
|
||||
In order to change the bindings you will need to change the [`bindings.cc`](codegen/generated/bindings.cc)
|
||||
file but this should not be done manually. The file is generated using the
|
||||
Python scripts and template files in the [`codegen`](codegen) folder, to edit
|
||||
the bindings you will need to change those files and re-generate [`bindings.cc`](codegen/generated/bindings.cc)
|
||||
using this command:
|
||||
若要修改绑定,需要修改 [`bindings.cc`](codegen/generated/bindings.cc) 文件,但不应手动编辑。该文件是使用 [`codegen`](codegen) 文件夹中的 Python 脚本和模板文件生成的。要编辑绑定,需要修改这些文件,并使用以下命令重新生成 [`bindings.cc`](codegen/generated/bindings.cc):
|
||||
|
||||
```sh
|
||||
PYTHONPATH=python/mujoco python3 -m wasm.codegen.update
|
||||
```
|
||||
|
||||
The codegen scripts use MuJoCo’s Python introspect library to generate the
|
||||
Embind `EMSCRIPTEN_BINDINGS` block that binds C++ functions and classes to
|
||||
JavaScript. The functions and classes that are bound are wrappers around
|
||||
MuJoCo's C API. These wrappers provide a convenient place to add features like
|
||||
bounds checking and nice error reporting.
|
||||
代码生成脚本使用 MuJoCo 的 Python 内省(introspect)库来生成将 C++ 函数和类绑定到 JavaScript 的 Embind `EMSCRIPTEN_BINDINGS` 代码块。被绑定的函数和类是 MuJoCo C API 的封装层。这些封装层便于添加边界检查和完善的错误报告等功能。
|
||||
|
||||
### Testing
|
||||
### 测试
|
||||
|
||||
1. **JavaScript API tests.**
|
||||
These verify that a wide variety of MuJoCo functions and classes work
|
||||
correctly when called from JavaScript. Run the tests as follows:
|
||||
1. **JavaScript API 测试。**
|
||||
验证从 JavaScript 调用各种 MuJoCo 函数和类时能否正确工作。运行测试命令:
|
||||
|
||||
```sh
|
||||
npm run test --prefix ./wasm
|
||||
```
|
||||
|
||||
2. **JavaScript API benchmark tests.**
|
||||
The current benchmark tests check JavaScript/C++ shared memory buffers
|
||||
performance. We will increase the coverage of the benchmarks overtime. Run
|
||||
the benchmarks using this command:
|
||||
2. **JavaScript API 基准性能测试。**
|
||||
目前的基准测试检查 JavaScript/C++ 共享内存缓冲区的性能。随着时间推移,我们将提高基准测试的覆盖率。运行基准测试:
|
||||
|
||||
```sh
|
||||
npm run benchmark --prefix ./wasm
|
||||
```
|
||||
|
||||
3. **Bindings generator tests.**
|
||||
These are relevant when developing or extending the bindings. The following
|
||||
command finds and runs all `test_*.py` or `*_test.py` files in the `wasm`
|
||||
folder:
|
||||
3. **绑定生成器测试。**
|
||||
在开发或扩展绑定时相关。以下命令会查找并运行 `wasm` 文件夹中的所有 `test_*.py` 或 `*_test.py` 文件:
|
||||
|
||||
```sh
|
||||
PYTHONPATH=python/mujoco python3 -m pytest ./wasm
|
||||
```
|
||||
|
||||
### Debugging
|
||||
### 调试
|
||||
|
||||
We provide a “sandbox” app where you can quickly write code to run in your
|
||||
browser. Write your code in the [`main.ts`](tests/sandbox/main.ts) file and use
|
||||
the following command to execute it in your browser:
|
||||
我们提供了一个“沙盒(sandbox)”应用,您可以在其中快速编写要在浏览器中运行的代码。在 [`main.ts`](tests/sandbox/main.ts) 文件中编写代码,并使用以下命令在浏览器中执行:
|
||||
|
||||
```sh
|
||||
npm run dev:sandbox --prefix ./wasm
|
||||
```
|
||||
|
||||
The page will be blank since the script only logs to the console output. You
|
||||
can add your code at the indicated placeholder and use Chrome DevTools for
|
||||
debugging. It is possible to set up a debug workflow where stack traces and
|
||||
stepping through code across language boundaries work correctly. Our current
|
||||
method to do this only works internally at Google, but it should be possible to
|
||||
replicate the experience with open-source tooling — community suggestions are
|
||||
welcome!
|
||||
页面将是空白的,因为脚本仅在控制台(console)输出日志。您可以在指定的占位符处添加代码,并使用 Chrome 开发者工具进行调试。可以设置支持跨语言边界正确调用栈追踪和单步调试的工作流。我们目前实现这一点的方法仅在 Google 内部有效,但应该可以使用开源工具链复制相同的体验——欢迎社区提出建议!
|
||||
|
||||
## Versioning
|
||||
Package versions follow the official MuJoCo release versions.
|
||||
For example:
|
||||
## 版本规范
|
||||
软件包版本遵循 MuJoCo 官方发布版本。
|
||||
例如:
|
||||
|
||||
| npm version | MuJoCo version |
|
||||
| npm 版本 | MuJoCo 版本 |
|
||||
|-------------|----------------|
|
||||
| 3.5.0 | 3.5.0 |
|
||||
|
||||
## Future Work
|
||||
## 未来工作
|
||||
|
||||
1. **Bind all useful APIs.**
|
||||
These bindings are not yet complete. While the main MuJoCo APIs (`mj_step`,
|
||||
`mj_loadXML`, etc.) are well tested, other APIs (e.g., functions from
|
||||
`mjspec.h`) remain untested in real web applications (though test code for
|
||||
the `mjspec` bindings does exist).
|
||||
1. **绑定所有实用 API。**
|
||||
这些绑定尚未完全覆盖所有接口。虽然核心 MuJoCo API(`mj_step`、`mj_loadXML` 等)已经过良好测试,但其他 API(例如来自 `mjspec.h` 的函数)在真实的 Web 应用程序中仍未经充分测试(尽管 `mjspec` 绑定的测试代码已存在)。
|
||||
|
||||
2. **Improve the developer experience.**
|
||||
There is still work to be done to improve the developer experience when
|
||||
developing the WASM bindings. The most obvious issue is that bindings
|
||||
generation is not yet fully automated. As a result, it is currently less
|
||||
convenient than we'd like to identify and apply the changes needed to update
|
||||
the bindings. The goal is to eventually automate all binding code generation
|
||||
and clearly communicate what changes are required in the WASM bindings as a
|
||||
result of C++ updates. This problem should only affect developers working on
|
||||
the MuJoCo engine in C++, not end users writing JavaScript.
|
||||
2. **改善开发者体验。**
|
||||
在开发 WASM 绑定本身的开发者体验方面仍有改进空间。最明显的问题是绑定生成尚未完全自动化。因此,当前识别和应用更新绑定所需的更改还不够便捷。目标是最终将所有绑定代码生成自动化,并清晰提示由于 C++ 更新而在 WASM 绑定中需要进行的更改。此问题仅影响从事 MuJoCo C++ 引擎开发的开发者,不影响编写 JavaScript 的最终用户。
|
||||
|
||||
3. **Improve the documentation.**
|
||||
The documentation in this README will eventually be merged into the main
|
||||
MuJoCo documentation once the bindings are complete and named access is
|
||||
implemented. We also intend to review the bindings APIs and make adjustments
|
||||
to minimize differences with the Python bindings (while respecting language
|
||||
idioms) to reduce the amount of additional documentation required.
|
||||
3. **完善文档。**
|
||||
一旦绑定完成且按名称访问完全实现,本 README 中的文档最终将合并到 MuJoCo 主文档中。我们还计划审查绑定 API 并进行调整,以在遵循语言惯用法的同时最大程度减少与 Python 绑定的差异,从而减少所需的额外文档量。
|
||||
|
||||
4. **Improve the [example](#example-application).**
|
||||
We aim to provide an example application that can be easily modified and
|
||||
embedded into a paper project page (see [this example](https://kzakka.com/robopianist/)).
|
||||
This could be achieved by extending the Three.js example or by compiling the
|
||||
MuJoCo platform C++ code using the Emscripten toolchain. Community
|
||||
suggestions and contributions are welcome!
|
||||
4. **完善[示例应用](#示例应用程序)。**
|
||||
我们的目标是提供一个可以轻松修改并嵌入到学术论文项目主页中的示例应用程序(参见[此示例](https://kzakka.com/robopianist/))。这可以通过扩展 Three.js 示例或使用 Emscripten 工具链编译 MuJoCo 平台 C++ 代码来实现。欢迎社区提出建议并参与贡献!
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,41 @@
|
||||
<!--
|
||||
Copyright 2025 DeepMind Technologies Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>MuJoCo WebAssembly</title>
|
||||
<style>
|
||||
#button-container {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="./assets/index-DpY1_NiW.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="button-container">
|
||||
<button id="pause-button">Pause</button>
|
||||
<button id="reset-button">Reset</button>
|
||||
<button id="contact-button">Show Contacts</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user