NC

カスタムマクロとサブプログラム入門:M98・M99・G65・#変数・IF・WHILE を例と図で

1 回書けば、寸法を変えて何度でも呼べる。まずは M98 と G65 の違いから!

マスコットのチップス
カスタムマクロとサブプログラム入門:M98・M99・G65・#変数・IF・WHILE を例と図で

同じ形で寸法だけ違う部品、円周上に並ぶボルト穴、何度もくり返す溝……。こうした加工を毎回 CAM や手計算で書き直していないでしょうか。NC には、プログラムの一部を呼び出してくり返す「サブプログラム」と、変数・計算・条件分岐を使える「カスタムマクロ」(メーカーによってはユーザマクロ、マクロ B などと呼びます)があります。この記事では M98・M99・G65・#変数・IF・WHILE を、動きの図とプログラム例で順に説明し、最後にボルト穴円のマクロを計算機で試せるようにしました。

メインプログラム O1000#1=500.G00 X#1→ X500.M98 P1100G00 X#1→ X700.(サブで #1 が書き換わった)G65 P9100 A600.G00 X#1→ X700.(マクロの #1=800. は影響しない)M30サブプログラム O1100#1=700.G00 X#1M99→ X700.マクロ O9100G00 X#1→ X600.#1=800.M99呼ぶM99 で戻るM98 で呼ぶと#1〜#33 は呼び出し元と同じ変数を使う中で書き換えると、戻っても値は変わったまま引数は渡せないG65 で呼ぶと#1〜#33 が新しい組になるA600. → #1 のように引数で値を渡せる戻ると元の #1 に戻る書かなかった引数は「空」
M98(サブプログラム)と G65(マクロ)の違い。M98 で呼んだ先で #1 を書き換えると、メインの #1 も変わります。G65 で呼んだ先の #1 は別物です(Haas の取扱説明書の説明による)[1]。

サブプログラムとマクロで何が楽になるか

  • 同じ動きのくり返し:等間隔の溝・同じ形のポケットを、1 か所分だけ書いて何度も呼ぶ(サブプログラム)
  • 寸法違いの部品をまとめる:直径・深さ・穴数を「引数」で渡し、1 本のプログラムで型番違いを削る(マクロ)
  • 自作の固定サイクル:ボルト穴円・角度パターン・面削りの往復などを、G81 のように 1 行で呼べる形にする[1][6]
  • 測定・管理:プローブで測った値で補正を直す、加工個数を数えて止める、工具の使用回数を数える[1][10]

研究でも使われていて、3D モデルから読み取った形状の寸法をマクロの引数に流し込み、CAM を使わずに加工プログラムを作る仕組み[14]や、専用の総形バイトを作らずに、普通の溝入れチップとマクロで非対称の台形ねじ(バットレスねじ)を削る方法[15]が報告されています。どちらも「寸法を変えたら引数を変えるだけ」というマクロの長所を生かしたものです。

カスタムマクロはオプション機能になっている NC があり、使える変数の範囲も機種とオプションで違います。この記事の書き方は FANUC 互換の書き方(いわゆるマクロ B)を基準にし、機種で違うところはその都度書きました。FANUC の取扱説明書は一般に公開されていないため、公開されている Haas・Siemens(ISO 方言)・Tornos などの資料で確かめた内容です。使う前に、必ず自分の機械の説明書で確認してください。

サブプログラム:M98 で呼んで M99 で戻る

サブプログラムは、別のプログラム番号に書いておいた加工を M98 で呼び出し、最後の M99 で呼び出し元の次の行へ戻る仕組みです[4][5]。

書き方意味機種による違い
M98 P1100O1100 を 1 回呼ぶ—
M98 P51100O1100 を 5 回呼ぶ(P の下 4 桁が番号、上の桁が回数)FANUC 互換の書き方。Siemens の ISO 方言の説明書でも同じ(例:M98 P30021 で 21 番を 3 回)[7]
M98 P1100 L5O1100 を 5 回呼ぶ(L で回数)Haas はこの書き方(L は 1〜99)[4]
M97 P100同じプログラムの中の N100 から始まる部分を呼ぶHaas のローカルサブプログラム。M30 の後ろに置き、M99 で終える[3]
M99呼び出し元の次の行へ戻るメインプログラムで実行すると先頭に戻ってくり返す(リセットまで)[5][12]
M99 P50呼び出し元の N50 へ戻る戻り先の探し方(前方・後方)は機種で違う[7]

