The drag knife faceplant suggested I must pay a bit more attention to fundamentals, so, with a 60° drag knife blade sticking out a reasonable amount, the next step is to see what effect the cutting “depth” (a.k.a. downforce) and speed have on the outcome.
A smidge of GCMC code later:

It’s not obvious, but each pattern steps downward by 0.5 mm from left to right. With the spring force equal to 375 g + 57 g/mm, the downforce ranges from 400 to 520 g over the five patterns.
Laminated scrap, meet drag knife:

Pulling up on the surrounding scrap left the patterns on the sticky mat:

Which suggested any cutting force would work just fine.
Flushed with success, I cut some speed variations at the minimum depth of Z=-0.5 mm = 400 g:

The blade cut through the top laminating film, the paper, and some sections of the bottom film, but mostly just scored the latter.
Repeating at Z=-1.5 mm = 460 g didn’t look much different:

However, the knife completely cut all the patterns:

As far as I can tell, the cutting speed doesn’t make much difference, although the test pattern is (deliberately) smooth & flowy like the Tek CC deck outlines. I’d been using 1000 mm/min and 2000 mm/min seems scary-fast, so 1500 mm/min may be a good compromise.
The GCMC source code as a GitHub Gist:
| // Calibrate Drag Knife – speed & feed | |
| // Ed Nisley – KE4ZNU | |
| // 2020-03 values for MPCNC | |
| //—– | |
| // Dimensions | |
| CutIncr = -0.5mm; | |
| BottomCutZ = -2.5mm; | |
| SpeedRatio = 2.0; | |
| MaxSpeed = 2000mm; | |
| MinSpeed = MaxSpeed / 8; | |
| StripWidth = 10mm; | |
| CornerRadius = StripWidth/2; | |
| PatternSize = StripWidth * [3,3]; | |
| PatternSpace = 1.25; | |
| SafeZ = 10.0mm; // above all obstructions | |
| TravelZ = 2.0mm; // within engraving / milling area | |
| FALSE = 0; | |
| TRUE = !FALSE; | |
| if (!isdefined("TestSelect")) { | |
| TestSelect = "Depth"; | |
| } | |
| comment("Test Selection: ",TestSelect); | |
| //—– | |
| // One complete pattern | |
| // Centered at ctr, ctr.z=cut depth | |
| function Pattern(ctr) { | |
| local d1 = CornerRadius; // useful relative distances | |
| local d2 = 2*d1; | |
| local d3 = 3*d1; | |
| local d4 = 4*d1; | |
| goto([-,-,TravelZ]); // set up for entry move | |
| goto(head(ctr,2) + [-d2,d3]); | |
| move([ctr.x + d2,-,ctr.z]); // enter to cut depth | |
| arc_cw_r([d1,-d1],d1); | |
| move_r([0,-d4]); | |
| arc_cw_r([-d1,-d1],d1); | |
| move_r([-d4,0]); | |
| arc_cw_r([0,d2],d1); | |
| move_r([d2,0]); | |
| arc_ccw_r([0,d2],d1); | |
| move_r([-d2,0]); | |
| arc_cw_r([0,d2],d1); | |
| move_r([d4,0]); // re-cut entire entry path | |
| goto([-,-,TravelZ]); // exit to surface | |
| // goto(head(ctr,2)); | |
| } | |
| //—– | |
| // Start cutting! | |
| goto([-,-,SafeZ]); | |
| goto([0,0,-]); | |
| goto([-,-,TravelZ]); | |
| if (TestSelect == "Depth") { | |
| comment("Depth variations"); | |
| s = MaxSpeed / 2; | |
| feedrate(s); | |
| c = [0,0,-]; // initial center at origin | |
| for (c.z = CutIncr; c.z >= BottomCutZ; c.z += CutIncr) { | |
| comment("At: ",c," speed:",s); | |
| Pattern(c); | |
| c.x += PatternSpace * PatternSize.x; | |
| } | |
| } | |
| if (TestSelect == "Speed") { | |
| comment("Speed variations"); | |
| c = [0,0,-2mm]; // initial center at origin | |
| for (s = MinSpeed; s <= MaxSpeed; s *= SpeedRatio) { | |
| comment("At: ",c," speed: ",s); | |
| feedrate(s); | |
| Pattern(c); | |
| c.x += PatternSpace * PatternSize.x; | |
| } | |
| } | |
| goto([-,-,SafeZ]); | |
| goto([0,0,-]); | |