Beispiel #1
0
    def make_robot_xml(cls, xml, robot_control_params):
        if robot_control_params.is_joint_actuated():
            # Modifying xml is required because setting eq_active only was not enough to fully
            # disable the mocap weld constraint. In my tests, setting eq_active to false would
            # disable the constraint, but somehow the arm would not move when the joints were
            # commanded. Removing from xml here seems to have the right effect.
            xml.remove_objects_by_name("mocap_weld")
            # Also add the actuations that are removed in the xml by default (since TCP does
            # not need them).
            joint_subdir = robot_control_params.arm_joint_calibration_path
            xml.append(
                MujocoXML.parse(
                    f"robot/ur16e/jointspec/calibrations/{joint_subdir}/ur16e_ik_class.xml"
                ))
            xml.append(
                MujocoXML.parse(
                    f"robot/ur16e/jointspec/calibrations/{joint_subdir}/joint_actuations.xml"
                ))
        else:
            # If not joint control mode or ik solver mode, use mocap defaults for joint parameters
            xml.append(
                MujocoXML.parse("robot/ur16e/jointspec/ur16e_mocap_class.xml"))

        # Add gripper actuators now (after joint actuators if required).
        xml.append(MujocoXML.parse("robot/ur16e/gripper_actuators.xml"))

        return xml
Beispiel #2
0
def test_set_attributes_mixed_precision():
    main = (MujocoXML().add_default_compiler_directive().append(
        MujocoXML.from_string(XML_BALL).set_named_objects_attr(
            "ball", pos=[1, 1e-8, 1e-12])))

    simulation = SimulationInterface(main.build())

    ball_id = simulation.sim.model.body_name2id("ball")
    ball_pos = simulation.sim.model.body_pos[ball_id]

    target_pos = np.array([1, 1e-8, 1e-12])

    # test relative error cause absolute error can be quite small either way
    assert np.linalg.norm((ball_pos / target_pos) - 1) < 1e-6
Beispiel #3
0
def make_mesh_object(name: str, files: List[str], scale: float) -> MujocoXML:
    # Center mesh properly by offsetting with center position of combined mesh.
    mesh = get_combined_mesh(files)

    pos = -mesh.center_mass * scale
    pos_string = " ".join(map(str, pos))

    scale_string = " ".join(map(str, [scale] * 3))
    assets = [
        f'<mesh file="{file}" name="{name}-{idx}" scale="{scale_string}" />'
        for idx, file in enumerate(files)
    ]
    geoms = [
        f'<geom type="mesh" mesh="{name}-{idx}" pos="{pos_string}"/>'
        for idx in range(len(files))
    ]
    assets_xml = "\n".join(assets)
    geoms_xml = "\n".join(geoms)
    xml_source = f"""
    <mujoco>
      <asset>
        {assets_xml}
      </asset>
      <worldbody>
        <body name="{name}" pos="0.0 0.0 0.0">
          {geoms_xml}
          <joint name="{name}:joint" type="free"/>
        </body>
      </worldbody>
    </mujoco>
    """
    return MujocoXML.from_string(xml_source)
Beispiel #4
0
def test_forward_kinematics_on_inverted_pendulum():
    mxml = MujocoXML.parse(
        "test/inverted_pendulum/inverted_double_pendulum.xml").add_name_prefix(
            "ivp:")
    simulation = SimulationInterface(mxml.build())
    simulation.register_joint_group("pendulum", "ivp:")

    joint_names = list(map(lambda x: "ivp:%s" % x, ["hinge", "hinge2"]))

    site_names = list(map(lambda x: "ivp:%s" % x, ["hinge2_site", "tip"]))

    KIN = ForwardKinematics.prepare(mxml, "ivp:cart", np.zeros(3), np.zeros(3),
                                    site_names, joint_names)

    for _ in range(5):
        simulation.mj_sim.data.ctrl[0] = random.random()
        for _ in range(100):
            simulation.step()

        simulation.forward()

        site_positions = np.array([
            simulation.mj_sim.data.get_site_xpos(site) for site in site_names
        ])

        joint_pos = simulation.get_qpos("pendulum")

        kinemetics_positions = KIN.compute(joint_pos, return_joint_pos=True)

        assert (np.abs(site_positions - kinemetics_positions[:2]) < 1e-6).all()
        assert (np.abs(site_positions[0] - kinemetics_positions[-1]) <
                1e-6).all()
