LoginSignup
1
0

More than 5 years have passed since last update.

ROS講座63 webでtwistを送る

Last updated at Posted at 2018-09-19

環境

この記事は以下の環境で動いています。

項目
CPU Core i5-8250U
Ubuntu 16.04
ROS Kinetic

インストールについてはROS講座02 インストールを参照してください。
またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。

概要

今までロボットを動かす場合は、geometry_msgs/Twist型が必要でした。しかし、JoyConを使ってtTwistを送る方法しかありませんでした。これではいつでもJoyConを持ち歩かないといけなく不便なので、GUIからTwistを送りたいです。
今回はHTMLのCanvas要素を使ってTwistを送ってみます。

ソースコード

web_lecture/www/twist.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
    <style type="text/css">
    <!--
      *{ padding:0px; margin:0px;}
    -->
    </style>
    <script src="https://static.robotwebtools.org/EventEmitter2/current/eventemitter2.min.js"></script>
    <script src="https://static.robotwebtools.org/roslibjs/current/roslib.min.js"></script>
  </head>
  <title>HTML5</title>
  <body>
    <p>status: <label id="state">Disconnect</label></p>
    <h1>/cmd_vel</h1>
    <canvas id='canvas' style="background-color:#EEEEEE;"></canvas>
    <p id="linearX" >linearX:  0.0 m/s</p>
    <p id="angularZ">angularZ: 0.0 rad/s</p>
    <script>

if(!Twist){
    var Twist = {
        ros : null,
        name : "",
        init : function(){
            this.ros = new ROSLIB.Ros();
            this.ros.on('error', function(error) {
                document.getElementById('state').innerHTML = "Error";
            });
            this.ros.on('connection', function(error) {
                document.getElementById('state').innerHTML = "Connect";
            });
            this.ros.on('close', function(error) {
                document.getElementById('state').innerHTML = "Close";
            });
            this.ros.connect('ws://' + location.hostname + ':9090');
        },
        send : function(linearX, angularZ){
            var pub = new ROSLIB.Topic({
                ros : this.ros,
                name : '/cmd_vel',
                messageType : 'geometry_msgs/Twist'
            });
            var twist = new ROSLIB.Message({
              linear:{x:linearX, y:0.0, z:0.0}, 
              angular:{x:0.0, y:0.0, z:angularZ}
            });
            pub.publish(twist);
        }
    }
    Twist.init();

    window.onload = function(){
    };
    window.onunload = function(){
        Twist.ros.close();
    };
}

//position -1.0~1.0
last_X=0.0;
last_Y=0.0;
last_enable=false;

window.onload=function(){
  canvas=$('canvas');
  expandCanvas();
  reticle();
}

function expandCanvas(){
  var b = document.body;
  var d = document.documentElement;
  canvas.width  = 400;
  canvas.height = 400;
}

canvas.onmousedown = function(e){
  last_X=(e.pageX - canvas.offsetLeft - 200) / 200;
  last_Y=(e.pageY - canvas.offsetTop  - 200) / 200;
  last_enable=true;
}

canvas.onmousemove = function(e){
  last_X=(e.pageX - canvas.offsetLeft - 200) / 200;
  last_Y=(e.pageY - canvas.offsetTop  - 200) / 200;
}

canvas.onmouseup=function(e){
  last_X=0.0
  last_Y=0.0
  last_enable=false;
  Twist.send(0.0, 0.0);
}

function reticle(){
  var target=$("canvas");
  var context=target.getContext('2d');
  center_x=200;
  center_y=200;
  line_a=10;
  line_b=100;

  context.beginPath();
  context.moveTo(center_x+line_a, center_y);
  context.lineTo(center_x+line_b, center_y);
  context.closePath();
  context.stroke();

  context.beginPath();
  context.moveTo(center_x-line_a, center_y);
  context.lineTo(center_x-line_b, center_y);
  context.closePath();
  context.stroke();

  context.beginPath();
  context.moveTo(center_x, center_y+line_a);
  context.lineTo(center_x, center_y+line_b);
  context.closePath();
  context.stroke();

  context.beginPath();
  context.moveTo(center_x, center_y-line_a);
  context.lineTo(center_x, center_y-line_b);
  context.closePath();
  context.stroke();
}

function interval_process(){
  if(last_enable){
    var linearX  = -0.5*last_Y;
    var angularZ = -1.0*last_X;

    document.getElementById("linearX" ).textContent = "linearX: " +linearX +" m/s" ;
    document.getElementById("angularZ").textContent = "angularZ: "+angularZ+" rad/s" ;
    console.log(linearX, angularZ);
    Twist.send(linearX, angularZ);
  }
}
setInterval("interval_process()",100);

function $(id){
  return document.getElementById(id);
}
    </script>
  </body>
</html>

if(!Twist){}の中はTwistを送るためにwebsocketの通信を行っているところです。前のpub.htmlとほぼ同じ内容です。jsonで複数階層のrosmsgを表すときは{a:{a0:0, a1:1}, b:{b0:2, b1:3}}のようにします。
window.onload=function(){}は起動時に呼ばれるcallbackを登録しています。

その下ではcanvas.onmousedown = function(e){}のようにcanvasでいイベントが起きた時に実行するcallbackを登録しています。
今回登録したのは以下のようなイベントです。

イベント xy座標 enable
canvas.onmousedown 現在位置にセット trueにセット
canvas.onmousemove 現在位置にセット 変化なし
canvas.onmouseup 0,0にセット falseにセット

その下のfunction interval_process(){}setInterval("interval_process()",100);では10Hzで実行するcallbackを設定しています。ここではenable==trueの時に保存されているxyに合わせてtwistを送信するTwist.pub()を呼んでいます。

実行

以下のように2つのターミナルを開いて、そのうえでブラウザでlocalhost:8085/web_lecture/twist.htmlにアクセスします。

1つ目のターミナルで
roslaunch vis_lecture move_robot.launch 
2つ目のターミナルで
roslaunch web_lecture bridge.launch 

web_twist.gif

参考

canvasオブジェクトを作る
javascriptで一定間隔にプログラムを実行
多階層のjsonの書き方
javascriptのイベント

目次ページへのリンク

ROS講座の目次へのリンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0