Tuesday, 25 August 2026

Bantam CNC - final homing and backlash optimisation (phew) - index homing no more.

There's still some work to be done on this homing accuracy business. Let's see if we can crack it finally. The decision to use the Claude Code extension within Visual Studio Code has certainly allowed me to do stuff I'd never have been able to figure out myself, it's true - but the experience is pretty trying at times. If Claude has a personality it's that of an excitable junior engineer - tends to sound very pleased with itself and jumps to conclusions that aren't backed up with evidence. I guess we can hope that Claude will develop over the coming years to the point that it is a lot more dependable and less fallible.

Let's look at the homing moves. It seems to me that the movements are rather jerky, whereas a servo system should be capable of smooth, quiet movement. Let's log some moves and see if there's anything to be learned.

Homing transient analysis

All 10 index-clear events from the final switch position, velocity-derived from the raw position samples and aligned to each cycle's own capture instant (t=0, dashed green). Commanded (blue) is what the trajectory planner is asking for; feedback (orange) is what the axis is actually doing. Faint lines are individual cycles; overlap (or lack of it) at t=0 shows how consistently the capture lands on the velocity profile.

This doesn't look ideal - what's all that sinusoidal wobble about? Looks like marginal stability to me.


Here's the same data plotted as distance rather than velocity. Velocity is simply the derivative of distance.


±1mm step transient (no homing involved)

Let's do some transient testing. Here's the g code for a backlash test. The moves are G91 (relative / incremental, and this is a lathe, remember), which allows me to position the tool anywhere within the machine limits before running the test - that way I can check for backlash at different positions of the ballscrew. The 1mm move each way should be enough to break any stiction - beyond that, there's little to be gained in moving further before reversing back to the starting point.

(X axis backlash check)
(Jogs X +/-1mm from wherever it currently sits, pausing 1s each time it)
(returns to the start point -- alternating which direction it approaches)
(from, so a DTI held at that point shows the gap between a + and - approach.)
(Runs purely relative to current position -- no work offset needed.)

(edit these two to taste)
#<feed>   = 200   (mm/min)
#<cycles> = 10    (number of +/- pairs)

G21   (metric)
G94   (feed rate in units/min, not units/rev)
G91   (incremental mode -- all moves relative to current position)

