Understanding servoj and lookahead time

After reading all related topics in this forum, I am still left with a feeling that I don’t fully understand the functionality of the servoj, and the lookahead_time specifically.

(Please feel free to correct me.)

As far as I understand, the combination of Gain and lookahead_time controls the smoothness versus accuracy ratio of the movement. Specifically, the lookahead_time affects how far into the future we look to predict the expected error, and the Gain would be the factor determining how strongly to correct the error.

If so, how does this help to smooth the movement? If I perform one servoj command for ‘t’ time and wait ‘t’ time before sending the next command, why doesn’t the robot stop in between, and how is this related to the lookahead_time?

Hi. I really relate to your question — I also struggled to understand servoj parameters at first. Not sure if this will help, but let me share how I understand servoj and the empirical trends I saw from my own parameter tests.

1) servoj is not a “one-shot move” command — it’s streaming servo tracking

movej/movel generate a full trajectory internally and execute it to completion.
servoj, on the other hand, means: “for the next t seconds, keep tracking this joint target.”
So servoj is designed for continuous streaming of targets, typically at a fixed rate like 125 Hz (8 ms).

2) Why doesn’t the robot stop if you call servoj for t and wait t?

This is not mainly due to lookahead_time.
It’s because the next target arrives on time, so the servo loop stays active.

  • If a new target arrives every t seconds (or faster),
    the robot assumes the stream is continuous and keeps tracking without braking.

  • If targets arrive later than t,
    the robot runs out of targets at the end of the interval, so it starts slowing down / pausing, and may even trigger protective stops.

So the “no stop in between” behavior comes from servoj’s streaming-tracking nature.

3) What lookahead_time really does (why it smooths motion)

lookahead_time makes the controller reference a near-future target, allowing it to keep velocity/acceleration more continuous even when the target stream is noisy or step-like.

  • Smaller lookahead_time (e.g., 0.03s)

    • very fast reaction to new targets

    • but more sensitive to noise/steps → tends to produce micro-vibrations or unstable motion

  • Larger lookahead_time (e.g., 0.2s)

    • motion is smoother and more stable

    • but with more lag, so it may fall behind when targets change quickly

This matches my tests:

  • lookahead 0.03 felt fast but noticeably less stable/accurate,

  • lookahead 0.2 felt much smoother/stable but slower to respond.

4) gain is “how aggressively the robot chases the target”

A more intuitive way to phrase it:
higher gain makes the robot latch onto the target faster and harder; lower gain makes it approach more gently.

  • High gain (e.g., 2000)

    • faster convergence / tighter tracking

    • but easier to excite vibration / overshoot if targets are noisy or discontinuous

  • Low gain (e.g., 100)

    • smoother and more stable

    • but slower response and more lag

Again consistent with my tests:

  • gain 100 → smooth but slow,

  • gain 2000 → fast but more vibration.

5) Effect of t (command duration / streaming period)

t is both how long each target is tracked and the expected update interval.

  • Smaller t (higher update rate) → faster tracking, but more sensitive to jitter/noise

  • Larger t (lower update rate) → smoother/stabler, but slower response

My tests showed the same trade-off:

  • t=0.02 → fast but unstable,

  • t=0.2 → smoother and stable but slow.

6) If your goal is real-time tracking, consider init_realtime_pose / set_realtime_pose

I implemented real-time tracking using URScript’s init_realtime_pose and set_realtime_pose.
If your purpose is to continuously update targets from an external source, this method can also be a good fit, sometimes even more straightforward than raw servoj streaming.

7) Tip: if joint targets aren’t intuitive, drive servoj from TCP pose

I personally found joint angles hard to reason about.
So I generated joint targets from a desired TCP pose via IK, which felt much more intuitive:

servoj(
    get_inverse_kin(new_pose),
    t=command_time_interval,
    lookahead_time=lookahead_time,
    gain=gain
)

Thinking in TCP space made it easier to understand what was happening and tune parameters.


In short:

  • t controls update bandwidth / tracking frequency

  • lookahead_time smooths motion by referencing near-future targets

  • gain controls how strongly and quickly the robot converges to each target

And the smoothness vs. accuracy/response trade-off comes from the combination of those three.