Sunday, 10 May 2026

Testing the lathe cycles

After some distinct buggerage with the lathe cycle graphics, let's do some actual testing of the cycles on a live machine. Of course, I can start by checking what commands the macros are sending to the machine controller but ultimately I need to see what happens when I run the machine.

The more conventional "turning" and "facing" operations are prett straightforward and although I can't pretend I've done any seriously thorough testing at this stage, they seem to work largely as expected. I'm sure I can finesse the instructions a bit but initially I need to know if the toolpaths are almost sensible looking.

The "chamfer" toolpaths don't seem to be working quite as expected. There are 3 "corner types" on offer, depending on which of the3 possible chamfers you want to machine.

External chamfer (Front Outside"):

Here's the dummy setup I'm planning to run. It's air cutting at this stage but the tool starting position and chamfer parameters are hopefully good starting points.


And here's the displayed toolpath trace ie where the tool actually went, as reported in the backplot panel within the main screen:


Ooof. What's happening here?
  • The backplot trace is all over the shop. This is due to the graphics freezing up, then waking up again and jumping to the current position. So parts of the toolpath are missing, with rogue traces where the jumps happened. The machine itself was working as expecting, so this seems to be a graphics problem.
  • The starting coordinates are inappropriate. The macros start by acquiring and storing the current machine coordinates under the active WCS. This position is then used as the retract position (G30 perhaps?), so ideally you'd get them right. In my case, I seem to have positioned the tool miles away from the stock. It's a learning process, I guess.
  • But more worryingly, the toolpath seems to be making rapid moves within the stock. For an external "Front Outside" chamfer, the tool should be clearing the work to the right and bottom of the screen for a front toolholder. The cause is actually quite visible - for this type of chamfer, the "chamfer distance" argument (5mm here) should immediately follow the feed argument (0.15mm) - check out the orange "call" script at the bottom of the setup panel). So, by sending the distance parameter as the 8th argument rather than the 7th argument, the GUI is accidentally specifying a "rear outside" chamfer. This turned out to be fairly simple to fix.
Here's the chamfer.ngc file:

chamfer
o<chamfer> sub

  o<backup_status> call

