宏程序(Custom Macro B)是 FANUC 上最容易被”会用但不理解”的功能:变量声明不用写、类型不用管,但变量放错分区、拿 null 当 0 用、DO 与 END 不配对,出的故障都不报错或者报得莫名其妙。这篇按官方手册口径把变量分区、空值陷阱、调用方式、嵌套上限与 PS 报警一次讲清,最后一个能直接跑的法兰螺栓圆周钻孔实例。
相关阅读:宏程序实战(换刀宏 O9001 与 3202#4 NE9 解锁)见 FANUC 换刀宏程序(O9001)编写与恢复;报警号总表见 FANUC 基本 PS/BG/SR 报警代码大全。
一、变量分区:#1–#33 / #100–#199 / #500–#999 怎么选
选错分区是宏程序第一错。三区的生命周期完全不同:
表 1 三区变量对照(Macro B 官方口径)
| 分区 | 范围 | 断电后 | 典型用途 | 常见误用 |
|---|---|---|---|---|
| 局部变量 | #1 – #33 | 调用结束即清 | G65/G66 传参(A=#1…Z=#26)、宏内部临时值 | 拿它存工件计数——换一个调用就没了 |
| 公共变量(易失) | #100 – #199 | 清零 | 单件加工内的临时计算、缓冲 | 拿它存刀具寿命——断电归零,寿命统计全丢 |
| 公共变量(保持) | #500 – #999 | 保留 | 工件计数、刀具寿命、原点偏置缓存、学习值 | 拿它存每件都重算的中间值——挤占掉电要留的槽位 |
判断口诀:跟着”调用”走的放 #1–#33;断电必须还在的放 #500 段;其余临时计算放 #100 段。工件计数和刀具寿命放 #500–#999,这是新手最常错的地方——放 #100 段的计数器,一次断电就清零,操作工还以为闹鬼。
🚨 #500 段的上限随机型与参数不同:有的只有 #500–#599,有的到 #999,先在本机变量画面确认再规划。另外 #100–#199 是否断电清零也可能被参数改写,以本机手册为准。

二、空值 ≠ 0:#1 没传参时是 null
没赋值的变量是”空”(null),不是 0。这是宏程序里最坑的静默故障:
| 写法 | #1 为空时的行为 | #1 = 0 时的行为 |
|---|---|---|
G01 X#1 F300 | 引用空的变量 → 该地址被忽略,轴不动、不报警 | 正常走到 X0 |
#2 = #1 + 5 | 空参与运算 → 结果按空处理 | #2 = 5 |
IF [#1 EQ 0] GOTO 10 | 空与 0 比较按不相等处理 | 条件成立 |
G01 X#1 在 #1 没赋值时不会报警、不会停、坐标就是不动——程序看起来在跑,件已经废了。排查办法:在变量画面里看该变量是空白(空)还是 0.000,空白就是没赋值。
两条纪律:
- 宏入口先把要用到的局部变量显式赋初值,别指望”它应该有值”;
- 判空用
[#1 NE #0](#0 永远是空),判零用[#1 EQ 0]——两个判断别混用。
三、比较必须用运算符:EQ NE GT LT GE LE
条件表达式里写 = 或 > 必然报错(格式错误类 PS 报警)。合法比较符只有六个:
| 运算符 | 含义 | 运算符 | 含义 |
|---|---|---|---|
| EQ | 等于(=) | LT | 小于(<) |
| NE | 不等于(≠) | GE | 大于等于(≥) |
| GT | 大于(>) | LE | 小于等于(≤) |
正确写法:IF [#101 GE 100] GOTO 20。写成 IF [#101 >= 100] 直接报表达式格式错。
四、逻辑组合:AND / OR / XOR 与方括号
- 每个条件各自包一层方括号:
IF [[#1 GT 0] AND [#2 LT 10]] GOTO 30; - AND / OR / XOR 是按位/逻辑运算符,写在中括号之间;
- 括号本身也有限制:嵌套(含函数括号)最多 5 层,超了报 PS0118(TOO MANY NESTED BRACKETS,部分系列为 0118 段报警)。
五、IF-GOTO 与 IF-THEN
两种写法都能用,场景不同:
IF [#101 GT 100] GOTO 20 (条件成立跳到 N20,宏内跳转用 GOTO)
IF [#101 GT 100] THEN #102 = 1 (条件成立执行一条宏语句)
- GOTO 只在同一个程序内跳,目标 N 号找不到报 PS0128(宏序列号不正确);
- IF-THEN 后面只能跟一条宏语句,跟 NC 语句(如 G01)不行——要执行多句就跳转或子程序;
- GOTO/WHILE 在 DNC 运行的主程序里不能用(相关报警 PS0123,视系列口径)。
六、WHILE / DO / END:最多 3 层、严格配对
#101 = 1
WHILE [#101 LE 10] DO1
...(循环体)
#101 = #101 + 1
END1
表 2 DO/END 的硬性规则
| 规则 | 内容 | 违反后果 |
|---|---|---|
| 循环号 | DO1/END1 到 DO3/END3,只有 1–3 | PS0126 ILLEGAL LOOP NUMBER |
| 配对 | 每个 DO 必须有同号的 END | PS0124 MISSING END STATEMENT(缺 END) |
| 嵌套 | 最多 3 层,且不能交叉(DO1…DO2…END1…END2 是交叉) | 报警或逻辑错乱 |
| 条件 | WHILE [条件] DO n,条件用第三章的六个比较符 | 格式错 |
死循环自救:循环变量忘了自增是最常见的死机原因,循环体内第一件事就是改循环变量,或在循环体里放一个计数上限判断强制 GOTO 跳出。
七、调用方式:G65 / G66 / G66.1 / G67 / M98
表 3 五种调用方式对照
| 方式 | 性质 | 行为 | 典型场景 |
|---|---|---|---|
| G65 P_ L_ | 非模态宏调用 | 调一次、执行完返回,L=重复次数 | 传参算一个孔位、算一个值 |
| G66 P_ | 模态宏调用 | 每遇移动轴指令就调一次宏 | 每个孔位都要执行的宏(配 G67 取消) |
| G66.1 P_ | 模态宏调用(每块) | 每个程序块都调一次(不管有没有移动) | 每块都要刷新的高响应宏 |
| G67 | 取消模态调用 | 取消 G66/G66.1 | 用完必须取消,否则后续每个块都在调宏 |
| M98 P_ | 子程序调用 | 无参数传递,固定重复 | 不需要传参的固定动作 |
G66 的经典用法(钻孔宏):G81 打孔模式下挂 G66 宏,之后每个 X_ Y_ 块都自动进宏算一次,最后 G67 取消——忘 G67 是”后面程序全部行为异常”的经典原因。

八、参数传递字母表:A=#1 … Z=#26 完整对照表
G65/G66 的自变量按字母 → 局部变量号固定映射(I/J/K 可重复传,按顺序依次入组):
表 4 自变量指定Ⅰ:字母 ↔ 局部变量号
| 字母 | 变量 | 字母 | 变量 | 字母 | 变量 |
|---|---|---|---|---|---|
| A | #1 | I | #4 | R | #18 |
| B | #2 | J | #5 | S | #19 |
| C | #3 | K | #6 | T | #20 |
| D | #7 | M | #13 | U | #21 |
| E | #8 | Q | #17 | V | #22 |
| F | #9 | — | — | W | #23 |
| H | #11 | X | #24 | Y | #25 |
| — | — | Z | #26 | — | — |
不能用来自变量指定的地址:G、L、N、O、P(G 被系统占用——用了报 PS0129 非法参数地址;L 是重复次数、O 是程序号、N 是顺序号、P 是程序号/次数)。
同一字母只能传一个值;要传”一组同性质数据”(比如多个孔的坐标),用 I/J/K 重复指定(自变量指定Ⅱ,按 #4#5#6 一组一组往下排)。
九、嵌套上限与报错:PS0121 / PS0122 / PS0126
表 5 嵌套相关报警(官方报警信息原文口径)
| 报警 | 信息 | 含义 | 处理 |
|---|---|---|---|
| PS0121 | TOO MANY SUB, MACRO NESTING | 子程序 + 宏调用总数超上限(常见于外部存储器子程序调用期间又发起调用) | 缩短调用链、检查漏写的 M99 |
| PS0122 | TOO MANY MACRO NESTING | 宏调用嵌套超上限(15i 手册明写上限 5 层;0i 系列常见口径为宏调用 4 层) | 减少宏套宏的层数 |
| PS0124 | MISSING END STATEMENT | 有 DO 没 END(DO-END 必须 1:1 配对) | 补 END;注意不是”交叉嵌套”那个错 |
| PS0126 | ILLEGAL LOOP NUMBER | DO/END 号不在 1–3 | 改成 DO1–DO3 |
🚨 层级口径随系列略有差异:15i 手册写宏嵌套 5 层,0i 系列手册常见”宏调用 4 层”,”子程序 15 层”是流传很广的资料口径、未在本次核实的原文中出现——以你手上这台上电后手册(参数说明)为准。
十、PS 报警速查:宏程序高频报警对照
表 6 写宏最容易撞上的 PS 报警
| 报警 | 信息 | 触发原因 | 处理 |
|---|---|---|---|
| PS0112 | ZERO DIVIDE | 除数为 0(#1/#0 或变量为 0 的除法) | 除之前判 [#2 NE 0] |
| PS0114 | ILLEGAL EXPRESSION FORMAT | 表达式格式错(括号/运算符) | 检查方括号配对与运算符写法 |
| PS0115 | VARIABLE NO. OUT OF RANGE | 变量号越界(如 #9999、超本机上限) | 先确认本机 #500 段到几 |
| PS0116 | WRITE PROTECTED VARIABLE | 给只读变量赋值(多数系统变量) | 只读别写 |
| PS0118 | TOO MANY NESTED BRACKETS | 括号嵌套超 5 层(含函数括号) | 拆表达式 |
| PS0122 | TOO MANY MACRO NESTING | 宏调用嵌套超上限 | 减层数 |
| PS0124 | MISSING END STATEMENT | DO 无对应 END | 补 END |
| PS0126 | ILLEGAL LOOP NUMBER | DO/END 号不在 1–3 | 用 DO1–DO3 |
| PS0127 | NC, MACRO STATEMENT IN SAME BLOCK | NC 指令与宏语句写在同一块(如 G01 X#1 #2=5) | 宏语句单独一行 |
| PS0128 | ILLEGAL MACRO SEQUENCE NUMBER | GOTO/M99P 的目标 N 号找不到 | 核对 N 号与范围 |
| PS0129 | ILLEGAL ARGUMENT ADDRESS | 用了不允许的自变量地址(如 G) | 换合法字母 |

十一、数学与函数:FIX / FUP / ROUND,角度一律用度
| 函数 | 含义 | 注意 |
|---|---|---|
| FIX[ ] | 向绝对值小的方向舍去小数 | FIX[1.7]=1,FIX[−1.7]=−1 |
| FUP[ ] | 向绝对值大的方向进位 | FUP[1.3]=2,FUP[−1.3]=−2 |
| ROUND[ ] | 四舍五入(在表达式内是”四舍五入到整数”) | 用在移动指令地址上时行为不同(按最小设定单位圆整)——表达式内与地址上别混用 |
| SQRT / ABS | 平方根 / 绝对值 | — |
| SIN / COS / TAN | 三角函数 | 角度一律是度,没有 RAD 关键字:SIN[30]=0.5 |
| ATAN | 反正切(格式 ATAN[y]/[x],部分系列带斜杠) | 返回值也是度 |
| LN / EXP | 自然对数 / e 的幂 | — |
两个易错点:
- 整数除法截断:
#1 = 7 / 2得 3(截断不四舍五入);要小数结果,先乘 1.0 或用浮点形式#1 = 7.0 / 2; - 三角函数用度——把 CAD 图上的弧度抄进 SIN/COS 是最常见的”宏算出来的坐标全偏”原因。
十二、可运行实例:法兰螺栓圆周均布钻孔
需求:法兰中心 (X0 Y0),分布圆半径 R(#18),孔数 N(#20),起始角 A(#1,度),孔深 Z(#26),钻速 F(#9)。宏 O9010:
O9010 (BOLT CIRCLE DRILL)
#101 = 0 (计数, 临时变量放 #100 段)
WHILE [#101 LT #20] DO1
#102 = #1 + #101 * 360.0 / #20 (当前孔角度=度)
#103 = #18 * COS[#102] (X = R*cos)
#104 = #18 * SIN[#102] (Y = R*sin)
G00 X#103 Y#104
G00 Z#105 (安全面, 由主程序传入或固定值)
G01 Z#26 F#9
G00 Z5.0
#101 = #101 + 1
END1
M99

主程序调用:G65 P9010 R60. A30. Z-15. F120. T8(R=分布半径 60,A=起始角 30°,8 孔)。角度是度(COS[30]=0.866),循环变量用 #100 段(不用保存),孔数与半径经字母表进 #20/#18。
改造练习:把”计数 + 逐孔算角度”改成 G66 模态版(每块进宏),体会 G67 忘写的后果;再把加工件数累计写到 #500 段做产量统计——正好覆盖第一、七章的两个要点。
常见问题
FANUC 宏程序里 #100 和 #500 有什么区别?哪个断电不丢?
#500 断电不丢。#1–#33 是局部变量,跟着 G65/G66 调用走,调用返回就清掉,只用来传参和存宏内临时值;#100–#199 是易失公共变量,断电清零,适合单件加工内的临时计算;#500–#999 是保持型公共变量,断电保留,工件计数、刀具寿命、原点缓存这类必须跨电源周期的值都放这里。把计数器放 #100 段是新手最常犯的错——一次断电统计全丢。注意 #500 段的上限随机型不同(有的只到 #599),先在变量画面确认本机范围再规划。
宏程序调用报警 PS0124 是怎么回事?
PS0124 是 MISSING END STATEMENT:程序里有一个 DO 却找不到与它配对的 END。Macro B 的 DO/END 必须同号 1:1 配对(DO1 对 END1),循环号只能在 1–3 之间(越界是另一个报警 PS0126),嵌套最多 3 层且不能交叉(DO1…DO2…END1…END2 就是交叉)。处理办法是从头数 DO/END:每个 DO 往下找同号 END,缺的补上;多层嵌套时建议在 END 后面加注释标明配对关系。如果 DO/END 数量看着是配对的还报 0124,检查是不是把 END 写在了 IF 分支里导致扫描路径上不闭合。
宏程序里的角度是弧度还是度?正弦函数怎么写?
一律是度,Macro B 没有 RAD 关键字。SIN[30] 就是 sin 30° = 0.5。常用写法:#101 = #18 * COS[#102]、#102 = #1 + #101 * 360.0 / #20(把孔序号换算成角度时直接用 360.0,不用做任何弧度换算)。与之相对,FANUC 的极坐标 G112/G12.1 或某些 CAM 后置里的三角计算可能用别的单位——跨系统抄公式时先确认单位。另外两个算术坑:整数除法会截断(7/2=3,要小数写成 7.0/2);ROUND 在表达式内是四舍五入到整数、用在移动地址上是按最小设定单位圆整,两者别混用。
最后更新:2026-10-25
面向海外读者的完整英文版,内容与本文对应 — 可直接转给非中文同事或海外客户。

























暂无评论内容