Shaper > Control

Shaper

Control

Use Covalent Shaper during runtime by loading the identified model, shaping the desired joint command, and sending the shaped command to the robot manufacturer's SDK.

Before using this page, complete Calibration and Identification so that src/robot/models/current/shaper contains the identified Shaper model files.

Complete Example

The complete runnable example is in:

text
src/robot/example_usage/shaper/shaper_example_usage.py

Run this example after completing First Steps, with the Python environment active and the package installed.

From the repository root, run:

bash
python3 src/robot/example_usage/shaper/shaper_example_usage.py

The example loads the Shaper model, generates a point-to-point command, compares unshaped and shaped simulated responses, and opens two plots:

  • Command position, velocity, and acceleration.
  • Simulated plant response for the unshaped and shaped commands using models loaded from robot/example_usage/shaper.

The terminal output should include a summary similar to:

text
Covalent Shaper example complete. Dominant simulation mode: wn=... rad/s, zeta=... Unshaped residual vibration: ... rad Always-on Shaper residual vibration: ... rad Residual-tail offline Shaper residual vibration: ... rad Sample-by-sample stream Shaper residual vibration: ... rad

Inputs

The Shaper needs:

  • The controller sample time, in seconds.
  • The identified model folder, usually src/robot/models/current/shaper.
  • The robot URDF file.
  • The number of shaped axes.
  • The total number of robot joints.
  • Desired joint position, velocity, and acceleration commands.

Example 1: Shape a Full Known Trajectory

Use this path when the full joint trajectory is known before the move starts. The Shaper receives the complete command and returns shaped position, velocity, and acceleration arrays.

python
import numpy as np import robot_sdk # Replace with the SDK package for the robot you are using. from reforge_core.control.shaper import ShaperInterface sample_time_s = 0.004 num_axes = 3 num_joints = 6 shaper = ShaperInterface( sample_time=sample_time_s, model_directory="src/robot/models/current/shaper", urdf_filepath="src/robot/urdf/<robot>.urdf", num_axes=num_axes, num_joints=num_joints, ) # Desired joint trajectory from your planner or robot SDK: [N, num_joints]. desired_trajectory_rad = np.asarray(robot_sdk.get_planned_joint_trajectory()) time_vector_s = np.arange(desired_trajectory_rad.shape[0]) * sample_time_s desired_velocity_rad_s = np.gradient( desired_trajectory_rad, sample_time_s, axis=0, ) desired_acceleration_rad_s2 = np.gradient( desired_velocity_rad_s, sample_time_s, axis=0, ) shaped_trajectory = shaper.process_trajectory( command=desired_trajectory_rad, command_dot=desired_velocity_rad_s, command_ddot=desired_acceleration_rad_s2, time_vector=list(time_vector_s), vibration_shaping_weight=1.0, vibration_weight_transition_s=0.0, residual_shaping_strategy=None, finalize_tail=False, ) robot_sdk.send_joint_trajectory( positions=shaped_trajectory.positions, velocities=shaped_trajectory.velocities, accelerations=shaped_trajectory.accelerations, sample_time=sample_time_s, )

Use this example when your planner or robot SDK can provide the full command before execution.

Example 2: Shape the Final Part of a Known Trajectory

Use this path when the full trajectory is known, but you want Shaper to focus on the residual vibration near the end of the move. This can reduce unnecessary command delay earlier in the move.

python
residual_tail_shaper = ShaperInterface( sample_time=sample_time_s, model_directory="src/robot/models/current/shaper", urdf_filepath="src/robot/urdf/<robot>.urdf", num_axes=num_axes, num_joints=num_joints, ) residual_tail_trajectory = residual_tail_shaper.process_trajectory( command=desired_trajectory_rad, command_dot=desired_velocity_rad_s, command_ddot=desired_acceleration_rad_s2, time_vector=list(time_vector_s), vibration_shaping_weight=1.0, vibration_weight_transition_s=0.0, residual_transition_margin_s=0.0, finalize_tail=False, ) robot_sdk.send_joint_trajectory( positions=residual_tail_trajectory.positions, velocities=residual_tail_trajectory.velocities, accelerations=residual_tail_trajectory.accelerations, sample_time=sample_time_s, )

Use this example when the end of the motion is the main vibration-sensitive part of the move.

Example 3: Shape a Sample-by-Sample Stream

Use this path when the full trajectory is not known ahead of time. The online planner generates one command sample, Shaper modifies that sample, and the shaped command is sent to the robot SDK before the next control cycle.

python
streaming_shaper = ShaperInterface( sample_time=sample_time_s, model_directory="src/robot/models/current/shaper", urdf_filepath="src/robot/urdf/<robot>.urdf", num_axes=num_axes, num_joints=num_joints, ) while robot_sdk.control_loop_is_running(): # Replace this with your online planner, joystick command, force controller, # task-space controller, or robot SDK trajectory generator. command_rad, velocity_rad_s, acceleration_rad_s2 = planner.next_joint_sample() shaped_sample = streaming_shaper.process_sample( command=command_rad, command_dot=velocity_rad_s, command_ddot=acceleration_rad_s2, vibration_shaping_weight=1.0, vibration_weight_transition_s=0.0, ) robot_sdk.send_joint_command( positions=shaped_sample.positions, velocities=shaped_sample.velocities, accelerations=shaped_sample.accelerations, )

Use this example when commands are generated online. The Shaper does not receive the complete future trajectory in this mode; it only receives the current command sample.