Skip to content

Best Practices

Softstart

In robotic prostheses (as well as in general robotics), startup and initialization can often produce abrupt changes in motor torque that are undesirable. These sudden step changes in torque can be unsettling to the user and unececssarily hard on the hardware. To address this, we recommend implementing a softstart mechanism. This approach ensures a smooth ramp-up of torque at startup by scaling the control parameters, such as torque, impedance gains, or position gains, over a specified period.

Although the opensourceleg library does not include a built-in softstart feature, it is straightforward to implement using the SaturatingRamp class from the math submodule. Below, we outline the general procedure and provide an example script for a position control implementation. Similar effects for impedance, torque, and velocity control can be achieved following the same steps.

Implementation Steps

  1. Define a SaturatingRamp Instance Create an instance of the SaturatingRamp class from the opensourceleg.math.math module, specifying the desired softstart duration (e.g., SOFT_START_TIME = 1.5 seconds).

  2. Scale Control Parameters During each iteration of your control loop, use the output of the SaturatingRamp instance to scale your control parameters (e.g., torque commands, impedance gains, or position gains). After the ramp duration, the SaturatingRamp object outputs a value of 1, leaving your parameters unchanged for the remainder of the execution.

Example: Position Control with Softstart

Here, we walk through an example script demonstrating softstart in a position control paradigm.

Key Code Snippets

  1. Initialize the SaturatingRamp Instance The following snippet shows how to create a SaturatingRamp instance with the desired softstart time:
        soft_start_ramp = SaturatingRamp(SOFT_START_TIME)
    
  2. Scale Control Gains During each loop iteration, call the ramp's update method and scale the control gains with the result:
            ss_scale = soft_start_ramp.update(t)
            actpack.set_position_gains(
                DEFAULT_POSITION_GAINS.kp * ss_scale,
                DEFAULT_POSITION_GAINS.kd * ss_scale,
                DEFAULT_POSITION_GAINS.ki * ss_scale,
            )
            command_position = start_position + (1 / 2) * np.pi
            actpack.set_output_position(value=command_position)

Here is the full script:

import numpy as np

from opensourceleg.actuators.base import CONTROL_MODES
from opensourceleg.actuators.dephy import DEFAULT_POSITION_GAINS, DephyActuator
from opensourceleg.logging.logger import Logger
from opensourceleg.math.math import SaturatingRamp
from opensourceleg.utilities.softrealtimeloop import SoftRealtimeLoop

FREQUENCY = 1000
DT = 1 / FREQUENCY
GEAR_RATIO = 1.0
SOFT_START_TIME = 1.0


def softstart_position_control():
    position_logger = Logger(
        log_path="./logs",
        file_name="position_control",
    )
    actpack = DephyActuator(
        port="/dev/ttyACM0", gear_ratio=GEAR_RATIO, frequency=FREQUENCY, debug_level=0, dephy_log=False
    )
    clock = SoftRealtimeLoop(dt=DT)
    soft_start_ramp = SaturatingRamp(SOFT_START_TIME)

    with actpack:
        actpack.set_control_mode(mode=CONTROL_MODES.POSITION)

        actpack.update()
        start_position = actpack.output_position

        for t in clock:
            ss_scale = soft_start_ramp.update(t)
            actpack.set_position_gains(
                DEFAULT_POSITION_GAINS.kp * ss_scale,
                DEFAULT_POSITION_GAINS.kd * ss_scale,
                DEFAULT_POSITION_GAINS.ki * ss_scale,
            )
            command_position = start_position + (1 / 2) * np.pi
            actpack.set_output_position(value=command_position)

            actpack.update()

            position_logger.info(f"Time: {t}; \
                                 Command Position: {command_position}; \
                                 Output Position: {actpack.output_position}")


if __name__ == "__main__":
    softstart_position_control()