例:等間隔の溝を 5 本切る

サブプログラムの中を増分(G91)で書くと、呼ぶたびに少しずつずれた位置で同じ動きをします。最後に G90 に戻してから M99 で帰るのがポイントです。

O1000 (MAIN)
G90 G17 G40 G49 G80
G54
T01 M06                (φ10 エンドミル)
S2000 M03
G00 X0. Y-10.
G43 Z50. H01 M08
Z2.
M98 P51100             (O1100 を 5 回:Haas なら M98 P1100 L5)
G90 G00 Z50. M09
G91 G28 Z0.
M30

O1100 (SLOT 1 PITCH)
G91 G00 X15.           (15 mm 横へ)
G01 Z-7. F100          (Z2. から 7 mm 下げて Z-5.)
Y40. F200              (溝を 40 mm 切る)
G00 Z7.                (Z2. へ戻る)
Y-40.                  (溝の始まりへ戻る)
G90                    (絶対値に戻してから帰る)
M99
12345開始点 X0. Y-10.(溝の手前)15M98 P51100 → 呼ぶたびに X が 15 mm ずつ進む
上から見た図(寸法は縮尺どおりではありません)。1 回目の呼び出しで X15. の位置に 1 本目を切り、5 回目で X75. の位置に 5 本目を切ります。

G91 のまま戻らないサブプログラムの中で G91 に切り替えたまま M99 で戻ると、メインの戻った次の行が増分で動き、思わぬ位置へ走ります。この例のようにサブの最後で G90 に戻し、メインの戻った直後の行にも G90 を書いておくと二重に安全です。

変数:#番号で値をしまう

マクロでは #1=100. のように「#」と番号で変数を表し、X#1 と書けば X100. として動きます。番号の範囲で役割が分かれています。

番号種類中身・使い方機種による違い
#0空(ヌル)値を持たない特別な変数。読むだけで、代入はできない。#1=#0 で #1 を空に戻せるHaas・Tornos・MachMotion で共通[1][8][9]
#1〜#33ローカル変数そのプログラムの中だけで使う。G65 で呼ぶと新しい組ができ、引数の値が入るSiemens の ISO 方言は引数を #1〜#33 ではなく $C_A〜$C_Z という別の変数で受ける[7]
#100〜#199
#500〜#999
コモン変数(共通変数)どのプログラムからも同じ値が見える。呼び出しをまたいで値を受け渡したり、加工個数を数えたりする電源を切って残るかは機種で違う。Haas はどちらも残し[1]、MachMotion は #100 番台を起動のたびに消す[9]。使える範囲もオプションで違う
#1000〜システム変数工具補正量・ワーク座標・タイマー・アラームなど、NC の中のデータを読み書きする番号の割り当ては機種で大きく違う。たとえば Haas は #2001〜 が工具長補正、#3001 がミリ秒タイマー、#3011 が年月日[1]

共通変数の番号は「空き」を確かめてから#500 番台などの共通変数は、プローブやパレット交換など機械メーカーが組み込んだプログラムも使っていることがあります。Haas の説明書では、プローブの校正値(#550〜#599)を上書きすると再校正が必要になると注意しています[1]。新しく使う前に、説明書と変数の画面で空いている番号を確かめてください。

「空」と「0」は違う

