Upload
buithien
View
220
Download
0
Embed Size (px)
Citation preview
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
15
//internet of things /
CNCルーター(切削機)は、とても便利な工作機械で、木やプラスチックのほか、軟らかい金属の素材のブロックを切削して物体を作成で
きます(CNCは、コンピュータ数値制御の略で、コンピュータから指示を送るルーターという意味です)。素材を積み重ねることによって物体を作成する3Dプリンタとは逆の働きをしますが、その有用性はいずれも同じです。平らな物体を切削することに加え、素材を細かく層状に削り取ることによって、耐久性のある素材から複雑な3D形状を作成することもできます。
従来、CNCルーターによる加工は大規模産業機械に限られていました。しかし、最新のデスクトップCNCルーターは、家庭のコンピュータの隣に置けるほど小型化されています。動作もかなり静かで、完全に筐体の中に収められているため、粉じんや削りくずが飛び散ることもありません。3Dプリンタと同じようなモーターが搭載されているため、精密な彫刻を作ることもできるほど高精度です。さらに、接続されているコンピュータやマイクロコントローラから送られる、標準のGコードによる命令を受け取ることができるため、制御もかなり簡単です。Gコードとは、シンプルなテキストベースの言語で、CNCルーターや3Dプリンタなどの機械を制御する低レベルの機械命令を記述するものです。Gコードのコマンドには、座標、移動、回転など、機械加工に使う機能を制御するものがあります。
本記事では、Carbide 3D製Nomad 883 Proルーターの接続方法について説明します。このルーターは、シリアル接続からGコードを受信し、埋込みArduinoボード上のGrblコントローラを使用しています。別のルーターを使用している場合、別のGコード初期化命令と別のコントローラ・ボードがあるはずです。ルーターの仕様を確認してください。
本記事で説明するサンプルの完全なコードは、GitHubに掲載しています。
ルーターへの接続データをルーターに送信するために、Will Winder氏によるUniversalGcodeSenderプロジェクトを使用します。このプロジェクトでは、
接続されているルーターにコマンドを送信するシンプルなAPIが公開されており、GrblまたはTinyGコントローラを使用するルーターを制御できます。
まず、最新バージョンのUniversalGcodeSender(本記事執筆時点ではバージョン1.0.9)をダウンロード・ページからダウンロードします。
次に、お好みのIDE(筆者はNetBeansを使用しています)で新しいプロジェクトを作成し、プロジェクトの依存性にUniversalGcodeSender.jarファイルを追加します。ルーターへの接続を新しく作成するためには、次のようにします。
static GrblController grblController;static final CutterListener listener = new CutterListener();static String PORT_NAME = "/dev/ttyACM0";
public static void main(String[] args) throws Exception { // 小数点は「,」ではなく「.」で送信します
Locale.setDefault(Locale.ENGLISH); grblController = new GrblController(); grblController.addListener(listener); grblController.setSingleStepMode(true); Boolean openCommPort = grblController.openCommPort(PORT_NAME, 115200); if (openCommPort != true) { throw new IllegalStateException( "Cannot open connection to the cutter"); }}
STEPHEN CHIN
Raspberry Piで制御するCNCルーターRaspberry Piのプログラムで切削加工を管理する
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
16
//internet of things /
PORT_NAMEは、ルーターが使用するシリアル・ポートを指定する定数です。115200は、ルーターとの通信に使用するボー・レートです。Mac OS XまたはLinuxに接続されたルーターのポート名を確認するためには、ルーターを接続した直後にdmesgコマンドの出力を確認します。Windowsでは、デバイス・マネージャの接続されているデバイスのポートのセクションから、ポート名を確認できます。
今回の例では、Nomad 883 Proでエラーが発生することを避けるため、シングルステップ・モードを使用します。このルーターは、コマンドをキューに入れるよりも、シーケンシャルに送信した方が安定して動作します。
正しいポート名を確認し、ルーターとの接続を初期化した後は、次のステップとして、ルーターのホーム位置合わせとツール計測のステップを開始します。Nomad 883 Proを初期化するために、次のGコード・コマンドを実行します。
static final List<String> PROBE1 = Arrays.asList("G4P0.005", "M05", "G92.1", "G54", "G10 L2 P1 X0 Y0 Z0", "G21", "G49", "G90", "G10 L2 P1 X0 Y0 Z0", "G0 X-2.5 Z-5", "G0 Z-35.000", "G38.2Z-105 F800", "G4P0.005");
static final List<String> PROBE2 = Arrays.asList("G0 Z-70", "G38.2Z-182.675F200.0", "G4P0.005");
static final List<String> PROBE3 = Arrays.asList("G0 Z-5", "G0 X-5");
PROBE1では、座標系を初期化し、プローブ位置に移動します。そして、800mm/mでプローブ長をチェックします。PROBE2では、それよりも低速で精度を上げた200mm/mでプロービングを繰り返します。最後に、PROBE3では、作業面から離れてホーム位置に戻ります。
GrblControllerは非同期に動作するよう設計されているため、コマンドを発行するために少しばかりの作業が必要になります。この手順を
簡単に行えるように、コマンドを送信してその終了を待機するいくつかのラッパー・コマンドを作成しています。次に示すのは、そのラッパー・メソッドを使った初期化サイクルです。
waitForConnection();homeAndWait();
sendSequenceAndWait(PROBE1);sendSequenceAndWait(PROBE2);sendSequenceAndWait(PROBE3);
ルーターの初期化サイクルは、コマンドを発行する前に終えておく必要があります。そのため、CNCルーターから初期化メッセージが返されるまで待機します。
private static void waitForConnection() throws InterruptedException { synchronized (listener) { while (!listener.connected) { listener.wait(); } }}homeAndWaitの実装は、次に示すように、コントローラ上の基盤となるperformHomingMethodを呼び出し、コマンドの実行が終了するまで待機します。
static void homeAndWait() throws Exception { synchronized(listener) { listener.commandComplete = false; grblController.performHomingCycle(); while (!listener.commandComplete) {
お好みのIDE(筆者はNetBeansを使用)で新しいプロジェクトを作成し、プロジェクトの依存性にUniversalGcodeSender.jarファイルを追加します。
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
17
//internet of things /
listener.wait(); } }}
sendSequenceAndWaitの実装では、配列に対して単純に反復処理を行い、各文字列を別々のコマンドとして送信しています。その後、コマンドのストリームが完了するまで待機します。
static void sendSequenceAndWait(List<String> sequence) throws Exception { synchronized(listener) { grblController.queueCommands(sequence); listener.fileStreamComplete = false; grblController.beginStreaming(); while (!listener.fileStreamComplete) { listener.wait(); } }}
これらのメソッドはすべて、コマンドとストリームが完了するタイミングを検出するために筆者が実装しなければならないリスナーを参照しています。
static class CutterListener implements ControllerListener { volatile boolean connected; volatile boolean commandComplete; volatile boolean fileStreamComplete; double prbZ;
@Override public synchronized void fileStreamComplete( String string, boolean bln) { fileStreamComplete = true;
notify(); }
@Override public synchronized void commandComplete( GcodeCommand gc) { commandComplete = true; notify(); }
@Override public void messageForConsole( String msg, Boolean verbose) { if (!verbose && msg.startsWith("['$H'|'$X' to unlock]")) { synchronized (this) { connected = true; notify(); } } if (!verbose && msg.startsWith("[PRB:")){ String pattern = "\\[PRB\\:-[0-9]*\\.[0-9]*,-[0-9]*\\.[0-9]*," + "(-[0-9]*\\.[0-9]*)\\:1\\]"; Matcher matcher = Pattern.compile(pattern).matcher(msg); if (matcher.find()) { prbZ = Double.parseDouble(matcher.group(1)); } } }
// その他の空メソッドは省略
}
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
18
//internet of things /
このプログラムを実行すると、ルーターがホーム位置に移動して、ツールの長さをテストするプローブ・アルゴリズムを実行します。最後のメソッドは、プローブ出力を解析して、エンドミルの先端がプローブに触れた際のルーターのZ座標を検出するものです。プローブの座標を出力してルーターをきれいにシャットダウンするために、次のコードで終了処理を行います。
System.out.println( "PRB_Z = " + listener.prbZ);TimeUnit.SECONDS.sleep(5);grblController.closeCommPort();
PRB_Zの値を書き留めておいてください。後ほど、作業面のオフセットを計算する際に必要になります。
作業領域の位置計算作業領域の位置計算を行うために、ルーターを手動で移動して作業領域の表面に触れるようにする必要があります。これを行うもっとも簡単な方法は、UniversalGcodeSenderユーザー・インタフェースを開くことです。これを実行するためには、プラットフォーム固有の適切なスクリプトを使用するか、コマンドラインで次のコマンドを実行します。
java -jar UniversalGcodeSender.jar
このUIを使うと、ポートとボー・レートを指定できます。JavaプログラムからCNCルーターに接続する際に使った値と同じ値を選択して、「Open」ボタンをクリックします。
ルーターに接続されると、ルーターは「WARN」状態になっています。これは、ルーターがホーム位置にないためです。「Machine Control」タブに移動し、「$H」ボタンをクリックして、ホーム位置に移動するコマンドを送信します。
これで、X、Y、Zの移動ボタンを使って、手動でルーターのヘッドを制御できるようになります。まず、高めの移動速度でヘッドを作業面に近づけます。近づいたら、それよりも低い移動速度に変更します。図1のように紙を使用し、その紙に触れないようにしてできるだけヘッドを作業面に近
づけます。画面のMachine Positionセクションに表示されているZの値は、作
業面の上端の位置を示しています。この値をWRK_Zとして記録します。次の式を使って、PROBE_OFFSETを計算してください。
PROBE_OFFSET = PRB_Z - WRK_Z - 5
筆者の環境では、得られた値を当てはめると、以下のようになりました。PROBE_OFFSET = -85.525 - (-91.57) - 5PROBE_OFFSET = 1.045
お使いのマシンのPROBE_OFFSETを計算すると、作業面の上端まで、物体を正確に何度でも切削できるようになります。
計算した新しいプローブ・オフセット値を使うようにプログラムを修正するために、ホーム位置に移動してプロービング手順を実行してから座標系を再設定します。これを行うためには、Gコード・コマンドG10L20を使用します。
このGコード・コマンドを使うと、新しい座標空間のX、Y、、Zの値を指定できます。次の例は、新しい座標を指定する静的文字列です。
図1:ヘッドを作業面に近づける
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
19
//internet of things /
static final String COORDINATE_RESET_TEMPLATE = "G10 L20 P0 X220 Y205"; // ZはPRB_Zを基にして追加
P0修飾子は、デフォルトの座標系を変更することを指定しています。XとYの値は、どのNomad 883 Proに対してもおおよそ適切な値であれば問題ありませんが、Z値には精度が必要です。そのため、先ほど計算したPROBE_OFFSET値を使って設定します。
Z値を設定するためには、プローブ・シーケンスの後に次の行を追加します。
sendCommandAndWait( COORDINATE_RESET_TEMPLATE + " Z" + (PROBE_OFFSET - listener.prbZ));
パスの構築JavaプログラムからGコードを生成する方法を説明するために、星形を切削する単純なジオメトリ・アルゴリズムを作ってみます。コードでこれを行うと、ポイントの数と、内半径および外半径を設定可能にすることができます。
星を作成する際の基本アルゴリズムは、円に沿って等間隔に並んだポイントの位置計算です。次のアルゴリズムを使うと、円の半径がRでP個のポイントを持つ星の外周の頂点vの位置を計算できます。
x = cos(v * 360 / P)y = sin(v * 360 / P)
これをラジアン(180度がπラジアン)に変換すると、次のように書くことができます。
x = cos(v * 2π / P)y = sin(v * 2π / P)
アルゴリズムを完成させるためには、内周の頂点も計算する必要があります。内周の頂点は、隣り合う外周の頂点の中間に対応する位置にあるものとします。内周と外周の両方の頂点を計算する、より一般化されたアルゴリズムは、Javaコードを使って次のように書くことができます。
for (int i=0; i<points * 2; i++) { double r = i%2 == 0 ? innerRadius : outerRadius; double x = Math.cos(i * Math.PI/points) * r; double y = Math.sin(i * Math.PI/points) * r;}
これで、星を作成するための頂点の位置がわかったため、トレースすべき線をCNCルーターに指示するGコードを出力できます。これに関係するGコード命令を次に示します。
■ G0:指定された位置に高速移動します。 ■ G1:指定された位置に直線移動します。
いずれの方法でも、オプションでX、Y、Zパラメータを指定できます。これらは、移動先の位置を10進数で指定するオプションです。
星の開始位置を得るために、最初の頂点の位置を計算します。都合よいことに、sin(0)は0でcos(0)は1です。そのため、先ほどのアルゴリズムは次のように簡略化できます。x = innerRadius;y = 0;
星を作業領域の中心にするためのオフセットと、先ほど説明したGコード機能を使うと、星の開始位置に移動する関数を次のように記述できます。
static void moveToStart( double innerRadius, double offset) throws Exception { sendCommandAndWait( "G0X" + String.format("%.3f", innerRadius + offset) + "Y" + String.format("%.3f", offset) + "Z" + String.format("%.3f", MATERIAL_THICKNESS + 1));}
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
20
//internet of things /
これは、1つのコマンドを受け取るようにsendSequenceAndWait関数を変更したものです。関数のコードは次のようになります。
static void sendCommandAndWait(String sequence) throws Exception { synchronized(listener) { grblController.queueCommand(sequence); listener.fileStreamComplete = false; grblController.beginStreaming(); while (!listener.fileStreamComplete) { listener.wait(); } }}
また、星のすべてのポイントをトレースするGコードを作成する完全なdrawStar関数を次に示します。
static List<String> drawStar( int points, double innerRadius, double outerRadius, double offset) { List<String> gcode = new ArrayList<>(points * 2 + 1); for (int i=0; i<points * 2; i++) { double r = i%2 == 0 ? innerRadius : outerRadius; double x = Math.cos(i * Math.PI/points) * r; double y = Math.sin(i * Math.PI/points) * r; gcode.add("G1X" + String.format("%.3f", x + offset) + "Y" + String.format("%.3f", y + offset)); } gcode.add(gcode.get(0));
return gcode;}
この例を完成させるためには、さらにいくつかのGコード・シーケンスが必要です。
static final List<String> START_SPINDLE = Arrays.asList("G21", "G90", "M3 S9000");static final List<String> END_SEQUENCE = Arrays.asList("M5", "$H", "M30");
この2つのシーケンスは、使うタイミングが異なります。上記コードの最初のシーケンスでは、単位をメートル法に設定(G21)し、絶対距離モードに設定(G90)して、スピンドルを開始(M3 S9000)しています。2つ目のシーケンスは、スピンドルを停止(M5)し、ヘッドをホーム位置に移動($H)して、プログラムを終了(M30)するエンド・シーケンスです。
また、アクリル樹脂素材は硬すぎて1回のパスでは切削できないため、複数回のパスをサポートする必要があります。厚さ1/8インチ(約3ミリ)のアクリル樹脂では、7回のパスを使って少しずつ切削することをお勧めします。次のコードでは、これを実現するためにdrawStar関数を複数回呼び出しています。
sendSequenceAndWait(START_SPINDLE);moveToStart(50, 100);for (int i = 1; i <= Z_STEPS; i++) { double newZ = MATERIAL_THICKNESS * (Z_STEPS - i) / Z_STEPS; sendCommandAndWait( "G1Z" + String.format("%.3f", newZ) + "F355.600"); sendCommandAndWait("F1117.600"); sendSequenceAndWait( drawStar(9, 50, 90, 100));}sendSequenceAndWait(END_SEQUENCE);
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
21
//internet of things /
このコードでは、G1に対して新しい引数Fを使用しています。これは、フィード速度を指定するものです。この部分を単独で呼び出して、以降の直線操作のフィード速度を設定することもできます。また、MATERIAL_THICKNESSと回数Z_STEPSという2つのパラメータがあります。これらは、使用する素材に応じて設定できます。
static final int Z_STEPS = 7;static final double MATERIAL_THICKNESS = 25.4 * 1/8;
プログラムが正しく動作することを確認するために、まず機械に素材を入れずに実行することをお勧めします。予期しないことが起こった場合、Javaプログラムを強制終了させるか、Nomad 883 Proの電源スイッチを押せば、操作は即座に停止します。
アプリケーションが正しく動作することを確認したら、アクリル樹脂をNomad 883 Proに入れ、もう一度プログラムを実行します。ルーターは深さを増しながら徐々にアクリル樹脂を切削し、最終的にルーターの緩衝ボードのすぐ上まで到達します。
終了したら、プラスチック片を吸引し、十分注意しながら、トレイから完成品を取り出します。図2のような、完璧な星が切り出されているはずです。
Raspberry Piでのコードの実行Raspberry Piでコードを実行する部分は、本記事でもっとも簡単な部分でしょう。Java 8は、標準のRaspbian Linuxディストリビューションにバンドルされているため、最新版をお持ちであれば、すでにJavaを実行できる状態になっています。NetBeansでは、Remote Java Platform機能を使ってRaspberry Pi上でコードを実行する機能が組込みでサポートされています。
NetBeansで初めてRaspberry Piを設定する場合は、次の手順に従います。 1. 「Tools」メニューから、「Java Platforms」を選択します。2. 「Add Platform」をクリックし、「Remote Java Standard Edition」
を選択します。3. 自由にプラットフォームの名前を付け、次の値を設定します(図3参
照)。
a. 「Host」で、Raspberry PiのIPアドレスを指定します。b. 「Username」で、SSHユーザー(デフォルトはpi)を指定しま
す。c. 「Password」で、SSHパスワード(デフォルトはraspberry)を
指定します。d. 「Remote JRE Path」で、Java 8の場所を指定します。
注:sudo update-alternatives --display javaを使うと、パスを探すことができます。
4. 「Finish」をクリックします。これで、JREの設定が完了し、動作できる状態になります。これをプロ
ジェクトに設定するために、プロジェクトのプロパティを表示するダイアログ・ボックスを開き、「Run」カテゴリを選択して、「Runtime Platform」の一覧から、作成した新しいプラットフォームを選択します。新しい構成を保存するよう求められたら、自由に名前を付けてください。
最後の手順として、コードの修正を行います。Raspbian LinuxからNomad 883 Proがどう見えるかに合わせてポート名を変更します。このポート名は、USB経由でルーターを接続してからdmesgコマンドを実行するとわかります。筆者の環境では、次の値を指定すると動作するようになりました。
図2:掲載したコードを使って切削したアクリル樹脂製の星
ORACLE.COM/JAVAMAGAZINE /////////////////// SEPTEMBER/OCTOBER 2016
22
//internet of things /
static String PORT_NAME = "/dev/ttyACM0";
すべての設定が完了したら、IDEからRaspberry Pi上で本記事のサンプル・コードを実行、デバッグ、プロファイルできるようになります。Raspberry Pi上でコードを実行する利点は、コンピュータを接続しなくてもアプリケーションを実行できることです。そのため、連続した繰り返しコマンドを単純に実行させるために高価なラップトップやデスクトップを使う必要がなくなります。また、Raspberry Piは専用デバイスであるため、処理タイミングの遅延やシステム・クラッシュの確率はかなり減少します。
Raspberry Pi上でJavaを実行させることについて詳しく知りたい方は、『Raspberry Pi with Java: Programming the Internet of Things (IoT)』(Oracle Press)という書籍をご覧ください。[編集注:Java Magazineの2015年5月/6月号には、この書籍の章の一部が掲載されています。]
まとめデバイスの基本プログラミングの大部分は、Javaプログラムからコマンドを受信できるように設定し、そのコマンドをデバイスが理解できる形式で送信することです。今回のプロジェクトから、そのことをおわかりいただけたのではないかと思います。Javaのツールやデバイス向けの大規模なソフトウェア・エコシステムによって、それが簡単に行えるようになっています。</article>
Stephen Chinは、Oracle Technology NetworkのJavaコミュニティ・マネージャーのリーダーで、『Raspberry Pi with Java』の著者であり、『Pro JavaFX Platform』の共著者でもあります。JavaOneコミュニティの議長を務めており、Rock Star Awardを5回受賞しています。Chinは、ハッカーの自然生息地でインタビューを行い、その動画をhttp://nighthacking.com/に投稿しています。
図3:NetBeansの構成画面