Modeling the actuator#

Before you start getting some data, you need to model your actuator in BAM software. Modeling an actuator consist in subclassing BAM’s Actuator class and implementing the required methods. The following questions need to be addressed:

  • How is the servomotor turning the state (encoder position, velocity) into torque ?
    • What control law is in play ?

    • What limits or smoothing is applied in the servo firmware ?

  • What important values need to be identified to model the actuator ?
    • In particular, you might want to have some initial guess ranges for those values

Fortunately, the answers to those questions are often the same. That is why BAM already provides few subclasses of Actuator:

Voltage controlled actuator#

If the command produced in firmware is a voltage, then you should subclass VoltageControlledActuator. Find examples of such subclassing in Dynamixel’s actuator.py.

Here are the constructor parameters:

  • vin: the supply voltage for the actuator

  • kp: the proportional gain of the firmware control law, we recommend keeping it in firmware unit, meaning that you can use the exact same value as the one documented by the vendor

  • error_gain: this gain should be such that u = error_gain * kp * (target - position) is the voltage command sent to the motor. It is a constant that correct the firmware unit into a voltage unit

  • max_pwm: the maximum PWM value (between 0 and 1) that the firmware can set the PWM to. If you don’t know, you can set it to 1.

  • max_current: if the firmware has a current limit, you can set it here. If you don’t know, you can set it to None.

And here are the typical model parameters you’ll want to identify:

  • \(k_t\): the torque constant of the motor, in \(N.m/A\)

  • \(R\): the resistance of the motor, in \(\Omega\)

  • \(I\): the apparent inertia of the motor, in \(kg.m^2\)

See below for initial guesses and ranges for those parameters.

Current controlled actuator#

If the command produced in firmware is a current, then you should subclass CurrentControlledActuator. Find examples of such subclassing in Dynamixel’s actuator.py.

Here are the constructor parameters:

  • vin: the supply voltage for the actuator

  • kp: the proportional gain of the firmware control law, we recommend keeping it in firmware unit, meaning that you can use the exact same value as the one documented by the vendor

  • error_gain: this gain should be such that i = error_gain * kp * (target - position) is the current command sent to the motor.

And here are the typical model parameters you’ll want to identify:

  • \(k_t\): the torque constant of the motor, in \(N.m/A\)

  • \(R\): the resistance of the motor, in \(\Omega\)

  • \(I\): the apparent inertia of the motor, in \(kg.m^2\)

  • \(I_{max}\): the maximum current that the firmware can send to the motor, in \(A\)

Initial guesses and ranges#

When declaring a model, you need to provide initial guesses and ranges for the parameters. For example:

# Torque constant [Nm/A] or [V/(rad/s)]
self.model.kt = Parameter(0.7, 0.25, 1.5)

This tells BAM that the initial guess for \(k_t\) is 0.7, and that it should be searched in the range [0.25, 1.5].

Torque constant \(k_t\)#

Torque constant \(k_t\) maps current in \(A\) to torque in \(N.m\). It turns out that the same constant also maps the motor velocity in \(rad/s\) to back-EMF voltage in \(V\).

Thus, to estimate \(k_t\), you can apply a known voltage to the motor and measure its free speed \(\omega\). The guess for \(k_t\) is then given by \(k_t = \frac{V}{\omega}\).

Don’t hesitate to take multiple samples and use a method like least square to estimate the torque constant more accurately.

Resistance \(R\)#

Motor resistance is usually small value, that you will not get accurately approximated with measuring tool like multimeter. The best way to estimate it is to measure the current draw of the motor when applying a known voltage command, while keeping the motor blocked. Then, you can use Ohm’s law to estimate the resistance \(R = \frac{V}{I}\).

Apparent inertia \(I\)#

Apparent inertia is the rotor inertia of the motor, which is multiplied by the square of the gear ratio. While the reduction ratio is often known, it is not easy to guess the value of the rotor inertia.

This value can typically be adjusted across multiple BAM fits, monitor if the value saturates either up or low during identification and ajust the range accordingly.

Rig-specific parameters#

Beyond the motor parameters above, every BAM model carries parameters that describe the test bench rather than the motor. You do not need to declare them: they are added by default to all models (m1 through m6), always identified, and written to the output JSON like any other parameter.

Position offset \(q_{offset}\)#

The zero of the servo encoder rarely coincides exactly with the physical reference of the pendulum (the vertical). \(q_{offset}\) is a constant angle added to the measured position everywhere the model sees the joint angle, so that gravity and the control law are evaluated at the true physical angle:

# Offset of the motor (test bench calibration error) [rad]
self.q_offset = Parameter(0.0, -0.1, 0.1)

It is a rig calibration term, not a motor property: it captures how the actuator was mounted on that particular bench. Fitting it (typically a few tens of milliradians) prevents a mounting misalignment from being wrongly compensated by the friction terms.

Note

\(q_{offset}\) is a single value fitted globally across all logs, which assumes the whole dataset shares the same mounting bias. If the actuator is remounted between recordings, that assumption no longer holds.

Command delay#

There is always some latency between the moment a target position is commanded and the moment the actuator actually reacts to it: the serial-bus round-trip plus the servo’s internal control period. This transport delay is not part of the motor equation, but it does shift the whole response in time, so BAM models it as a rig-level parameter:

# Command/communication delay [s]
self.command_delay = Parameter(0.0, 0.0, 0.05)

During a rollout the recorded goal-position sequence is delayed by command_delay seconds before being fed to the control law. The shift is fractional (linear interpolation between the two neighbouring samples), so the delay can be identified to sub-timestep precision; a value of 0 reduces exactly to the undelayed goal, which keeps older parameter files backward-compatible.

This is a property of the communication bus and firmware scheduling of the rig, not of the motor. Identifying it prevents the phase lag it introduces from being wrongly absorbed by the apparent inertia or the viscous friction.

Note

The electronics and communication hardware used during identification are usually different from those of the real system (bus, baud rate, controller, cabling, control period). The identified delay can sometimes be reused, but it will generally differ on the target setup and may need to be re-estimated there.