Package: freecad Version: 0.20.2+dfsg1-4 Severity: important Forwarded: https://github.com/FreeCAD/FreeCAD/issues/9866 Tags: patch
I created a FreeCAD model and generated some Path Jobs to mill it out, and was very surprised when the generated G-code ended up digging the first cutter straight into the work table. This is using the linuxcnc post processor, but is unrelated to the postprocessor used. The model is available from <URL: https://codeberg.org/pere/thinkpad-x230-dc-socket-bracket >. I used the G-code in the subdirectory linuxcnc-jobs in commit ff248e59b7c44bda3e6320e6499ee9c240156dae for this failure. I tracked this down to the first move in the generated in the G-code file, which is a "G0 Z", which is no longer safe when the 15 cm tool height compensation has been wiped out with the G49 in the preamble. This is the generated G-code: (Exported by FreeCAD) (Post Processor: linuxcnc_post) (Output Time:2023-07-03 14:24:06.190303) (begin preamble) G17 G54 G40 G49 G80 G90 G21 (begin operation: G54) (machine units: mm/min) G54 G0 Z19.000 ; <-------- This one is dangerous while G49 is active (finish operation: G54) (begin operation: 6mm-endmill001) (machine units: mm/min) (6mm-endmill001) M5 M6 T3 G43 H3 M3 S2500 (finish operation: 6mm-endmill001) (begin operation: MillFace) (machine units: mm/min) (MillFace) G0 Z19.000 I see from <URL: https://forum.freecad.org/viewtopic.php?t=37839 > the issue has been known since 2019, and was even mentioned as an issue when the introduction of G43 was discussed in <URL: https://forum.freecad.org/viewtopic.php?t=27180 >. The dangerous code is only generated when splitting the G-code into separate files per tool. It is unclear to me why the G49 is used in the preamble to begin with. Is it not better to put it just before the G43, and avoid any G0 move before G43 has been used to load the correct tool height compensation? I had used the automatic tool height measurement in the machine to set the correct tool height compensation for the tool holder and bit in question, and was very surprised to see that it dived straight down as the first move. Was not able to stop it quickly enough to avoid damage. I believe the following patch will fix the issue. It is already sent upstream. I recommend considering it for the Debian package too. commit d8f2f87130b28a769e91fe20ffad885feecf381d Author: Petter Reinholdtsen <p...@hungry.com> Date: Mon Jul 3 16:42:58 2023 +0200 Avoid dagerous move without tool height compensation after setting first fixture The issue only happen when splitting jobs on tools (orderby == Tool), and when USE_TLO was active and the preamble include G49. The first move is then done before tool height is set, and can cause damage if the existing tool height is set to more than the gap between the spindle and the table or work piece, when the machine take a sudden dive straight down. Removed move between G49 and first G43, to ensure all moves are done after G43 correctly set tool height compensation. Rewrote code to introduce new method fixtureSetup() to ensure all orderby alternatives behave the same way. Fixes #9866. diff --git a/src/Mod/Path/Path/Post/Command.py b/src/Mod/Path/Path/Post/Command.py index 9393e7282f..d1f0635cf5 100644 --- a/src/Mod/Path/Path/Post/Command.py +++ b/src/Mod/Path/Path/Post/Command.py @@ -252,6 +252,33 @@ def resolveFileName(job, subpartname, sequencenumber): return fullPath +def fixtureSetup(order, fixture, job): + """Convert a Fixure setting to _TempObject instance with a G0 move to a + safe height every time the fixture coordinate system change. Skip + the move for first fixture, to avoid moving before tool and tool + height compensation is enabled. + + """ + + fobj = _TempObject() + c1 = Path.Command(fixture) + fobj.Path = Path.Path([c1]) + # Avoid any tool move after G49 in preamble and before tool change + # and G43 in case tool height compensation is in use, to avoid + # dangerous move without tool compesation. + if order != 0: + c2 = Path.Command( + "G0 Z" + + str( + job.Stock.Shape.BoundBox.ZMax + + job.SetupSheet.ClearanceHeightOffset.Value + ) + ) + fobj.Path.addCommands(c2) + fobj.InList.append(job) + return fobj + + def buildPostList(job): """Takes the job and determines the specific objects and order to postprocess Returns a list of objects which can be passed to @@ -269,20 +296,7 @@ def buildPostList(job): currTool = None for index, f in enumerate(wcslist): # create an object to serve as the fixture path - fobj = _TempObject() - c1 = Path.Command(f) - fobj.Path = Path.Path([c1]) - if index != 0: - c2 = Path.Command( - "G0 Z" - + str( - job.Stock.Shape.BoundBox.ZMax - + job.SetupSheet.ClearanceHeightOffset.Value - ) - ) - fobj.Path.addCommands(c2) - fobj.InList.append(job) - sublist = [fobj] + sublist = [fixtureSetup(index, f, job)] # Now generate the gcode for obj in job.Operations.Group: @@ -306,20 +320,9 @@ def buildPostList(job): # Build the fixture list fixturelist = [] - for f in wcslist: + for index, f in enumerate(wcslist): # create an object to serve as the fixture path - fobj = _TempObject() - c1 = Path.Command(f) - c2 = Path.Command( - "G0 Z" - + str( - job.Stock.Shape.BoundBox.ZMax - + job.SetupSheet.ClearanceHeightOffset.Value - ) - ) - fobj.Path = Path.Path([c1, c2]) - fobj.InList.append(job) - fixturelist.append(fobj) + fixturelist.append(fixtureSetup(index, f, job)) # Now generate the gcode curlist = [] # list of ops for tool, will repeat for each fixture @@ -374,7 +377,6 @@ def buildPostList(job): # Order by operation means ops are done in each fixture in # sequence. currTool = None - firstFixture = True # Now generate the gcode for obj in job.Operations.Group: @@ -386,22 +388,8 @@ def buildPostList(job): sublist = [] Path.Log.debug("obj: {}".format(obj.Name)) - for f in wcslist: - fobj = _TempObject() - c1 = Path.Command(f) - fobj.Path = Path.Path([c1]) - if not firstFixture: - c2 = Path.Command( - "G0 Z" - + str( - job.Stock.Shape.BoundBox.ZMax - + job.SetupSheet.ClearanceHeightOffset.Value - ) - ) - fobj.Path.addCommands(c2) - fobj.InList.append(job) - sublist.append(fobj) - firstFixture = False + for index, f in enumerate(wcslist): + sublist.append(fixtureSetup(index, f, job)) tc = PathUtil.toolControllerForOp(obj) if tc is not None: if job.SplitOutput or (tc.ToolNumber != currTool): -- Happy hacking Petter Reinholdtsen -- debian-science-maintainers mailing list debian-science-maintainers@alioth-lists.debian.net https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/debian-science-maintainers