Here we present the controllers capable of use by multiple joints.
This controller applies a small, constant torque while printing statements of relevance (command value/sign, torque sensor value/sign, angle sensor value/sign) to the Arduino Serial Monitor.
There are no user-defined parameters associated with this controller.
After you have built an exo, one of the first things to check is the motor directions and the signs (positive/negative) of any of your external sensors (torque, angle, etc.). Having correct motor directions is important as it allows the exo controller designers to focus on the control itself, knowing that, if calibrated properly before use, the exo assistance will always be in the designed directions. The same principle applies for the sensors involved with control. Since PID control is implemented in many of our exo controllers, it is crucial to make sure that the directions match. This article will elaborate how to conduct the direction calibration using the CalibrManager controller. On each exo, this calibration process only needs to be done once, its results will need to be manually saved on the SD card and will be loaded automatically every time the exo starts up.
When designing a controller, the values returned from the “calc_motor_cmd()” function determines the motor torque command (feedforward). To ease the controller design process, we propose that controller designers follow the following direction definitions for torque commands:
Ankle Joints: Positive for Dorsiflexion, and negative for Plantarflexion
Knee Joints: Positive for Extension, and negative for Flexion
Hip Joints: Positive for Flexion, and negative for Extension
Elbow joints: Positive for Flexion, and negative for Extension
In summary, this follows the right-hand rule.
Following the motor direction definitions, we need to verify that, when the motor supplies a positive torque command, the torque sensors, when zeroed properly, also returns a positive reading when the motor shaft is not spinning.
Set Calibration Manager as the default controller for the current exo.
Connect the Teensy to a computer through USB and open up the Arduino Serial monitor.
Connect the exo to the python GUI.
Power on the exo and start a new session.
Make sure you are securely holding the joint of interest (either with your hand or worn on your body) so that it can not freely move. If left unsecured the joint will actuate upon start which could cause injury.
Upon start, the Calibration Manager will send a positive torque command (3.5 Nm by default) to each motor.
Feel the direction that the torque is trying to actuate.
If the torque is not rotating in the positive direction (see above for the specific directions), power off the device, remove the SD Card, and flip the motor direction to its correct direction by modifying the appropriate “JointFlipMotorDir” in “Config.ini”.
After modifying all motor directions (if needed), start the test over again [once again making sure the device is secured], and this time focus on the sensor readings (e.g., torque, angle) shown on the Arduino Serial Monitor.
Again feel the direction that the torque is trying to actuate (it should now match the definitions above).
Now try to manually move the joint back to its neutral position (e.g., move the hip upright away from a flexed position into a neutral position).
If the corresponding sensor does not return a positive value, you will need to modify the SD card to flip the sensor direction.
Again, power off the device, remove the SD card, and flip the sensor direction by modifying the appropriate “JointFlipSensorDir” in “Config.ini”.
This controller applies a user defined sine wave that increases in frequency at a set rate. This can be used to characterize the bandwidth of the system.
Parameter index order can be found in ControllerData.h.
amplitude: Amplitude, in Nm, of the torque sine wave.
start_frequency: Starting frequency for the chirp
end_frequency: Ending frequency for the chirp
duration: The duration that you want the chirp to be applied
yshift: Shifts the center of the chirp if you want it to be something other than zero
use_pid: This flag turns PID on(1) or off(0)
p_gain: Proportional gain for closed loop control
i_gain: Integral gain for closed loop control
d_gain: Derivative gain for closed loop control
This controller attempts to apply a constant torque to the joint. This can be done with or without closed loop control.
Parameter index order can be found in ControllerData.h.
amplitude: Magnitude of the applied torque, in Nm
direction: Flag to flip the direction of the applied torque
alpha: Filtering term for exponentially wieghted moving average (EWMA) filter, used on torque sensor to cut down on noise. The lower this value the higher the delay caused by the filtering.
use_pid: This flag turns PID on(1) or off(0)
p_gain: Proportional gain for closed loop control
i_gain: Integral gain for closed loop control
d_gain: Derivative gain for closed loop control
This controller attempts to apply a constant torque at the joint for a set duration and for a set number of times.
Parameter index order can be found in ControllerData.h.
amplitude: Magnitude of the applied torque, in Nm
duration: Duration of the applied torque
repetitions: Number of times the torque is applied
spacing: Time between each application of torque
use_pid: This flag turns PID on(1) or off(0)
p_gain: Proportional gain for closed loop control
i_gain: Integral gain for closed loop control
d_gain: Derivative gain for closed loop control
alpha: Filtering term for exponentially wieghted moving average (EWMA) filter, used on torque sensor to cut down on noise.
This controller attempts to regulate the joint so that the output torque is zero. It can be operated with or without closed-loop control.
Parameter index order can be found in ControllerData.h.
use_pid: This flag turns PID on(1) or off(0)
p_gain: Proportional gain for closed loop control
i_gain: Integral gain for closed loop control
d_gain: Derivative gain for closed loop control
For implementation details specific to individual joints, refer to the subfolders for:
Ankle Controllers
Hip Controllers
Elbow Controllers
The subfolders (each with its own index.rst file) are linked above in the toctree. They provide additional documentation and examples for configuring and tuning controllers tailored to the specific dynamics of each joint.
OpenExo has builtin Proportional–integral–derivative (a control loop mechanism) support. High level controllers in OpenExo have the option to utilize our easy to use PID function for closed-loop control. An example of this function is torque control where the exoskeleton tracks the high-level controller’s prescribed torque and minimizes the error in the measured torque, compared to the prescribed torque.
How to use the OpenExo pid function:
PID is defined in the Controller class and can be used by any controller.
float motor_cmd = _pid(float cmd_ff, float measurement, float p_gain, float i_gain, float d_gain)
The dynamics of the exoskeleton system relies heavily on factors such as actuation, transmission and control. To achieve your desired exoskeleton tracking performance, we suggest tuning the PID gains while the exoskeleton is worn and used in environments similar to the real use environment. To tune the gains, we recommend the Ziegler–Nichols method:
Set the I and D gains to zero
Increase the P gain from zero and tune the P gain
Tune the I and D gains
Analog sensory data such as torque, FSR (force sensitive resistors), or angle are noisy and require filtering before being used by high-level controllers. We created an easy-to-use low-pass filter (Exponential Weighted Moving Average).
How to use the OpenExo EWMA function:
EWMA is defined in Utilities.cpp and can be used by any controller.
float new_filter_value = utils::ewma(float new_value, float old_filter_value, float alpha)
Sidenote: A lower “alpha” results in stronger filtering.
Provides a deterministic, time‑based gait phase for controllers when real gait estimation is unavailable or when running in simulation mode.
When simulation is disabled, the utility returns the live gait estimate from the system (percent_stance).
When simulation is enabled, it advances a virtual gait cycle based on elapsed time and wraps at 100%.
Uses a timer helper to accumulate elapsed microseconds.
Converts elapsed time to a percent gait value in the range [0, 100).
The simulated gait cycle duration is fixed at 1 second:
percent_gait = (elapsed_us % 1,000,000) / 1,000,000 * 100
The simulated signal is continuous and periodic, independent of sensor inputs.
Used by controllers that expose a “sim %gait” parameter flag.