#<chamfer_x>              = #1
#<chamfer_ss>             = #2
#<chamfer_doc>            = #3  ;depth of cut
#<chamfer_z>              = #4
#<chamfer_tool>           = #5
#<chamfer_feed>           = #6
#<chamfer_exterior_front> = #7   ;will be cut if > 0
#<chamfer_interior>       = #8   ;will be cut if > 0
#<chamfer_exterior_rear>  = #9   ;will be cut if > 0
#<chamfer_coolant>        = #10
#<chamfer_maxrpm>         = #11
#<chamfer_units>          = #12  (=21) (20=inch, 21=mm)

        o100 if [#<chamfer_tool> NE #<_current_tool>]
             (MSG,ERROR : Set tool before use macro)
             o100 return [-2]
        o100 endif

        o105 if [#<chamfer_doc> LE 0]
             (MSG,ERROR: Depth of cut must be greater than zero)
             o105 return [-2]
        o105 endif

G8  ; Lathe radius Mode
G18 ; XZ Plane
G90 ; Absolute Distance
G21 ; Metric

#<chamfer_x> = [#<chamfer_x> / 2] ; because of radius mode
#<starting_X> = #<_x> (starting X)
#<starting_Z> = #<_z> (starting Z)

G96 D#<chamfer_maxrpm> S#<chamfer_ss> ; Constant Surface Speed Mode
M3            ; Start Spindle
G95 F#<chamfer_feed>      ; Feed-Per-Rev Mode
G4P1          ; Wait to reach speed

        o110 if [#<chamfer_coolant>]
                M8
        o110 endif

#<temp> = 0
        o200 if [#<chamfer_exterior_front> GT 0] ; front outside
             o201 while [[#<temp> + #<chamfer_doc> ] lt #<chamfer_exterior_front> ]
                  #<temp> = [#<temp> + #<chamfer_doc> ]
                  G0 x[#<chamfer_x> - #<temp>] z#<starting_Z>
                  G1 z#<chamfer_z>
                  G1 x#<chamfer_x> z[#<chamfer_z> - #<temp>]
                  G1 x#<starting_X>
                  G0 z#<starting_Z>
             o201 endwhile

             G0 x#<starting_X> z#<starting_Z>
             G0 x[#<chamfer_x> - #<chamfer_exterior_front> ]
             G1 z#<chamfer_z>
             G1 x#<chamfer_x> z[#<chamfer_z> - #<chamfer_exterior_front> ]
             G1 x#<starting_X>
             G0 z#<starting_Z>
        o200 elseif [#<chamfer_interior> GT 0] ; front inside
             o202 while [[#<temp> + #<chamfer_doc> ] lt #<chamfer_interior>  ]
                  #<temp> = [#<temp> + #<chamfer_doc> ]
                  G0 x[#<chamfer_x> + #<temp>] z#<starting_Z>
                  G1 z#<chamfer_z>
                  G1 x#<chamfer_x> z[#<chamfer_z> - #<temp>]
                  G1 x#<starting_X>
                  G0 z#<starting_Z>
             o202 endwhile

             G0 x#<starting_X> z#<starting_Z>
             G0 x[#<chamfer_x> + #<chamfer_interior>  ]
             G1 z#<chamfer_z>
             G1 x#<chamfer_x> z[#<chamfer_z> - #<chamfer_interior> ]
             G1 x#<starting_X>
             G0 z#<starting_Z>
        o200 elseif [#<chamfer_exterior_rear> GT 0] ; back outside
             o203 while [[#<temp> + #<chamfer_doc> ] lt #<chamfer_exterior_rear>]
                  #<temp> = [#<temp> + #<chamfer_doc> ]
                  G0 x[#<chamfer_x> - #<temp>] z#<starting_Z>
                  G1 z#<chamfer_z>
                  G1 x#<chamfer_x> z[#<chamfer_z> + #<temp>]
                  G1 x#<starting_X>
                  G0 z#<starting_Z>
             o203 endwhile

             G0 x#<starting_X> z#<starting_Z>
             G0 x[#<chamfer_x> - #<chamfer_exterior_rear>]
             G1 z#<chamfer_z>
             G1 x#<chamfer_x> z[#<chamfer_z> + #<chamfer_exterior_rear>]
             G1 x#<starting_X>
             G0 z#<starting_Z>
        o200 endif
M5
M9

  o<backup_restore> call     ; restore g20/21, g90/g91, feedrate cuter-comp and other using now global _backup_var

o<chamfer> endsub

M2
%

By placing the chamfer distance in argument #7 instead of #8, the cycle will select the correct section of code to execute("if [#<chamfer_exterior_front> GT 0] ; front outside" etc).

External chamfer ("Rear Outside"):


This looks almost credible, with the caveat that again, I seem to have positioned the virtual tool miles from the actual stock. And again, the graphics is sorely messed up due to the freezing / jumping behaviour:

Internal Chamfer ("Front Inside"):


Ditto, the freezing graphics issue and starting position have made a mess of things but otherwise it's actually looking OK:


There then followed a session where Claude buggered about with the Python code that generates these calls. This fixed the front / rear chamfer mixup.

Graphics problems:

As for the freezing graphics, "we" ended up changing "wake states" in the BIOS (no, me neither), which then highlighted an apparent "bug" (hate that word) in the rendering of the backplot. This resulted in the backplot completely vanishing for most of the rest of the day. I was able to monitor the PC between real life tasks elsewhere using the GL-iNet KVM (remote KVM over the network) as Claude beasted away trying (mostly in vain) to identify and fix the problem - and finally we got our backplot back by the end of play yesterday.

The __doCommonSetViewWork() in setViewXZ is at line 1732. I need to add a self.renderer.ResetCamera() call after it, but the file is owned by root. Please run:

sudo sed -i '1732 a\        self.renderer.ResetCamera()' /usr/lib/python3/dist-packages/qtpyvcp/widgets/display_widgets/vtk_backplot/vtk_backplot.py

This adds ResetCamera() right after __doCommonSetViewWork() in setViewXZ, which will auto-fit the camera scale to show the full machine. Then restart LinuxCNC.

Woohoo that fixed it. What was the root cause in the end?

The root cause was a bug in the QtPyVCP VTK backplot code, unmasked by the i915 boot.

The bug: setViewXZ() (the lathe's default view) positions the camera but never sets the parallel projection scale. VTK defaults to scale = 1.0, which means only ±1mm of the scene is visible. The tool cursor exists in the scene, but the camera is so zoomed in you can't see anything.

Why it worked before: Almost certainly the camera was being auto-scaled by a ResetCamera() call that was subsequently commented out in the code (line 600 has # self.renderer.ResetCamera()). At some point during PBL development someone commented it out — probably because it was causing an unwanted camera jump — but left the lathe XZ view broken as a side effect.

Why the i915 boot exposed it: Before May 11, PBL had probably been running continuously or being restarted without a full cold-start. Something in the Qt/VTK window lifecycle (resize event, first paint, etc.) was accidentally triggering a camera reset that masked the bug. The i915.enable_rc6=0 boot forced a different initialisation path — VTK's GL context was set up differently — which stopped that accidental rescaling from happening. After reverting i915, the "lucky" rescaling never came back, leaving the bug fully exposed.

The fix: Explicitly calling renderer.ResetCamera() after setViewXZ() sets the scale correctly from machine actor bounds every time.

which file has been corrected?

/usr/lib/python3/dist-packages/qtpyvcp/widgets/display_widgets/vtk_backplot/vtk_backplot.py

Note that this is a system package file — it will be overwritten if python3-qtpyvcp is ever upgraded. If that happens, you'd need to reapply the sed command. Worth keeping a note of it.


And here's what we get when we choose a sensible starting point for the tool position:

There's still the graphic problem causing the freezing / stuttering / jumping issue. I guess that's the next thing to focus on.

No comments:

Post a Comment

Swap out VFD on the LinuxCNC Bantam - Xtravert to Yaskawa

Just when it was looking as if I'd almost finished messing with this machine, I've decided to go a step further.  This machine has a...