G65 でマクロを呼ぶと、新しいローカル変数はすべて空になり、呼び出しの行に書いたアドレスに対応する変数にだけ値が入ります[1]。つまり書き忘れた引数は 0 ではなく空です。空の変数を座標に使うと、その軸の指令が無視されて「動かない」ことがあり、計算に使うと 0 として扱われることがあります(扱いは機種の説明書で確認)。そこで、大事な引数は最初に IF [#11 EQ #0] GOTO 900 のように空かどうかを調べて、空ならアラームで止めるのが定番です[10][9]。

LinuxCNC(RS274NGC 系)には「空」がなく、渡さなかった引数は 0 になります[11]。FANUC 互換のマクロをそのまま持っていくと、空の判定が効かないので注意してください。

計算:四則演算と関数

変数には計算した結果を入れられます。計算の順番は [ ](角かっこ)で決めます(丸かっこ ( ) はコメントなので使えません)。Haas の例では [3*[9+3]] が 36 になります[6]。

書き方意味メモ
#3=#1+#2 - * /四則演算掛け算・割り算が先、足し算・引き算が後
SIN[#1] COS[#1] TAN[#1]三角関数角度は「度」(ラジアンではない)[8][11]
ATAN[#1]/[#2]逆正接(2 つの値から角度)書き方(/ か ,)と答えの範囲は機種で違う[13]
SQRT[#1] ABS[#1]平方根・絶対値—
ROUND[#1] FIX[#1] FUP[#1]四捨五入・切り捨て・切り上げ(整数に)負の数の丸め方は処理系で違う(LinuxCNC の FIX[-2.8] は -3)[11]
EQ NE GT LT GE LE比較:= ≠ > < ≧ ≦IF・WHILE の条件に使う[1][8]
AND OR XOR論理演算条件を組み合わせる[8][13]

小数を「等しい」で比べない計算した値は 0.0000001 のような誤差を含むことがあり、EQ が成り立たないことがあります。Haas の説明書は、比べる前に ROUND を通すよう勧めています[1]。くり返しの回数を数える変数は、整数で数えるのが安全です。

G65:引数を渡してマクロを呼ぶ

G65 は、G65 P9100 X0. Y0. D100. H6. のようにアルファベットの引数を付けてマクロを呼びます。P はプログラム番号、L は回数で、そのほかのアドレスの値がローカル変数に入ります[2][1]。どの文字がどの変数に入るかは決まっていて(引数指定 I)、G・L・N・O・P は引数に使えません。

A#1
B#2
C#3
D#7
E#8
F#9
G×
H#11
I#4
J#5
K#6
L×
M#13
N×
O×
P×
Q#17
R#18
S#19
T#20
U#21
V#22
W#23
X#24
Y#25
Z#26
引数指定 I の対応(Haas・Tornos・MachMotion の資料で一致)[1][8][9]。灰色の G・L・N・O・P は使えません。I・J・K を何組も並べる「引数指定 II」もあり、#1〜#33 の全部に値を渡せます。

アルファベット順と変数の番号がそろっていないところ(I=#4、D=#7、H=#11 など)が間違えやすいので、マクロの先頭に「どの引数を何に使うか」をコメントで書いておくと、あとで読む人が助かります。引数指定 I で値が入らない #10・#12・#14・#15・#16・#27・#28・#29・#30・#31・#32・#33 は、マクロの中で計算の途中結果をしまう場所に使えます。

引数には小数点を付ける小数点のない引数は、最小単位で読まれることがあります。Haas の説明書では、ミリの設定で A1 と書くと #1 は 1 ではなく 0.001 になります(D・E・F・H・L・M・S・T は除く)[1]。Siemens の ISO 方言でも、小数点のない値の読み方は機械の設定で変わります[7]。A1. のように、引数には必ず小数点を付けてください。

M98 との違いと、G66(モーダル呼び出し)

  • 引数:G65 は渡せる。M98 は渡せない(値を渡したいときは共通変数を使う)[1][7]
  • ローカル変数:G65 は呼ぶたびに新しい組になり、戻ると元に戻る。M98 は呼び出し元と同じ変数をそのまま使う[1]
  • G66:G66 P_ 引数 … のあと、移動の指令が終わるたびにマクロが呼ばれる。穴の位置だけを並べて、毎回同じ加工マクロを走らせる使い方。G67 で解除する[7][10]

IF・GOTO・WHILE:分岐とくり返し

書き方動き
GOTO 100N100 の行へ飛ぶ(条件なし)
IF [#1 GT 10.] GOTO 100#1 が 10 より大きいときだけ N100 へ飛ぶ
IF [#1 GT 10.] THEN #1=10.条件が成り立つときだけ、後ろの 1 文を実行する(THEN が使えない古い NC もある)[8][10]
WHILE [#1 LT 5] DO 1
 …
END 1
条件が成り立つ間、DO 1 から END 1 までをくり返す[6][8]
○ 入れ子は OK(3 重まで)× 交差は NG× 外から中へ飛び込むのは NG○ 中から外へ抜けるのは OKWHILE[…] DO 1WHILE[…] DO 2…END 2END 1WHILE[…] DO 1WHILE[…] DO 2END 1END 2GOTO 10WHILE[…] DO 1N10 …END 1WHILE[…] DO 1IF[…] GOTO 10END 1N10 …内側を外側にすっぽり入れるEND 1 が DO 2 の中にあるループの中の N 番号へ飛ばない途中で抜けたいときに使う
DO と END の番号(1〜3)はそろえ、重ねるときは内側を外側にすっぽり入れます。3 重までの入れ子は FANUC 搭載機の公開資料(Tornos)による[8]。番号は、ループが終われば同じ番号をまた使えます。

アラームで止める:#3000

引数の書き忘れや、あり得ない値を見つけたら、#3000=1 (H WO KAKU) のように #3000 に番号を入れて機械を止めます。Haas では番号に 1000 を足したアラーム(#3000=1 なら 1001)になり、かっこの中のコメントが画面に出ます[1]。表示のしかたは機種で違いますが、マクロの安全装置としてどの機種でも定番の使い方です[8][10]。

似た番号の #3006(メッセージを出して止める)は、機種によって「一時停止して再開を待つ」「メッセージだけ出して止まらない」「リセットが要るアラーム」と動きがばらばらです[1][8][9]。使う前に必ず説明書で確かめてください。

例:ボルト穴円(PCD)のマクロ

円周上に等間隔で並ぶ穴を、中心・PCD・穴数・始まりの角度を渡すだけであけるマクロです。中では WHILE で穴の数だけくり返し、COS・SIN で各穴の X・Y を計算して G81 であけます。Haas の説明書にも、同じ考え方の「斜め一列の穴あけマクロ」の例があります[1]。

(呼び出し側:工具・回転・G43 まで済ませてから)
G65 P9100 X0. Y0. D100. H6. A0. Z-15. R3. F150.

O9100 (BOLT HOLE CIRCLE)
(X=#24 Y=#25 中心  D=#7 PCD  H=#11 穴数)
(A=#1 始まりの角度  Z=#26 穴底  R=#18 R点  F=#9 送り)
IF [#7 EQ #0] GOTO 901          (PCD が空なら止める)
IF [#11 EQ #0] GOTO 902         (穴数が空なら止める)
IF [#1 EQ #0] THEN #1=0.        (角度を省いたら 0 度から)
#27=0                           (いま何穴目か)
#28=360./#11                    (穴と穴の間の角度)
WHILE [#27 LT #11] DO 1
  #29=#1+#27*#28                (この穴の角度)
  #30=#24+#7/2*COS[#29]         (この穴の X)
  #31=#25+#7/2*SIN[#29]         (この穴の Y)
  G90 G99 G81 X#30 Y#31 Z#26 R#18 F#9
  #27=#27+1
END 1
G80
M99
N901 #3000=1 (D PCD GA NAI)
N902 #3000=2 (H ANASU GA NAI)

ローカル変数のうち、引数指定 I で値が入らない #27〜#31 を計算の置き場に使っています。角度は X 軸のプラス側から反時計回りに数え、度で計算します。穴と穴の間にクランプがあるときは、G99 を G98 にして初期点まで戻してください(固定サイクル一覧の G98・G99)。

そのまま流さないこのプログラムは書き方の例です。THEN が使えない NC、変数の範囲や #3000 の書き方が違う NC があります。機械に入れたら、まずシングルブロック・ドライラン(Z を上げた状態)で 1 穴ずつ位置を確かめ、変数の画面で #29〜#31 の値が計算どおりかを見てから加工してください。Haas のように先読みで演算を先に済ませる NC では、出力のオン・オフを挟むマクロが思ったとおりに動かないことがあります(Haas は G103 P1 で先読みを制限)[1][6]。

計算機:マクロが出す穴の座標を確かめる

上のマクロと同じ計算で、各穴の X・Y と呼び出しの 1 行を出します。機械に流す前の検算に使ってください。PCD から穴の座標を出すだけなら PCD の計算機もあります。

穴角度(#29)X(#30)Y(#31)

#28 = 360 ÷ 穴数 #29 = 始まりの角度 + (穴の番号 − 1) × #28 X = 中心 X + PCD ÷ 2 × cos #29 Y = 中心 Y + PCD ÷ 2 × sin #29(角度は X 軸のプラス側から反時計回り)

よくある失敗 8 つ

  1. 引数に小数点を付け忘れる:Z-15 が -0.015 になる機種がある。引数は Z-15. と書く[1]
  2. 引数の文字を取り違える:I は #4、D は #7、H は #11。アルファベット順と番号はそろっていない
  3. G・L・N・O・P を引数にしようとする:P はプログラム番号、L は回数として読まれる
  4. M98 で呼んだ先でローカル変数を書き換える:呼び出し元の #1〜#33 も変わる。値を守りたいなら G65 で呼ぶ
  5. サブプログラムを G91 のまま抜ける:戻った先が増分で動く。M99 の前で G90 に戻す
  6. 共通変数を他のプログラムとぶつける:プローブや機械メーカーのマクロが使っている番号を上書きする[1]
  7. 小数を EQ で比べる:計算誤差で成り立たない。ROUND を通すか、整数で数える[1]
  8. WHILE の終わりの条件が成り立たない:数える変数を増やし忘れると無限ループになる。最初はシングルブロックで回して確かめる

よくある質問

カスタムマクロは、どの NC でも使えますか?
オプション機能になっている NC が多く、入っていないと G65 や #変数がアラームになります。使える変数の範囲もオプションで違います。また、Siemens は ISO 方言(FANUC 互換モード)のときに G65 を受け付けますが、引数を受ける変数が #1〜#33 ではありません[7]。まず自分の機械の仕様と説明書を確認してください。
サブプログラムの中から、さらにサブプログラムを呼べますか?
呼べます(多重呼び出し・ネスト)。ただし何重まで呼べるかは機種で違い、Siemens の ISO 方言の M98 は版によって 4 重または 16 重[7]、LinuxCNC は 10 重です[12]。WHILE の入れ子(3 重まで)とは別の上限です。
メインプログラムの最後を M99 にすると、どうなりますか?
先頭に戻ってくり返します(リセットするまで)。暖機運転のくり返しなどに使われます[5][12]。部品の加工では、止め忘れに注意してください。
マクロを自分の G コードや M コードとして呼べますか?
多くの NC で、パラメータの設定により特定の G・M コードでマクロを呼べます。Haas は O9010〜9019 を G コード、O9000〜9009 を M コードに割り当てられ、G コードでは引数を渡せます[1]。FANUC の研修でも G102 のような独自の G コードや、M・T コードでの呼び出しを扱っています[10]。

G・M コードの 1 つずつの意味は G・M コード辞典、穴あけの固定サイクルは 固定サイクル一覧にまとめています。

出典・参考資料

  1. Haas Automation「Mill Operator's Manual 13 - Macros」
  2. Haas Automation「G65 Macro Subprogram Call」
  3. Haas Automation「M97 Local Subprogram Call」
  4. Haas Automation「M98 Subprogram Call」
  5. Haas Automation「M99 Subprogram Return or Loop」
  6. Haas Automation「Macro Programming Information」(PDF)
  7. Siemens「SINUMERIK ISO Milling Programming Manual」(ISO 方言、2007)
  8. Tornos「Tips & Tricks: B Macro – ISO machines」(PDF)
  9. MachMotion「Macro B Reference Guide」
  10. ファナック FANUC eACADEMY「オンデマンドセミナ CNC カスタムマクロ」(コース案内)
  11. LinuxCNC「G-code Overview」(パラメータ・式・関数)
  12. LinuxCNC「O Codes」(サブプログラム・M98/M99)
  13. NIST「The NIST RS274NGC Interpreter – Version 3」(NISTIR 6556)
  14. Nguyen, Phung, Bui「Novel Integration of CAPP in a G-Code Generation Module Using Macro Programming for CNC Application」Machines 8(4), 61(2020)
  15. Kasabov ほか「A Parametric CNC Approach for Buttress Thread Machining」Engineering Proceedings 150, 91(2026)

ほかの読みもの