目標: 物押しタスクをロボットに割り当てて実行する。
ロボットを操作するチュートリアルでは次のような流れでロボットを操作しました。
Object lock = new Object(); Resource wheels = robot.requireResource(WheelsController.class, lock); wheels.goForward(); // (略) wheels.stop(); robot.freeResource(wheels, lock);
ただ、いちいちリソースを取得したり解放したりするのは面倒です。リソースのことを意識せずにロボットプログラミングを行えるよう、この一連の流れを一つのクラスとしてまとめたのがタスク(Task)です。
例えば、タスクを利用してロボットを7秒間前進させて止めるコードは次のような流れになります。処理内容は番号ごとに先のリストと対応しています。
具体的には次に示すようなコードになります。(hakoniwa/GoForwardWithTask.javaより転載)
hakoniwa/GoForward.javaと見比べてみてください。単純に同じことをさせてもつまらないので、今回は2次元簡易シミュレータのHakoniwa上で動作するコードにしてみました。
import jp.digitalmuseum.mr.Matereal; import jp.digitalmuseum.mr.entity.Robot; import jp.digitalmuseum.mr.gui.DisposeOnCloseFrame; import jp.digitalmuseum.mr.gui.ImageProviderPanel; import jp.digitalmuseum.mr.hakoniwa.Hakoniwa; import jp.digitalmuseum.mr.hakoniwa.HakoniwaRobot; import jp.digitalmuseum.mr.task.GoForward; import jp.digitalmuseum.utils.ScreenPosition; /** * Assign one task to a robot. Get the robot to go forward for 7 seconds. * * @author Jun KATO */ public class GoForwardWithTask { public static void main(String[] args) { // Run hakoniwa. Hakoniwa hakoniwa = new Hakoniwa(640, 480); hakoniwa.start(); // Make a window for showing captured image. DisposeOnCloseFrame frame = new DisposeOnCloseFrame(new ImageProviderPanel(hakoniwa) { private static final long serialVersionUID = 1L; @Override public void dispose() { super.dispose(); Matereal.getInstance().dispose(); } }); frame.setResizable(false); frame.setFrameSize(hakoniwa.getWidth(), hakoniwa.getHeight()); // Connect to a robot. Instantiate a task. Robot robot = new HakoniwaRobot("Hakobot", hakoniwa.screenToReal(new ScreenPosition(320, 240))); GoForward goForward = new GoForward(); // Assign the task to the robot. if (goForward.assign(robot)) { goForward.start(); try { Thread.sleep(7000); } catch (InterruptedException e) { e.printStackTrace(); } goForward.stop(); } } }
先の例だとコード行数が増えてかえって分かりづらくなったように感じられるかもしれません。次の例は、ネットタンサーを前進させながら搭載したカメラ(Cameraリソース)の映像をキャプチャして画面に表示します。
CameraリソースにはBufferedImageを返すgetImage()メソッドだけが用意されており、定期的に画像を取得して動画像として画面に表示するには次のようにgetImage()を定期的に呼ぶ処理を書く必要があります。
while (true) { BufferedImage image = camera.getImage(); updateImage(image); try { Thread.sleep(1000f/30); } catch (InterruptedException e) { e.printStackTrace(); break; } }
Captureタスクはそのような処理をrun()に記述してあり、ImageProviderインタフェースを実装しているため、タスクを利用する側はタスクを割り当てて開始し、ImageProvider.ImageListenerを追加するだけで画像を定期的に取得できます。
次のコードではカメラを使うチュートリアルのときと同じようにImageProviderPanelを使って動画像を表示しています。
タスクは、終了条件が決まっているもの(MoveやPush)以外は明示的に停止させるまで続きます。
import jp.digitalmuseum.mr.Matereal; import jp.digitalmuseum.mr.entity.NetTansor; import jp.digitalmuseum.mr.entity.Robot; import jp.digitalmuseum.mr.gui.DisposeOnCloseFrame; import jp.digitalmuseum.mr.gui.ImageProviderPanel; import jp.digitalmuseum.mr.task.Capture; import jp.digitalmuseum.mr.task.GoForward; /** * Test code to go forward and capture images simultaneously. * * @author Jun KATO */ public class GoForwardAndCapture { public static void main(String[] args) { new GoForwardAndCapture(); } public GoForwardAndCapture() { Robot robot = new NetTansor("Tansor", "http://192.168.32.92:8081"); GoForward gf = new GoForward(); Capture cap = new Capture(); if (gf.assign(robot)) { gf.start(); if (cap.assign(robot)) { cap.start(); // NetTansor goes forward and captures images simultaneously. new DisposeOnCloseFrame(new ImageProviderPanel(cap)) { private static final long serialVersionUID = 1L; public void dispose() { super.dispose(); Matereal.getInstance().dispose(); } }.setFrameSize(cap.getWidth(), cap.getHeight()); // The tasks continue till the window is closed. }; } } }
ロボットに物を押させるコードの肝はBring it here! | materealで解説しています。やっていることは、クリックされた座標と押す対象の物体を引数にPushタスクをインスタンス化し、ロボットに割り当てているだけです。
全文はhakoniwa/BringItHere.javaを読んでください。現在の状況を可視化するためのGUIプログラミングのコードが70行あたりから始まり、全体の大部分を占めます。
シミュレータを使ったコードとほとんど一緒ですが、実ロボットを使ったコードはmarker/robot/BringItHere.javaを読んでください。