o100 repeat [#<cycles>]
    G1 X1  F#<feed>     (jog +1mm)
    G1 X-1 F#<feed>     (return to start, approaching from +)
    G4 P1               (dwell 1s -- read DTI now: approach from +)

    G1 X-1 F#<feed>     (jog -1mm)
    G1 X1  F#<feed>     (return to start, approaching from -)
    G4 P1               (dwell 1s -- read DTI now: approach from -)
o100 endrepeat

G90   (back to absolute mode)

M2

All 10 dwell events from the z_backlash_check.ngc +/-1mm relative-jog test (no switch, no homing, no index involved at all -- a plain commanded stop), aligned to each move's own stop instant (t=0, dashed green). Commanded (blue) is what the trajectory planner is asking for; feedback (orange) is what the axis is actually doing. Faint lines are individual cycles. 





Nasty lumpy movement is still visible. Homing in on the transient portions of the +/-1mm backlash test, shows some unauthorised wobbling:

Cruise-phase velocity ripple

Z velocity during the constant-speed middle portion of a single +/-1mm move (200mm/min commanded) -- not the stop/settle transient, the actual translation. Commanded (blue) should be a flat line; feedback (orange) swings from near zero to more than double the commanded speed, cyclically, roughly every 40ms (~24-25Hz). 



Fundamental frequency is ~26Hz. Is this caused by the Linuxcnc controller or is it caused by the Lichuan servo driver? That's worth establishing before trying to optimise the wrong PID loop. IIRC, the Lichuan servo driver is actually pretty well set up for the matching motor.

Ripple: LinuxCNC's own commanded output vs. actual

Same +/-1mm cruise-phase window as before, but now including pid.z.output -- the actual velocity command LinuxCNC's PID sends downstream to the stepgen, before it ever reaches the drive. It shows the same ~26Hz ripple as the feedback, and at far larger amplitude than the nominal -3.33mm/s command -- meaning LinuxCNC's own loop output is actively oscillating, not just passing through a clean signal that gets corrupted later.



Setting D=0 in the INI file makes quite a difference:

Ripple fixed: pid.z.Dgain set to 0

Same +/-1mm cruise-phase window and signals as the previous capture, but with pid.z.Dgain set to 0 (was 3). pid.z.output now ramps smoothly and holds steady instead of oscillating between roughly -17 and +14mm/s -- suggests that the ~26Hz ripple was mostly the D-term amplifying quantization noise from the 5µm scale, not a real mechanical resonance.



Looks to me like too much Kp term. Reduce Kp to 35 from 50 and Kd to 0 from 3, I unchanged.

PID tuning progress

pid.z.output (the velocity command LinuxCNC sends to the stepgen) across three tuning stages, same +/-1mm cruise window each time. This is the clearest single signal for the whole investigation -- how hard the loop is working to hold a supposedly constant speed.


That is starting to look a lot better - the wobble is almost certainly due to too much Kp and Kd terms, resulting in overshoot and marginal stability.



PID tuning progress - Z axis

pid.z.output (the velocity command LinuxCNC sends to the stepgen) across three tuning stages, same +/-1mm cruise window each time. This is the clearest single signal for the whole investigation -- how hard the loop is working to hold a supposedly constant speed.

Looking a lot better now:



Let's look at the X axis now:

PID tuning progress - X axis

pid.x.output across the X-axis tuning sequence -- same diagnostic process as Z, but a different root cause: X's weak feedforward (FF1=0.4) meant Kp was carrying too much of the tracking burden, so raising FF1 (not cutting Kp or Kd) was the fix here.



Good - so we now seem to have a decent transient response on both axes. How has that improved the homing accuracy, if at all?

Servo homing results, Z and X

Each dot is one homing cycle's resting position (raw linear-scale feedback, not the DRO -- unaffected by the homing offset reset), shown as deviation from that group's own mean so spreads are directly comparable across groups measured in different sessions. Same jog-away and settle protocol throughout.

The Z axis is looking pretty good:


But the X axis is still pretty crap, even if it has improved a bit:

Final X axis backlash tests:

PID tuning progress - X axis

Clean before/after: original X gains (P=60 I=40 D=1 FF1=0.4) vs. the final tuned config (P=100 I=40 D=0 FF1=0.85). Mean peak position error went from ~41µm to 5.0µm (one encoder increment) on every single cycle.




And as for the X homing - has that finally improved? I'm close to losing the will to live here but I want to get this thing cracked.

Servo homing results, Z and X

Each dot is one homing cycle's resting position (raw linear-scale feedback, not the DRO -- unaffected by the homing offset reset), shown as deviation from that group's own mean so spreads are directly comparable across groups measured in different sessions. Same jog-away and settle protocol throughout.



Woohoo - finally looking half decent. I need to convince myself the effort was worth it - perhaps that conviction will come with time.

Note that these X distances are radius, not diameter. So the diameter home position error is 4x that of the Z axis ie +/-20um (0.8 thou). Hmm, that's shit. What more can be done?

One key difference is that the Z axis is still using index mark. we don't know how it would behave without that, as many changes happened since we implemented it. Is the x axis just as good as the z would be without index? In other words, has all this messing about with index marks been a waste of time?

Let's compare index mark homing vs the original proximity switch to double check....

Z axis homing - with index and without:


Conclusion: using the index mark from the servo driver makes sod all difference. So I had no need to bugger about with connections from the servo driver to the 7i85 interface and all the buggerage that it entailed. Instead, careful setup of the homing and PID parameters delivered the same results.

Final numbers:
  • X axis homing accuracy - typically +/- 5-10um on radius (+/- 20um on diameter). 
  • Z axis homing accuracy - typically +/- 5-10um.
And here's the proof of the homing accuracy (actually repeatability) for the X axis, typically within 5um after settling (10um on diameter):


And the backlash tests for the X axis (typically 15um variation):


Sod it. That may have to do for now unless I can be arsed to repeat the homing and backlash videos for the Z axis. Only time will tell.

And no, I'm not going to be using "index mark" homing on the Z axis. I've removed the relevant code from the HAL and INI files. And I never implemented index mark homing on the X axis, so no change required there.

No comments:

Post a Comment

Bantam CNC - final homing and backlash optimisation (phew) - index homing no more.

There's still some work to be done on this homing accuracy business. Let's see if we can crack it finally. The decision to use the C...