© Copyright, 2025 G. Schaer.

SPDX-License-Identifier: GPL-3.0-only

Tutorial 1: External Forces and Torques

Tutorial Description

This tutorial covers creating a condynsate Project in which external forces are applied to the center of mass of a a cube. We will cover:

  1. Appling forces and torques to the center of mass of an object.

  2. Measuring the position, orientation, velocity, and rotational velocity of the center of mass of that object.

Imports

To begin, we import the same modules for the same reasons as Tutorial 0.

[1]:
from condynsate import Project
from condynsate import __assets__ as assets

Initializing the Project Class

Here we initialize the Project in the same way as Tutorial 0.

[2]:
# Create the project
proj = Project()
You can open the visualizer by visiting the following URL:
http://127.0.0.1:7028/static/
[3]:
# Turn off the grid and axes
proj.visualizer.set_axes(False) # Returns 0 on success
proj.visualizer.set_grid(False) # Returns 0 on success
[3]:
0
[4]:
# Load a fixed plane as the ground object
ground = proj.load_urdf(assets['plane_medium.urdf'], fixed=True)
[5]:
# Load a cube object that is resting on the ground plane
cube = proj.load_urdf(assets['cube.urdf'])
cube.set_initial_state(position=(0, 0, 0.5))
[5]:
0
[6]:
# Refresh the visualizer to show changes to the cube's position
proj.refresh_visualizer() # Returns 0 on success
[6]:
0

Running a Simulation Loop

Similary to Tutorial 0, we start the simulation loop by calling condynsate.Project.reset.

[7]:
proj.reset() # Returns 0 on success
[7]:
0

In each step of the loop we take 4 steps

  1. Calculate the cube’s height above the ground

  2. Apply an upward force (in the +z world direction) to the center of mass of the cube proportional to the cube’s height

  3. Apply an external torque to the cube

  4. Take a single simulation step

To get state information about the cube, we access the cube’s state attribute. The cube’s state attribute has the following child attributes:

-----------------------------------------------------------------------------
| condynsate.simulator.dataclasses.BodyState                                |
-----------------------------------------------------------------------------
Attributes
----------
position : 3 tuple of floats
    The (x,y,z) position in world coordinates.
orientation : 4 tuple of floats
    The wxyz quaternion representation of the orientation in world
    coordinates.
ypr : 3 tuple of floats
    The (z-y'-x' Tait–Bryan) Euler angles in radians ordered as
    (yaw, pitch, roll).
velocity : 3 tuple of floats
    The (x,y,z) velocity in world coordinates.
omega : 3 tuple of floats
    The (x,y,z) angular velocity in world coordinates.
velocity_in_body : 3 tuple of floats
    The (x,y,z) velocity in body coordinates.
omega_in_body : 3 tuple of floats
    The (x,y,z) angular velocity in body coordinates.

To get the cube’s position in world coordinates, we access the position child attribute. Based on this position and the length of the cube, we can then calculate how high the cube is above the ground plane.

External forces are applied to simulator objects with the condynsate.simulator.objects.Body.apply_force function

-----------------------------------------------------------------------------
| condynsate.simulator.objects.Body.apply_force                             |
-----------------------------------------------------------------------------
Applies force to the center of mass of the body.

Parameters
----------
force : 3 tuple of floats
    The force being applied to the center of mass.
**kwargs

Keyword Args
------------
body : bool, optional
    A Boolean flag that indicates if the force argument is in
    body coordinates (True), or in world coordinates (False).
    The default is False.
draw_arrow : bool, optional
    A Boolean flag that indicates if an arrow should be drawn
    to represent the applied force. The default is False.
arrow_scale : float, optional
    The scaling factor, relative to the size of the applied force,
    that is used to size the force arrow. The default is 1.0.

Returns
-------
ret_code : int
    0 if successful, -1 if something went wrong.

External torques are applied to simulator objects with the condynsate.simulator.objects.Body.apply_torque function

-----------------------------------------------------------------------------
| condynsate.simulator.objects.Body.apply_torque                            |
-----------------------------------------------------------------------------
Applies external torque to the body.

Parameters
----------
torque : 3 tuple of floats
    The torque being applied.
**kwargs

Keyword Args
------------
body : bool, optional
    A Boolean flag that indicates if the torque argument is in
    body coordinates (True), or in world coordinates (False).
    The default is False.
draw_arrow : bool, optional
    A Boolean flag that indicates if an arrow should be drawn
    to represent the applied torque. The default is False.
arrow_scale : float, optional
    The scaling factor, relative to the size of the applied torque,
    that is used to size the torque arrow. The default is 1.0.

Returns
-------
ret_code : int
    0 if successful, -1 if something went wrong.
[8]:
# Run a 10 second simulation
while proj.simtime <= 10.:

    # Get the cube's height above the ground
    position = cube.state.position
    height = position[2] - 0.5

    # Calculate an upward force proportional to the cube's height and apply it to the cube
    upward_force = 9.81 + (1.0-height) * 0.1
    cube.apply_force((0, 0, upward_force), body=False, draw_arrow=True, arrow_scale=0.15)

    # Apply a small external torque to the cube
    cube.apply_torque((0.02, 0.02, 0.02), body=False, draw_arrow=True, arrow_scale=55.0)

    # Take a single simulation step
    proj.step(real_time=True, # Run the simulation in real time
              stable_step=False # Dynamically adjust the refresh rate for best total run time
             )

Note that after condynsate.Project.step is called, the forces and torques applied in the previous simulation step will not be applied in the next step. Therefore, we must call the apply_force and apply_torque functions before every time step in which we want to apply external inputs

Finally, we ensure all children threads exit gracefully.

[9]:
proj.terminate() # Returns 0 on success
[9]:
0

Challenge

This tutorial is now complete. For an added challenge, think of how you would modify the simulation loop so that the force applied is also porportional to the magnitude of the velocity of the cube.

[ ]: