Unit contract¶
Every value in this library says what it is in its own name. position_rad is radians;
position_deg is degrees; velocity_erpm is electrical RPM. There is no bare
position, because the one thing worse than an ambiguous unit is a plausible one.
Wire units and where they come from¶
Quantity |
Wire |
Library type |
Conversion |
|---|---|---|---|
MIT position |
|
rad, output side |
|
MIT velocity |
|
rad/s, output side |
|
MIT torque |
|
N·m, output side |
|
MIT temperature |
|
°C |
|
Servo position |
|
degrees, mechanical † |
|
Servo velocity |
|
ERPM |
see below |
Servo current |
|
A (q-axis) |
— |
† Which side of the gearbox servo position refers to is not documented. position_deg
always works; output_rad raises UnresolvedFrameError until you settle it on the bench
(step B7) and record it with spec.evolve(...).
ERPM ↔ rad/s¶
rad/s at output = ERPM · 2π / (60 · pole_pairs · gear_ratio)
For the AK40-10 (14 pole pairs, 10:1) one ERPM is 7.480e-4 rad/s. This number is
per-motor and there is no universal constant: TMotorCANControl hard-codes 5.82e-4 for
every model, which is 22% wrong for the AK40-10 and 5% wrong even for the AK80-9 it was
derived on.
If pole_pairs or gear_ratio is unknown for your spec, the conversion raises
instead of guessing.
Rotor versus output¶
output = rotor / gear_ratio for angles and speeds; torque is the inverse. Accessors are
explicit about which side they mean:
state.position_rad # output shaft
state.position_rotor_rad # before the gearbox
MIT position and velocity are output-side. For the AK40-10 this is confirmed rather than assumed: the velocity field’s 45.5 rad/s is 434.5 rpm, which is the datasheet’s 435 rpm no-load speed.
Torque and current¶
Kt is rotor-side, so output torque is amps · Kt · gear_ratio. The AK40-10’s
datasheet is self-consistent here: 7.3 A × 0.056 × 10 = 4.09 N·m against a published
4.1 N·m peak.
The MIT reply’s third field is torque, not current — the manual’s own unpack_reply
names it so. state.torque_nm is what the wire carries. state.estimated_current_a
inverts an idealised lossless model and warns once while Kt is not Source.MEASURED.
MIT Kp and Kd are not in SI units¶
They are the firmware’s own gains, dimensionless to us, spanning 0–500 and 0–5. They are
not stiffness in N·m/rad or damping in N·m·s/rad, and nothing in this library converts
them. TMotorCANControl’s set_impedance_gains_real_unit(K, B) documents them as N·m/rad
and N·m·s/rad and then passes them through unconverted — the name is the bug.
Feed the derivative, not zero¶
The firmware computes:
tau = kp * (p_des - p) + kd * (v_des - v) + tau_ff
If you command a moving position while leaving velocity at 0, the kd term actively
fights the motion needed to follow it, and you get a following error of roughly
kd * v / kp. Measured on an AK40-10 tracking a ±0.5 rad sinusoid at 0.3 Hz
(peak 0.94 rad/s) with kp=20, kd=0.5:
command |
median following error |
|---|---|
|
23.33 mrad |
predicted |
23.6 mrad |
|
6.74 mrad |
Feeding the derivative cut the error 3.5×, and the prediction matched the measurement to 1%. The 6.74 mrad residual is close to the datasheet’s 5.24 mrad of gearbox backlash, so that is the mechanical floor rather than a tuning problem.
w = 2 * math.pi * freq
m.update(
position=amp * math.sin(w * t),
velocity=amp * w * math.cos(w * t), # not 0.0
kp=20.0,
kd=0.5,
)
Quantisation¶
No field can encode an exact zero. Each is a symmetric range over an even-sized field, so the midpoint sits half an LSB above zero:
Field |
LSB |
value of a commanded |
|---|---|---|
position |
0.381 mrad |
+0.191 mrad |
velocity |
22.2 mrad/s |
+11.1 mrad/s |
torque |
2.44 mN·m |
+1.22 mN·m |
The torque floor matters for a safe stop. On an AK40-10 it is 2% of the 60 mN·m break-away torque, so the output genuinely cannot move — but it is a floor, not a null.
Field range is not a limit¶
A FieldRange exists to be handed to the quantiser and carries bits. A
PhysicalLimits says what the motor can do and has no bits, so it can never reach a
codec. They are different types because across the AK line they differ in both
directions:
Motor |
MIT torque field |
datasheet peak |
binding |
|---|---|---|---|
AK40-10 |
±5.0 N·m |
4.1 N·m |
the motor |
AK80-9 V3.0 |
±18 N·m |
22 N·m |
the field |
ClampMode.EFFECTIVE (the default) clamps to min(field, physical) per field.