### Run an example environment with random actions Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/locomotion/README.md Builds an example environment and steps through it using random actions. Requires importing `composer`, `basic_cmu_2019`, and `numpy`. ```python from dm_control import composer from dm_control.locomotion.examples import basic_cmu_2019 import numpy as np # Build an example environment. env = basic_cmu_2019.cmu_humanoid_run_walls() # Get the `action_spec` describing the control inputs. action_spec = env.action_spec() # Step through the environment for one episode with random actions. time_step = env.reset() while not time_step.last(): action = np.random.uniform(action_spec.minimum, action_spec.maximum, size=action_spec.shape) time_step = env.step(action) print("reward = {}, discount = {}, observations = {}.".format( time_step.reward, time_step.discount, time_step.observation)) ``` -------------------------------- ### Install dm_control from repository Source: https://github.com/google-deepmind/dm_control/blob/main/README.md Install an unreleased version of dm_control directly from its GitHub repository. ```sh pip install git+https://github.com/google-deepmind/dm_control.git ``` -------------------------------- ### Install dm_control Source: https://github.com/google-deepmind/dm_control/blob/main/README.md Install dm_control from PyPI. Do not use editable mode. ```sh pip install dm_control ``` -------------------------------- ### Install MuJoCo and dm_control on Colab Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mujoco/tutorial.ipynb Installs MuJoCo and dm_control, configures the EGL rendering backend for GPU acceleration, and verifies the installation by loading a simple environment. ```python #@title Run to install MuJoCo and `dm_control` import distutils.util import os import subprocess if subprocess.run('nvidia-smi').returncode: raise RuntimeError( 'Cannot communicate with GPU. ' 'Make sure you are using a GPU Colab runtime. ' 'Go to the Runtime menu and select Choose runtime type.') # Add an ICD config so that glvnd can pick up the Nvidia EGL driver. # This is usually installed as part of an Nvidia driver package, but the Colab # kernel doesn't install its driver via APT, and as a result the ICD is missing. # (https://github.com/NVIDIA/libglvnd/blob/master/src/EGL/icd_enumeration.md) NVIDIA_ICD_CONFIG_PATH = '/usr/share/glvnd/egl_vendor.d/10_nvidia.json' if not os.path.exists(NVIDIA_ICD_CONFIG_PATH): with open(NVIDIA_ICD_CONFIG_PATH, 'w') as f: f.write("""{ "file_format_version" : "1.0.0", "ICD" : { "library_path" : "libEGL_nvidia.so.0" } } """) print('Installing dm_control...') !pip install -q dm_control>=1.0.42 # Configure dm_control to use the EGL rendering backend (requires GPU) %env MUJOCO_GL=egl print('Checking that the dm_control installation succeeded...') try: from dm_control import suite env = suite.load('cartpole', 'swingup') pixels = env.physics.render() except Exception as e: raise e from RuntimeError( 'Something went wrong during installation. Check the shell output above ' 'for more information.\n' 'If using a hosted Colab runtime, make sure you enable GPU acceleration ' 'by going to the Runtime menu and selecting "Choose runtime type".') else: del suite, pixels print('dm_control installation succeeded.') ``` -------------------------------- ### Launch Viewer with Environment Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/viewer/README.md Launches the viewer with a specified environment from the Control Suite. Ensure dm_control is installed. ```python from dm_control import suite from dm_control import viewer # Load an environment from the Control Suite. env = suite.load(domain_name="humanoid", task_name="stand") # Launch the viewer application. viewer.launch(env) ``` -------------------------------- ### Load and Interact with Control Suite Environments Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/suite/README.md Demonstrates how to load a specific task, iterate over all benchmarking tasks, and step through an episode using random actions. Ensure dm_control and numpy are installed. ```python from dm_control import suite import numpy as np # Load one task: env = suite.load(domain_name="cartpole", task_name="swingup") # Iterate over a task set: for domain_name, task_name in suite.BENCHMARKING: env = suite.load(domain_name, task_name) # Step through an episode and print out reward, discount and observation. action_spec = env.action_spec() time_step = env.reset() while not time_step.last(): action = np.random.uniform(action_spec.minimum, action_spec.maximum, size=action_spec.shape) time_step = env.step(action) print(time_step.reward, time_step.discount, time_step.observation) ``` -------------------------------- ### Instantiate and Run a Soccer Environment Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/locomotion/soccer/README.md Instantiates a 2-vs-2 BOXHEAD soccer environment and steps through it with random actions. Ensure dm_control and numpy are installed. ```python import numpy as np from dm_control.locomotion import soccer as dm_soccer # Instantiates a 2-vs-2 BOXHEAD soccer environment with episodes of 10 seconds # each. Upon scoring, the environment reset player positions and the episode # continues. In this example, players can physically block each other and the # ball is trapped within an invisible box encapsulating the field. env = dm_soccer.load(team_size=2, time_limit=10.0, disable_walker_contacts=False, enable_field_box=True, terminate_on_goal=False, walker_type=dm_soccer.WalkerType.BOXHEAD) # Retrieves action_specs for all 4 players. action_specs = env.action_spec() # Step through the environment for one episode with random actions. timestep = env.reset() while not timestep.last(): actions = [] for action_spec in action_specs: action = np.random.uniform( action_spec.minimum, action_spec.maximum, size=action_spec.shape) actions.append(action) timestep = env.step(actions) for i in range(len(action_specs)): print( "Player {}: reward = {}, discount = {}, observations = {} ".format( i, timestep.reward[i], timestep.discount, timestep.observation[i])) ``` -------------------------------- ### Install MuJoCo and dm_control Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Installs the dm_control package and its dependencies, including MuJoCo. Requires a GPU-enabled Colab runtime and configures dm_control to use the EGL rendering backend. ```python #@title Run to install MuJoCo and `dm_control` import distutils.util import os import subprocess if subprocess.run('nvidia-smi').returncode: raise RuntimeError( 'Cannot communicate with GPU. ' 'Make sure you are using a GPU Colab runtime. ' 'Go to the Runtime menu and select Choose runtime type.') # Add an ICD config so that glvnd can pick up the Nvidia EGL driver. # This is usually installed as part of an Nvidia driver package, but the Colab # kernel doesn't install its driver via APT, and as a result the ICD is missing. # (https://github.com/NVIDIA/libglvnd/blob/master/src/EGL/icd_enumeration.md) NVIDIA_ICD_CONFIG_PATH = '/usr/share/glvnd/egl_vendor.d/10_nvidia.json' if not os.path.exists(NVIDIA_ICD_CONFIG_PATH): with open(NVIDIA_ICD_CONFIG_PATH, 'w') as f: f.write(""" { "file_format_version" : "1.0.0", "ICD" : { "library_path" : "libEGL_nvidia.so.0" } } """) print('Installing dm_control...') !pip install -q dm_control>=1.0.42 # Configure dm_control to use the EGL rendering backend (requires GPU) %env MUJOCO_GL=egl print('Checking that the dm_control installation succeeded...') try: from dm_control import suite env = suite.load('cartpole', 'swingup') pixels = env.physics.render() except Exception as e: raise e from RuntimeError( 'Something went wrong during installation. Check the shell output above ' 'for more information.\n' 'If using a hosted Colab runtime, make sure you enable GPU acceleration ' 'by going to the Runtime menu and selecting "Choose runtime type".') else: del pixels, suite !echo Installed dm_control $(pip show dm_control | grep -Po "(?<=Version: ).+") ``` -------------------------------- ### Create Plugin Installer Zip Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/blender/mujoco_exporter/README.md This command creates a zip archive of the MuJoCo model exporter addon for installation in Blender. ```shell cd ./addons && zip -r mujoco_model_exporter.zip mujoco_model_exporter/* ``` -------------------------------- ### Instantiate and Reset Environment Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Instantiates a dm_control environment with a custom task and resets it. This is the initial step before starting a simulation or training loop. ```python #@title Instantiating an environment{vertical-output: true} creature = Creature(num_legs=4) task = PressWithSpecificForce(creature) env = composer.Environment(task, random_state=np.random.RandomState(42)) env.reset() PIL.Image.fromarray(env.physics.render()) ``` -------------------------------- ### Conflicting Global Options Error (Python) Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mjcf/README.md Example demonstrating how attempting to attach models with conflicting global compiler options (e.g., angle units) raises an error. ```python model_1 = mjcf.RootElement() model_1.compiler.angle = 'radian' model_2 = mjcf.RootElement() model_2.compiler.angle = 'degree' model_1.attach(model_2) # Error! ``` -------------------------------- ### Render Initial State of Mujoco Model Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mujoco/tutorial.ipynb This snippet loads a complex Mujoco model defined in MJCF format, which includes a bat, a piñata (represented by a box and sphere), and a wire tendon connecting them. It then renders a single image of the initial state of the simulation. Use this to quickly inspect the setup of a Mujoco model. ```python #@title bat and piñata: {vertical-output: true} MJCF = """ " physics = mujoco.Physics.from_xml_string(MJCF) PIL.Image.fromarray(physics.render(480, 480, "fixed") ) ``` -------------------------------- ### dm_control Core Imports Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Imports essential components from dm_control for mujoco, PyMJCF, and Composer functionalities. Includes specific imports for locomotion and manipulation examples. ```python #@title All `dm_control` imports required for this tutorial # The basic mujoco wrapper. from dm_control import mujoco # Access to enums and MuJoCo library functions. from dm_control.mujoco.wrapper.mjbindings import enums from dm_control.mujoco.wrapper.mjbindings import mjlib # PyMJCF from dm_control import mjcf # Composer high level imports from dm_control import composer from dm_control.composer.observation import observable from dm_control.composer import variation # Imports for Composer tutorial example from dm_control.composer.variation import distributions from dm_control.composer.variation import noises from dm_control.locomotion.arenas import floors # Control Suite from dm_control import suite # Run through corridor example from dm_control.locomotion.walkers import cmu_humanoid from dm_control.locomotion.arenas import corridors as corridor_arenas from dm_control.locomotion.tasks import corridors as corridor_tasks # Soccer from dm_control.locomotion import soccer # Manipulation from dm_control import manipulation ``` -------------------------------- ### Define a Custom Task: PressWithSpecificForce Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Defines a custom task for the dm_control environment, including arena setup, entity attachment, observable configuration, and reward calculation. This is useful for creating specialized control problems. ```python #@title The `PressWithSpecificForce` task class PressWithSpecificForce(composer.Task): def __init__(self, creature): self._creature = creature self._arena = floors.Floor() self._arena.add_free_entity(self._creature) self._arena.mjcf_model.worldbody.add('light', pos=(0, 0, 4)) self._button = Button() self._arena.attach(self._button) # Configure initial poses self._creature_initial_pose = (0, 0, 0.15) button_distance = distributions.Uniform(0.5, .75) self._button_initial_pose = UniformCircle(button_distance) # Configure variators self._mjcf_variator = variation.MJCFVariator() self._physics_variator = variation.PhysicsVariator() # Configure and enable observables pos_corrptor = noises.Additive(distributions.Normal(scale=0.01)) self._creature.observables.joint_positions.corruptor = pos_corrptor self._creature.observables.joint_positions.enabled = True vel_corruptor = noises.Multiplicative(distributions.LogNormal(sigma=0.01)) self._creature.observables.joint_velocities.corruptor = vel_corruptor self._creature.observables.joint_velocities.enabled = True self._button.observables.touch_force.enabled = True def to_button(physics): button_pos, _ = self._button.get_pose(physics) return self._creature.global_vector_to_local_frame(physics, button_pos) self._task_observables = {} self._task_observables['button_position'] = observable.Generic(to_button) for obs in self._task_observables.values(): obs.enabled = True self.control_timestep = NUM_SUBSTEPS * self.physics_timestep @property def root_entity(self): return self._arena @property def task_observables(self): return self._task_observables def initialize_episode_mjcf(self, random_state): self._mjcf_variator.apply_variations(random_state) def initialize_episode(self, physics, random_state): self._physics_variator.apply_variations(physics, random_state) creature_pose, button_pose = variation.evaluate( (self._creature_initial_pose, self._button_initial_pose), random_state=random_state) self._creature.set_pose(physics, position=creature_pose) self._button.set_pose(physics, position=button_pose) def get_reward(self, physics): return self._button.num_activated_steps / NUM_SUBSTEPS ``` -------------------------------- ### Launch Viewer with Policy Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/viewer/README.md Launches the viewer with an environment and a custom policy for action generation. The policy must accept a TimeStep and return actions. ```python from dm_control import suite from dm_control import viewer import numpy as np env = suite.load(domain_name="humanoid", task_name="stand") action_spec = env.action_spec() # Define a uniform random policy. def random_policy(time_step): del time_step # Unused. return np.random.uniform(low=action_spec.minimum, high=action_spec.maximum, size=action_spec.shape) # Launch the viewer application. viewer.launch(env, policy=random_policy) ``` -------------------------------- ### Parent and Child MJCF Models Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mjcf/README.md Example XML structure for a parent and a child model before attachment. ```xml ``` -------------------------------- ### Launch the environment viewer Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/locomotion/README.md Launches the `dm_control.viewer` to visualize and interact with the environment. Requires importing `viewer` and specifying an `environment_loader`. ```python from dm_control import viewer viewer.launch(environment_loader=basic_cmu_2019.cmu_humanoid_run_walls) ``` -------------------------------- ### Accessing Cartesian Positions of Geoms Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Get the world frame Cartesian positions of geoms using `physics.data.geom_xpos`. ```python print(physics.data.geom_xpos) ``` -------------------------------- ### Visualize Initial Task States Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Loads and visualizes the initial state of one task per domain in the Control Suite. Requires numpy and matplotlib for visualization. ```python #@title Visualizing an initial state of one task per domain in the Control Suite #@test {"timeout": 180} domains_tasks = {domain: task for domain, task in suite.ALL_TASKS} random_state = np.random.RandomState(42) num_domains = len(domains_tasks) n_col = num_domains // int(np.sqrt(num_domains)) n_row = num_domains // n_col + int(0 < num_domains % n_col) _, ax = plt.subplots(n_row, n_col, figsize=(12, 12)) for a in ax.flat: a.axis('off') a.grid(False) print(f'Iterating over all {num_domains} domains in the Suite:') for j, [domain, task] in enumerate(domains_tasks.items()): print(domain, task) env = suite.load(domain, task, task_kwargs={'random': random_state}) timestep = env.reset() pixels = env.physics.render(height=200, width=200, camera_id=0) ax.flat[j].imshow(pixels) ax.flat[j].set_title(domain + ': ' + task) clear_output() ``` -------------------------------- ### Simulate and Render MuJoCo Model to Video Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Simulates a dynamic MuJoCo model over a specified duration and collects frames to create a video. Requires a model with DOFs. ```python #@title Making a video {vertical-output: true} duration = 2 # (seconds) framerate = 30 # (Hz) # Visualize the joint axis scene_option = mujoco.wrapper.core.MjvOption() scene_option.flags[enums.mjtVisFlag.mjVIS_JOINT] = True # Simulate and display video. frames = [] physics.reset() # Reset state and time while physics.data.time < duration: physics.step() if len(frames) < physics.data.time * framerate: pixels = physics.render(scene_option=scene_option) frames.append(pixels) display_video(frames, framerate) ``` -------------------------------- ### Animate tippe-top simulation Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Simulates the tippe-top model for a specified duration and framerate, rendering and collecting frames to display as a video. ```python #@title Video of the tippe-top {vertical-output: true} duration = 7 # (seconds) framerate = 60 # (Hz) # Simulate and display video. frames = [] physics.reset(0) # Reset to keyframe 0 (load a saved state). while physics.data.time < duration: physics.step() if len(frames) < (physics.data.time) * framerate: pixels = physics.render(camera_id='closeup') frames.append(pixels) display_video(frames, framerate) ``` -------------------------------- ### Load and Simulate a Control Suite Task Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Loads a specific task from the Control Suite and simulates it with random actions for a given duration. This snippet demonstrates environment loading, stepping, rendering, and data collection. ```python #@title Loading and simulating a `suite` task{vertical-output: true} # Load the environment random_state = np.random.RandomState(42) env = suite.load('hopper', 'stand', task_kwargs={'random': random_state}) # Simulate episode with random actions duration = 4 # Seconds frames = [] ticks = [] rewards = [] observations = [] spec = env.action_spec() time_step = env.reset() while env.physics.data.time < duration: action = random_state.uniform(spec.minimum, spec.maximum, spec.shape) time_step = env.step(action) camera0 = env.physics.render(camera_id=0, height=200, width=200) camera1 = env.physics.render(camera_id=1, height=200, width=200) frames.append(np.hstack((camera0, camera1))) rewards.append(time_step.reward) observations.append(copy.deepcopy(time_step.observation)) ticks.append(env.physics.data.time) html_video = display_video(frames, framerate=1./env.control_timestep()) # Show video and plot reward and observations num_sensors = len(time_step.observation) _, ax = plt.subplots(1 + num_sensors, 1, sharex=True, figsize=(4, 8)) ax[0].plot(ticks, rewards) ax[0].set_ylabel('reward') ax[-1].set_xlabel('time') for i, key in enumerate(time_step.observation): data = np.asarray([observations[j][key] for j in range(len(observations))]) ax[i+1].plot(ticks, data, label=key) ax[i+1].set_ylabel(key) html_video ``` -------------------------------- ### Load and Simulate Manipulation Task Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Loads a specified manipulation environment, defines a function to sample random actions, and simulates a full episode, recording camera observations. ```python #@title Loading and simulating a `manipulation` task{vertical-output: true} env = manipulation.load('stack_2_of_3_bricks_random_order_vision', seed=42) action_spec = env.action_spec() def sample_random_action(): return env.random_state.uniform( low=action_spec.minimum, high=action_spec.maximum, ).astype(action_spec.dtype, copy=False) # Step the environment through a full episode using random actions and record # the camera observations. frames = [] timestep = env.reset() frames.append(timestep.observation['front_close']) while not timestep.last(): timestep = env.step(sample_random_action()) frames.append(timestep.observation['front_close']) all_frames = np.concatenate(frames, axis=0) display_video(all_frames, 30) ``` -------------------------------- ### List All Manipulation Tasks Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Prints the names of all available environments within the dm_control manipulation suite. ```python #@title Listing all `manipulation` tasks{vertical-output: true} # `ALL` is a tuple containing the names of all of the environments in the suite. print('\n'.join(manipulation.ALL)) ``` -------------------------------- ### Load Model, Simulate, and Render with MuJoCo Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mujoco/README.md Loads a model from an XML string, renders the default camera view, resets the simulation with joint modifications, prints geom positions, and advances the simulation. ```python from dm_control import mujoco # Load a model from an MJCF XML string. xml_string = """ " physics = mujoco.Physics.from_xml_string(xml_string) # Render the default camera view as a numpy array of pixels. pixels = physics.render() # Reset the simulation, move the slide joint upwards and recompute derived # quantities (e.g. the positions of the body and geoms). with physics.reset_context(): physics.named.data.qpos['up_down'] = 0.5 # Print the positions of the geoms. print(physics.named.data.geom_xpos) # FieldIndexer(geom_xpos): # x y z # 0 floor [ 0 0 0 ] # 1 box [ 0 0 0.8 ] # 2 sphere [ 0.2 0.2 1 ] # Advance the simulation for 1 second. while physics.time() < 1.: physics.step() # Print the new z-positions of the 'box' and 'sphere' geoms. print(physics.named.data.geom_xpos[['box', 'sphere'], 'z']) # [ 0.19996362 0.39996362] ``` -------------------------------- ### Define and simulate the tippe-top model Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Defines a tippe-top model with a free joint, custom assets, and a low center of mass for flipping behavior. Renders an initial state using a defined camera. ```python #@title The "tippe-top" model{vertical-output: true} tippe_top = """ " physics = mujoco.Physics.from_xml_string(tippe_top) PIL.Image.fromarray(physics.render(camera_id='closeup')) ``` -------------------------------- ### Initialize MjvOption with Default Values (Before) Source: https://github.com/google-deepmind/dm_control/blob/main/migration_guide_1.0.md Before version 1.0, initializing MjvOption required explicit calls to default functions. This pattern is no longer necessary. ```python from dm_control.mujoco import wrapper from dm_control.mujoco.wrapper import mjbindings mjlib = mjbindings.mjlib scene_option = wrapper.core.MjvOption() mjlib.mjv_defaultOption(scene_option.ptr) ``` -------------------------------- ### Measure and Plot Tippe-Top Dynamics Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mujoco/tutorial.ipynb Simulates the tippe-top and collects data on time, angular velocity, and stem height. This data is then plotted to visualize the simulation's dynamics. ```python #@title Measuring values {vertical-output: true} timevals = [] angular_velocity = [] stem_height = [] # Simulate and save data physics.reset(0) while physics.data.time < duration: physics.step() timevals.append(physics.data.time) angular_velocity.append(physics.data.qvel[3:6].copy()) stem_height.append(physics.named.data.geom_xpos['stem', 'z']) dpi = 100 width = 480 height = 640 figsize = (width / dpi, height / dpi) _, ax = plt.subplots(2, 1, figsize=figsize, dpi=dpi, sharex=True) ax[0].plot(timevals, angular_velocity) ax[0].set_title('angular velocity') ax[0].set_ylabel('radians / second') ax[1].plot(timevals, stem_height) ax[1].set_xlabel('time (seconds)') ax[1].set_ylabel('meters') _ = ax[1].set_title('stem height') ``` -------------------------------- ### Inspect tippe-top model state Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Prints the joint positions (including quaternion orientation) and velocities of the tippe-top model after initialization. ```python print('positions', physics.data.qpos) print('velocities', physics.data.qvel) ``` -------------------------------- ### Iterate Over Control Suite Benchmark Tasks Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Iterates through the predefined benchmark tasks in the Control Suite, printing the domain and task names. Useful for understanding the available benchmarks. ```python #@title Iterating over tasks{vertical-output: true} max_len = max(len(d) for d, _ in suite.BENCHMARKING) for domain, task in suite.BENCHMARKING: print(f'{domain:<{max_len}} {task}') ``` -------------------------------- ### RunThroughCorridor Environment Simulation Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Instantiates and resets the RunThroughCorridor environment, renders camera observations, and displays them as a horizontally stacked image. ```python #@title The `RunThroughCorridor` environment env = composer.Environment( task=task, time_limit=10, random_state=np.random.RandomState(42), strip_singleton_obs_buffer_dim=True, ) env.reset() pixels = [] for camera_id in range(3): pixels.append(env.physics.render(camera_id=camera_id, width=240)) PIL.Image.fromarray(np.hstack(pixels)) ``` -------------------------------- ### Virtual Spring-Damper Simulation with Kinematic Jacobians Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mujoco/tutorial.ipynb Creates a virtual spring-damper system using the kinematic Jacobian of an end effector to reach a random target position. Renders the simulation from two camera perspectives. ```python #@ title virtual spring-damper: {vertical-output: true} MJCF = """ " physics = mujoco.Physics.from_xml_string(MJCF) # virtual spring coefficient KP = 3 # prepare simulation jac_pos = np.zeros((3, physics.model.nv)) jac_rot = np.zeros((3, physics.model.nv)) n_frames = 50 height = 320 width = 320 video = np.zeros((n_frames, height, 2*width, 3), dtype=np.uint8) # place target in random location with physics.reset_context(): target_pos = np.random.rand(3)*.5 target_pos[:2] -= .25 physics.named.model.geom_pos["target"][:] = target_pos physics.named.model.geom_sameframe["target"] = 0 # simulate and render for i in range(n_frames): while physics.data.time < i/15.0: # get Jacobian of fingertip position mjlib.mj_jacGeom(physics.model.ptr, physics.data.ptr, jac_pos, jac_rot, physics.model.name2id('fingertip', 'geom')) # multiply the jacobian by error to get vector in joint space err = (physics.named.data.geom_xpos["target"] - physics.named.data.geom_xpos["fingertip"]) jnt_err = np.dot(err, jac_pos) # set virutal spring force physics.data.qfrc_applied[:] = KP * jnt_err # step physics.step() video[i] = np.hstack((physics.render(height, width, "y"), physics.render(height, width, "x"))) display_video(video, framerate=24) ``` -------------------------------- ### Analyze Contact Forces and Kinematics Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mujoco/tutorial.ipynb This snippet simulates a Mujoco environment, collects data on contact forces, accelerations, velocities, and number of contacts over time, and then visualizes this data. It iterates through active contacts to extract force and penetration depth. Use this to understand the dynamics of contact interactions in a simulation. ```python #@title contact-related quantities: {vertical-output: true} n_steps = 499 # allocate sim_time = np.zeros(n_steps) ncon = np.zeros(n_steps) force = np.zeros((n_steps,3)) velocity = np.zeros((n_steps, physics.model.nv)) penetration = np.zeros(n_steps) acceleration = np.zeros((n_steps, physics.model.nv)) forcetorque = np.zeros(6) # random initial rotational velocity: with physics.reset_context(): physics.data.qvel[3:6] = 2*np.random.randn(3) # simulate and save data for i in range(n_steps): physics.step() sim_time[i] = physics.data.time ncon[i] = physics.data.ncon velocity[i] = physics.data.qvel[:] acceleration[i] = physics.data.qacc[:] # iterate over active contacts, save force and distance for j,c in enumerate(physics.data.contact): mjlib.mj_contactForce(physics.model.ptr, physics.data.ptr, j, forcetorque) force[i] += forcetorque[0:3] penetration[i] = min(penetration[i], c.dist) # we could also do # force[i] += physics.data.qfrc_constraint[0:3] # do you see why? # plot _, ax = plt.subplots(3, 2, sharex=True, figsize=(7, 10)) lines = ax[0,0].plot(sim_time, force) ax[0,0].set_title('contact force') ax[0,0].set_ylabel('Newton') ax[0,0].legend(iter(lines), ('normal z', 'friction x', 'friction y')); ax[1,0].plot(sim_time, acceleration) ax[1,0].set_title('acceleration') ax[1,0].set_ylabel('(meter,radian)/s/s') ax[2,0].plot(sim_time, velocity) ax[2,0].set_title('velocity') ax[2,0].set_ylabel('(meter,radian)/s') ax[2,0].set_xlabel('second') ax[0,1].plot(sim_time, ncon) ax[0,1].set_title('number of contacts') ax[0,1].set_yticks(range(6)) ax[1,1].plot(sim_time, force[:,0]) ax[1,1].set_yscale('log') ax[1,1].set_title('normal (z) force - log scale') ax[1,1].set_ylabel('Newton') z_gravity = -physics.model.opt.gravity[2] mg = physics.named.model.body_mass["box_and_sphere"] * z_gravity mg_line = ax[1,1].plot(sim_time, np.ones(n_steps)*mg, label='m*g', linewidth=1) ax[1,1].legend() ax[2,1].plot(sim_time, 1000*penetration) ax[2,1].set_title('penetration depth') ax[2,1].set_ylabel('millimeter') ax[2,1].set_xlabel('second') plt.tight_layout() ``` -------------------------------- ### Use mujoco module directly instead of mjbindings.mjlib Source: https://github.com/google-deepmind/dm_control/blob/main/migration_guide_1.0.md Replace imports and calls from dm_control.mujoco.wrapper.mjbindings.mjlib to the direct mujoco module. ```python import dm_control.mujoco.wrapper.mjbindings jlib = mjbindings.mjlib jlib.mj_objectVelocity( physics.model.ptr, physics.data.ptr, enums.mjtObj.mjOBJ_SITE, site_id, vel, 0) ``` ```python import mujoco jmujoco.mj_objectVelocity( physics.model.ptr, physics.data.ptr, mujoco.mjtObj.mjOBJ_SITE, site_id, vel, 0) ``` -------------------------------- ### dm_control Core and Composer Imports Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mujoco/tutorial.ipynb Imports the basic mujoco wrapper, bindings for MuJoCo library functions, and high-level composer modules. Also includes specific imports for composer variations and the floors arena. ```python #@title All `dm_control` imports required for this tutorial # The basic mujoco wrapper. from dm_control import mujoco # Access to enums and MuJoCo library functions. from dm_control.mujoco.wrapper.mjbindings import enums from dm_control.mujoco.wrapper.mjbindings import mjlib # Composer high level imports from dm_control import composer from dm_control.composer.observation import observable from dm_control.composer import variation # Imports for Composer tutorial example from dm_control.composer.variation import distributions from dm_control.composer.variation import noises from dm_control.locomotion.arenas import floors # Control Suite from dm_control import suite # Run through corridor example from dm_control.locomotion.walkers import cmu_humanoid from dm_control.locomotion.arenas import corridors as corridor_arenas from dm_control.locomotion.tasks import corridors as corridor_tasks # Soccer from dm_control.locomotion import soccer # Manipulation from dm_control import manipulation ``` -------------------------------- ### Button Class Implementation Source: https://github.com/google-deepmind/dm_control/blob/main/tutorial.ipynb Defines a Button Entity that changes color when pressed with a specific force. It implements `_build`, `_build_observables`, `initialize_episode`, and `after_substep` methods. Use this class to create interactive elements in your simulation. ```python #@title The `Button` class NUM_SUBSTEPS = 25 # The number of physics substeps per control timestep. class Button(composer.Entity): """A button Entity which changes colour when pressed with certain force.""" def _build(self, target_force_range=(5, 10)): self._min_force, self._max_force = target_force_range self._mjcf_model = mjcf.RootElement() self._geom = self._mjcf_model.worldbody.add( 'geom', type='cylinder', size=[0.25, 0.02], rgba=[1, 0, 0, 1]) self._site = self._mjcf_model.worldbody.add( 'site', type='cylinder', size=self._geom.size*1.01, rgba=[1, 0, 0, 0]) self._sensor = self._mjcf_model.sensor.add('touch', site=self._site) self._num_activated_steps = 0 def _build_observables(self): return ButtonObservables(self) @property def mjcf_model(self): return self._mjcf_model # Update the activation (and colour) if the desired force is applied. def _update_activation(self, physics): current_force = physics.bind(self.touch_sensor).sensordata[0] self._is_activated = (current_force >= self._min_force and current_force <= self._max_force) physics.bind(self._geom).rgba = ( [0, 1, 0, 1] if self._is_activated else [1, 0, 0, 1]) self._num_activated_steps += int(self._is_activated) def initialize_episode(self, physics, random_state): self._reward = 0.0 self._num_activated_steps = 0 self._update_activation(physics) def after_substep(self, physics, random_state): self._update_activation(physics) @property def touch_sensor(self): return self._sensor @property def num_activated_steps(self): return self._num_activated_steps ``` ```python class ButtonObservables(composer.Observables): """A touch sensor which averages contact force over physics substeps.""" @composer.observable def touch_force(self): return observable.MJCFFeature('sensordata', self._entity.touch_sensor, buffer_size=NUM_SUBSTEPS, aggregator='mean') ``` -------------------------------- ### Compose MJCF Models with PyMJCF Source: https://github.com/google-deepmind/dm_control/blob/main/dm_control/mjcf/README.md Demonstrates composing MJCF models by instantiating reusable components like 'Arm' within a parent 'UpperBody' class. Automatically handles name prefixing to avoid collisions. ```python from dm_control import mjcf class Arm: def __init__(self, name): self.mjcf_model = mjcf.RootElement(model=name) self.upper_arm = self.mjcf_model.worldbody.add('body', name='upper_arm') self.shoulder = self.upper_arm.add('joint', name='shoulder', type='ball') self.upper_arm.add('geom', name='upper_arm', type='capsule', pos=[0, 0, -0.15], size=[0.045, 0.15]) self.forearm = self.upper_arm.add('body', name='forearm', pos=[0, 0, -0.3]) self.elbow = self.forearm.add('joint', name='elbow', type='hinge', axis=[0, 1, 0]) self.forearm.add('geom', name='forearm', type='capsule', pos=[0, 0, -0.15], size=[0.045, 0.15]) class UpperBody: def __init__(self): self.mjcf_model = mjcf.RootElement() self.mjcf_model.worldbody.add( 'geom', name='torso', type='box', size=[0.15, 0.045, 0.25]) left_shoulder_site = self.mjcf_model.worldbody.add( 'site', size=[1e-6]*3, pos=[-0.15, 0, 0.25]) right_shoulder_site = self.mjcf_model.worldbody.add( 'site', size=[1e-6]*3, pos=[0.15, 0, 0.25]) self.left_arm = Arm(name='left_arm') left_shoulder_site.attach(self.left_arm.mjcf_model) self.right_arm = Arm(name='right_arm') right_shoulder_site.attach(self.right_arm.mjcf_model) body = UpperBody() physics = mjcf.Physics.from_mjcf_model(body.mjcf_model) ```