{ "cells": [ { "cell_type": "markdown", "id": "9bd6dd9a-a0f6-4248-89d4-657c318e0d79", "metadata": {}, "source": [ "# Tutorial 00: Introduction" ] }, { "cell_type": "markdown", "id": "3c18bd9e-7470-4b09-b7a7-fac8bb2dec17", "metadata": {}, "source": [ "## Tutorial Description" ] }, { "cell_type": "markdown", "id": "b7e48aef-41be-4597-be37-1bd196bc97a5", "metadata": {}, "source": [ "This tutorial covers creating the backend of a new project using condynsate. In this tutorial, we will cover how to: \n", "1. Import physics objects in the condynsate physics environment.\n", "2. Test that physics objects behave as expected.\n", " \n", "We will accomplish these goals by importing a cube 1 meter above a solid ground plane, starting the physics engine, then observing the dynamics of the cube. Note that this project will not coverhow to create your own .URDF files. Instead, we recommend reviewing https://wiki.ros.org/urdf." ] }, { "cell_type": "markdown", "id": "be64a904-72de-4411-8a77-37a630f0c3a4", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "markdown", "id": "5ca5c838-ffae-45eb-9e60-0ad1e0ac8698", "metadata": {}, "source": [ "To begin, we import the required dependencies. In general, for projects that will simulate the physics of a set of urdf objects, the only \n", "module needed from the condynsate project is `condynsate.simulator`. Note that we also import `condynsate.__assets__` so that we can load and use a condynsate default cube .URDF file." ] }, { "cell_type": "code", "execution_count": 1, "id": "b2b6dc1e-8420-4242-91a3-e73422ce713e", "metadata": {}, "outputs": [], "source": [ "from condynsate import Simulator as con_sim\n", "from condynsate import __assets__ as assets" ] }, { "cell_type": "markdown", "id": "61754c2f-38bd-4bed-8c3b-691c24fd175e", "metadata": {}, "source": [ "To see what default .URDF files are available to us, we can list the keys of `assets`:" ] }, { "cell_type": "code", "execution_count": 2, "id": "2683e90e-88af-4340-92ab-65f901732817", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['cart', 'check_img', 'concrete_img', 'cube', 'double_pendulum', 'gyro', 'pendulum', 'plane_big', 'sphere', 'target_arrow', 'wheel'])" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "assets.keys()" ] }, { "cell_type": "markdown", "id": "dea83d70-5877-4057-b588-983d28579958", "metadata": {}, "source": [ "## Building the Project Class" ] }, { "cell_type": "markdown", "id": "507fb740-3a04-4ff7-af5e-fbbd3c73f962", "metadata": {}, "source": [ "The `Project` class is that object that front end users interact with. In its simplest form, the `Project` class contains two class functions: `__init__` and `run`. In `__init__` the physics engine is initialized, .URDF files are imported into the engine, and initial conditions are set. In `run` initial conditions are reset, and the physics engine is turned on." ] }, { "cell_type": "code", "execution_count": 3, "id": "098a10b0-6cec-4676-8cd2-1ac6b47ad94b", "metadata": {}, "outputs": [], "source": [ "class Project():\n", " def __init__(self):\n", " '''\n", " ##################################################################\n", " STEP 1: Initialize an instance of the condynsate.simulator class. \n", " Note that for this project, we do not want to use animation. \n", " This means that we do not want to plot things in real time. \n", " To avoid condynsate from assuming that we want to do this and\n", " running some things in the background that take compute power, \n", " we simply set the animation flag in the initialization function\n", " to be false. We also do not want to use the keyboard for \n", " interactivity, so we set keyboard to False.\n", " ##################################################################\n", " '''\n", " self.s = con_sim(animation = False,\n", " keyboard = False)\n", " \n", " '''\n", " ##################################################################\n", " STEP 2: Add solid ground to the physics environment. Condynsate \n", " already includes several default .URDF files that are listed in \n", " the condynsate.__assets__ variable. To load any one of these, \n", " we simply select the path to the asset we want and call the \n", " load_urdf function.\n", " \n", " load_urdf has 5 arguments. urdf_path tells the function where the\n", " .URDF file is that we want to load. For the ground we will use\n", " 'plane_big'.\n", "\n", " position and wxyz_quaternion define the position and orientation \n", " in which the URDF object will be placed. These positions and \n", " orientations are defined around the parent axes of the URDF \n", " object. By setting position = [0, 0, 0] and \n", " wxyz_quaternion = [1, 0, 0, 0], we place the plane at (0, 0, 0) \n", " aligned to the XY plane.\n", " \n", " By setting fixed = True, we are telling the simulator NOT to \n", " update the physics of the base of the object. It will still have \n", " collision, but no other forces, including gravity, will affect it.\n", "\n", " By setting update_vis = False, we are telling the simulator NOT \n", " to send updates to the visualizer for this object. This flag is \n", " usually set to False for unjointed, fixed URDFs to reduce the \n", " amount of compute power required by the visualization\n", " ##################################################################\n", " '''\n", " self.ground = self.s.load_urdf(urdf_path = assets['plane_big'],\n", " position = [0., 0., 0.],\n", " wxyz_quaternion = [1., 0., 0., 0],\n", " fixed = True,\n", " update_vis = False)\n", " \n", " '''\n", " ##################################################################\n", " STEP 3: Load a cube into the simulator as a physics object. For \n", " this we will use the 'cube' asset.\n", " \n", " By setting position = [0, 0, 1.5] and \n", " wxyz_quaternion = [1, 0, 0, 0], we place the cube at (0, 0, 1.5) \n", " in the orientation defined by the URDF parent axes. As it turns \n", " out, the cube is 1x1x1, so that a center position of [0, 0, 1.5] \n", " places the bottom of the cube exactly 1 meter above the ground.\n", " \n", " By setting fixed = False, we are telling the simulator apply \n", " physics to this .URDF when the engine is running.\n", " \n", " By setting update_vis = True, we are telling the simulator to \n", " send updates to the visualizer for this object. This is usually \n", " set to true when an object is not fixed and will change its \n", " position and/or orientation.\n", " ##################################################################\n", " '''\n", " self.cube = self.s.load_urdf(urdf_path = assets['cube'],\n", " position = [0., 0., 1.5],\n", " wxyz_quaternion = [1., 0., 0., 0],\n", " fixed = False,\n", " update_vis = True)\n", "\n", " def run(self, max_time=None):\n", " '''\n", " ##################################################################\n", " Now that we have created an initialization function, we move on \n", " to the run function. This function will run a physics simulation \n", " using condynsate. Essentially, we will do two things:\n", " 1) Reset the simulation environment to the initial \n", " conditions. Whereas this will not do anything for this \n", " particular example, it is best practice to always call \n", " condynsate.simulator.reset before running a simulation \n", " loop.\n", " 3) Run a simulation to completion. In this case, completion \n", " is defined as the simulator reaching max_time. We will \n", " describe how to do this using a while loop and \n", " condynsate.simulator.step below.\n", " ##################################################################\n", " '''\n", " \n", " '''\n", " ##################################################################\n", " STEP 1: Reset the simulator. It is best practice to do this at\n", " the beginning of every run function.\n", " ##################################################################\n", " '''\n", " # Reset the simulator.\n", " self.s.reset()\n", "\n", " '''\n", " ##################################################################\n", " STEP 2: Run the simulation loop. condynsate.simulator cannot run \n", " an entire simulation by itself. Instead, it can take one time \n", " step of 0.01 seconds. Therefore, the way we run an entire \n", " simulation is to place the condynsate.simulator.step function \n", " inside a while loop that executes until the boolean flag \n", " condynsate.simulator.is_done is True. Note that this flag will \n", " automatically be set to true when max_time is reached or, if \n", " the keyboard is enabled, when the user presses the esc key. \n", " If the max_time argument to is None, is_done will not be set \n", " to true until the esc key is pressed. Further, if max_time is \n", " None AND keyboard interactivity is disabled, max_time fails safe \n", " to 10 seconds.\n", " ##################################################################\n", " '''\n", " # Run the simulation loop until done\n", " while(not self.s.is_done):\n", " ret_code = self.s.step(max_time = max_time)" ] }, { "cell_type": "markdown", "id": "420433c6-c1d2-4702-aba4-52295bf753e2", "metadata": {}, "source": [ "## Running the Project Class" ] }, { "cell_type": "markdown", "id": "da509ec5-56c1-4bc4-a9d0-0db238bd069a", "metadata": {}, "source": [ "Now that we have made the `Project` class, we can test it by initializing it and then calling the `run` function. Remember to press the enter key to start the simulation and the esc key to end the simulation." ] }, { "cell_type": "code", "execution_count": 4, "id": "a9461fb2-213e-4b6e-92c0-d0fb1ad00176", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "You can open the visualizer by visiting the following URL:\n", "http://127.0.0.1:7002/static/\n" ] } ], "source": [ "# Create an instance of the Project class. \n", "proj = Project()\n", "\n", "# Run the simulation. max_time defines when the simulation automatically terminates. \n", "# Set to None for unlimited runtime terminated by user keypress.\n", "proj.run(max_time = 2.0)" ] }, { "cell_type": "markdown", "id": "36d1e2de-e3f4-4dfb-a530-766a4420f876", "metadata": {}, "source": [ "## Challenge" ] }, { "cell_type": "markdown", "id": "5ff6e017-b96d-4668-879d-5ebaf083ae88", "metadata": {}, "source": [ "This tutorial is now complete. For an added challenge, think of how you would modify `__init__` so that two cubes, one above the other, are loaded but only the top one has physics applied to it." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.7" } }, "nbformat": 4, "nbformat_minor": 5 }