Beispiel #5
0
    def make_objects_xml(cls, xml, simulation_params):
        def get_cube_mesh(c) -> str:
            return f"""<mesh name="object:letter_{c}" file="{ASSETS_DIR}/stls/openai_cube/{c}.stl" />"""

        def get_cube_body(idx, c) -> str:
            color = "0.71 0.61 0.3"
            letter_material = "cube:letter_white"

            return f"""
<mujoco>
    <worldbody>
        <body name="object{idx}">
            <joint name="object{idx}:joint" type="free"/>
            <geom name="object{idx}" size="0.0285 0.0285 0.0285" type="box" rgba="{color} 1" />
            <body name="object:letter_{c}:face_top" pos="0 0 0.0285">
                <geom name="object:letter_{c}" pos="0 0 -0.0009" quat="0.499998 0.5 -0.500002 0.5" type="mesh" material="{letter_material}" mesh="object:letter_{c}" />
            </body>
        </body>
    </worldbody>
</mujoco>
"""

        letter_meshes = "\n".join([get_cube_mesh(c) for c in "OPENAI"])

        assets_xml = f"""
<mujoco>
    <asset>
        <material name="cube:top_background" specular="1" shininess="0.3" />
        <material name="cube:letter" specular="1" shininess="0.3" rgba="0. 0. 1 1"/>
        <material name="cube:letter_white" specular="1" shininess="0.3" rgba="1 1 1 1"/>
        {letter_meshes}
    </asset>
</mujoco>
        """

        xml.append(MujocoXML.from_string(assets_xml))

        xmls = []

        for idx, c in enumerate("OPENAI"):
            if idx >= simulation_params.num_objects:
                break
            object_xml = MujocoXML.from_string(get_cube_body(idx, c))
            xmls.append((object_xml, make_target(object_xml)))

        return xmls
Beispiel #6
0
def _get_default_xml():
    xml_source = """
    <mujoco>
      <asset>
        <material name="block_mat" specular="0" shininess="0.5" reflectance="0" rgba="1 0 0 1"></material>
      </asset>
    </mujoco>
    """
    xml = MujocoXML.from_string(xml_source)
    return xml
Beispiel #7
0
    def _make_object_xml(cls, object_id: int, config: ObjectConfig):
        object_name = f"object{object_id}"
        xml = (MujocoXML.parse(config.xml_path).set_objects_attr(
            tag="joint", name="joint").add_name_prefix(
                f"{object_name}:",
                exclude_attribs=["class"]).set_objects_attr(tag="body",
                                                            name=object_name))

        xml.set_objects_attrs(config.tag_args)
        xml.set_objects_attrs(config.material_args)

        return xml
Beispiel #8
0
    def _build_mujoco_cube_xml(cls, xml, cube_xml_path):
        xml.append(
            MujocoXML.parse(cube_xml_path).remove_objects_by_name(
                "annotation:outer_bound").add_name_prefix(
                    "cube:").set_named_objects_attr(
                        "cube:middle", tag="body",
                        pos=[1.0, 0.87,
                             0.2]).set_named_objects_attr("cube:middle",
                                                          tag="geom",
                                                          density=421.0))

        # Target cube
        xml.append(
            MujocoXML.parse(cube_xml_path).remove_objects_by_name(
                "annotation:outer_bound").add_name_prefix(
                    "target:").set_named_objects_attr("target:middle",
                                                      tag="body",
                                                      pos=[1.0, 0.87, 0.2])
            # Disable collisions
            .set_objects_attr(tag="geom",
                              group="2",
                              conaffinity="0",
                              contype="0"))
Beispiel #9
0
def test_remove_elem():
    ball_without_joint = MujocoXML.from_string(XML_BALL).remove_objects_by_tag(
        "freejoint")

    ref_xml = """
<mujoco>
  <worldbody>
    <body name="ball">
      <geom name="sphere" pos="0.00 0.00 0.00" size="0.1 0.1 0.1" type="sphere" />
    </body>
  </worldbody>
</mujoco>
"""
    assert ref_xml.strip() == ball_without_joint.xml_string().strip()
