Robotics
Real-Time Motion Control: Servo Drives, EtherCAT, and the Pursuit of Determinism
Behind every smooth robotic motion is a control loop that must close on time, every time. A look at the servo drives, fieldbuses, and real-time discipline that separate precise machines from jittery ones — no machine learning involved.
Real-time motion control is one of the few areas of software engineering where the average case is irrelevant. A control loop that meets its deadline 99.9% of the time is not 99.9% correct — it is a machine that produces a defect, or a collision, several times per shift. The engineering discipline here is entirely about bounding the worst case.
The cascade, and why bandwidth separation is not optional
Servo control is conventionally three nested loops: an innermost current (torque) loop, a velocity loop around it, and an outermost position loop. Each loop treats the one inside it as an ideal actuator, and that approximation only holds if the inner loop is substantially faster than the outer.
| Loop | Rate | Bandwidth | Runs on |
|---|---|---|---|
| Current / torque | 8–20 kHz | 1–2 kHz | Drive DSP |
| Velocity | 2–8 kHz | 150–400 Hz | Drive DSP |
| Position | 1–4 kHz | 20–60 Hz | Drive or controller |
| Trajectory / interpolation | 250 Hz–1 kHz | — | Motion controller |
| Supervisory / sequencing | 10–100 Hz | — | PLC or Linux host |
Sampling introduces its own constraint. A zero-order hold contributes a phase lag equal to half a sample period, which at the closed-loop bandwidth directly consumes phase margin. This is why the sample rate cannot simply be set to whatever the CPU can sustain.
φZOH ≈ − ω Ts / 2 → fs ≳ 20 · fbw
Following error is a design output, not a tuning result
A position loop with proportional gain tracking a constant velocity exhibits a steady-state lag that is fully determined by the gain. No amount of careful tuning removes it, because it is structural.
ess = v / Kv
On a single axis this lag is invisible in the finished part. On coordinated axes it is not: two axes with different K<sub>v</sub> lag by different amounts, and a commanded straight line comes out as a curve. Contour error on interpolated motion is dominated by K<sub>v</sub> mismatch, which is why matching velocity constants across axes matters more than optimising any one of them.
The correct fix is velocity feedforward. Injecting the commanded velocity directly into the velocity loop cancels the lag term without raising the gain, so tracking improves without moving the system toward instability. Adding acceleration feedforward through an inverse-dynamics model handles the remaining inertial term.
Jerk limiting: paying a little time to avoid exciting the machine
A trapezoidal velocity profile has discontinuous acceleration, and a step in acceleration is a broadband excitation containing energy at every frequency the structure has a mode at. The result is residual vibration after each move — settling time that shows up as reduced throughput and surface finish defects.
An S-curve profile bounds jerk, rolling the acceleration on and off. This lengthens the nominal move slightly and shortens the settling substantially, and on lightly damped structures the net cycle time almost always improves.
Tmove = d/vmax + vmax/amax + amax/jmax
The fieldbus: determinism you can actually specify
EtherCAT dominates industrial motion because of how it handles frames. Rather than addressing slaves individually, a single frame passes through every node, which reads its data and inserts its response as the frame moves through — processing on the fly, in hardware, at wire speed. One frame therefore services an entire axis group within a cycle.
Synchronisation comes from Distributed Clocks. One slave's clock is designated the reference, propagation delays between nodes are measured during initialisation, and every node compensates. Well-configured systems hold inter-node synchronisation under a microsecond, and this is what allows physically separate drives to apply their setpoints simultaneously — the property that coordinated motion depends on.
struct timespec next;
clock_gettime(CLOCK_MONOTONIC, &next);
for (;;) {
next.tv_nsec += CYCLE_NS; /* 1 ms */
while (next.tv_nsec >= NSEC_PER_SEC) {
next.tv_nsec -= NSEC_PER_SEC; next.tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
ecrt_master_receive(master); /* inbound process data */
ecrt_domain_process(domain);
control_step(); /* no malloc, no I/O, no locks */
ecrt_domain_queue(domain);
ecrt_master_send(master); /* outbound, same cycle */
}The comment on the control step is the whole discipline. Inside a cyclic task there must be no dynamic allocation, no file or network I/O, no unbounded lock, and no page fault. Each of these has an unbounded worst case, and one unbounded term makes the whole loop unbounded regardless of how good the average looks.
Configuring the host so the kernel stops being the problem
A stock Linux kernel will schedule a motion task when convenient. Convenient is not a specification. The configuration below is what turns a general-purpose machine into one with a defensible latency bound:
- PREEMPT_RT kernel, so kernel code paths become preemptible and priority inversion is handled by inheritance.
- SCHED_FIFO for the cyclic thread, at a priority above every driver thread it does not depend on.
- mlockall(MCL_CURRENT | MCL_FUTURE) plus a pre-faulted stack — a single page fault mid-loop dwarfs the entire cycle budget.
- isolcpus and IRQ affinity, so the control core runs the control task and the NIC interrupt and nothing else.
- Disable frequency scaling and deep C-states; the wake-up latency from a deep sleep state is measured in the same units as the cycle time.
- Measure with cyclictest under the actual production load, not idle. Idle latency figures are marketing.
What you are producing is a number: the maximum observed latency over a long run under worst-case load, with margin. On tuned industrial hardware, sub-50 µs worst case at a 1 ms cycle is achievable and verifiable. Without this work the figure is unbounded, and an unbounded figure cannot go in a specification.
The honest summary
Motion control rewards conservatism in a way most software does not. Bandwidth separation, feedforward instead of gain, bounded jerk, hardware synchronisation, and a scheduler configured to make promises it can keep. Every one of these is a decision to accept slightly less nominal performance in exchange for a worst case you can write down — and on a machine that runs three shifts, the worst case is the only number that ever matters.
Explore how Root Digit can support your team
From discovery workshops to production deployment, our engineers and consultants partner with you across the lifecycle of your AI, robotics, and IoT initiatives.