Status: Draft (2026-10-18), Measured. Numbers are calculated using Python 3.9.6 and numpy 2.0.2 on a laptop (Apple M1 Max, macOS 26.5). Numerical simulation values. The physical constants of the joints, arms, and motors are assumed values, and nothing has been measured with actual motors and controllers (hardware unmeasured). Raw data and code: working directory
measure/robot-arm-basics/ofm09_control.py,out/m09_control.json. References were accessed on 2026-10-18. Writing rules: Measured data is the default, unmeasured items are marked as 「Unmeasured」, and unverified items are marked as 「Unverified」.
Table of Contents
- 1. Intuition: Two Ways of Following
- 2. Diagram: Layers of Control
- 3. Calculations
- 4. Measurement (Simulation)
- 5. Limitations
- 6. Summary
- References
1. Intuition: Two Ways of Following
Think of a driver following the car ahead. One driver only watches the distance to the car ahead: stepping on the pedal when it gets further and releasing it when it gets closer. They are always a step behind. The other driver looks at the brake lights and road signs to know what the car ahead will do in advance. Thus, they press the pedal before the distance opens up. The former is feedback, and the latter is feedforward.
The trajectory of the table tennis robot was already fully calculated in Part 8. Every moment, the desired position, velocity, andaccelerationare known. Therefore, there is no reason not to use feedforward. Feedback only needs to correct as much as the model is wrong. This article uses simulations to measure how large that difference is, and why the control period and the current loop inside the motor are important.
2. Diagram: Layers of Control
- The trajectory generator(Part 8) outputs q_r, q̇_r, and q̈_r.
- The position and velocity loopdetermines torque based on the error and adds the torque calculated via inverse dynamics. The simulations in this series run at 1 kHz.
- The current loopconverts torque commands into motor currents. Since the torque of a brushless motor is proportional to the current i_q in the direction orthogonal to the rotor magnets, 3-phase currents are transformed into the rotor-reference coordinate system (d-q axes) to control only i_q. This is FOC (field oriented control).
Looking at open-source motor controllers, the periods differ across layers. The documentation for moteus states that both the position loop and current loop run at the switching frequency (default 30 kHz). ODrive 0.6.12 documentation describes control loop iteration counters and assumes an 8 kHz control loop frequency. Robot controllers above that are typically much slower. The table tennis robot in Koç et al. 2018 issued commands at a 500 Hz real-time control rate, Google 2023's learning policy at 100 Hz, and Google DeepMind 2024's low-level skills at 50 Hz.
3. Calculations
3.1 PID and Feedforward
PD: τ = K_p e + K_d ė e = q_r − q
PID: τ = K_p e + K_d ė + K_i ∫e dt
PD + 앞먹임: τ = J q̈_r + b q̇_r + τ_c sign(q̇_r) + K_p e + K_d ė
└ 역동역학(관성·점성·쿨롱 마찰) ┘ └ 되먹임 ┘
The gains were set according to the inertia J and target bandwidth ω_n: K_p = J ω_n², K_d = 2 J ω_n (critical damping). All comparisons use the same ω_n = 2π·20 Hz.
3.2 Computed Torque Control
When an arm has multiple joints, the acceleration of one joint exerts force on another (inertial coupling). Centrifugal force, Coriolis force, and gravity also vary depending on the posture. The equation of motion for the entire arm is as follows.
M(q) q̈ + C(q, q̇) q̇ + g(q) = τ
Computed torque control uses this equation directly to cancel out the couplings. Equation (11.35) in Modern Robotics is in this form, and the same book notes that K_i is often set to 0 in practice.
τ = M̃(q) (q̈_r + K_p e + K_d ė) + C̃(q, q̇) q̇ + g̃(q) (~ 는 모델 값)
If the model is accurate, the error for each joint follows the independent second-order differential equation ë + K_d ė + K_p e = 0.
3.3 Current Loop
The q-axis current is a first-order circuit with phase resistance R and inductance L. Setting the PI gains to K_p = L ω_c and K_i = R ω_c cancels the pole of the circuit, resulting in a first-order response with bandwidth ω_c. Since digital controllers output the voltage calculated from measured currents in the next cycle , if ω_c is too high relative to the loop period, oscillations occur due to this single-cycle delay.
3.4 Simulation Setup
- Single joint: Inertia J = 0.015 kg·m², viscous friction b = 0.02 N·m·s, Coulomb friction 0.1 N·m, torque limit 6 N·m. Simulation integration uses 20 µs intervals; the controller reads position and velocity every cycle and outputs the calculated torque in the next cycle (one-cycle delay).
- Reference trajectory: Striking trajectory from Part 8. Passes 0.6 rad in 0.12 s, reaching 10 rad/s (acceleration 0), holds for 0.1 s, then stops for 0.1 s.
- Two-link vertical arm: Link lengths 0.40 and 0.35 m, masses 1.2 and 0.7 kg, with gravity. Both joints simultaneously pass through the striking posture at 6 and 8 rad/s in 0.15 s and stop in 0.1 s. The maximum inverse dynamics torque for this trajectory was 39.9 N·m for joint 1 and 17.4 N·m for joint 2.
4. Measurement (Simulation)
4.1 Controller and Control Period
| Period | Controller | Max Error | RMS Error | Position Error at Strike | Velocity Error at Strike |
|---|---|---|---|---|---|
| 1 kHz | PD | 8.5 mrad | 5.1 mrad | 4.3 mrad | −0.20 rad/s |
| 1 kHz | PID | 10.3 | 5.1 | −1.0 | −0.22 |
| 1 kHz | PD + Feedforward | 0.42 | 0.28 | −0.29 | −0.007 |
| 2 kHz | PD + Feedforward | 0.21 | 0.14 | −0.15 | −0.003 |
| 500 Hz | PD + Feedforward | 0.86 | 0.46 | −0.58 | −0.014 |
| 200 Hz | PD | 39.9 | 17.5 | −2.6 | 2.25 |
| 200 Hz | PD + Feedforward | 34.8 | 14.4 | 1.1 | 3.37 |
- A single feedforward term reduced the error by over 20-fold. PD falls behind throughout the acceleration phase because it requires an error to generate torque. Feedforward generates the required torque in advance.
- The integral term (PID) actually performed worse. In fast movements lasting 0.2 seconds, the integral term accumulates and releases late, causing overshoot. Integral control is a tool to eliminate slowly varying biases like gravity, not a tool for fast trajectory tracking.
- The period changes the error almost linearly until it collapses past a certain point. With feedforward, the maximum error doubled at each step from 2 kHz → 1 kHz → 500 Hz, registering 0.21 → 0.42 → 0.86 mrad. This is because the one-cycle delay directly becomes the error. At 200 Hz (5 ms), there were only 24 control opportunities during the 0.12-second swing, causing feedforward to output in a stair-step manner, and the 5 ms delay interacted with the 20 Hz gain to cause severe oscillation. The strike-moment velocity error of 3.4 rad/s translates to about 1.7 m/s for a racket with a 0.5 m arm length, which would cause the landing point to miss by over 1 m if linearly scaled from the "10 cm per 0.1 m/s" rule in Part 7.
What happens if we increase the gains? A PD controller with ω_n doubled to 2π·40 Hz reduced the maximum error from 8.5 to 2.2 mrad at 1 kHz, but nearly diverged to 134 mrad at 200 Hz. Increasing gains in a slow loop is dangerous, and even in a fast loop, it is inferior to feedforward.
Feedforward with an inaccurate model was also measured. The maximum error for feedforward (1 kHz) with inertia underestimated or overestimated by 20% was 1.8 and 1.9 mrad. Although 4 times worse than the accurate model (0.42), it is still 4 times better than PD (8.5).
4.2 Effect of Current Loop Delay on the Position Loop
The above experiments assumed that the commanded torque was produced instantly. In reality, it takes time for the current loop to generate torque. Assuming torque is produced with a first-order delay of bandwidth f_i, the error of the 1 kHz PD + feedforward was measured again.
| Current (Torque) Bandwidth | Max Error | RMS Error | Velocity Error at Strike |
|---|---|---|---|
| No Delay | 0.42 mrad | 0.28 mrad | −0.007 rad/s |
| 1,000 Hz | 0.46 | 0.31 | −0.007 |
| 300 Hz | 0.58 | 0.35 | −0.009 |
| 100 Hz | 0.90 | 0.48 | −0.014 |
| 50 Hz | 1.42 | 0.68 | −0.021 |
When the current loop bandwidth is similar to the position loop period (1 kHz), there is almost no difference, but when it drops below 100 Hz, the error increases two- to three-fold. This numerical result embodies the principle of hierarchical control: "The inner loop must be sufficiently faster than the outer loop."
4.3 Current Loop Itself: How Fast Can It Go at 30 kHz
The q-axis current with a phase resistance of 0.2 Ω and inductance of 100 µH was step-responded to 10 A using 30 kHz PI control (voltage limit 48 V, one-cycle delay).
| Target Bandwidth | 10→90% Rise Time | Ideal 1st-Order Response | Overshoot |
|---|---|---|---|
| 500 Hz | 578 µs | 700 µs | 0% |
| 1 kHz | 212 µs | 350 µs | 0% |
| 2 kHz | 63 µs | 175 µs | 16.5% |
| 4 kHz | 30 µs | 88 µs | 84% |
| 8 kHz | 17 µs | 44 µs | 200% |
When the target bandwidth exceeded 1/15th of the loop frequency (2 kHz), overshoot occurred, and at 1/4th (8 kHz), it practically oscillated. This is because the single-cycle delay (33 µs) erodes phase margin. Consequently, the practical bandwidth achievable with a 30 kHz current loop is 1 to 2 kHz, meaning the position loop above it runs at hundreds of Hz to 1 kHz, and robot policies above that run at tens to hundreds of Hz. Each layer slows down by a factor of roughly 10.
4.4 Two-Link Arm: Coupling and Gravity
| Controller (1 kHz, torque limit 60 N·m) | Max Error | RMS | Position / Velocity Error at Strike |
|---|---|---|---|
| PD | 41.0 mrad | 20.0 | 30.7 mrad / 0.95 rad/s |
| PD + Gravity Compensation | 37.8 | 18.8 | 27.9 / 0.92 |
| Computed Torque (Exact Model) | 1.7 | 0.83 | 1.0 / 0.11 |
| Computed Torque (Mass 20% Underestimated) | 10.5 | 4.7 | 8.4 / 0.28 |
| Computed Torque (Mass 20% Overestimated) | 7.0 | 3.1 | 7.0 / 0.03 |
- Compensating only for gravity showed almost no improvement. What dominates fast swings is not gravity, but inertia and coupling terms.
- Computed torque achieved 1/24th of the PD error, and even with a 20% mass error, it performed 4 to 6 times better than PD.
Lowering the torque limit to 30 N·m (less than the 40 N·m required by the trajectory) caused both computed torque (max error 264 mrad) and PD (218 mrad) to collapse. A controller cannot execute an impossible trajectory. When creating a trajectory (Part 8), you must first check whether the inverse dynamics torque is within limits.
5. Limitations
- Everything is a simulation. Reducer backlash, belt elasticity, encoder noise and quantization, communication delay jitter, and motor torque-speed curves were not included (unmeasured).
- The control gains were set using a single rule. Tuning the gains separately for each cycle could improve the 200 Hz results.
- The current loop was simplified to a single q-axis RL circuit. Back EMF, d-q axis coupling, and PWM voltage quantization were not addressed.
6. Conclusion
- For a robot that knows its trajectory in advance, the greatest improvement comes from feedforward. Adding inverse dynamics to the same PD reduced the error by more than 20 times.
- In multi-joint arms, computed torque cancels out coupling. Even if the model is 20% off, it is much better than PD.
- A control rate of around 1 kHz is sufficient, whereas 200 Hz failed to track a 0.1-second swing. The current loop must be at least 10 times faster than that, and the practical bandwidth of a 30 kHz current loop was 1 to 2 kHz.
- Numbers to look for when choosing open-hardware components: the position loop rate of the motor controller and whether it supports feedforward torque input (moteus's position mode control equation includes a feedforward torque term), as well as the communication rate between the higher-level controller and the motor controller.
Titi's note. You already know where the racket is going, so why wait until an error occurs? Just generate the required force beforehand, and use feedback only to correct for the discrepancy!
References
All materials were accessed on 2026-10-18.
- Lynch, Park, Modern Robotics, 2017, open preprint, Chapter 11 (11.4 Torque Input Control, Equation 11.35).
- mjbots, moteus docs/reference/theory.md(position/current loop structure, default 30 kHz).
- ODrive Robotics, ODrive API Reference 0.6.12(
n_evt_control_loop(assuming 8 kHz as explained). - Koç, Maeda, Peters, RAS 105 (2018), author's open version, Section 6.2 (500 Hz control).
- D'Ambrosio et al., arXiv:2309.03315(policy 100 Hz, ABB EGM 248 Hz), arXiv:2408.03906(low-level tech 50 Hz).