Beispiel #10
0
def test_simple_mujoco_setup():
    ball_one = (MujocoXML.from_string(XML_BALL).add_name_prefix(
        "ball_one:").set_named_objects_attr("ball_one:ball", pos=[1, 0, 0]))

    ball_two = (MujocoXML.from_string(XML_BALL).add_name_prefix(
        "ball_two:").set_named_objects_attr("ball_two:ball", pos=[-1, 0, 0]))

    main = (MujocoXML().add_default_compiler_directive().append(
        ball_one).append(ball_two))

    simulation = SimulationInterface(main.build())

    simulation.register_joint_group("ball_one", "ball_one:ball_joint")
    simulation.register_joint_group("ball_two", "ball_two:ball_joint")

    assert simulation.get_qpos("ball_one").shape == (7, )
    assert simulation.get_qpos("ball_two").shape == (7, )

    assert simulation.get_qvel("ball_one").shape == (6, )
    assert simulation.get_qvel("ball_two").shape == (6, )

    qpos1 = np.random.randn(3)
    qrot1 = uniform_quat(global_randstate)
    qpos1_combined = np.concatenate([qpos1, qrot1])

    qpos2 = np.random.randn(3)
    qrot2 = uniform_quat(global_randstate)
    qpos2_combined = np.concatenate([qpos2, qrot2])

    simulation.set_qpos("ball_one", qpos1_combined)
    simulation.set_qpos("ball_two", qpos2_combined)

    assert np.linalg.norm(simulation.get_qpos("ball_one") -
                          qpos1_combined) < 1e-6
    assert np.linalg.norm(simulation.get_qpos("ball_two") -
                          qpos2_combined) < 1e-6
Beispiel #11
0
    def _build_mujoco_cube_xml(cls, xml, cube_xml_path):
        xml.append(
            MujocoXML.parse(cube_xml_path).add_name_prefix(
                "cube:").set_named_objects_attr("cube:middle",
                                                tag="body",
                                                pos=[1.0, 0.87, 0.2])
            # Delete springs for now
            .remove_objects_by_prefix(prefix="cube:cubelet:spring:",
                                      tag="joint"))

        # Target
        xml.append(
            MujocoXML.parse(cube_xml_path).add_name_prefix(
                "target:").set_named_objects_attr("target:middle",
                                                  tag="body",
                                                  pos=[1.0, 0.87, 0.2])
            # Delete springs for now
            .remove_objects_by_prefix(prefix="target:cubelet:spring:",
                                      tag="joint")
            # Disable collisions
            .set_objects_attr(tag="geom",
                              group="2",
                              conaffinity="0",
                              contype="0"))
Beispiel #12
0
def test_more_complex_mujoco_setup():
    xml = (MujocoXML().add_default_compiler_directive().append(
        MujocoXML.from_string(XML_ARM).add_name_prefix(
            "arm_one:").set_named_objects_attr(
                "arm_one:ball", pos=[0, 1, 0])).append(
                    MujocoXML.from_string(XML_ARM).add_name_prefix(
                        "arm_two:").set_named_objects_attr("arm_two:ball",
                                                           pos=[0, -1, 0])))

    simulation = SimulationInterface(xml.build())

    simulation.register_joint_group("arm_one", "arm_one:")
    simulation.register_joint_group("arm_one_hinge", "arm_one:hinge_joint")
    simulation.register_joint_group("arm_two", "arm_two:")
    simulation.register_joint_group("arm_two_hinge", "arm_two:hinge_joint")

    assert simulation.get_qpos("arm_one").shape == (2, )
    assert simulation.get_qvel("arm_one").shape == (2, )
    assert simulation.get_qpos("arm_two").shape == (2, )
    assert simulation.get_qvel("arm_two").shape == (2, )

    assert simulation.get_qpos("arm_one_hinge").shape == (1, )
    assert simulation.get_qvel("arm_one_hinge").shape == (1, )
    assert simulation.get_qpos("arm_two_hinge").shape == (1, )
    assert simulation.get_qvel("arm_two_hinge").shape == (1, )

    initial_qpos_one = simulation.get_qpos("arm_one")
    initial_qpos_two = simulation.get_qpos("arm_two")

    simulation.set_qpos("arm_one_hinge", 0.1)

    # Chech that we are setting the right hinge joint
    assert np.linalg.norm(simulation.get_qpos("arm_one") -
                          initial_qpos_one) > 0.09
    assert np.linalg.norm(simulation.get_qpos("arm_two") -
                          initial_qpos_two) < 1e-6
