Merge remote-tracking branch 'upstream/main' into tendons
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
||||
include LICENSE *.md
|
||||
recursive-include mujoco *.h *.cc *.mm CMakeLists.txt *.cmake
|
||||
recursive-include mujoco *.h *.cc *.cc.inc *.mm CMakeLists.txt *.cmake
|
||||
recursive-include mujoco/mjpython mjpython.* Info.plist
|
||||
|
||||
@@ -44,6 +44,8 @@ python "${package_dir}"/mujoco/codegen/generate_enum_traits.py > \
|
||||
mujoco/enum_traits.h
|
||||
python "${package_dir}"/mujoco/codegen/generate_function_traits.py > \
|
||||
mujoco/function_traits.h
|
||||
python "${package_dir}"/mujoco/codegen/generate_spec_bindings.py > \
|
||||
mujoco/specs.cc.inc
|
||||
export PYTHONPATH="${old_pythonpath}"
|
||||
|
||||
# Copy over the LICENSE file.
|
||||
|
||||
@@ -405,6 +405,29 @@ target_link_libraries(
|
||||
structs_header
|
||||
)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/specs.cc.inc)
|
||||
add_custom_command(
|
||||
OUTPUT specs.cc.inc
|
||||
COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${mujoco_SOURCE_DIR}/mujoco ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/generate_spec_bindings.py > specs.cc.inc
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/codegen/generate_spec_bindings.py
|
||||
)
|
||||
endif()
|
||||
|
||||
mujoco_pybind11_module(
|
||||
_specs
|
||||
specs.cc
|
||||
specs.cc.inc
|
||||
)
|
||||
target_link_libraries(
|
||||
_specs
|
||||
PRIVATE mujoco
|
||||
Eigen3::Eigen
|
||||
errors_header
|
||||
raw
|
||||
structs_header
|
||||
)
|
||||
|
||||
mujoco_pybind11_module(_simulate simulate.cc)
|
||||
target_link_libraries(
|
||||
_simulate
|
||||
@@ -424,6 +447,7 @@ set(LIBRARIES_FOR_WHEEL
|
||||
"$<TARGET_FILE:_render>"
|
||||
"$<TARGET_FILE:_rollout>"
|
||||
"$<TARGET_FILE:_simulate>"
|
||||
"$<TARGET_FILE:_specs>"
|
||||
"$<TARGET_FILE:_structs>"
|
||||
"$<TARGET_FILE:mujoco>"
|
||||
)
|
||||
@@ -459,6 +483,7 @@ if(MUJOCO_PYTHON_MAKE_WHEEL)
|
||||
_render
|
||||
_rollout
|
||||
_simulate
|
||||
_specs
|
||||
_structs
|
||||
mujoco
|
||||
)
|
||||
|
||||
@@ -43,6 +43,7 @@ from mujoco._enums import *
|
||||
from mujoco._errors import *
|
||||
from mujoco._functions import *
|
||||
from mujoco._render import *
|
||||
from mujoco._specs import *
|
||||
from mujoco._structs import *
|
||||
from mujoco.gl_context import *
|
||||
from mujoco.renderer import Renderer
|
||||
|
||||
@@ -409,19 +409,6 @@ class MuJoCoBindingsTest(parameterized.TestCase):
|
||||
np.testing.assert_array_equal(self.model.geom_size[1], [0.5, 0.5, 0.5])
|
||||
np.testing.assert_array_equal(model_copy.geom_size[1], [0.1, 0.1, 0.1])
|
||||
|
||||
def test_assets_array_filename_too_long(self):
|
||||
# Longest allowed filename (excluding null byte)
|
||||
limit = mujoco.mjMAXVFSNAME - 1
|
||||
contents = b'<mujoco/>'
|
||||
valid_filename = 'a' * limit
|
||||
mujoco.MjModel.from_xml_path(valid_filename, {valid_filename: contents})
|
||||
invalid_filename = 'a' * (limit + 1)
|
||||
expected_message = (
|
||||
f'Filename length 1000 exceeds 999 character limit: {invalid_filename}')
|
||||
with self.assertRaisesWithLiteralMatch(ValueError, expected_message):
|
||||
mujoco.MjModel.from_xml_path(invalid_filename,
|
||||
{invalid_filename: contents})
|
||||
|
||||
def test_mjdata_can_copy(self):
|
||||
self.data.qpos = [0, 0, 0.1*np.sqrt(2) - 0.001,
|
||||
np.cos(np.pi/8), np.sin(np.pi/8), 0, 0, 0,
|
||||
@@ -856,9 +843,6 @@ Return the current version of MuJoCo as a null-terminated string.
|
||||
Euler integrator, semi-implicit in velocity.
|
||||
""")
|
||||
|
||||
def test_int_constant(self):
|
||||
self.assertEqual(mujoco.mjMAXVFSNAME, 1000)
|
||||
|
||||
def test_float_constant(self):
|
||||
self.assertEqual(mujoco.mjMAXVAL, 1e10)
|
||||
self.assertEqual(mujoco.mjMINVAL, 1e-15)
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
# Copyright 2024 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.
|
||||
# ==============================================================================
|
||||
"""Generates the bindings for the MuJoCo specs."""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from absl import app
|
||||
|
||||
from introspect import ast_nodes
|
||||
from introspect import structs
|
||||
|
||||
|
||||
SCALAR_TYPES = {'int', 'double', 'float', 'mjtByte', 'mjtNum'}
|
||||
|
||||
|
||||
def _value_binding_code(
|
||||
field: ast_nodes.ValueType, classname: str = '', varname: str = ''
|
||||
) -> str:
|
||||
"""Creates a string that defines Python bindings for a value type."""
|
||||
fulltype = field.name
|
||||
if field.name not in SCALAR_TYPES:
|
||||
fulltype += '&'
|
||||
fullvarname = varname
|
||||
rawclassname = classname.replace('mjs', 'raw::Mjs')
|
||||
if classname == 'mjSpec': # raw mjSpec has a wrapper
|
||||
rawclassname = classname.replace('mjS', 'MjS')
|
||||
fullvarname = 'ptr->' + varname
|
||||
if field.name.startswith('mjs'): # all other mjs are raw structs
|
||||
fulltype = field.name.replace('mjs', 'raw::Mjs')
|
||||
if field.name != 'mjsPlugin' and field.name != 'mjsOrientation':
|
||||
fulltype = fulltype + '*' # plugin and orientation are pointers
|
||||
|
||||
def_property_args = (
|
||||
f'"{varname}"',
|
||||
f"""[]({rawclassname}& self) -> {fulltype} {{
|
||||
return self.{fullvarname};
|
||||
}}""",
|
||||
f"""[]({rawclassname}& self, {fulltype} {varname}) {{
|
||||
self.{fullvarname} = {varname};
|
||||
}}""",
|
||||
)
|
||||
|
||||
if field.name not in SCALAR_TYPES:
|
||||
def_property_args += ('py::return_value_policy::reference_internal',)
|
||||
|
||||
return f'{classname}.def_property({",".join(def_property_args)});'
|
||||
|
||||
|
||||
def _array_binding_code(
|
||||
field: ast_nodes.ArrayType, classname: str = '', varname: str = ''
|
||||
) -> str:
|
||||
"""Creates a string that declares Python bindings for an array type."""
|
||||
if len(field.extents) > 1:
|
||||
raise NotImplementedError()
|
||||
innertype = field.inner_type.decl()
|
||||
rawclassname = classname.replace('mjs', 'raw::Mjs')
|
||||
fullvarname = varname
|
||||
if classname == 'mjSpec': # raw mjSpec has a wrapper
|
||||
rawclassname = classname.replace('mjS', 'MjS')
|
||||
fullvarname = 'ptr->' + varname
|
||||
if innertype == 'double' or innertype == 'mjtNum':
|
||||
innertype = 'MjDouble' # custom Eigen type
|
||||
elif innertype == 'float':
|
||||
innertype = 'MjFloat' # custom Eigen type
|
||||
elif innertype == 'int':
|
||||
innertype = 'MjInt' # custom Eigen type
|
||||
elif innertype == 'char':
|
||||
# char array special case
|
||||
return f"""\
|
||||
{classname}.def_property(
|
||||
"{varname}",
|
||||
[]({rawclassname}& self) -> py::array_t<char> {{
|
||||
return py::array_t<char>({field.extents[0]}, self.{fullvarname});
|
||||
}},
|
||||
[]({rawclassname}& self, py::object rhs) {{
|
||||
int i = 0;
|
||||
for (auto val : rhs) {{
|
||||
self.{fullvarname}[i++] = py::cast<char>(val);
|
||||
}}
|
||||
}}, py::return_value_policy::reference_internal);"""
|
||||
# all other array types
|
||||
return f"""\
|
||||
{classname}.def_property(
|
||||
"{varname}",
|
||||
[]({rawclassname}& self) -> {innertype}{field.extents[0]} {{
|
||||
return {innertype}{field.extents[0]}(self.{fullvarname});
|
||||
}},
|
||||
[]({rawclassname}& self, {innertype}Ref{field.extents[0]} {varname}) {{
|
||||
{innertype}{field.extents[0]}(self.{fullvarname}) = {varname};
|
||||
}}, py::return_value_policy::reference_internal);"""
|
||||
|
||||
|
||||
def _ptr_binding_code(
|
||||
field: ast_nodes.PointerType, classname: str = '', varname: str = ''
|
||||
) -> str:
|
||||
"""Creates a string that declares Python bindings for a pointer type."""
|
||||
vartype = field.inner_type.decl()
|
||||
rawclassname = classname.replace('mjs', 'raw::Mjs')
|
||||
fullvarname = varname
|
||||
if classname == 'mjSpec': # raw mjSpec has a wrapper
|
||||
rawclassname = classname.replace('mjS', 'MjS')
|
||||
fullvarname = 'ptr->' + varname
|
||||
if vartype == 'mjsElement': # this is ignored by the caller
|
||||
return 'mjsElement'
|
||||
if vartype.startswith('mjs'): # for structs, use the value case
|
||||
return _value_binding_code(field.inner_type, classname, varname)
|
||||
elif vartype == 'mjString': # C++ string -> Python string
|
||||
return f"""\
|
||||
{classname}.def_property(
|
||||
"{varname}",
|
||||
[]({rawclassname}& self) -> std::string_view {{
|
||||
return *self.{fullvarname};
|
||||
}},
|
||||
[]({rawclassname}& self, std::string_view {varname}) {{
|
||||
*(self.{fullvarname}) = {varname};
|
||||
}});"""
|
||||
elif ( # C++ vectors of values -> Python array
|
||||
vartype == 'mjDoubleVec'
|
||||
or vartype == 'mjFloatVec'
|
||||
or vartype == 'mjIntVec'
|
||||
):
|
||||
vartype = vartype.replace('mj', '').replace('Vec', '').lower()
|
||||
return f"""\
|
||||
{classname}.def_property(
|
||||
"{varname}",
|
||||
[]({rawclassname}& self) -> py::array_t<{vartype}> {{
|
||||
return py::array_t<{vartype}>(self.{fullvarname}->size(),
|
||||
self.{fullvarname}->data());
|
||||
}},
|
||||
[]({rawclassname}& self, py::object rhs) {{
|
||||
self.{fullvarname}->clear();
|
||||
self.{fullvarname}->reserve(py::len(rhs));
|
||||
for (auto val : rhs) {{
|
||||
self.{fullvarname}->push_back(py::cast<{vartype}>(val));
|
||||
}}
|
||||
}}, py::return_value_policy::reference_internal);"""
|
||||
elif vartype == 'mjStringVec': # C++ vector of strings -> Python list
|
||||
return f"""\
|
||||
{classname}.def_property(
|
||||
"{varname}",
|
||||
[]({rawclassname}& self) -> py::list {{
|
||||
py::list list;
|
||||
for (auto val : *self.{fullvarname}) {{
|
||||
list.append(val);
|
||||
}}
|
||||
return list;
|
||||
}},
|
||||
[]({rawclassname}& self, py::object rhs) {{
|
||||
self.{fullvarname}->clear();
|
||||
self.{fullvarname}->reserve(py::len(rhs));
|
||||
for (auto val : rhs) {{
|
||||
self.{fullvarname}->push_back(py::cast<std::string>(val));
|
||||
}}
|
||||
}}, py::return_value_policy::reference_internal);"""
|
||||
elif 'VecVec' in vartype: # C++ vector of vectors -> Python list of lists
|
||||
vartype = vartype.replace('mj', '').replace('VecVec', '').lower()
|
||||
return f"""\
|
||||
{classname}.def_property(
|
||||
"{varname}",
|
||||
[]({rawclassname}& self) -> py::list {{
|
||||
py::list list;
|
||||
for (auto inner_vec : *self.{fullvarname}) {{
|
||||
py::list inner_list;
|
||||
for (auto val : inner_vec) {{
|
||||
inner_list.append(val);
|
||||
}}
|
||||
list.append(inner_list);
|
||||
}}
|
||||
return list;
|
||||
}},
|
||||
[]({rawclassname}& self, py::object rhs) {{
|
||||
self.{fullvarname}->clear();
|
||||
self.{fullvarname}->reserve(py::len(rhs));
|
||||
for (auto inner_list : rhs) {{
|
||||
auto inner_vec = py::cast<std::vector<{vartype}>>(inner_list);
|
||||
self.{fullvarname}->push_back(inner_vec);
|
||||
}}
|
||||
}}, py::return_value_policy::reference_internal);"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _binding_code(field: ast_nodes.StructFieldDecl, key: str) -> str:
|
||||
if isinstance(field.type, ast_nodes.ValueType):
|
||||
return _value_binding_code(field.type, key, field.name)
|
||||
elif isinstance(field.type, ast_nodes.PointerType):
|
||||
return _ptr_binding_code(field.type, key, field.name)
|
||||
elif isinstance(field.type, ast_nodes.ArrayType):
|
||||
return _array_binding_code(field.type, key, field.name)
|
||||
return ''
|
||||
|
||||
|
||||
def generate() -> None:
|
||||
for key in structs.STRUCTS.keys():
|
||||
if (key.startswith('mjs') or key == 'mjSpec') and key != 'mjsElement':
|
||||
print('\n // ' + key)
|
||||
for field in structs.STRUCTS[key].fields:
|
||||
code = _binding_code(field, key)
|
||||
if code != 'mjsElement':
|
||||
print(code)
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
generate()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
@@ -57,8 +57,6 @@ PYBIND11_MODULE(_constants, pymodule) {
|
||||
X(mjMINIMP);
|
||||
X(mjMAXIMP);
|
||||
X(mjMAXCONPAIR);
|
||||
X(mjMAXVFS);
|
||||
X(mjMAXVFSNAME);
|
||||
X(mjNEQDATA);
|
||||
X(mjNDYN);
|
||||
X(mjNGAIN);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
@@ -29,6 +28,7 @@
|
||||
#include "functions.h"
|
||||
#include "private.h"
|
||||
#include "raw.h"
|
||||
#include "structs.h"
|
||||
#include <pybind11/eigen.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
@@ -108,9 +108,10 @@ PYBIND11_MODULE(_functions, pymodule) {
|
||||
// Skipped: mj_copyModel (have MjModel.__copy__, memory managed by MjModel)
|
||||
pymodule.def(
|
||||
"mj_saveModel",
|
||||
[](const MjModelWrapper& m, const std::optional<std::string>& filename = std::nullopt,
|
||||
std::optional<
|
||||
Eigen::Ref<Eigen::Vector<std::uint8_t, Eigen::Dynamic>>> buffer = std::nullopt) {
|
||||
[](const MjModelWrapper& m,
|
||||
const std::optional<std::string>& filename = std::nullopt,
|
||||
std::optional<Eigen::Ref<Eigen::Vector<std::uint8_t, Eigen::Dynamic>>>
|
||||
buffer = std::nullopt) {
|
||||
void* buffer_ptr = nullptr;
|
||||
int buffer_sz = 0;
|
||||
if (buffer.has_value()) {
|
||||
@@ -122,8 +123,7 @@ PYBIND11_MODULE(_functions, pymodule) {
|
||||
buffer_ptr, buffer_sz);
|
||||
},
|
||||
py::arg("m"), py::arg_v("filename", std::nullopt),
|
||||
py::arg_v("buffer", std::nullopt),
|
||||
py::doc(traits::mj_saveModel::doc),
|
||||
py::arg_v("buffer", std::nullopt), py::doc(traits::mj_saveModel::doc),
|
||||
py::call_guard<py::gil_scoped_release>());
|
||||
// Skipped: mj_loadModel (have MjModel.from_binary_path)
|
||||
// Skipped: mj_deleteModel (have MjModel.__del__)
|
||||
@@ -700,8 +700,8 @@ PYBIND11_MODULE(_functions, pymodule) {
|
||||
Def<traits::mju_dist3>(pymodule);
|
||||
Def<traits::mju_mulMatVec3>(pymodule);
|
||||
Def<traits::mju_mulMatTVec3>(pymodule);
|
||||
Def<traits::mju_rotVecMat>(pymodule);
|
||||
Def<traits::mju_rotVecMatT>(pymodule);
|
||||
// skipped: mju_rotVecMat
|
||||
// skipped: mju_rotVecMatT
|
||||
Def<traits::mju_cross>(pymodule);
|
||||
Def<traits::mju_zero4>(pymodule);
|
||||
Def<traits::mju_unit4>(pymodule);
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
X( float, light_, specular, nlight, 3 )
|
||||
|
||||
#define MJMODEL_MATERIAL \
|
||||
X( int, mat_, texid, nmat, 1 ) \
|
||||
X( int, mat_, texid, nmat, mjNTEXMAT ) \
|
||||
X( mjtByte, mat_, texuniform, nmat, 1 ) \
|
||||
X( float, mat_, texrepeat, nmat, 2 ) \
|
||||
X( float, mat_, emission, nmat, 1 ) \
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright 2024 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.
|
||||
# ==============================================================================
|
||||
"""Test that copying mujoco.MjData multiple times doesn't leak memory."""
|
||||
|
||||
import copy
|
||||
import textwrap
|
||||
|
||||
from absl.testing import absltest
|
||||
import mujoco
|
||||
|
||||
|
||||
class MemoryLeakTest(absltest.TestCase):
|
||||
|
||||
# Regression test for https://github.com/google-deepmind/mujoco/issues/1572
|
||||
def test_deepcopy_mjdata_leak(self):
|
||||
# MuJoCo model with textures that take up significant memory.
|
||||
model_xml = textwrap.dedent("""
|
||||
<mujoco>
|
||||
<asset>
|
||||
<material name="body"/>
|
||||
<texture name="grid" type="2d" builtin="checker"
|
||||
width="1024" height="1024" rgb1="0 0 0" rgb2="1 1 1"/>
|
||||
<material name="grid" texture="grid" texrepeat="1 1"
|
||||
texuniform="true" reflectance=".2"/>
|
||||
</asset>
|
||||
<worldbody>
|
||||
<geom size="0 0 .05" type="plane" material="grid"/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
""")
|
||||
limit = self._memory_limit(4 * 2**30)
|
||||
try:
|
||||
model = mujoco.MjModel.from_xml_string(model_xml)
|
||||
data = mujoco.MjData(model)
|
||||
total = 0
|
||||
for _ in range(1000):
|
||||
mujoco.mj_step(model, data)
|
||||
last_data = copy.deepcopy(data)
|
||||
total += last_data.time
|
||||
finally:
|
||||
self._memory_limit(limit)
|
||||
|
||||
def _memory_limit(self, limit_in_bytes: int) -> int:
|
||||
"""Limits max memory usage, and returns previous limit."""
|
||||
soft = -1
|
||||
try:
|
||||
import resource # pylint: disable=g-import-not-at-top
|
||||
soft, hard = resource.getrlimit(resource.RLIMIT_AS)
|
||||
resource.setrlimit(resource.RLIMIT_AS, (limit_in_bytes, hard))
|
||||
except (ImportError, ValueError):
|
||||
# On Windows or systems where setting resource limits fails, do nothing.
|
||||
pass
|
||||
return soft
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
absltest.main()
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <mujoco/mjdata.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mjrender.h>
|
||||
#include <mujoco/mjspec.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
|
||||
// Type aliases for MuJoCo C structs to allow us refer to consistently refer
|
||||
@@ -28,6 +29,35 @@ using MjContact = ::mjContact;
|
||||
using MjData = ::mjData;
|
||||
using MjLROpt = ::mjLROpt;
|
||||
using MjModel = ::mjModel;
|
||||
using MjSpec = ::mjSpec;
|
||||
using MjsElement = ::mjsElement;
|
||||
using MjsOrientation = ::mjsOrientation;
|
||||
using MjsPlugin = ::mjsPlugin;
|
||||
using MjsBody = ::mjsBody;
|
||||
using MjsFrame = ::mjsFrame;
|
||||
using MjsJoint = ::mjsJoint;
|
||||
using MjsGeom = ::mjsGeom;
|
||||
using MjsSite = ::mjsSite;
|
||||
using MjsCamera = ::mjsCamera;
|
||||
using MjsLight = ::mjsLight;
|
||||
using MjsFlex = ::mjsFlex;
|
||||
using MjsMesh = ::mjsMesh;
|
||||
using MjsHField = ::mjsHField;
|
||||
using MjsSkin = ::mjsSkin;
|
||||
using MjsTexture = ::mjsTexture;
|
||||
using MjsMaterial = ::mjsMaterial;
|
||||
using MjsPair = ::mjsPair;
|
||||
using MjsExclude = ::mjsExclude;
|
||||
using MjsEquality = ::mjsEquality;
|
||||
using MjsTendon = ::mjsTendon;
|
||||
using MjsWrap = ::mjsWrap;
|
||||
using MjsActuator = ::mjsActuator;
|
||||
using MjsSensor = ::mjsSensor;
|
||||
using MjsNumeric = ::mjsNumeric;
|
||||
using MjsText = ::mjsText;
|
||||
using MjsTuple = ::mjsTuple;
|
||||
using MjsKey = ::mjsKey;
|
||||
using MjsDefault = ::mjsDefault;
|
||||
using MjOption = ::mjOption;
|
||||
using MjSolverStat = ::mjSolverStat;
|
||||
using MjStatistic = ::mjStatistic;
|
||||
|
||||
@@ -54,6 +54,9 @@ class MjWrapper<raw::MjrContext> : public WrapperBase<raw::MjrContext> {
|
||||
X(auxFBO_r);
|
||||
X(auxColor);
|
||||
X(auxColor_r);
|
||||
X(mat_texid);
|
||||
X(mat_texuniform);
|
||||
X(mat_texrepeat);
|
||||
X(textureType);
|
||||
X(texture);
|
||||
X(skinvertVBO);
|
||||
@@ -95,6 +98,9 @@ MjrContextWrapper::MjWrapper()
|
||||
X(auxFBO_r),
|
||||
X(auxColor),
|
||||
X(auxColor_r),
|
||||
X(mat_texid),
|
||||
X(mat_texuniform),
|
||||
X(mat_texrepeat),
|
||||
X(textureType),
|
||||
X(texture),
|
||||
X_SKIN(skinvertVBO),
|
||||
@@ -119,6 +125,9 @@ MjrContextWrapper::MjWrapper(const MjModelWrapper& model, int fontscale)
|
||||
X(auxFBO_r),
|
||||
X(auxColor),
|
||||
X(auxColor_r),
|
||||
X(mat_texid),
|
||||
X(mat_texuniform),
|
||||
X(mat_texrepeat),
|
||||
X(textureType),
|
||||
X(texture),
|
||||
X_SKIN(skinvertVBO),
|
||||
@@ -238,6 +247,9 @@ PYBIND11_MODULE(_render, pymodule) {
|
||||
X(auxFBO_r);
|
||||
X(auxColor);
|
||||
X(auxColor_r);
|
||||
X(mat_texid);
|
||||
X(mat_texuniform);
|
||||
X(mat_texrepeat);
|
||||
X(textureType);
|
||||
X(texture);
|
||||
X(skinvertVBO);
|
||||
|
||||
@@ -123,6 +123,9 @@ class SimulateWrapper {
|
||||
|
||||
mujoco::Simulate* simulate() { return simulate_; }
|
||||
|
||||
py::object GetModel() const { return m_; }
|
||||
py::object GetData() const { return d_; }
|
||||
|
||||
private:
|
||||
mujoco::Simulate* simulate_;
|
||||
std::atomic_int destroyed_ = 0;
|
||||
@@ -222,6 +225,8 @@ PYBIND11_MODULE(_simulate, pymodule) {
|
||||
.def("lock", GetIfNotNull(&mujoco::Simulate::mtx),
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::return_value_policy::reference_internal)
|
||||
.def_property_readonly("m", &SimulateWrapper::GetModel)
|
||||
.def_property_readonly("d", &SimulateWrapper::GetData)
|
||||
.def_property_readonly("ctrl_noise_std",
|
||||
GetIfNotNull(&mujoco::Simulate::ctrl_noise_std),
|
||||
py::call_guard<py::gil_scoped_release>())
|
||||
|
||||
@@ -0,0 +1,954 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef> // IWYU pragma: keep
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view> // IWYU pragma: keep
|
||||
#include <vector> // IWYU pragma: keep
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Eigen>
|
||||
#include <mujoco/mjspec.h> // IWYU pragma: keep
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "errors.h"
|
||||
#include "indexers.h" // IWYU pragma: keep
|
||||
#include "raw.h"
|
||||
#include "structs.h" // IWYU pragma: keep
|
||||
#include <pybind11/cast.h>
|
||||
#include <pybind11/eigen.h>
|
||||
#include <pybind11/eigen/matrix.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/pytypes.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
namespace py = ::pybind11;
|
||||
|
||||
namespace mujoco::python {
|
||||
using MjInt2 = Eigen::Map<Eigen::Vector2i>;
|
||||
using MjInt3 = Eigen::Map<Eigen::Vector3i>;
|
||||
using MjFloat2 = Eigen::Map<Eigen::Vector2f>;
|
||||
using MjFloat3 = Eigen::Map<Eigen::Vector3f>;
|
||||
using MjFloat4 = Eigen::Map<Eigen::Vector4f>;
|
||||
using MjDouble2 = Eigen::Map<Eigen::Vector2d>;
|
||||
using MjDouble3 = Eigen::Map<Eigen::Vector3d>;
|
||||
using MjDouble4 = Eigen::Map<Eigen::Vector4d>;
|
||||
using MjDouble5 = Eigen::Map<Eigen::Matrix<double, 5, 1>>;
|
||||
using MjDouble6 = Eigen::Map<Eigen::Matrix<double, 6, 1>>;
|
||||
using MjDouble10 = Eigen::Map<Eigen::Matrix<double, 10, 1>>;
|
||||
using MjDouble11 = Eigen::Map<Eigen::Matrix<double, 11, 1>>;
|
||||
using MjDoubleVec = Eigen::Map<Eigen::VectorXd>;
|
||||
|
||||
using MjIntRef2 = Eigen::Ref<const Eigen::Vector2i>;
|
||||
using MjIntRef3 = Eigen::Ref<const Eigen::Vector3i>;
|
||||
using MjFloatRef2 = Eigen::Ref<const Eigen::Vector2f>;
|
||||
using MjFloatRef3 = Eigen::Ref<const Eigen::Vector3f>;
|
||||
using MjFloatRef4 = Eigen::Ref<const Eigen::Vector4f>;
|
||||
using MjDoubleRef2 = Eigen::Ref<const Eigen::Vector2d>;
|
||||
using MjDoubleRef3 = Eigen::Ref<const Eigen::Vector3d>;
|
||||
using MjDoubleRef4 = Eigen::Ref<const Eigen::Vector4d>;
|
||||
using MjDoubleRef5 = Eigen::Ref<const Eigen::Matrix<double, 5, 1>>;
|
||||
using MjDoubleRef6 = Eigen::Ref<const Eigen::Matrix<double, 6, 1>>;
|
||||
using MjDoubleRef10 = Eigen::Ref<const Eigen::Matrix<double, 10, 1>>;
|
||||
using MjDoubleRef11 = Eigen::Ref<const Eigen::Matrix<double, 11, 1>>;
|
||||
using MjDoubleRefVec = Eigen::Ref<const Eigen::VectorXd>;
|
||||
|
||||
struct MjSpec {
|
||||
MjSpec() : ptr(mj_makeSpec()) {}
|
||||
~MjSpec() { mj_deleteSpec(ptr); }
|
||||
raw::MjSpec* ptr;
|
||||
};
|
||||
|
||||
PYBIND11_MODULE(_specs, m) {
|
||||
auto structs_m = py::module::import("mujoco._structs");
|
||||
py::function mjmodel_from_spec_ptr =
|
||||
structs_m.attr("MjModel").attr("_from_spec_ptr");
|
||||
py::function mjmodel_mjdata_from_spec_ptr =
|
||||
structs_m.attr("_recompile_spec_addr");
|
||||
|
||||
py::class_<MjSpec> mjSpec(m, "MjSpec");
|
||||
py::class_<raw::MjsElement> mjsElement(m, "MjsElement");
|
||||
py::class_<raw::MjsDefault> mjsDefault(m, "MjsDefault");
|
||||
py::class_<raw::MjsBody> mjsBody(m, "MjsBody");
|
||||
py::class_<raw::MjsFrame> mjsFrame(m, "MjsFrame");
|
||||
py::class_<raw::MjsGeom> mjsGeom(m, "MjsGeom");
|
||||
py::class_<raw::MjsJoint> mjsJoint(m, "MjsJoint");
|
||||
py::class_<raw::MjsLight> mjsLight(m, "MjsLight");
|
||||
py::class_<raw::MjsMaterial> mjsMaterial(m, "MjsMaterial");
|
||||
py::class_<raw::MjsSite> mjsSite(m, "MjsSite");
|
||||
py::class_<raw::MjsMesh> mjsMesh(m, "MjsMesh");
|
||||
py::class_<raw::MjsSkin> mjsSkin(m, "MjsSkin");
|
||||
py::class_<raw::MjsTexture> mjsTexture(m, "MjsTexture");
|
||||
py::class_<raw::MjsText> mjsText(m, "MjsText");
|
||||
py::class_<raw::MjsTuple> mjsTuple(m, "MjsTuple");
|
||||
py::class_<raw::MjsCamera> mjsCamera(m, "MjsCamera");
|
||||
py::class_<raw::MjsFlex> mjsFlex(m, "MjsFlex");
|
||||
py::class_<raw::MjsHField> mjsHField(m, "MjsHField");
|
||||
py::class_<raw::MjsKey> mjsKey(m, "MjsKey");
|
||||
py::class_<raw::MjsNumeric> mjsNumeric(m, "MjsNumeric");
|
||||
py::class_<raw::MjsPair> mjsPair(m, "MjsPair");
|
||||
py::class_<raw::MjsExclude> mjsExclude(m, "MjsExclude");
|
||||
py::class_<raw::MjsEquality> mjsEquality(m, "MjsEquality");
|
||||
py::class_<raw::MjsTendon> mjsTendon(m, "MjsTendon");
|
||||
py::class_<raw::MjsSensor> mjsSensor(m, "MjsSensor");
|
||||
py::class_<raw::MjsActuator> mjsActuator(m, "MjsActuator");
|
||||
py::class_<raw::MjsPlugin> mjsPlugin(m, "MjsPlugin");
|
||||
py::class_<raw::MjsOrientation> mjsOrientation(m, "MjsOrientation");
|
||||
py::class_<raw::MjsWrap> mjsWrap(m, "MjsWrap");
|
||||
|
||||
// ============================= MJSPEC =====================================
|
||||
mjSpec.def(py::init<>());
|
||||
mjSpec.def("recompile", [mjmodel_mjdata_from_spec_ptr](
|
||||
const MjSpec& self, py::object m, py::object d) {
|
||||
return mjmodel_mjdata_from_spec_ptr(reinterpret_cast<uintptr_t>(self.ptr),
|
||||
m, d);
|
||||
});
|
||||
mjSpec.def(
|
||||
"copy",
|
||||
[](const MjSpec& self) -> raw::MjSpec* { return mj_copySpec(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"worldbody",
|
||||
[](MjSpec& self) -> raw::MjsBody* {
|
||||
return mjs_findBody(self.ptr, "world");
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"find_body",
|
||||
[](MjSpec& self, std::string& name) -> raw::MjsBody* {
|
||||
return mjs_findBody(self.ptr, name.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"find_mesh",
|
||||
[](MjSpec& self, std::string& name) -> raw::MjsMesh* {
|
||||
return mjs_findMesh(self.ptr, name.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"find_frame",
|
||||
[](MjSpec& self, std::string& name) -> raw::MjsFrame* {
|
||||
return mjs_findFrame(self.ptr, name.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"find_keyframe",
|
||||
[](MjSpec& self, std::string& name) -> raw::MjsKey* {
|
||||
return mjs_findKeyframe(self.ptr, name.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"find_default",
|
||||
[](MjSpec& self, std::string& classname) -> raw::MjsDefault* {
|
||||
return mjs_findDefault(self.ptr, classname.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def("compile", [mjmodel_from_spec_ptr](MjSpec& self) {
|
||||
return mjmodel_from_spec_ptr(reinterpret_cast<uintptr_t>(self.ptr));
|
||||
});
|
||||
mjSpec.def(
|
||||
"copy_back",
|
||||
[](MjSpec& self, raw::MjModel& model) {
|
||||
return mj_copyBack(self.ptr, &model);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def("to_xml", [](MjSpec& self) -> std::string {
|
||||
int size = mj_saveXMLString(self.ptr, nullptr, 0, nullptr, 0);
|
||||
std::unique_ptr<char[]> buf(new char[size + 1]);
|
||||
std::array<char, 1024> err;
|
||||
buf[0] = '\0';
|
||||
err[0] = '\0';
|
||||
mj_saveXMLString(self.ptr, buf.get(), size + 1, err.data(), err.size());
|
||||
if (err[0] != '\0') {
|
||||
throw FatalError(std::string(err.data()));
|
||||
}
|
||||
return std::string(buf.get());
|
||||
});
|
||||
mjSpec.def("from_file", [](MjSpec& self, std::string& filename) -> void {
|
||||
std::array<char, 1024> err;
|
||||
err[0] = '\0';
|
||||
mj_deleteSpec(self.ptr);
|
||||
self.ptr = mj_parseXML(filename.c_str(), 0, err.data(), err.size());
|
||||
if (!self.ptr) {
|
||||
throw FatalError(std::string(err.data()));
|
||||
}
|
||||
});
|
||||
mjSpec.def("from_string", [](MjSpec& self, std::string& xml) -> void {
|
||||
std::array<char, 1024> err;
|
||||
err[0] = '\0';
|
||||
mj_deleteSpec(self.ptr);
|
||||
self.ptr = mj_parseXMLString(xml.c_str(), 0, err.data(), err.size());
|
||||
if (!self.ptr) {
|
||||
throw FatalError(std::string(err.data()));
|
||||
}
|
||||
});
|
||||
mjSpec.def(
|
||||
"add_default",
|
||||
[](MjSpec* spec, std::string& classname,
|
||||
raw::MjsDefault* parent) -> raw::MjsDefault* {
|
||||
return mjs_addDefault(spec->ptr, classname.c_str(), parent);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"default",
|
||||
[](MjSpec& self) -> raw::MjsDefault* {
|
||||
return mjs_getSpecDefault(self.ptr);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_material",
|
||||
[](MjSpec& self, raw::MjsDefault* default_) -> raw::MjsMaterial* {
|
||||
return mjs_addMaterial(self.ptr, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_mesh",
|
||||
[](MjSpec& self, raw::MjsDefault* default_) -> raw::MjsMesh* {
|
||||
return mjs_addMesh(self.ptr, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_skin",
|
||||
[](MjSpec& self) -> raw::MjsSkin* { return mjs_addSkin(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_texture",
|
||||
[](MjSpec& self) -> raw::MjsTexture* { return mjs_addTexture(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_text",
|
||||
[](MjSpec& self) -> raw::MjsText* { return mjs_addText(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_tuple",
|
||||
[](MjSpec& self) -> raw::MjsTuple* { return mjs_addTuple(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_flex",
|
||||
[](MjSpec& self) -> raw::MjsFlex* { return mjs_addFlex(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_hfield",
|
||||
[](MjSpec& self) -> raw::MjsHField* { return mjs_addHField(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_key",
|
||||
[](MjSpec& self) -> raw::MjsKey* { return mjs_addKey(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_numeric",
|
||||
[](MjSpec& self) -> raw::MjsNumeric* { return mjs_addNumeric(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_pair",
|
||||
[](MjSpec& self, raw::MjsDefault* default_) -> raw::MjsPair* {
|
||||
return mjs_addPair(self.ptr, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_exclude",
|
||||
[](MjSpec& self) -> raw::MjsExclude* { return mjs_addExclude(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_equality",
|
||||
[](MjSpec& self, raw::MjsDefault* default_) -> raw::MjsEquality* {
|
||||
return mjs_addEquality(self.ptr, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_tendon",
|
||||
[](MjSpec& self, raw::MjsDefault* default_) -> raw::MjsTendon* {
|
||||
return mjs_addTendon(self.ptr, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_sensor",
|
||||
[](MjSpec& self) -> raw::MjsSensor* { return mjs_addSensor(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_actuator",
|
||||
[](MjSpec& self, raw::MjsDefault* default_) -> raw::MjsActuator* {
|
||||
return mjs_addActuator(self.ptr, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def(
|
||||
"add_plugin",
|
||||
[](MjSpec& self) -> raw::MjsPlugin* { return mjs_addPlugin(self.ptr); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def("detach_body", [](MjSpec& self, raw::MjsBody& body) {
|
||||
mjs_detachBody(self.ptr, &body);
|
||||
});
|
||||
mjSpec.def_property_readonly(
|
||||
"actuators",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_ACTUATOR);
|
||||
while (el) {
|
||||
list.append(mjs_asActuator(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"sensors",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_SENSOR);
|
||||
while (el) {
|
||||
list.append(mjs_asSensor(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"flexes",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_FLEX);
|
||||
while (el) {
|
||||
list.append(mjs_asFlex(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"pairs",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_PAIR);
|
||||
while (el) {
|
||||
list.append(mjs_asPair(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"equality",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_EQUALITY);
|
||||
while (el) {
|
||||
list.append(mjs_asEquality(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"excludes",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_EXCLUDE);
|
||||
while (el) {
|
||||
list.append(mjs_asExclude(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"tendons",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_TENDON);
|
||||
while (el) {
|
||||
list.append(mjs_asTendon(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"numeric",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_NUMERIC);
|
||||
while (el) {
|
||||
list.append(mjs_asNumeric(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"text",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_TEXT);
|
||||
while (el) {
|
||||
list.append(mjs_asText(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"tuple",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_TUPLE);
|
||||
while (el) {
|
||||
list.append(mjs_asTuple(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"key",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_KEY);
|
||||
while (el) {
|
||||
list.append(mjs_asKey(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"mesh",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_MESH);
|
||||
while (el) {
|
||||
list.append(mjs_asMesh(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"hfield",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_HFIELD);
|
||||
while (el) {
|
||||
list.append(mjs_asHField(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"skin",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_SKIN);
|
||||
while (el) {
|
||||
list.append(mjs_asSkin(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"texture",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_TEXTURE);
|
||||
while (el) {
|
||||
list.append(mjs_asTexture(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjSpec.def_property_readonly(
|
||||
"material",
|
||||
[](MjSpec& self) -> py::list {
|
||||
py::list list;
|
||||
raw::MjsElement* el = mjs_firstElement(self.ptr, mjOBJ_MATERIAL);
|
||||
while (el) {
|
||||
list.append(mjs_asMaterial(el));
|
||||
el = mjs_nextElement(self.ptr, el);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSBODY =====================================
|
||||
mjsBody.def_property_readonly(
|
||||
"id", [](raw::MjsBody& self) -> int { return mjs_getId(self.element); });
|
||||
mjsBody.def("delete", [](raw::MjsBody& self) { mjs_delete(self.element); });
|
||||
mjsBody.def(
|
||||
"add_body",
|
||||
[](raw::MjsBody& self, raw::MjsDefault* default_) -> raw::MjsBody* {
|
||||
return mjs_addBody(&self, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"add_frame",
|
||||
[](raw::MjsBody& self, raw::MjsFrame* parentframe_) -> raw::MjsFrame* {
|
||||
return mjs_addFrame(&self, parentframe_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"add_geom",
|
||||
[](raw::MjsBody& self, raw::MjsDefault* default_) -> raw::MjsGeom* {
|
||||
return mjs_addGeom(&self, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"add_joint",
|
||||
[](raw::MjsBody& self, raw::MjsDefault* default_) -> raw::MjsJoint* {
|
||||
return mjs_addJoint(&self, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"add_freejoint",
|
||||
[](raw::MjsBody& self) -> raw::MjsJoint* {
|
||||
return mjs_addFreeJoint(&self);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"add_light",
|
||||
[](raw::MjsBody& self, raw::MjsDefault* default_) -> raw::MjsLight* {
|
||||
return mjs_addLight(&self, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"add_site",
|
||||
[](raw::MjsBody& self, raw::MjsDefault* default_) -> raw::MjsSite* {
|
||||
return mjs_addSite(&self, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"add_camera",
|
||||
[](raw::MjsBody& self, raw::MjsDefault* default_) -> raw::MjsCamera* {
|
||||
return mjs_addCamera(&self, default_);
|
||||
},
|
||||
py::arg_v("default", nullptr),
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def("set_frame",
|
||||
[](raw::MjsBody& self, raw::MjsFrame& frame) -> void {
|
||||
mjs_setFrame(self.element, &frame);
|
||||
});
|
||||
mjsBody.def("set_default",
|
||||
[](raw::MjsBody& self, raw::MjsDefault& default_) -> void {
|
||||
mjs_setDefault(self.element, &default_);
|
||||
});
|
||||
mjsBody.def(
|
||||
"default",
|
||||
[](raw::MjsBody& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"find_child",
|
||||
[](raw::MjsBody& self, std::string& name) -> raw::MjsBody* {
|
||||
return mjs_findChild(&self, name.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"first_body",
|
||||
[](raw::MjsBody& self) -> raw::MjsBody* {
|
||||
return mjs_asBody(mjs_firstChild(&self, mjOBJ_BODY));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"next_body",
|
||||
[](raw::MjsBody& self, raw::MjsBody& child) -> raw::MjsBody* {
|
||||
return mjs_asBody(mjs_nextChild(&self, child.element));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"first_camera",
|
||||
[](raw::MjsBody& self) -> raw::MjsCamera* {
|
||||
return mjs_asCamera(mjs_firstChild(&self, mjOBJ_CAMERA));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"next_camera",
|
||||
[](raw::MjsBody& self, raw::MjsCamera& child) -> raw::MjsCamera* {
|
||||
return mjs_asCamera(mjs_nextChild(&self, child.element));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"first_light",
|
||||
[](raw::MjsBody& self) -> raw::MjsLight* {
|
||||
return mjs_asLight(mjs_firstChild(&self, mjOBJ_LIGHT));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"next_light",
|
||||
[](raw::MjsBody& self, raw::MjsLight& child) -> raw::MjsLight* {
|
||||
return mjs_asLight(mjs_nextChild(&self, child.element));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"first_joint",
|
||||
[](raw::MjsBody& self) -> raw::MjsJoint* {
|
||||
return mjs_asJoint(mjs_firstChild(&self, mjOBJ_JOINT));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"next_joint",
|
||||
[](raw::MjsBody& self, raw::MjsJoint& child) -> raw::MjsJoint* {
|
||||
return mjs_asJoint(mjs_nextChild(&self, child.element));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"first_geom",
|
||||
[](raw::MjsBody& self) -> raw::MjsGeom* {
|
||||
return mjs_asGeom(mjs_firstChild(&self, mjOBJ_GEOM));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"next_geom",
|
||||
[](raw::MjsBody& self, raw::MjsGeom& child) -> raw::MjsGeom* {
|
||||
return mjs_asGeom(mjs_nextChild(&self, child.element));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"first_site",
|
||||
[](raw::MjsBody& self) -> raw::MjsSite* {
|
||||
return mjs_asSite(mjs_firstChild(&self, mjOBJ_SITE));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"next_site",
|
||||
[](raw::MjsBody& self, raw::MjsSite& child) -> raw::MjsSite* {
|
||||
return mjs_asSite(mjs_nextChild(&self, child.element));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"first_frame",
|
||||
[](raw::MjsBody& self) -> raw::MjsFrame* {
|
||||
return mjs_asFrame(mjs_firstChild(&self, mjOBJ_FRAME));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"next_frame",
|
||||
[](raw::MjsBody& self, raw::MjsFrame& child) -> raw::MjsFrame* {
|
||||
return mjs_asFrame(mjs_nextChild(&self, child.element));
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def(
|
||||
"spec",
|
||||
[](raw::MjsBody& self) -> raw::MjSpec* { return mjs_getSpec(&self); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsBody.def("attach_frame",
|
||||
[](raw::MjsBody& self, raw::MjsFrame& frame, std::string& prefix,
|
||||
std::string& suffix) -> void {
|
||||
mjs_attachFrame(&self, &frame, prefix.c_str(), suffix.c_str());
|
||||
});
|
||||
|
||||
// ============================= MJSFRAME ====================================
|
||||
mjsFrame.def_property_readonly(
|
||||
"id", [](raw::MjsFrame& self) -> int { return mjs_getId(self.element); });
|
||||
mjsFrame.def("delete", [](raw::MjsFrame& self) { mjs_delete(self.element); });
|
||||
mjsFrame.def("set_frame", [](raw::MjsFrame& self, raw::MjsFrame& frame) {
|
||||
mjs_setFrame(self.element, &frame);
|
||||
});
|
||||
mjsFrame.def("attach_body", [](raw::MjsFrame& self, raw::MjsBody& body,
|
||||
std::string& prefix, std::string& suffix) {
|
||||
mjs_attachBody(&self, &body, prefix.c_str(), suffix.c_str());
|
||||
});
|
||||
|
||||
// ============================= MJSGEOM =====================================
|
||||
mjsGeom.def_property_readonly(
|
||||
"id", [](raw::MjsGeom& self) -> int { return mjs_getId(self.element); });
|
||||
mjsGeom.def("delete", [](raw::MjsGeom& self) { mjs_delete(self.element); });
|
||||
mjsGeom.def("set_frame", [](raw::MjsGeom& self, raw::MjsFrame& frame) {
|
||||
mjs_setFrame(self.element, &frame);
|
||||
});
|
||||
mjsGeom.def("set_default", [](raw::MjsGeom& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsGeom.def(
|
||||
"default",
|
||||
[](raw::MjsGeom& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSJOINT ====================================
|
||||
mjsJoint.def_property_readonly(
|
||||
"id", [](raw::MjsJoint& self) -> int { return mjs_getId(self.element); });
|
||||
mjsJoint.def("delete", [](raw::MjsJoint& self) { mjs_delete(self.element); });
|
||||
mjsJoint.def("set_frame", [](raw::MjsJoint& self, raw::MjsFrame& frame) {
|
||||
mjs_setFrame(self.element, &frame);
|
||||
});
|
||||
mjsJoint.def("set_default", [](raw::MjsJoint& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsJoint.def(
|
||||
"default",
|
||||
[](raw::MjsJoint& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSSITE =====================================
|
||||
mjsSite.def_property_readonly(
|
||||
"id", [](raw::MjsSite& self) -> int { return mjs_getId(self.element); });
|
||||
mjsSite.def("delete", [](raw::MjsSite& self) { mjs_delete(self.element); });
|
||||
mjsSite.def("set_frame", [](raw::MjsSite& self, raw::MjsFrame& frame) {
|
||||
mjs_setFrame(self.element, &frame);
|
||||
});
|
||||
mjsSite.def("set_default", [](raw::MjsSite& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsSite.def(
|
||||
"default",
|
||||
[](raw::MjsSite& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSCAMERA ===================================
|
||||
mjsCamera.def_property_readonly("id", [](raw::MjsCamera& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsCamera.def("delete",
|
||||
[](raw::MjsCamera& self) { mjs_delete(self.element); });
|
||||
mjsCamera.def("set_frame", [](raw::MjsCamera& self, raw::MjsFrame& frame) {
|
||||
mjs_setFrame(self.element, &frame);
|
||||
});
|
||||
mjsCamera.def("set_default", [](raw::MjsCamera& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsCamera.def(
|
||||
"default",
|
||||
[](raw::MjsCamera& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSLIGHT ====================================
|
||||
mjsLight.def_property_readonly(
|
||||
"id", [](raw::MjsLight& self) -> int { return mjs_getId(self.element); });
|
||||
mjsLight.def("delete", [](raw::MjsLight& self) { mjs_delete(self.element); });
|
||||
mjsLight.def("set_frame", [](raw::MjsLight& self, raw::MjsFrame& frame) {
|
||||
mjs_setFrame(self.element, &frame);
|
||||
});
|
||||
mjsLight.def("set_default", [](raw::MjsLight& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsLight.def(
|
||||
"default",
|
||||
[](raw::MjsLight& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSMATERIAL =================================
|
||||
mjsMaterial.def_property_readonly("id", [](raw::MjsMaterial& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsMaterial.def("delete",
|
||||
[](raw::MjsMaterial& self) { mjs_delete(self.element); });
|
||||
mjsMaterial.def("set_default",
|
||||
[](raw::MjsMaterial& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsMaterial.def(
|
||||
"default",
|
||||
[](raw::MjsMaterial& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSMESH =====================================
|
||||
mjsMesh.def_property_readonly(
|
||||
"id", [](raw::MjsMesh& self) -> int { return mjs_getId(self.element); });
|
||||
mjsMesh.def("delete", [](raw::MjsMesh& self) { mjs_delete(self.element); });
|
||||
mjsMesh.def("set_default", [](raw::MjsMesh& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsMesh.def(
|
||||
"default",
|
||||
[](raw::MjsMesh& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSPAIR =====================================
|
||||
mjsPair.def_property_readonly(
|
||||
"id", [](raw::MjsPair& self) -> int { return mjs_getId(self.element); });
|
||||
mjsPair.def("delete", [](raw::MjsPair& self) { mjs_delete(self.element); });
|
||||
mjsPair.def("set_default", [](raw::MjsPair& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsPair.def(
|
||||
"default",
|
||||
[](raw::MjsPair& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSEQUAL ====================================
|
||||
mjsEquality.def_property_readonly("id", [](raw::MjsEquality& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsEquality.def("delete",
|
||||
[](raw::MjsEquality& self) { mjs_delete(self.element); });
|
||||
mjsEquality.def("set_default",
|
||||
[](raw::MjsEquality& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsEquality.def(
|
||||
"default",
|
||||
[](raw::MjsEquality& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSACTUATOR =================================
|
||||
mjsActuator.def_property_readonly("id", [](raw::MjsActuator& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsActuator.def("delete",
|
||||
[](raw::MjsActuator& self) { mjs_delete(self.element); });
|
||||
mjsActuator.def("set_default",
|
||||
[](raw::MjsActuator& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsActuator.def(
|
||||
"default",
|
||||
[](raw::MjsActuator& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSTENDON ===================================
|
||||
mjsTendon.def_property_readonly("id", [](raw::MjsTendon& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsTendon.def("delete",
|
||||
[](raw::MjsTendon& self) { mjs_delete(self.element); });
|
||||
mjsTendon.def("set_default", [](raw::MjsTendon& self, raw::MjsDefault& def) {
|
||||
mjs_setDefault(self.element, &def);
|
||||
});
|
||||
mjsTendon.def(
|
||||
"default",
|
||||
[](raw::MjsTendon& self) -> raw::MjsDefault* {
|
||||
return mjs_getDefault(self.element);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsTendon.def(
|
||||
"wrap_site",
|
||||
[](raw::MjsTendon& self, std::string& name) {
|
||||
return mjs_wrapSite(&self, name.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsTendon.def(
|
||||
"wrap_geom",
|
||||
[](raw::MjsTendon& self, std::string& name, std::string& sidesite) {
|
||||
return mjs_wrapGeom(&self, name.c_str(), sidesite.c_str());
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsTendon.def(
|
||||
"wrap_joint",
|
||||
[](raw::MjsTendon& self, std::string& name, double coef) {
|
||||
return mjs_wrapJoint(&self, name.c_str(), coef);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsTendon.def(
|
||||
"wrap_pulley",
|
||||
[](raw::MjsTendon& self, double divisor) {
|
||||
return mjs_wrapPulley(&self, divisor);
|
||||
},
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
// ============================= MJSSENSOR ===================================
|
||||
mjsSensor.def_property_readonly("id", [](raw::MjsSensor& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsSensor.def("delete",
|
||||
[](raw::MjsSensor& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSFLEX =====================================
|
||||
mjsFlex.def_property_readonly(
|
||||
"id", [](raw::MjsFlex& self) -> int { return mjs_getId(self.element); });
|
||||
mjsFlex.def("delete", [](raw::MjsFlex& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSHFIELD ===================================
|
||||
mjsHField.def_property_readonly("id", [](raw::MjsHField& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsHField.def("delete",
|
||||
[](raw::MjsHField& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSSKIN =====================================
|
||||
mjsSkin.def_property_readonly(
|
||||
"id", [](raw::MjsSkin& self) -> int { return mjs_getId(self.element); },
|
||||
py::return_value_policy::reference_internal);
|
||||
mjsSkin.def("delete", [](raw::MjsSkin& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSTEXTURE ==================================
|
||||
mjsTexture.def_property_readonly("id", [](raw::MjsTexture& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsTexture.def("delete",
|
||||
[](raw::MjsTexture& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSKEY ======================================
|
||||
mjsKey.def_property_readonly(
|
||||
"id", [](raw::MjsKey& self) -> int { return mjs_getId(self.element); });
|
||||
mjsKey.def("delete", [](raw::MjsKey& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSTEXT =====================================
|
||||
mjsText.def_property_readonly(
|
||||
"id", [](raw::MjsText& self) -> int { return mjs_getId(self.element); });
|
||||
mjsText.def("delete", [](raw::MjsText& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSNUMERIC ==================================
|
||||
mjsNumeric.def_property_readonly("id", [](raw::MjsNumeric& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsNumeric.def("delete",
|
||||
[](raw::MjsNumeric& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSEXCLUDE ==================================
|
||||
mjsExclude.def_property_readonly("id", [](raw::MjsExclude& self) -> int {
|
||||
return mjs_getId(self.element);
|
||||
});
|
||||
mjsExclude.def("delete",
|
||||
[](raw::MjsExclude& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSTUPLE ====================================
|
||||
mjsTuple.def_property_readonly(
|
||||
"id", [](raw::MjsTuple& self) -> int { return mjs_getId(self.element); });
|
||||
mjsTuple.def("delete", [](raw::MjsTuple& self) { mjs_delete(self.element); });
|
||||
|
||||
// ============================= MJSPLUGIN ===================================
|
||||
mjsPlugin.def_property_readonly("id", [](raw::MjsPlugin& self) -> int {
|
||||
return mjs_getId(self.instance);
|
||||
});
|
||||
mjsPlugin.def("delete",
|
||||
[](raw::MjsPlugin& self) { mjs_delete(self.instance); });
|
||||
|
||||
#include "specs.cc.inc"
|
||||
} // PYBIND11_MODULE // NOLINT
|
||||
} // namespace mujoco::python
|
||||
@@ -0,0 +1,237 @@
|
||||
# Copyright 2024 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.
|
||||
# ==============================================================================
|
||||
"""Tests for mjSpec bindings."""
|
||||
|
||||
import inspect
|
||||
import textwrap
|
||||
|
||||
from absl.testing import absltest
|
||||
import mujoco
|
||||
import numpy as np
|
||||
|
||||
|
||||
def get_linenumber():
|
||||
cf = inspect.currentframe()
|
||||
return cf.f_back.f_lineno
|
||||
|
||||
|
||||
class SpecsTest(absltest.TestCase):
|
||||
|
||||
def test_basic(self):
|
||||
# Create a spec.
|
||||
spec = mujoco.MjSpec()
|
||||
|
||||
# Check that euler sequence order is set correctly.
|
||||
self.assertEqual(spec.eulerseq[0], ord('x'))
|
||||
spec.eulerseq = ['z', 'y', 'x']
|
||||
self.assertEqual(spec.eulerseq[0], ord('z'))
|
||||
|
||||
# Add a body, check that it has default orientation.
|
||||
body = spec.worldbody.add_body()
|
||||
self.assertEqual(body.name, '')
|
||||
np.testing.assert_array_equal(body.quat, [1, 0, 0, 0])
|
||||
|
||||
# Change the name of the body and read it back twice.
|
||||
body.name = 'foobar'
|
||||
self.assertEqual(body.name, 'foobar')
|
||||
body.name = 'baz'
|
||||
self.assertEqual(body.name, 'baz')
|
||||
|
||||
# Change the position of the body and read it back.
|
||||
body.pos = [1, 2, 3]
|
||||
np.testing.assert_array_equal(body.pos, [1, 2, 3])
|
||||
self.assertEqual(body.pos.shape, (3,))
|
||||
|
||||
# Change the orientation of the body and read it back.
|
||||
body.quat = [0, 1, 0, 0]
|
||||
np.testing.assert_array_equal(body.quat, [0, 1, 0, 0])
|
||||
self.assertEqual(body.quat.shape, (4,))
|
||||
|
||||
# Add a site to the body with user data and read it back.
|
||||
site = body.add_site()
|
||||
site.name = 'sitename'
|
||||
site.userdata = [1, 2, 3, 4, 5, 6]
|
||||
self.assertEqual(site.name, 'sitename')
|
||||
np.testing.assert_array_equal(site.userdata, [1, 2, 3, 4, 5, 6])
|
||||
|
||||
# Check that the site and body have no id before compilation.
|
||||
self.assertEqual(body.id, -1)
|
||||
self.assertEqual(site.id, -1)
|
||||
|
||||
# Compile the spec and check for expected values in the model.
|
||||
model = spec.compile()
|
||||
self.assertEqual(spec.worldbody.id, 0)
|
||||
self.assertEqual(body.id, 1)
|
||||
self.assertEqual(site.id, 0)
|
||||
self.assertEqual(model.nbody, 2) # 2 bodies, including the world body
|
||||
np.testing.assert_array_equal(model.body_pos[1], [1, 2, 3])
|
||||
np.testing.assert_array_equal(model.body_quat[1], [0, 1, 0, 0])
|
||||
self.assertEqual(model.nsite, 1)
|
||||
self.assertEqual(model.nuser_site, 6)
|
||||
np.testing.assert_array_equal(model.site_user[0], [1, 2, 3, 4, 5, 6])
|
||||
|
||||
self.assertEqual(spec.to_xml(), textwrap.dedent("""\
|
||||
<mujoco model="MuJoCo Model">
|
||||
<compiler angle="radian"/>
|
||||
|
||||
<size nuser_site="6"/>
|
||||
|
||||
<worldbody>
|
||||
<body name="baz" pos="1 2 3" quat="0 1 0 0">
|
||||
<site name="sitename" pos="0 0 0" user="1 2 3 4 5 6"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
"""),)
|
||||
|
||||
def test_compile_errors_with_line_info(self):
|
||||
spec = mujoco.MjSpec()
|
||||
|
||||
added_on_line = get_linenumber() + 1
|
||||
geom = spec.worldbody.add_geom()
|
||||
geom.name = 'MyGeom'
|
||||
geom.info = f'geom added on line {added_on_line}'
|
||||
|
||||
# Try to compile, get error.
|
||||
expected_error = (
|
||||
'Error: size 0 must be positive in geom\n'
|
||||
+ f'Element name \'MyGeom\', id 0, geom added on line {added_on_line}'
|
||||
)
|
||||
with self.assertRaisesRegex(ValueError, expected_error):
|
||||
spec.compile()
|
||||
|
||||
def test_recompile(self):
|
||||
# Create a spec.
|
||||
spec = mujoco.MjSpec()
|
||||
|
||||
# Add movable body1.
|
||||
body1 = spec.worldbody.add_body()
|
||||
geom = body1.add_geom()
|
||||
geom.size[0] = 1
|
||||
geom.pos = [1, 1, 0]
|
||||
joint = body1.add_joint()
|
||||
joint.type = mujoco.mjtJoint.mjJNT_BALL
|
||||
|
||||
# Compile model, make data.
|
||||
model = spec.compile()
|
||||
data = mujoco.MjData(model)
|
||||
|
||||
# Simulate for 1 second.
|
||||
while data.time < 1:
|
||||
mujoco.mj_step(model, data)
|
||||
|
||||
# Add movable body2.
|
||||
body2 = spec.worldbody.add_body()
|
||||
body2.pos[1] = 3
|
||||
geom = body2.add_geom()
|
||||
geom.size[0] = 1
|
||||
geom.pos = [0, 1, 0]
|
||||
joint = body2.add_joint()
|
||||
joint.type = mujoco.mjtJoint.mjJNT_BALL
|
||||
|
||||
# Recompile model and data while maintaining the state.
|
||||
model_new, data_new = spec.recompile(model, data)
|
||||
|
||||
# Check that the state is preserved.
|
||||
np.testing.assert_array_equal(model_new.body_pos[1], model.body_pos[1])
|
||||
np.testing.assert_array_equal(data_new.qpos[:4], data.qpos)
|
||||
np.testing.assert_array_equal(data_new.qvel[:3], data.qvel)
|
||||
|
||||
def test_uncompiled_spec_cannot_be_written(self):
|
||||
spec = mujoco.MjSpec()
|
||||
|
||||
# Cannot write XML of an uncompiled spec.
|
||||
expected_error = 'XML Write error: Only compiled model can be written'
|
||||
with self.assertRaisesWithLiteralMatch(mujoco.FatalError, expected_error):
|
||||
spec.to_xml()
|
||||
|
||||
def test_modelname_default_class(self):
|
||||
spec = mujoco.MjSpec()
|
||||
spec.modelname = 'test'
|
||||
|
||||
main = spec.default()
|
||||
main.geom.size[0] = 2
|
||||
|
||||
def1 = spec.add_default('def1', main)
|
||||
def1.geom.size[0] = 3
|
||||
|
||||
spec.worldbody.add_geom(def1)
|
||||
spec.worldbody.add_geom(main)
|
||||
|
||||
spec.compile()
|
||||
self.assertEqual(spec.to_xml(), textwrap.dedent("""\
|
||||
<mujoco model="test">
|
||||
<compiler angle="radian"/>
|
||||
|
||||
<default>
|
||||
<geom size="2 0 0"/>
|
||||
<default class="def1">
|
||||
<geom size="3 0 0"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<geom class="def1"/>
|
||||
<geom/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
"""))
|
||||
spec = mujoco.MjSpec()
|
||||
spec.modelname = 'test'
|
||||
|
||||
main = spec.default()
|
||||
main.geom.size[0] = 2
|
||||
|
||||
def1 = spec.add_default('def1', main)
|
||||
def1.geom.size[0] = 3
|
||||
|
||||
spec.worldbody.add_geom(def1)
|
||||
spec.worldbody.add_geom(main)
|
||||
|
||||
spec.compile()
|
||||
self.assertEqual(spec.to_xml(), textwrap.dedent("""\
|
||||
<mujoco model="test">
|
||||
<compiler angle="radian"/>
|
||||
|
||||
<default>
|
||||
<geom size="2 0 0"/>
|
||||
<default class="def1">
|
||||
<geom size="3 0 0"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<geom class="def1"/>
|
||||
<geom/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
"""))
|
||||
|
||||
def test_element_list(self):
|
||||
spec = mujoco.MjSpec()
|
||||
sensor1 = spec.add_sensor()
|
||||
sensor2 = spec.add_sensor()
|
||||
sensor3 = spec.add_sensor()
|
||||
sensor1.name = 'sensor1'
|
||||
sensor2.name = 'sensor2'
|
||||
sensor3.name = 'sensor3'
|
||||
self.assertLen(spec.sensors, 3)
|
||||
self.assertEqual(spec.sensors[0].name, 'sensor1')
|
||||
self.assertEqual(spec.sensors[1].name, 'sensor2')
|
||||
self.assertEqual(spec.sensors[2].name, 'sensor3')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
absltest.main()
|
||||
+47
-23
@@ -28,6 +28,7 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@@ -316,24 +317,22 @@ static raw::MjModel* LoadModelFileImpl(
|
||||
const std::string& filename,
|
||||
const std::vector<VfsAsset>& assets,
|
||||
LoadFunc&& loadfunc) {
|
||||
std::unique_ptr<mjVFS, void(*)(mjVFS*)> vfs(nullptr, [](mjVFS*){});
|
||||
mjVFS vfs;
|
||||
mjVFS* vfs_ptr = nullptr;
|
||||
if (!assets.empty()) {
|
||||
// mjVFS should be allocated on the heap, because it's ~2MB
|
||||
vfs = decltype(vfs)(new mjVFS, [](mjVFS* vfs) {
|
||||
mj_deleteVFS(vfs);
|
||||
delete vfs;
|
||||
});
|
||||
mj_defaultVFS(vfs.get());
|
||||
mj_defaultVFS(&vfs);
|
||||
vfs_ptr = &vfs;
|
||||
for (const auto& asset : assets) {
|
||||
const int vfs_error = InterceptMjErrors(mj_addBufferVFS)(
|
||||
vfs.get(), asset.name, asset.content, asset.content_size);
|
||||
vfs_ptr, asset.name, asset.content, asset.content_size);
|
||||
if (vfs_error) {
|
||||
throw py::value_error("assets dict is too big");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raw::MjModel* model = loadfunc(filename.c_str(), vfs.get());
|
||||
raw::MjModel* model = loadfunc(filename.c_str(), vfs_ptr);
|
||||
mj_deleteVFS(vfs_ptr);
|
||||
if (model && !model->buffer) {
|
||||
mj_deleteModel(model);
|
||||
model = nullptr;
|
||||
@@ -350,12 +349,6 @@ ConvertAssetsDict(
|
||||
std::vector<VfsAsset> out;
|
||||
if (assets.has_value()) {
|
||||
for (const auto& [name, content] : *assets) {
|
||||
if (name.length() >= mjMAXVFSNAME) {
|
||||
std::ostringstream error;
|
||||
error << "Filename length " << name.length() << " exceeds "
|
||||
<< mjMAXVFSNAME - 1 << " character limit: " << name;
|
||||
throw py::value_error(error.str());
|
||||
}
|
||||
out.emplace_back(name.c_str(), PYBIND11_BYTES_AS_STRING(content.ptr()),
|
||||
py::len(content));
|
||||
}
|
||||
@@ -430,6 +423,27 @@ MjModelWrapper MjModelWrapper::LoadXML(
|
||||
return MjModelWrapper(model);
|
||||
}
|
||||
|
||||
MjModelWrapper MjModelWrapper::CompileSpec(raw::MjSpec* spec) {
|
||||
auto m = mj_compile(spec, nullptr);
|
||||
if (!m || mjs_isWarning(spec)) {
|
||||
throw py::value_error(mjs_getError(spec));
|
||||
}
|
||||
return MjModelWrapper(m);
|
||||
}
|
||||
|
||||
py::tuple RecompileSpec(raw::MjSpec* spec, const MjModelWrapper& old_m,
|
||||
const MjDataWrapper& old_d) {
|
||||
raw::MjModel* m = static_cast<raw::MjModel*>(mju_malloc(sizeof(mjModel)));
|
||||
m->buffer = nullptr;
|
||||
raw::MjData* d = mj_copyData(nullptr, old_m.get(), old_d.get());
|
||||
mj_recompile(spec, nullptr, m, d);
|
||||
|
||||
py::object m_pyobj = py::cast((MjModelWrapper(m)));
|
||||
py::object d_pyobj =
|
||||
py::cast((MjDataWrapper(py::cast<MjModelWrapper*>(m_pyobj), d)));
|
||||
return py::make_tuple(m_pyobj, d_pyobj);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A byte at the start of serialized mjModel structs, which can be incremented
|
||||
// when we change the serialization logic to reject pickles from an unsupported
|
||||
@@ -1056,7 +1070,6 @@ MjvGLCameraWrapper::MjWrapper(const MjvGLCameraWrapper& other)
|
||||
#define X(var) var(InitPyArray(ptr_->var, owner_))
|
||||
MjvGeomWrapper::MjWrapper()
|
||||
: WrapperBase(new raw::MjvGeom{}),
|
||||
X(texrepeat),
|
||||
X(size),
|
||||
X(pos),
|
||||
mat([this]() {
|
||||
@@ -1069,7 +1082,6 @@ MjvGeomWrapper::MjWrapper()
|
||||
|
||||
MjvGeomWrapper::MjWrapper(raw::MjvGeom* ptr, py::handle owner)
|
||||
: WrapperBase(ptr, owner),
|
||||
X(texrepeat),
|
||||
X(size),
|
||||
X(pos),
|
||||
mat([this]() {
|
||||
@@ -1557,6 +1569,11 @@ PYBIND11_MODULE(_structs, m) {
|
||||
py::arg("xml"), py::arg_v("assets", py::none()),
|
||||
py::doc(
|
||||
R"(Loads an MjModel from an XML string and an optional assets dictionary.)"));
|
||||
mjModel.def_static(
|
||||
"_from_spec_ptr", [](uintptr_t addr) {
|
||||
return MjModelWrapper::CompileSpec(
|
||||
reinterpret_cast<raw::MjSpec*>(addr));
|
||||
});
|
||||
mjModel.def_static(
|
||||
"from_xml_path", &MjModelWrapper::LoadXMLFile,
|
||||
py::arg("filename"), py::arg_v("assets", py::none()),
|
||||
@@ -1907,9 +1924,11 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
mjData.def("__copy__", [](const MjDataWrapper& other) {
|
||||
return MjDataWrapper(other);
|
||||
});
|
||||
mjData.def("__deepcopy__", [](const MjDataWrapper& other, py::dict) {
|
||||
MjModelWrapper* model_copy = new MjModelWrapper(other.model());
|
||||
return MjDataWrapper(other, model_copy);
|
||||
mjData.def("__deepcopy__", [](const MjDataWrapper& other, py::dict memo) {
|
||||
// Use copy.deepcopy(model) to make a model that Python is aware of.
|
||||
py::object new_model_py =
|
||||
py::cast(other.model()).attr("__deepcopy__")(memo);
|
||||
return MjDataWrapper(other, new_model_py.cast<MjModelWrapper*>());
|
||||
});
|
||||
mjData.def(py::pickle(
|
||||
[](const MjDataWrapper& d) { // __getstate__
|
||||
@@ -2185,8 +2204,7 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
X(objtype);
|
||||
X(objid);
|
||||
X(category);
|
||||
X(texid);
|
||||
X(texuniform);
|
||||
X(matid);
|
||||
X(texcoord);
|
||||
X(segid);
|
||||
X(emission);
|
||||
@@ -2199,7 +2217,6 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
#undef X
|
||||
|
||||
#define X(var) DefinePyArray(mjvGeom, #var, &MjvGeomWrapper::var)
|
||||
X(texrepeat);
|
||||
X(size);
|
||||
X(pos);
|
||||
X(mat);
|
||||
@@ -2404,5 +2421,12 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
},
|
||||
py::arg("cam1"), py::arg("cam2"),
|
||||
py::doc(python_traits::mjv_averageCamera::doc));
|
||||
|
||||
m.def(
|
||||
"_recompile_spec_addr",
|
||||
[](uintptr_t spec_addr, const MjModelWrapper& m, const MjDataWrapper& d) {
|
||||
return RecompileSpec(reinterpret_cast<raw::MjSpec*>(spec_addr), m, d);
|
||||
}
|
||||
);
|
||||
} // PYBIND11_MODULE NOLINT(readability/fn_size)
|
||||
} // namespace mujoco::python::_impl
|
||||
|
||||
+13
-6
@@ -462,6 +462,10 @@ class MjWrapper<raw::MjModel> : public WrapperBase<raw::MjModel> {
|
||||
public:
|
||||
MjWrapper(const MjWrapper&);
|
||||
MjWrapper(MjWrapper&&);
|
||||
|
||||
// Takes ownership of the raw mjModel pointer.
|
||||
explicit MjWrapper(raw::MjModel* ptr);
|
||||
|
||||
~MjWrapper();
|
||||
|
||||
MjModelIndexer& indexer() { return indexer_; }
|
||||
@@ -485,6 +489,8 @@ class MjWrapper<raw::MjModel> : public WrapperBase<raw::MjModel> {
|
||||
const std::optional<
|
||||
std::unordered_map<std::string, pybind11::bytes>>& assets);
|
||||
|
||||
static MjWrapper CompileSpec(raw::MjSpec* spec);
|
||||
|
||||
static constexpr char kFromRawPointer[] =
|
||||
"__MUJOCO_STRUCTS_MJMODELWRAPPER_LOOKUP";
|
||||
static MjWrapper* FromRawPointer(raw::MjModel* m) noexcept;
|
||||
@@ -502,8 +508,6 @@ class MjWrapper<raw::MjModel> : public WrapperBase<raw::MjModel> {
|
||||
pybind11::bytes paths_bytes;
|
||||
|
||||
protected:
|
||||
explicit MjWrapper(raw::MjModel* ptr);
|
||||
|
||||
MjModelIndexer indexer_;
|
||||
};
|
||||
|
||||
@@ -591,8 +595,14 @@ class MjWrapper<raw::MjData>: public WrapperBase<raw::MjData> {
|
||||
explicit MjWrapper(MjModelWrapper* model);
|
||||
MjWrapper(const MjWrapper& other);
|
||||
MjWrapper(MjWrapper&&);
|
||||
|
||||
// Used for deepcopy
|
||||
MjWrapper(const MjWrapper& other, MjModelWrapper* model);
|
||||
|
||||
// Internal constructor which takes ownership of given mjData pointer.
|
||||
// Used for deserialization and recompile.
|
||||
explicit MjWrapper(MjModelWrapper* model, raw::MjData* d);
|
||||
|
||||
~MjWrapper();
|
||||
|
||||
const MjModelWrapper& model() const { return *model_; }
|
||||
@@ -622,9 +632,6 @@ class MjWrapper<raw::MjData>: public WrapperBase<raw::MjData> {
|
||||
py_array_or_tuple_t<mjtNum> energy;
|
||||
|
||||
protected:
|
||||
// Internal constructor which takes ownership of given mjData pointer.
|
||||
// Used for deserialization.
|
||||
explicit MjWrapper(MjModelWrapper* model, raw::MjData* d);
|
||||
raw::MjData* Copy() const;
|
||||
|
||||
// A reference to the model that was used to create this mjData.
|
||||
@@ -727,7 +734,7 @@ class MjWrapper<raw::MjvGeom> : public WrapperBase<raw::MjvGeom> {
|
||||
py_array_or_tuple_t< \
|
||||
std::remove_all_extents_t<decltype(raw::MjvGeom::var)>> \
|
||||
var
|
||||
X(texrepeat);
|
||||
X(matid);
|
||||
X(size);
|
||||
X(pos);
|
||||
X(mat);
|
||||
|
||||
@@ -247,7 +247,11 @@ class USDExporter:
|
||||
|
||||
assert geom_name not in self.geom_names
|
||||
|
||||
texture_file = self.texture_files[geom.texid] if geom.texid != -1 else None
|
||||
texture_file = (
|
||||
self.texture_files[self.model.mat_texid[mujoco.mjNTEXMAT*geom.matid]]
|
||||
if geom.matid != -1
|
||||
else None
|
||||
)
|
||||
|
||||
# handling meshes in our scene
|
||||
if geom.type == mujoco.mjtGeom.mjGEOM_MESH:
|
||||
|
||||
@@ -94,6 +94,20 @@ class Handle:
|
||||
def user_scn(self):
|
||||
return self._user_scn
|
||||
|
||||
@property
|
||||
def m(self):
|
||||
sim = self._sim()
|
||||
if sim is not None:
|
||||
return sim.m
|
||||
return None
|
||||
|
||||
@property
|
||||
def d(self):
|
||||
sim = self._sim()
|
||||
if sim is not None:
|
||||
return sim.d
|
||||
return None
|
||||
|
||||
def close(self):
|
||||
sim = self._sim()
|
||||
if sim is not None:
|
||||
|
||||
@@ -347,6 +347,7 @@ setuptools.setup(
|
||||
CMakeExtension('mujoco._render'),
|
||||
CMakeExtension('mujoco._rollout'),
|
||||
CMakeExtension('mujoco._simulate'),
|
||||
CMakeExtension('mujoco._specs'),
|
||||
CMakeExtension('mujoco._structs'),
|
||||
],
|
||||
scripts=[
|
||||
|
||||
+197
-176
@@ -420,8 +420,8 @@
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"\n",
|
||||
"# Make renderer, render and show the pixels\n",
|
||||
"renderer = mujoco.Renderer(model)\n",
|
||||
"media.show_image(renderer.render())"
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -445,10 +445,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"renderer.update_scene(data)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" mujoco.mj_forward(model, data)\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
"\n",
|
||||
"media.show_image(renderer.render())"
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -480,11 +481,11 @@
|
||||
"model = mujoco.MjModel.from_xml_string(xml)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"\n",
|
||||
"renderer = mujoco.Renderer(model)\n",
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"renderer.update_scene(data)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" mujoco.mj_forward(model, data)\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
"\n",
|
||||
"media.show_image(renderer.render())"
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -508,9 +509,10 @@
|
||||
"source": [
|
||||
"# Run this cell multiple times for different colors\n",
|
||||
"model.geom('red_box').rgba[:3] = np.random.rand(3)\n",
|
||||
"renderer.update_scene(data)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
"\n",
|
||||
"media.show_image(renderer.render())"
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -540,12 +542,13 @@
|
||||
"# Simulate and display video.\n",
|
||||
"frames = []\n",
|
||||
"mujoco.mj_resetData(model, data) # Reset state and time.\n",
|
||||
"while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=framerate)"
|
||||
]
|
||||
@@ -583,7 +586,6 @@
|
||||
"\"\"\"\n",
|
||||
"model = mujoco.MjModel.from_xml_string(xml)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"renderer = mujoco.Renderer(model)\n",
|
||||
"\n",
|
||||
"# enable joint visualization option:\n",
|
||||
"scene_option = mujoco.MjvOption()\n",
|
||||
@@ -595,12 +597,13 @@
|
||||
"# Simulate and display video.\n",
|
||||
"frames = []\n",
|
||||
"mujoco.mj_resetData(model, data)\n",
|
||||
"while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, scene_option=scene_option)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, scene_option=scene_option)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=framerate)"
|
||||
]
|
||||
@@ -651,12 +654,13 @@
|
||||
"# Simulate and display video.\n",
|
||||
"frames = []\n",
|
||||
"mujoco.mj_resetData(model, data)\n",
|
||||
"while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, scene_option=scene_option)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, scene_option=scene_option)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=60)"
|
||||
]
|
||||
@@ -760,12 +764,12 @@
|
||||
"\"\"\"\n",
|
||||
"model = mujoco.MjModel.from_xml_string(tippe_top)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"renderer = mujoco.Renderer(model)\n",
|
||||
"\n",
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"renderer.update_scene(data, camera=\"closeup\")\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" renderer.update_scene(data, camera=\"closeup\")\n",
|
||||
"\n",
|
||||
"media.show_image(renderer.render())"
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -821,12 +825,13 @@
|
||||
"# Simulate and display video.\n",
|
||||
"frames = []\n",
|
||||
"mujoco.mj_resetDataKeyframe(model, data, 0) # Reset the state to keyframe 0\n",
|
||||
"while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, \"closeup\")\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, \"closeup\")\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=framerate)"
|
||||
]
|
||||
@@ -939,12 +944,14 @@
|
||||
"\"\"\"\n",
|
||||
"model = mujoco.MjModel.from_xml_string(chaotic_pendulum)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"renderer = mujoco.Renderer(model, 480, 640)\n",
|
||||
"height = 480\n",
|
||||
"width = 640\n",
|
||||
"\n",
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"renderer.update_scene(data, camera=\"fixed\")\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" mujoco.mj_forward(model, data)\n",
|
||||
" renderer.update_scene(data, camera=\"fixed\")\n",
|
||||
"\n",
|
||||
"media.show_image(renderer.render())"
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -970,7 +977,8 @@
|
||||
"framerate = 30 # Hz\n",
|
||||
"n_frames = int(n_seconds * framerate)\n",
|
||||
"frames = []\n",
|
||||
"renderer = mujoco.Renderer(model, 240, 320)\n",
|
||||
"height = 240\n",
|
||||
"width = 320\n",
|
||||
"\n",
|
||||
"# set initial state\n",
|
||||
"mujoco.mj_resetData(model, data)\n",
|
||||
@@ -981,17 +989,18 @@
|
||||
"sim_time = 0\n",
|
||||
"render_time = 0\n",
|
||||
"n_steps = 0\n",
|
||||
"for i in range(n_frames):\n",
|
||||
" while data.time * framerate < i:\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" for i in range(n_frames):\n",
|
||||
" while data.time * framerate < i:\n",
|
||||
" tic = time.time()\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" sim_time += time.time() - tic\n",
|
||||
" n_steps += 1\n",
|
||||
" tic = time.time()\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" sim_time += time.time() - tic\n",
|
||||
" n_steps += 1\n",
|
||||
" tic = time.time()\n",
|
||||
" renderer.update_scene(data, \"fixed\")\n",
|
||||
" frame = renderer.render()\n",
|
||||
" render_time += time.time() - tic\n",
|
||||
" frames.append(frame)\n",
|
||||
" renderer.update_scene(data, \"fixed\")\n",
|
||||
" frame = renderer.render()\n",
|
||||
" render_time += time.time() - tic\n",
|
||||
" frames.append(frame)\n",
|
||||
"\n",
|
||||
"# print timing and play video\n",
|
||||
"step_time = 1e6*sim_time/n_steps\n",
|
||||
@@ -1233,12 +1242,14 @@
|
||||
"\"\"\"\n",
|
||||
"model = mujoco.MjModel.from_xml_string(free_body_MJCF)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"renderer = mujoco.Renderer(model, 400, 600)\n",
|
||||
"height = 400\n",
|
||||
"width = 600\n",
|
||||
"\n",
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"renderer.update_scene(data, \"fixed\")\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" mujoco.mj_forward(model, data)\n",
|
||||
" renderer.update_scene(data, \"fixed\")\n",
|
||||
"\n",
|
||||
"media.show_image(renderer.render())"
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1262,7 +1273,6 @@
|
||||
"height = 240\n",
|
||||
"width = 320\n",
|
||||
"frames = []\n",
|
||||
"renderer = mujoco.Renderer(model, height, width)\n",
|
||||
"\n",
|
||||
"# visualize contact frames and forces, make body transparent\n",
|
||||
"options = mujoco.MjvOption()\n",
|
||||
@@ -1282,12 +1292,13 @@
|
||||
"data.qvel[3:6] = 5*np.random.randn(3)\n",
|
||||
"\n",
|
||||
"# Simulate and display video.\n",
|
||||
"for i in range(n_frames):\n",
|
||||
" while data.time < i/120.0: #1/4x real time\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" renderer.update_scene(data, \"track\", options)\n",
|
||||
" frame = renderer.render()\n",
|
||||
" frames.append(frame)\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" for i in range(n_frames):\n",
|
||||
" while data.time < i/120.0: #1/4x real time\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" renderer.update_scene(data, \"track\", options)\n",
|
||||
" frame = renderer.render()\n",
|
||||
" frames.append(frame)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=30)"
|
||||
]
|
||||
@@ -1440,16 +1451,16 @@
|
||||
"# load\n",
|
||||
"model = mujoco.MjModel.from_xml_string(MJCF)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"renderer = mujoco.Renderer(model, height, width)\n",
|
||||
"\n",
|
||||
"# Simulate and display video.\n",
|
||||
"mujoco.mj_resetData(model, data)\n",
|
||||
"for i in range(n_frames):\n",
|
||||
" while data.time < i/30.0:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" renderer.update_scene(data, \"y\")\n",
|
||||
" frame = renderer.render()\n",
|
||||
" frames.append(frame)\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" mujoco.mj_resetData(model, data)\n",
|
||||
" for i in range(n_frames):\n",
|
||||
" while data.time < i/30.0:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" renderer.update_scene(data, \"y\")\n",
|
||||
" frame = renderer.render()\n",
|
||||
" frames.append(frame)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=30)"
|
||||
]
|
||||
@@ -1520,12 +1531,14 @@
|
||||
"\"\"\"\n",
|
||||
"model = mujoco.MjModel.from_xml_string(MJCF)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"renderer = mujoco.Renderer(model, 480, 480)\n",
|
||||
"height = 480\n",
|
||||
"width = 480\n",
|
||||
"\n",
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"renderer.update_scene(data, \"fixed\")\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" mujoco.mj_forward(model, data)\n",
|
||||
" renderer.update_scene(data, \"fixed\")\n",
|
||||
"\n",
|
||||
"media.show_image(renderer.render())\n"
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1553,21 +1566,20 @@
|
||||
"times = []\n",
|
||||
"sensordata = []\n",
|
||||
"\n",
|
||||
"renderer = mujoco.Renderer(model, height, width)\n",
|
||||
"\n",
|
||||
"# constant actuator signal\n",
|
||||
"mujoco.mj_resetData(model, data)\n",
|
||||
"data.ctrl = 20\n",
|
||||
"\n",
|
||||
"# Simulate and display video.\n",
|
||||
"for i in range(n_frames):\n",
|
||||
" while data.time < i/fps:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" times.append(data.time)\n",
|
||||
" sensordata.append(data.sensor('accelerometer').data.copy())\n",
|
||||
" renderer.update_scene(data, \"fixed\")\n",
|
||||
" frame = renderer.render()\n",
|
||||
" frames.append(frame)\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" for i in range(n_frames):\n",
|
||||
" while data.time < i/fps:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" times.append(data.time)\n",
|
||||
" sensordata.append(data.sensor('accelerometer').data.copy())\n",
|
||||
" renderer.update_scene(data, \"fixed\")\n",
|
||||
" frame = renderer.render()\n",
|
||||
" frames.append(frame)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=fps)"
|
||||
]
|
||||
@@ -1644,12 +1656,12 @@
|
||||
"</mujoco>\n",
|
||||
"\"\"\"\n",
|
||||
"model = mujoco.MjModel.from_xml_string(xml)\n",
|
||||
"renderer = mujoco.Renderer(model)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"\n",
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"renderer.update_scene(data)\n",
|
||||
"media.show_image(renderer.render())"
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" mujoco.mj_forward(model, data)\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
" media.show_image(renderer.render())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1664,9 +1676,10 @@
|
||||
"\n",
|
||||
"scene_option.frame = mujoco.mjtFrame.mjFRAME_GEOM\n",
|
||||
"scene_option.flags[mujoco.mjtVisFlag.mjVIS_TRANSPARENT] = True\n",
|
||||
"renderer.update_scene(data, scene_option=scene_option)\n",
|
||||
"frame = renderer.render()\n",
|
||||
"media.show_image(frame)"
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" renderer.update_scene(data, scene_option=scene_option)\n",
|
||||
" frame = renderer.render()\n",
|
||||
" media.show_image(frame)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1679,25 +1692,24 @@
|
||||
"source": [
|
||||
"#@title Depth rendering {vertical-output: true}\n",
|
||||
"\n",
|
||||
"# update renderer to render depth\n",
|
||||
"renderer.enable_depth_rendering()\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" # update renderer to render depth\n",
|
||||
" renderer.enable_depth_rendering()\n",
|
||||
"\n",
|
||||
"# reset the scene\n",
|
||||
"renderer.update_scene(data)\n",
|
||||
" # reset the scene\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
"\n",
|
||||
"# depth is a float array, in meters.\n",
|
||||
"depth = renderer.render()\n",
|
||||
" # depth is a float array, in meters.\n",
|
||||
" depth = renderer.render()\n",
|
||||
"\n",
|
||||
"# Shift nearest values to the origin.\n",
|
||||
"depth -= depth.min()\n",
|
||||
"# Scale by 2 mean distances of near rays.\n",
|
||||
"depth /= 2*depth[depth <= 1].mean()\n",
|
||||
"# Scale to [0, 255]\n",
|
||||
"pixels = 255*np.clip(depth, 0, 1)\n",
|
||||
" # Shift nearest values to the origin.\n",
|
||||
" depth -= depth.min()\n",
|
||||
" # Scale by 2 mean distances of near rays.\n",
|
||||
" depth /= 2*depth[depth <= 1].mean()\n",
|
||||
" # Scale to [0, 255]\n",
|
||||
" pixels = 255*np.clip(depth, 0, 1)\n",
|
||||
"\n",
|
||||
"media.show_image(pixels.astype(np.uint8))\n",
|
||||
"\n",
|
||||
"renderer.disable_depth_rendering()"
|
||||
" media.show_image(pixels.astype(np.uint8))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1710,25 +1722,26 @@
|
||||
"source": [
|
||||
"#@title Segmentation rendering {vertical-output: true}\n",
|
||||
"\n",
|
||||
"# update renderer to render segmentation\n",
|
||||
"renderer.enable_segmentation_rendering()\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" renderer.disable_depth_rendering()\n",
|
||||
"\n",
|
||||
"# reset the scene\n",
|
||||
"renderer.update_scene(data)\n",
|
||||
" # update renderer to render segmentation\n",
|
||||
" renderer.enable_segmentation_rendering()\n",
|
||||
"\n",
|
||||
"seg = renderer.render()\n",
|
||||
" # reset the scene\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
"\n",
|
||||
"# Display the contents of the first channel, which contains object\n",
|
||||
"# IDs. The second channel, seg[:, :, 1], contains object types.\n",
|
||||
"geom_ids = seg[:, :, 0]\n",
|
||||
"# Infinity is mapped to -1\n",
|
||||
"geom_ids = geom_ids.astype(np.float64) + 1\n",
|
||||
"# Scale to [0, 1]\n",
|
||||
"geom_ids = geom_ids / geom_ids.max()\n",
|
||||
"pixels = 255*geom_ids\n",
|
||||
"media.show_image(pixels.astype(np.uint8))\n",
|
||||
" seg = renderer.render()\n",
|
||||
"\n",
|
||||
"renderer.disable_segmentation_rendering()"
|
||||
" # Display the contents of the first channel, which contains object\n",
|
||||
" # IDs. The second channel, seg[:, :, 1], contains object types.\n",
|
||||
" geom_ids = seg[:, :, 0]\n",
|
||||
" # Infinity is mapped to -1\n",
|
||||
" geom_ids = geom_ids.astype(np.float64) + 1\n",
|
||||
" # Scale to [0, 1]\n",
|
||||
" geom_ids = geom_ids / geom_ids.max()\n",
|
||||
" pixels = 255*geom_ids\n",
|
||||
" media.show_image(pixels.astype(np.uint8))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1792,37 +1805,39 @@
|
||||
"source": [
|
||||
"#@title Project from world to camera coordinates {vertical-output: true}\n",
|
||||
"\n",
|
||||
"# reset the scene\n",
|
||||
"renderer.update_scene(data)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" renderer.disable_segmentation_rendering()\n",
|
||||
" # reset the scene\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
"\n",
|
||||
"# Get the world coordinates of the box corners\n",
|
||||
"box_pos = data.geom_xpos[model.geom('red_box').id]\n",
|
||||
"box_mat = data.geom_xmat[model.geom('red_box').id].reshape(3, 3)\n",
|
||||
"box_size = model.geom_size[model.geom('red_box').id]\n",
|
||||
"offsets = np.array([-1, 1]) * box_size[:, None]\n",
|
||||
"xyz_local = np.stack(list(itertools.product(*offsets))).T\n",
|
||||
"xyz_global = box_pos[:, None] + box_mat @ xyz_local\n",
|
||||
" # Get the world coordinates of the box corners\n",
|
||||
" box_pos = data.geom_xpos[model.geom('red_box').id]\n",
|
||||
" box_mat = data.geom_xmat[model.geom('red_box').id].reshape(3, 3)\n",
|
||||
" box_size = model.geom_size[model.geom('red_box').id]\n",
|
||||
" offsets = np.array([-1, 1]) * box_size[:, None]\n",
|
||||
" xyz_local = np.stack(list(itertools.product(*offsets))).T\n",
|
||||
" xyz_global = box_pos[:, None] + box_mat @ xyz_local\n",
|
||||
"\n",
|
||||
"# Camera matrices multiply homogenous [x, y, z, 1] vectors.\n",
|
||||
"corners_homogeneous = np.ones((4, xyz_global.shape[1]), dtype=float)\n",
|
||||
"corners_homogeneous[:3, :] = xyz_global\n",
|
||||
" # Camera matrices multiply homogenous [x, y, z, 1] vectors.\n",
|
||||
" corners_homogeneous = np.ones((4, xyz_global.shape[1]), dtype=float)\n",
|
||||
" corners_homogeneous[:3, :] = xyz_global\n",
|
||||
"\n",
|
||||
"# Get the camera matrix.\n",
|
||||
"m = compute_camera_matrix(renderer, data)\n",
|
||||
" # Get the camera matrix.\n",
|
||||
" m = compute_camera_matrix(renderer, data)\n",
|
||||
"\n",
|
||||
"# Project world coordinates into pixel space. See:\n",
|
||||
"# https://en.wikipedia.org/wiki/3D_projection#Mathematical_formula\n",
|
||||
"xs, ys, s = m @ corners_homogeneous\n",
|
||||
"# x and y are in the pixel coordinate system.\n",
|
||||
"x = xs / s\n",
|
||||
"y = ys / s\n",
|
||||
" # Project world coordinates into pixel space. See:\n",
|
||||
" # https://en.wikipedia.org/wiki/3D_projection#Mathematical_formula\n",
|
||||
" xs, ys, s = m @ corners_homogeneous\n",
|
||||
" # x and y are in the pixel coordinate system.\n",
|
||||
" x = xs / s\n",
|
||||
" y = ys / s\n",
|
||||
"\n",
|
||||
"# Render the camera view and overlay the projected corner coordinates.\n",
|
||||
"pixels = renderer.render()\n",
|
||||
"fig, ax = plt.subplots(1, 1)\n",
|
||||
"ax.imshow(pixels)\n",
|
||||
"ax.plot(x, y, '+', c='w')\n",
|
||||
"ax.set_axis_off()"
|
||||
" # Render the camera view and overlay the projected corner coordinates.\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" fig, ax = plt.subplots(1, 1)\n",
|
||||
" ax.imshow(pixels)\n",
|
||||
" ax.plot(x, y, '+', c='w')\n",
|
||||
" ax.set_axis_off()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1894,17 +1909,18 @@
|
||||
"mujoco.mj_resetData(model, data)\n",
|
||||
"mujoco.mj_forward(model, data)\n",
|
||||
"\n",
|
||||
"while data.time < duration:\n",
|
||||
" # append data to the traces\n",
|
||||
" positions.append(data.geom_xpos[data.geom(\"green_sphere\").id].copy())\n",
|
||||
" times.append(data.time)\n",
|
||||
" speeds.append(get_geom_speed(model, data, \"green_sphere\"))\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
" modify_scene(renderer.scene)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"with mujoco.Renderer(model) as renderer:\n",
|
||||
" while data.time < duration:\n",
|
||||
" # append data to the traces\n",
|
||||
" positions.append(data.geom_xpos[data.geom(\"green_sphere\").id].copy())\n",
|
||||
" times.append(data.time)\n",
|
||||
" speeds.append(get_geom_speed(model, data, \"green_sphere\"))\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data)\n",
|
||||
" modify_scene(renderer.scene)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=framerate)"
|
||||
]
|
||||
@@ -2018,8 +2034,7 @@
|
||||
"</mujoco>\n",
|
||||
"\"\"\"\n",
|
||||
"model = mujoco.MjModel.from_xml_string(dominos_xml)\n",
|
||||
"data = mujoco.MjData(model)\n",
|
||||
"renderer = mujoco.Renderer(model, height=1024, width=1440)"
|
||||
"data = mujoco.MjData(model)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2035,16 +2050,19 @@
|
||||
"\n",
|
||||
"duration = 2.5 # (seconds)\n",
|
||||
"framerate = 60 # (Hz)\n",
|
||||
"height = 1024\n",
|
||||
"width = 1440\n",
|
||||
"\n",
|
||||
"# Simulate and display video.\n",
|
||||
"frames = []\n",
|
||||
"mujoco.mj_resetData(model, data) # Reset state and time.\n",
|
||||
"while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, camera='top')\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate:\n",
|
||||
" renderer.update_scene(data, camera='top')\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=framerate)"
|
||||
]
|
||||
@@ -2061,6 +2079,8 @@
|
||||
"#@title Render from moving camera\n",
|
||||
"\n",
|
||||
"duration = 3 # (seconds)\n",
|
||||
"height = 1024\n",
|
||||
"width = 1440\n",
|
||||
"\n",
|
||||
"# find time when box is thrown (speed > 2cm/s)\n",
|
||||
"throw_time = 0.0\n",
|
||||
@@ -2115,13 +2135,14 @@
|
||||
"slowdown = 4 # 4x slow-down\n",
|
||||
"mujoco.mj_resetData(model, data)\n",
|
||||
"frames = []\n",
|
||||
"while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate * slowdown:\n",
|
||||
" cam.distance, cam.azimuth, cam.elevation, cam.lookat = cam_motion()\n",
|
||||
" renderer.update_scene(data, cam)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"with mujoco.Renderer(model, height, width) as renderer:\n",
|
||||
" while data.time < duration:\n",
|
||||
" mujoco.mj_step(model, data)\n",
|
||||
" if len(frames) < data.time * framerate * slowdown:\n",
|
||||
" cam.distance, cam.azimuth, cam.elevation, cam.lookat = cam_motion()\n",
|
||||
" renderer.update_scene(data, cam)\n",
|
||||
" pixels = renderer.render()\n",
|
||||
" frames.append(pixels)\n",
|
||||
"\n",
|
||||
"media.show_video(frames, fps=framerate)"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user