Problem Details
A common shop-floor building block is a G65-called circular interpolation macro that bores or finishes a circular pocket from the part center using cutter compensation and three arc moves. The calling program passes clearance plane, absolute depth, nominal diameter and feed as macro arguments; the macro program does the lead-in, one full circle, and a tangential lead-out.
Typical caller and macro body:
O6001 (CALL CIRCLE MILLING)
T1 M6
G0 G54 X0 Y0 M3 S1500
G0 G43 H1 D1 Z50
G65 P7001 R2 Z-6 D30 F500
G0 G53 G49 Z0 M5 M9
M30
O7001 (CIRCLE MILLING)
G0 Z#18
G1 Z#26 F[#9/4]
G91 G41 X[#7/2-0.3] Y0.3 F#9
G3 X-[#7/2-0.3] Y[#7/2-0.3] I-[#7/2-0.3] J0
G3 X0 Y0 I0 J-[#7/2]
G3 X-[#7/2-0.3] Y-[#7/2-0.3] I0 J-[#7/2-0.3]
G1 G40 X[#7/2-0.3] Y-0.3
G0 G90 Z#18
M99
Reported symptoms fall into four buckets: the macro runs but cuts a wildly undersized or oversized circle; the control alarms on cutter compensation at the lead-in block; the tool plunges at rapid or at the wrong Z; or the macro appears to "do nothing" because the arguments never reached the local variables.
Argument-to-Local-Variable Map
G65 argument specification (Type I) maps addresses to local variables at the new nesting level. Every value used inside O7001 must match this table exactly — a mismatch here is the single most frequent cause of a macro that "ignores" its inputs.
| Address in G65 block | Local variable | Meaning in this macro | Example value |
|---|---|---|---|
R |
#18 |
Clearance / approach plane (absolute Z) | 2.0 |
Z |
#26 |
Final depth, absolute | -6.0 |
D |
#7 |
Nominal finished diameter | 30.0 |
F |
#9 |
Contour feed (mm/min) | 500 |
Other Type I assignments worth knowing when you extend the macro: A=#1, B=#2, C=#3, I=#4, J=#5, K=#6, E=#8, H=#11, M=#13, Q=#17, S=#19, T=#20, U=#21, V=#22, W=#23, X=#24, Y=#25.
G65/G66. If the program is called with M98 P7001, the local variables stay null (#0) and every bracketed expression either alarms or evaluates unpredictably. Never substitute M98 for G65 on this macro.
Root Cause Analysis
1. Decimal points in the G65 call
Argument values written without a decimal point are interpreted in the least input increment of that address on many FANUC configurations. On a metric control with 0.001 mm increment, D30 can be read as 0.030 mm and R2 as 0.002 mm — the macro then commands a microscopic circle at a clearance plane essentially at Z0. Always write D30. Z-6. R2. F500. in the call, or confirm the decimal-point (calculator-type) programming setting in your control's parameter manual before relying on integer arguments.
2. Missing plane and modal state
The macro issues G3 with I/J but never commands G17. If the caller or a previous cycle left G18/G19 active, the I/J offsets are meaningless for the active plane and the control alarms or cuts in the wrong plane. Likewise, feed mode is inherited: if G95 (feed per rev) is modal, F500 is 500 mm/rev.
3. Cutter compensation offset number vs. the D argument
The wear/geometry offset used for compensation comes from D1 commanded in O6001 on the G0 G43 H1 D1 Z50 line. Inside the G65 block, D30 is an argument, not an offset selection — it does not change the modal D offset. That behaviour is correct, but it makes the macro silently dependent on the caller. If the caller omits D1, compensation starts with offset 0 and the tool cuts the programmed path centerline, producing a hole oversized by one tool diameter.
4. Geometry constraint on the lead-in
The hard-coded 0.3 mm lead-in stand-off sets the quarter-arc radius to #7/2 - 0.3. Two limits apply:
-
#7/2 - 0.3must be greater than zero, i.e. nominal diameter > 0.6 mm. - Tool radius must be smaller than
#7/2, and in practice smaller than the lead-in arc radius, otherwise the compensated path reverses. Typical FANUC responses are the cutter-compensation "no intersection point" alarm (033) or the interference alarm (041).
5. Full-depth plunge
G1 Z#26 F[#9/4] drives straight to final depth in one move at one quarter of contour feed. That requires a center-cutting end mill or a pre-drilled start hole, and it caps depth of cut at whatever the cutter can plunge. There is no Q peck or helical entry in the original.
6. M30 inside the subprogram
An M30 placed after M99 in the macro body is unreachable while the macro is a separate program, but it becomes a program-ending land mine if the two programs are ever merged into one file or if a block is edited above it. Delete it; keep only M99.
Geometry Verification
Trace with D=30 (radius 15), lead-in radius r = 14.7, starting at the part center. All arc blocks are incremental (G91) and CCW.
| Block | Arc center (from part center) | End point | Function |
|---|---|---|---|
G91 G41 X14.7 Y0.3 |
— | (14.7, 0.3) | Comp start-up, straight lead-in |
G3 X-14.7 Y14.7 I-14.7 J0 |
(0, 0.3) | (0, 15.0) | 90° tangential entry arc onto contour |
G3 X0 Y0 I0 J-15 |
(0, 0) | (0, 15.0) | Full 360° circle, R15 |
G3 X-14.7 Y-14.7 I0 J-14.7 |
(0, 0.3) | (-14.7, 0.3) | 90° tangential exit arc |
G1 G40 X14.7 Y-0.3 |
— | (0, 0) | Comp cancel, return to center |
The math closes: the tool returns exactly to the calling XY position and G0 G90 Z#18 restores absolute mode and the clearance plane. With G41 and M3 the cutter runs climb, and the left-hand offset pushes the tool center inward, so the finished bore equals the programmed D value. Geometry is not the defect — modal state, decimal points and offset handling are.
Hardened Macro
This version adds argument validation, explicit modal setup, a default clearance plane, an optional Q depth increment, and a single variable for the lead-in stand-off.
O7001 (CIRCLE MILLING - INTERNAL FINISH)
(R=#18 CLEARANCE Z=#26 DEPTH ABS D=#7 DIA F=#9 FEED Q=#17 STEP)
IF [#7 EQ #0] GOTO 90
IF [#26 EQ #0] GOTO 91
IF [#9 EQ #0] GOTO 92
IF [#18 EQ #0] THEN #18 = 2.
IF [#17 EQ #0] THEN #17 = ABS[#26]
#100 = #7 / 2 - 0.3 (LEAD-IN ARC RADIUS)
#101 = #7 / 2 (CONTOUR RADIUS)
IF [#100 LE 0] GOTO 93
G17 G90 G94
G0 Z#18
#102 = #18 (CURRENT Z)
N10 #102 = #102 - #17
IF [#102 LT #26] THEN #102 = #26
G1 Z#102 F[#9/4]
G91 G41 X#100 Y0.3 F#9
G3 X-#100 Y#100 I-#100 J0
G3 X0 Y0 I0 J-#101
G3 X-#100 Y-#100 I0 J-#100
G1 G40 X#100 Y-0.3
G90
IF [#102 GT #26] GOTO 10
G0 G90 Z#18
M99
N90 #3000 = 1 (NO DIAMETER D)
N91 #3000 = 2 (NO DEPTH Z)
N92 #3000 = 3 (NO FEED F)
N93 #3000 = 4 (DIAMETER TOO SMALL)
Call it the same way, with decimal points and an explicit cutter-comp offset:
G0 G54 X0 Y0 M3 S1500
G0 G43 H1 D1 Z50.
G65 P7001 R2. Z-6. D30. F500. Q3.
Commissioning Checklist
- Load D1 geometry offset with the actual tool radius and confirm the wear offset is zeroed for the first cut.
- Run the caller in single block with
Z50.raised and dry-run active; watch the macro variable page and confirm#7=30.,#26=-6.,#18=2.,#9=500.after the G65 block executes. - Verify the modal display shows
G17 G90 G94before the first arc andG41active only between the lead-in andG40. - Confirm the tool returns to the exact call position in X and Y at the end. Any residual offset means a
G91/G90mismatch. - Cut air at Z above stock, then a test bore in scrap; measure and correct diameter with the D1 wear offset only, never by changing the macro's 0.3 mm lead-in constant.
- Test the guard rails: call the macro with the
Dargument omitted and confirm the macro alarm fires instead of a crash.
FAQ
Why do my G65 macro arguments come through as tiny values?
Values passed without a decimal point can be interpreted in the least input increment of the address, so D30 becomes 0.030 mm on a 0.001 mm metric control. Always write D30. Z-6. R2. with decimal points in the G65 block.
Which local variables do R, Z, D and F map to in a G65 call?
Under Type I argument specification, R=#18, Z=#26, D=#7, F=#9. Use exactly these inside the macro body; M98 does not pass arguments at all.
Does the D word in a G65 block change my cutter compensation offset?
No. Inside a G65 block D is an argument only. The active compensation offset stays whatever was commanded earlier, which is why the caller must include D1 on the G43 H1 line.
Why does the control alarm at the cutter-compensation start-up block?
The lead-in arc radius is D/2 - 0.3. If the tool radius approaches or exceeds that value, the compensated path reverses and the control raises a cutter-compensation no-intersection or interference alarm. Use a smaller cutter or add a diameter-versus-radius check in the macro.
Can I add depth stepping to this circular milling macro?
Yes. Pass a Q increment (#17), loop the arc block set with an IF ... GOTO, and clamp the running Z to the commanded #26 depth so the last pass lands exactly on size.