Beispiel #13
0
    def make_world_xml(
        cls, *, mujoco_timestep: float, simulation_params=None, **kwargs
    ):
        if simulation_params is None:
            simulation_params = MeshRearrangeSimParameters()
        xml = super().make_world_xml(
            simulation_params=simulation_params,
            mujoco_timestep=mujoco_timestep,
            **kwargs
        )

        xml.append(
            MujocoXML.from_string(
                CHESSBOARD_XML.format(n_obj=simulation_params.num_objects // 2)
            )
        )

        return xml
Beispiel #14
0
def make_block(name: str, object_size: np.ndarray) -> MujocoXML:
    """Creates a block.

    :param name: The name of the block
    :param object_size: The size of the block (3-dimensional). This is half-size as per Mujoco
        convention
    """
    xml_source = f"""
    <mujoco>
      <worldbody>
        <body name="{name}" pos="0.0 0.0 0.0">
          <geom type="box" rgba="0.0 0.0 0.0 0.0" material="block_mat"/>
          <joint name="{name}:joint" type="free"/>
        </body>
      </worldbody>
    </mujoco>
    """
    xml = MujocoXML.from_string(xml_source).set_objects_attr(
        tag="geom", size=object_size
    )

    return xml
Beispiel #15
0
    def make_world_xml(cls,
                       *,
                       mujoco_timestep: float,
                       simulation_params=None,
                       **kwargs):
        if simulation_params is None:
            simulation_params = HoldoutRearrangeSimParameters()
        world_xml = super().make_world_xml(simulation_params=simulation_params,
                                           mujoco_timestep=mujoco_timestep)
        table_dims = cls.get_table_dimensions_from_xml(world_xml)

        for object_id, object_config in enumerate(
                simulation_params.scene_object_configs):
            world_xml.append(
                cls._make_scene_object_xml(
                    object_id,
                    object_config,
                    table_dims,
                ))
        if simulation_params.shared_settings:
            world_xml.append(MujocoXML.parse(
                simulation_params.shared_settings))
        return world_xml
Beispiel #16
0
    def build(cls, n_substeps: int = 10):
        """Construct a ShadowHandReachSimulation object.

        :param n_substeps: (int) sim.nsubsteps, num of substeps
        :return: a ShadowHandReachSimulation object with properly constructed sim.
        """
        xml = MujocoXML()
        xml.add_default_compiler_directive()

        xml.append(
            MujocoXML.parse(cls.FLOOR_XML).set_named_objects_attr(
                "floor", tag="body", pos=[1, 1, 0]))

        target = MujocoXML.parse(cls.TARGET_XML)

        colors = [
            [1.0, 0.0, 0.0, 1.0],
            [0.0, 1.0, 0.0, 1.0],
            [0.0, 0.0, 1.0, 1.0],
            [1.0, 1.0, 0.0, 1.0],
            [1.0, 0.0, 1.0, 1.0],
        ]

        for site, color in zip(FINGERTIP_SITE_NAMES, colors):
            target.set_named_objects_attr(
                f"target_{site}",
                pos=[0.5, 0.5, 0.0],
                type="sphere",
                rgba=color,
                size=0.005,
            )

        xml.append(target)

        xml.append(
            MujocoXML.parse(cls.HAND_XML).add_name_prefix(
                "robot0:").set_named_objects_attr(
                    "robot0:hand_mount",
                    tag="body",
                    pos=[1.0, 1.25, 0.15],
                    euler=[np.pi / 2, 0, np.pi],
                ).remove_objects_by_name("robot0:annotation:outer_bound")
            # Remove hand base free joint so that hand is immovable
            .remove_objects_by_name("robot0:hand_base"))

        xml.append(MujocoXML.parse(cls.LIGHT_XML))

        simulation = cls(xml.build(nsubsteps=n_substeps))

        # Move fingers out of the way.
        simulation.shadow_hand.set_position_control(
            simulation.shadow_hand.denormalize_position_control(
                simulation.shadow_hand.zero_control()))

        for _ in range(20):
            simulation.step()

        return simulation
Beispiel #17
0
    def build(
        cls, n_substeps: int = 10, timestep: float = 0.008, name_prefix: str = "robot0:"
    ):
        """
        Construct MjSim object for this simulation

        :param name_prefix - to append to names of all objects in the MuJoCo model of the hand;
            by default no prefix is appended.
        """
        xml = MujocoXML()
        xml.add_default_compiler_directive()

        max_contacts_params = dict(njmax=2000, nconmax=200)

        xml.append(
            MujocoXML.parse(cls.FLOOR_XML).set_named_objects_attr(
                "floor", tag="body", pos=[1, 1, 0]
            )
        )

        xml.append(
            MujocoXML.parse(cls.HAND_XML)
            .add_name_prefix(name_prefix)
            .set_objects_attr(tag="size", **max_contacts_params)
            .set_objects_attr(tag="option", timestep=timestep)
            .set_named_objects_attr(
                f"{name_prefix}hand_mount",
                tag="body",
                pos=[1.0, 1.25, 0.15],
                euler=[np.pi / 2, 0, np.pi],
            )
            .remove_objects_by_name(f"{name_prefix}annotation:outer_bound")
            # Remove hand base free joint so that hand is immovable
            .remove_objects_by_name(f"{name_prefix}hand_base")
        )

        xml.append(MujocoXML.parse(cls.LIGHT_XML))

        return cls(sim=xml.build(nsubsteps=n_substeps), hand_prefix=name_prefix)
Beispiel #18
0
    def _build_mujoco_cube_xml(cls, xml, cube_xml_path):
        xml.append(
            MujocoXML.parse(cube_xml_path).add_name_prefix(
                "cube:").set_named_objects_attr("cube:middle",
                                                tag="body",
                                                pos=[1.0, 0.87, 0.2])
            # Leave +/- z driver joints
            .remove_objects_by_name(
                names="cube:cubelet:driver:neg_x",
                tag="joint").remove_objects_by_name(
                    names="cube:cubelet:driver:pos_x",
                    tag="joint").remove_objects_by_name(
                        names="cube:cubelet:driver:neg_y",
                        tag="joint").remove_objects_by_name(
                            names="cube:cubelet:driver:pos_y", tag="joint")
            # Remove x/y cubelet hinge joints
            .remove_objects_by_prefix(prefix="cube:cubelet:rotx:",
                                      tag="joint").remove_objects_by_prefix(
                                          prefix="cube:cubelet:roty:",
                                          tag="joint")
            # Delete springs for now
            .remove_objects_by_prefix(prefix="cube:cubelet:spring:",
                                      tag="joint")
            # Remove remaining cubelet joints we're not interested in
            .remove_objects_by_name(
                names=[
                    "cube:cubelet:rotz:neg_x_pos_y",
                    "cube:cubelet:rotz:neg_x_neg_y",
                    "cube:cubelet:rotz:pos_x_pos_y",
                    "cube:cubelet:rotz:pos_x_neg_y",
                ],
                tag="joint",
            ))

        # Target
        xml.append(
            MujocoXML.parse(cube_xml_path).add_name_prefix(
                "target:").set_named_objects_attr("target:middle",
                                                  tag="body",
                                                  pos=[1.0, 0.87, 0.2])
            # Disable collisions
            .set_objects_attr(tag="geom",
                              group="2",
                              conaffinity="0",
                              contype="0")
            # Leave +/- z driver joints
            .remove_objects_by_name(
                names="target:cubelet:driver:neg_x",
                tag="joint").remove_objects_by_name(
                    names="target:cubelet:driver:pos_x",
                    tag="joint").remove_objects_by_name(
                        names="target:cubelet:driver:neg_y",
                        tag="joint").remove_objects_by_name(
                            names="target:cubelet:driver:pos_y", tag="joint")
            # Remove x/y cubelet hinge joints
            .remove_objects_by_prefix(
                prefix="target:cubelet:rotx:",
                tag="joint").remove_objects_by_prefix(
                    prefix="target:cubelet:roty:",
                    tag="joint").remove_objects_by_prefix(
                        prefix="target:cubelet:spring:", tag="joint")
            # Remove remaining cubelet joints we're not interested in
            .remove_objects_by_name(
                names=[
                    "target:cubelet:rotz:neg_x_pos_y",
                    "target:cubelet:rotz:neg_x_neg_y",
                    "target:cubelet:rotz:pos_x_pos_y",
                    "target:cubelet:rotz:pos_x_neg_y",
                ],
                tag="joint",
            ))
Beispiel #19
0
    def build(
        cls,
        n_substeps: int = 10,
        simulation_params: CubeSimulationParameters = CubeSimulationParameters(),
    ):
        """
        Construct a CubeSimulationInterface object with perpendicular cube.
        """

        cube_xml_path, size_params = cls._get_model_xml_path_and_params(
            simulation_params.cube_appearance
        )

        xml = MujocoXML()
        xml.add_default_compiler_directive()

        cls._build_mujoco_cube_xml(xml, cube_xml_path)

        xml.append(
            MujocoXML.parse(cls.FLOOR_XML).set_named_objects_attr(
                "floor", tag="body", pos=[1, 1, 0]
            )
        )

        xml.append(
            MujocoXML.parse(cls.HAND_XML)
            .add_name_prefix("robot0:")
            .set_objects_attr(tag="size", **size_params)
            .set_named_objects_attr(
                "robot0:hand_mount",
                tag="body",
                pos=[1.0, 1.25, 0.15],
                euler=[np.pi / 2, 0, np.pi],
            )
            .remove_objects_by_name("robot0:annotation:outer_bound")
            # Remove hand base free joint so that hand is immovable
            .remove_objects_by_name("robot0:hand_base")
        )

        xml.append(MujocoXML.parse(cls.LIGHT_XML))

        simulation = cls(xml.build(nsubsteps=n_substeps))

        if simulation_params.hide_target:
            simulation.hide_target()

        return simulation
Beispiel #20
0
FINGERTIP_SITE_NAMES = [
    "S_fftip",
    "S_mftip",
    "S_rftip",
    "S_lftip",
    "S_thtip",
]

REFERENCE_SITE_NAMES = [
    "phasespace_ref0",
    "phasespace_ref1",
    "phasespace_ref2",
]

HAND_KINEMATICS = ForwardKinematics.prepare(
    MujocoXML.parse("robot/shadowhand/main.xml"),
    "hand_mount",
    [1.0, 1.25, 0.15],
    [np.pi / 2, 0, np.pi],
    REFERENCE_SITE_NAMES + FINGERTIP_SITE_NAMES,
    hand_interface.JOINTS,
)

ZERO_JOINT_POSITIONS = HAND_KINEMATICS.compute(
    np.zeros(len(hand_interface.JOINTS)))
REFERENCE_POSITIONS = ZERO_JOINT_POSITIONS[:3]


def hand_forward_kinematics(qpos, return_joint_pos=False):
    """ Calculate forward kinematics of the hand """
    return HAND_KINEMATICS.compute(qpos, return_joint_pos)[3:]
Beispiel #21
0
def make_composed_mesh_object(
    name: str,
    primitives: List[str],
    random_state: np.random.RandomState,
    mesh_size_range: tuple = (0.01, 0.1),
    attachment: str = "random",
    object_size: Optional[float] = None,
) -> MujocoXML:
    """
    Composes an object out of mesh primitives by combining them in a random but systematic
    way. In the resulting object, all meshes are guaranteed to be connected.

    :param name: The name of the resulting object.
    :param primitives: A list of STL files that will be used as primitives in the provided order.
    :param random_state: The random state used for sampling.
    :param mesh_size_range: Each mesh is randomly resized (iid per dimension) but each side is
        guaranteed to be within this size. This is full-size, not half-size.
    :param attachment: How primitives are connected. If "random", the parent geom is randomly
        selected from the already placed geoms. If "last", the geom that was place last is used.
    :param object_size: If this is not None, the final object) will be re-scaled so that the longest
        side has exactly object_size half-size. This parameter is in half-size, as per Mujoco
        convention.
    :return: a MujocoXML object.
    """
    assert 0 <= mesh_size_range[0] <= mesh_size_range[1]
    assert attachment in ["random", "last"]

    def compute_pos_and_size(geoms):
        min_max_xyzs = np.array([geom.min_max_xyz() for geom in geoms])
        min_xyz = np.min(min_max_xyzs[:, 0, :], axis=0)
        max_xyz = np.max(min_max_xyzs[:, 1, :], axis=0)
        size = (max_xyz - min_xyz) / 2.0
        pos = min_xyz + size
        return pos, size

    geoms: List[MeshGeom] = []
    for i, mesh_path in enumerate(primitives):
        # Load mesh.
        mesh = trimesh.load(mesh_path)

        # Scale randomly but such that the mesh is within mesh_size_range.
        min_scale = mesh_size_range[0] / mesh.bounding_box.extents
        max_scale = mesh_size_range[1] / mesh.bounding_box.extents
        assert min_scale.shape == max_scale.shape == (3,)
        scale = random_state.uniform(min_scale, max_scale) * random_state.choice(
            [-1, 1], size=3
        )
        assert scale.shape == (3,)
        scale_matrix = np.eye(4)
        np.fill_diagonal(scale_matrix[:3, :3], scale)

        # Rotate randomly.
        quat = uniform_quat(random_state)
        rotation_matrix = np.eye(4)
        rotation_matrix[:3, :3] = quat2mat(quat)

        # Apply transformations. Apply scaling first since we computed the scale
        # in the original reference frame! In principle, we could also sheer the
        # object, but we currently do not do this.
        mesh.apply_transform(scale_matrix)
        mesh.apply_transform(rotation_matrix)

        if len(geoms) == 0:
            pos = -mesh.center_mass
        else:
            if attachment == "random":
                parent_geom = random_state.choice(geoms)
            elif attachment == "last":
                parent_geom = geoms[-1]
            else:
                raise ValueError()
            # We sample 10 points here because sample_surface sometimes returns less points
            # than we requested (unclear why).
            surface_pos = trimesh.sample.sample_surface(parent_geom.mesh, 10)[0][0]
            pos = parent_geom.pos + (surface_pos - mesh.center_mass)

        geom = MeshGeom(mesh_path=mesh_path, mesh=mesh, pos=pos, quat=quat, scale=scale)
        geoms.append(geom)

    # Shift everything so that the reference of the body is at the very center of the composed
    # object. This is very important.
    off_center_pos, _ = compute_pos_and_size(geoms)
    for geom in geoms:
        geom.pos -= off_center_pos

    # Ensure that the object origin is exactly at the center.
    assert np.allclose(compute_pos_and_size(geoms)[0], 0.0)

    # Resize object.
    if object_size is not None:
        _, size = compute_pos_and_size(geoms)

        # Apply global scale (so that ratio is not changed and longest side is exactly
        # object_size).
        ratio = object_size / np.max(size)
        for geom in geoms:
            geom.scale *= ratio
            geom.pos *= ratio

    geoms_str = "\n".join([g.to_geom_xml(name, idx) for idx, g in enumerate(geoms)])
    meshes_str = "\n".join([g.to_mesh_xml(name, idx) for idx, g in enumerate(geoms)])

    xml_source = f"""
    <mujoco>
        <asset>
          {meshes_str}
        </asset>
        <worldbody>
        <body name="{name}" pos="0 0 0">
          {geoms_str}
          <joint name="{name}:joint" type="free"/>
        </body>
        </worldbody>
    </mujoco>
    """
    return MujocoXML.from_string(xml_source)
Beispiel #22
0
 def make_world_xml(cls, *, contact_params: dict, mujoco_timestep: float,
                    **kwargs):
     return (MujocoXML.parse(cls.BASE_XML).set_objects_attr(
         tag="option", timestep=mujoco_timestep).set_objects_attr(
             tag="size", **contact_params).add_default_compiler_directive())
Beispiel #23
0
def make_openai_block(name: str, object_size: np.ndarray) -> MujocoXML:
    """ Creates a block with OPENAI letters on it faces.

    :param name: The name of the block
    :param object_size: The size of the block (3-dimensional). This is half-size as per Mujoco
        convention
    """
    default_object_size = 0.0285
    default_letter_offset = 0.0009

    # scale face meshes properly
    scale = object_size / default_object_size
    letter_offset = default_letter_offset * scale

    def to_str(x: np.ndarray):
        return " ".join(map(str, x.tolist()))

    face_pos = {
        "top": {
            "body": to_str(np.array([0, 0, object_size[2]])),
            "geom": to_str(np.array([0, 0, -letter_offset[2]])),
        },
        "bottom": {
            "body": to_str(np.array([0, 0, -object_size[2]])),
            "geom": to_str(np.array([0, 0, letter_offset[2]])),
        },
        "back": {
            "body": to_str(np.array([0, object_size[1], 0])),
            "geom": to_str(np.array([0, -letter_offset[1], 0])),
        },
        "right": {
            "body": to_str(np.array([object_size[0], 0, 0])),
            "geom": to_str(np.array([-letter_offset[0], 0, 0])),
        },
        "front": {
            "body": to_str(np.array([0, -object_size[1], 0])),
            "geom": to_str(np.array([0, letter_offset[1], 0])),
        },
        "left": {
            "body": to_str(np.array([-object_size[0], 0, 0])),
            "geom": to_str(np.array([letter_offset[0], 0, 0])),
        },
    }
    face_euler = {
        "top": to_str(np.array([np.pi / 2, 0, np.pi / 2])),
        "bottom": to_str(np.array([np.pi / 2, 0, np.pi / 2])),
        "back": to_str(np.array([0, 0, np.pi / 2])),
        "right": to_str(np.array([0, 0, 0])),
        "front": to_str(np.array([0, 0, -np.pi / 2])),
        "left": to_str(np.array([0, 0, np.pi])),
    }

    def face_xml(_name: str, _face: str, _c: str):
        xml = f"""
        <body name="{_face}:{_name}" pos="{face_pos[_face]['body']}">
            <geom name="letter_{_c}:{_name}" mesh="{_name}{_c}" euler="{face_euler[_face]}"
             pos="{face_pos[_face]['geom']}" type="mesh" material="{_name}letter"
             conaffinity="0" contype="0" />
        </body>
        """
        return xml

    size_string = " ".join(map(str, list(object_size)))
    scale_string = " ".join(map(str, list(scale)))

    xml_source = f"""
    <mujoco>
        <asset>
            <material name="{name}letter" specular="1" shininess="0.3" rgba="1 1 1 1"/>
            <mesh name="{name}O" file="{ASSETS_DIR}/stls/openai_cube/O.stl"
             scale="{scale_string}" />
            <mesh name="{name}P" file="{ASSETS_DIR}/stls/openai_cube/P.stl"
             scale="{scale_string}" />
            <mesh name="{name}E" file="{ASSETS_DIR}/stls/openai_cube/E.stl"
             scale="{scale_string}" />
            <mesh name="{name}N" file="{ASSETS_DIR}/stls/openai_cube/N.stl"
             scale="{scale_string}" />
            <mesh name="{name}A" file="{ASSETS_DIR}/stls/openai_cube/A.stl"
             scale="{scale_string}" />
            <mesh name="{name}I" file="{ASSETS_DIR}/stls/openai_cube/I.stl"
             scale="{scale_string}" />
        </asset>
        <worldbody>
            <body name="{name}">
                <geom name="{name}" size="{size_string}" type="box"
                 rgba="0.0 0.0 0.0 0.0" material="block_mat"/>
                <joint name="{name}:joint" type="free"/>
                {face_xml(name, "top", "O")}
                {face_xml(name, "bottom", "P")}
                {face_xml(name, "back", "E")}
                {face_xml(name, "right", "N")}
                {face_xml(name, "front", "A")}
                {face_xml(name, "left", "I")}
            </body>
        </worldbody>
    </mujoco>
    """
    return MujocoXML.from_string(xml_source)