Robohouse ’26 Library
Contents

Chapter 13

From joint angles to step counts

4 sections · about 3 minutes

13.1 The conversion

This is the bridge between the kinematics and the hardware. The arithmetic is trivial; getting the conventions right is not.

microsteps=θjoint(degrees)360×200×microstepping×gear ratio\text{microsteps} = \frac{\theta_{\text{joint}}\,(\text{degrees})}{360} \times 200 \times \text{microstepping} \times \text{gear ratio}

The 200 is full steps per motor revolution. The microstepping is 32 on the PAROL6. The gear ratio converts joint revolutions to motor revolutions.

Working it through for each joint, with 200 × 32 = 6400 microsteps per motor revolution:

JointRatioMicrosteps per joint revMicrosteps per degreeAngle per microstep
J16.440,960113.780.00879° (31.6″)
J220128,000355.560.00281° (10.1″)
J318.1115,840321.780.00311° (11.2″)
J4425,60071.110.01406° (50.6″)
J5425,60071.110.01406° (50.6″)
J61064,000177.780.00563° (20.3″)

A single microstep on joint 1 rotates the arm by 0.0088°, which at the full 400 mm reach moves the tool by about 0.061 mm. On joint 2 it is 0.0028°, or about 0.020 mm at full reach. Every joint's quantisation is comfortably below the arm's quoted 0.1 mm repeatability.

Which is the point from Chapter 5 with numbers behind it: microstep resolution is not what limits this machine's accuracy. Backlash, belt stretch, structural flex in printed PETG, and thermal expansion all dominate, and that is where the effort goes if you want a more accurate arm.

13.2 Rounding, and why you must accumulate

The conversion produces a non-integer number of microsteps, and you cannot send a fraction of a step. If you round each move independently, the rounding errors accumulate over thousands of moves and the arm slowly drifts away from where it thinks it is.

The fix is to keep the authoritative position as an integer microstep count per joint, and to convert the target angle to an integer microstep count, then command the difference. The residual fraction never accumulates because you never store a fractional position — the integer count is the truth, and the angle is derived from it, not the other way round.

STEPS_PER_DEG = [113.778, 355.556, 321.778, 71.111, 71.111, 177.778]

def angle_to_steps(joint, degrees):
    return int(round(degrees * STEPS_PER_DEG[joint]))

def steps_to_angle(joint, steps):
    return steps / STEPS_PER_DEG[joint]

Do the same on the Teensy side, with int32_t counters. A 32-bit signed integer holds ±2.1 billion microsteps, which at 128,000 per revolution of joint 2 is over sixteen thousand revolutions.

13.3 Direction and zero

Two per-joint constants complete the picture: a direction sign and a zero offset.

The direction sign accounts for how the motor happens to be wired and mounted, and whether positive joint rotation corresponds to positive or negative step direction. Keep it as an explicit +1 or −1 in a configuration table rather than fixing it by swapping motor wires, so that the reason is still visible in six months.

The zero offset is the microstep count corresponding to the joint's defined zero. After homing, you set the counter to the value corresponding to the known angle of the home position, and everything else follows. The PAROL6's published joint limits give both the range and the standby position for each joint, and those are the numbers to encode.

Keep all of this — steps per degree, direction sign, home angle, soft limits, maximum velocity, maximum acceleration, driver current — in a single per-joint configuration structure. One table, one place to change things, one thing to print when you are debugging.

13.4 Soft limits

Enforce joint limits in software, in microstep units, at the lowest level of the motion pipeline. The planner, the IK, and the host software should all check too, but the last check belongs in the step generator, where nothing can bypass it.

The check is one comparison per axis per segment. In exchange, no bug in the planner, no corrupted command from the host, and no arithmetic error in the IK can drive a joint past its mechanical stop — which on a printed arm is printed plastic.