© Copyright, 2025 G. Schaer.
SPDX-License-Identifier: GPL-3.0-only
Tutorial 3: Link Forces
Tutorial Description
This tutorial covers creating a condynsate Project in which forces are applied to specific links of an articulated body. We will cover:
Applying forces to individual links of a .urdf object.
Measuring the state of individual links of a .urdf 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 2.
[2]:
# Create the project
proj = Project()
You can open the visualizer by visiting the following URL:
http://127.0.0.1:7030/static/
[3]:
proj.visualizer.set_axes(False) # Returns 0 on success
[3]:
0
[4]:
# Load a pendulum object and set its initial position to resting on the ground
pendulum = proj.load_urdf(assets['pendulum.urdf'],
fixed=True
)
[5]:
# Set the initial angle of the pendulum arm
pendulum.joints['base_to_arm'].set_initial_state(angle=0.1745) # All angles in condynsate are radians
[5]:
0
[6]:
# Refresh the visualizer to show changes to the pendulums's position and angle
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 3 steps
Get the pendulum’s mass’s position
Apply a force to the pendulum mass proportional to the mass’s position
Take a single simulation step
To access the links of a body, we use the links attribute. The links attribute is a dictionary that indexes link names (as defined in the .urdf files from which bodies are loaded) to members of the condynsate.simulator.objects.Link class.
To get state information about links, we access a links’s state attribute. The state attribute has the following child attributes:
-----------------------------------------------------------------------------
| condynsate.simulator.dataclasses.LinkState |
-----------------------------------------------------------------------------
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 pendulum’s joint angle, we access the angle child attribute.
To apply a force to a link, we call the condynsate.simulator.objects.Link.apply_force function
-----------------------------------------------------------------------------
| condynsate.simulator.objects.Link.apply_force |
-----------------------------------------------------------------------------
Applies force to the center of mass of a link.
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.
[8]:
# Run a 10 second simulation
while proj.simtime <= 10.:
# Get the pendulum's mass's x position
link_state = pendulum.links['mass'].state
x_position = link_state.position[0]
# Apply a force to the pendulum's mass
force = (-20.0 * x_position, 0, 0)
pendulum.links['mass'].apply_force(force, draw_arrow=True, arrow_scale=0.25)
# 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 exactly like the previous tutorial, after condynsate.Project.step is called, the forces applied in the previous simulation step will not be applied in the next step. Therefore, we must call apply_force function before every time step in which we want to apply link forces.
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 to implement a PD controller where the force is applied in the body coordinates of the mass, instead of world coordinates.
[ ]: