スマホでGoPiGoの操作をしよう
ではいよいよカメラ映像を見ながらGoPiGo実機の操作ができるようにしてみましょう。ここまで来れば遠隔操作まであと一歩です。
最終的に、下図のような構成を作ります(Webサーバは省略)。つまり、「1. カメラ映像をブラウザで見てみる」「2. スマホからGoPiGoを操作できるようにする」の構成をそのまま利用します。唯一変わったのは、/cmd_velの受け手がrostopic echoコマンドで表示するのではなく、GoPiGo台車ノードに変えたところです(図の右下)。
このノードは、第1、2回でも紹介したようにGoPiGoの制御ノードで、速度トピック/cmd_velを受けとると、その速度で移動・回転します。
では、遠隔操作を実行してみましょう。ここまでのコマンドをまとめると、下記のように5つのコマンドになります。
$ roscore $ rosrun libuvc_camera camera_node $ rosrun web_video_server web_video_server $ roslaunch rosbridge_server rosbridge_websocket.launch $ roslaunch roswww roswww.launch
これらがすでに起動できていることを確認したあと、GoPiGoの台車のノード(図の右下)を起動しましょう。
$ roslaunch ropigo ropigo.launch
起動後、スマホで下記にアクセスして操作しましょう。
http://GoPiGoのIPアドレス:8085/webjoy_example/index.html
上の動画のように、ジョイスティックを動かした方向にあわせて、ロボットが移動・回転しましたか? 部屋の中を自由に走らせたり、別の部屋に探索しに行ったりと、自由に楽しんでください!
ここからはオプションですが、以上の計6つのコマンドを1発にまとめたlaunchファイルをwebjoy_exampleパッケージ内に用意しました。実行するには、今までに入力したすべてのコマンドをCtrl-Cで停止し、次のコマンドを入力してください。
$ roslaunch webjoy_example webjoy_example.launch
先ほどと同じように操作できれば成功です!
余裕のある方は、このファイルの中身を見てみてください。roscoreを除く5つのコマンドが記述されていることが分かります。roscoreはlaunchファイル実行時は自動で起動します。
コードを理解しよう
上記「パッケージの作成」の項で解説したように、webjoy_exampleという名前のパッケージを新規に作りました。これは、遠隔操作を行うことができる画面を生成し、ジョイスティックの操作コマンドをROSに送る役割を果たします。
このパッケージ(のwwwディレクトリ)はroswww(Webサーバ)の起動時に読み込まれ、スマホなどでクライアントから接続されると自動で画面生成、ROSとの通信が開始されます。
では、その動作の詳細をコードを読んで理解しましょう。
webjoy_exampleパッケージのwwwディレクトリには、2つのファイルがあります。
www
├── index.html
└── webjoy.js
まず、index.htmlの中身をみてみましょう。
index.htmlの解説
9~16行目はbodyで、bodyのロード時にROSの初期化処理を行います。
また、表示のためdivのidにカメラ映像(video)や速度(joy_vel)、ジョイスティック(joystick)を割り当てています。
9 <body onload="init_ros();"> 10 <h2>GoPiGo Joystick Example</h2> 11 <div id="video"></div> 12 <div id="joy_vel"> 13 <h3>Linear: 0.0, Angular: 0.0</h3> 14 </div> 15 <div id="joystick"></div> 16 </body>
18行目~22行目ではJavaScriptの読み込みを行っています。
18 <script type="text/javascript" src="http://cdn.robotwebtools.org/EventEmitter2/current/eventemitter2.js"></script> 19 <script type="text/javascript" src="http://cdn.robotwebtools.org/roslibjs/current/roslib.js"></script> 20 <script type="text/javascript" src="http://cdn.robotwebtools.org/mjpegcanvasjs/current/mjpegcanvas.min.js"></script> 21 <script type="text/javascript" src="https://rawgit.com/yoannmoinet/nipplejs/master/dist/nipplejs.js"></script> 22 <script type="text/javascript" src="webjoy.js"></script>
19行目はROSとの通信を行うためのroslibjsの読み込みを行い、20行目はストリーミングサーバと接続して映像を表示するためのmjpegcanvasの読み込みを行なっています。
また、21行目はnippleJSというバーチャルジョイスティックのライブラリを読み込んでいます。
22行目はwebjoy.jsという今回のアプリ向けに作成したJavaScriptファイルです。こちらのファイルについて詳しく解説します。
webjoy.jsの解説
webjoy.jsは主に3つのことを行っています。
- ROSのrosbridge_server(WebSocketサーバ)との接続
- ジョイスティックイベントに応じて、速度トピックを配信
- カメラ映像の取り込み
1. rosbridge_serverとの接続
rosbridge_serverと接続するために、まずROSのオブジェクトを生成します。また、次の行で接続先のサーバのホスト名とポート番号(9090)を指定しています。
1 var ros = new ROSLIB.Ros(); 2 3 var serverURL = "ws://" + location.hostname + ":9090";
次に、先ほど指定したサーバの宛先URLを指定してrosbridge_serverと接続します。
28 function init_ros() {
29 try {
30 ros.connect(serverURL);
31 console.log("Connected to ROS.");
32 } catch (error) {
33 console.error(error);
34 }
35 }
また、ROSとの接続時や接続エラー時などのイベントに対してコールバックを登録できます。
37 ros.on('connection', function() {
38 console.log('Rosbridge connected.');
39 });
40
41 ros.on('error', function(error) {
42 console.log("Rosbridge Error: " + error);
43 disconnectServer();
44 });
45
46 ros.on('close', function(error) {
47 console.log("Rosbridge Close: " + error);
48 });
2. ジョイスティック操作に応じて、速度トピックを配信
まず、55~61行目でジョイスティックを使うためのnippleJSのジョイスティックオブジェクトを生成しています。
55 var joystick = nipplejs.create({
56 zone: document.getElementById('joystick'),
57 mode: 'static',
58 position: { left: '50%', top: '85%' },
59 color: 'blue',
60 size: 150
61 });
これはパラメータを細かく指定することによって、zone(どのdivにジョイスティックを表示するか)やmode(ジョイスティックの表示位置モード)などを細かく指定できます。ここでは、joystickという名前のdivにジョイスティックを割り当て、staticモード(表示位置固定)で表示しています。
次に、nippleJSによるジョイスティックのイベントコールバックを見てみましょう。3種類あります。
- start end:ジョイスティックに触れたタイミング、離したタイミングで呼ばれます
- dir:ジョイスティックの押されている方向が確定した段階で呼ばれ、4方向(上下右左)どれかの値を返します
- move:ジョイスティックが移動されている最中に呼ばれ、ジョイスティック中心からの距離などを返します
これらを踏まえ、コード量の多いmoveのコールバックを見てみましょう(73~108行目)。
73 joystick.on('move', function (evt, data) {
74 var distance = data.distance;
75 var vel_x = vel_theta = 0.0;
76
77 var x = LINEAR_VEL * distance / 75.0;
78 var theta = ANGULAR_VEL * distance / 75.0;
79
80 if(joyDirection === "up"){
81 vel_x = x;
82 }
83 else if(joyDirection === "down"){
84 vel_x = -x;
85 }
86 else if(joyDirection === "right"){
87 vel_theta = -theta;
88 }
89 else if(joyDirection === "left"){
90 vel_theta = theta;
91 }
92
93 twist = new ROSLIB.Message({
94 linear : {
95 x : vel_x,
96 y : 0.0,
97 z : 0.0
98 },
99 angular : {
100 x : 0.0,
101 y : 0.0,
102 z : vel_theta
103 }
104 });
105 cmdVel.publish(twist);
106
107 document.getElementById("joy_vel").innerHTML = "<h3>Linear: "+vel_x.toFixed(1)+", Angular: "+vel_theta.toFixed(1)+"</h3>";
108 });
ここでは、ジョイスティック中心からの距離や方向に合わせ、そのGoPiGoに与えるスピードを決定しています(73~91行目)。
そして、決定した速度を/cmd_velという名前のトピックとしてWebからROSに配信(publish)しています(93~105行目)。
3. カメラ映像の取り込み
最後に、カメラ映像の取り込みを行なっている部分です(5~13行目)。
5 var mjpegViewer = new MJPEGCANVAS.Viewer({
6 divID : 'video',
7 host : document.location.hostname,
8 port : 8080,
9 width : Math.round(window.innerWidth),
10 height : Math.round(window.innerWidth* 480/640),
11 quality : 50,
12 topic : "/image_raw"
13 });
mjpegcanvas.jsが提供するビューアを生成し、その引数に、映像の表示divの指定やストリーミングサーバのホスト名やポート番号(8080)、縦横の大きさ、画質、読み込むトピック名を指定することでカメラ映像が取り込まれ、HTMLでブラウザに表示されます。
おわりに
少し長くなりましたが、今回の記事ではGoPiGoをスマホで遠隔操作する方法を解説しました。次回はいよいよローカル環境を飛び出し、クラウドの世界に入っていく予定です。次回もお楽しみに!
