{ "cells": [ { "cell_type": "markdown", "id": "f66f7560", "metadata": {}, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tayalmanan28/MuJoCo-Tutorial/blob/main/tutorial/05_contact.ipynb)\n", "\n", "# Tutorial 5: Contacts & Collisions\n", "\n", "Understand how MuJoCo handles contacts, read contact forces, and configure contact parameters." ] }, { "cell_type": "code", "execution_count": null, "id": "1cf52385", "metadata": {}, "outputs": [], "source": [ "!pip install -q mujoco mediapy matplotlib" ] }, { "cell_type": "code", "execution_count": null, "id": "048fbb07", "metadata": {}, "outputs": [], "source": [ "import mujoco\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import mediapy as media" ] }, { "cell_type": "markdown", "id": "4433d861", "metadata": {}, "source": [ "## Contact Detection\n", "\n", "MuJoCo detects contacts automatically between all geom pairs.\n", "Active contacts are stored in `data.contact[:data.ncon]`." ] }, { "cell_type": "code", "execution_count": null, "id": "7f9a56f4", "metadata": {}, "outputs": [], "source": [ "xml = \"\"\"\n", "\n", " \n", "\"\"\"\n", "\n", "model = mujoco.MjModel.from_xml_string(xml)\n", "data = mujoco.MjData(model)\n", "\n", "# Simulate until contact\n", "contact_times, contact_forces, heights = [], [], []\n", "while data.time < 2.0:\n", " mujoco.mj_step(model, data)\n", " heights.append(data.qpos[2])\n", " \n", " # Read contact forces\n", " total_force = 0.0\n", " for i in range(data.ncon):\n", " force = np.zeros(6)\n", " mujoco.mj_contactForce(model, data, i, force)\n", " total_force += np.linalg.norm(force[:3]) # normal + friction\n", " \n", " contact_times.append(data.time)\n", " contact_forces.append(total_force)\n", "\n", "fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), sharex=True)\n", "ax1.plot(contact_times, heights); ax1.set_ylabel('Height (m)'); ax1.grid(True)\n", "ax2.plot(contact_times, contact_forces, color='red'); ax2.set_ylabel('Contact Force (N)')\n", "ax2.set_xlabel('Time (s)'); ax2.grid(True)\n", "plt.suptitle('Box falling: height and contact force'); plt.tight_layout(); plt.show()" ] }, { "cell_type": "markdown", "id": "ba686441", "metadata": {}, "source": [ "## Contact Parameters\n", "\n", "Control contact behavior with `solref` (stiffness/damping) and `friction`:" ] }, { "cell_type": "code", "execution_count": null, "id": "f4d4bfb0", "metadata": {}, "outputs": [], "source": [ "# Compare different friction values\n", "xml_template = \"\"\"\n", "\n", " \n", "\"\"\"\n", "\n", "plt.figure(figsize=(10, 4))\n", "for friction in [0.1, 0.5, 1.0, 2.0]:\n", " m = mujoco.MjModel.from_xml_string(xml_template.format(friction=friction))\n", " d = mujoco.MjData(m)\n", " positions = []\n", " while d.time < 2.0:\n", " mujoco.mj_step(m, d)\n", " positions.append(d.qpos[0]) # x position\n", " plt.plot(np.linspace(0, 2, len(positions)), positions, label=f'friction={friction}')\n", "\n", "plt.xlabel('Time (s)'); plt.ylabel('X position (m)')\n", "plt.title('Effect of friction on sliding'); plt.legend(); plt.grid(True); plt.show()" ] }, { "cell_type": "markdown", "id": "3b815e57", "metadata": {}, "source": [ "## Collision Filtering\n", "\n", "Use `contype` and `conaffinity` to control which geoms collide:" ] }, { "cell_type": "code", "execution_count": null, "id": "2a97ea0a", "metadata": {}, "outputs": [], "source": [ "xml_filter = \"\"\"\n", "\n", " \n", "\"\"\"\n", "\n", "model_f = mujoco.MjModel.from_xml_string(xml_filter)\n", "data_f = mujoco.MjData(model_f)\n", "renderer = mujoco.Renderer(model_f, height=300, width=400)\n", "\n", "frames = []\n", "while data_f.time < 2.0:\n", " mujoco.mj_step(model_f, data_f)\n", " if len(frames) < data_f.time * 30:\n", " renderer.update_scene(data_f)\n", " frames.append(renderer.render().copy())\n", "\n", "renderer.close()\n", "media.show_video(frames, fps=30)" ] }, { "cell_type": "markdown", "id": "ac94a9ce", "metadata": {}, "source": [ "The red sphere collides with the floor; the blue sphere passes through it (different collision group)." ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }