vueでroslibjs使ってみた

# はじめに

vueでroslibjsを使ってrosと通信する方法を記載します．

使用環境は以下の通りです．

```
Ubuntu 20.04
ROS noetic
Vue 3.0
```

# 準備

まずはvuecliをインストールし,vueプロジェクトを作成します．

https://qiita.com/erichama427/items/1b6e485ff733371a1582

vueプロジェクトを作成したら,まずROSとの通信に使用するroslibをインストールします

```
npm install roslib
```

あとはsrc/App.vueを以降の記事内用に記載しているサンプルに置き換えます．
また，`roslaunch rosbridge_server rosbridge_websocket.launch`も同時に実行します．

# サンプル
## 1.ベースプログラム

rosbridgeとのコネクションをするだけのコードです.今後記載するサンプルのベースプログラムとなります.

```src/App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

<script>
import ROSLIB from "roslib" // 追加
import HelloWorld from './components/HelloWorld.vue'

// *** 追加 ここから *** //
const ros =  new ROSLIB.Ros({
    url: 'ws://localhost:9090'
});
// *** 追加 ここまで *** //

export default {
  name: 'App',
  components: {
    HelloWorld
  },

  // *** 追加 ここから *** //
  mounted() {
    this.init();
  },
  methods: {
    init: function () {
      ros.on("connection", function() {
        console.log("connected to websocket server.");
      });
      ros.on("error", function(error) {
        console.log("error connecting to websocket server: ", error);
      });
      ros.on("close", function() {
        console.log("connection to websocket server closed.");
      });
    },
    // *** 追加 ここまで *** //
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

```

## 2.Publisher

ブラウザからPublishするサンプルプログラムです。

```src/App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
  <p><button v-on:click="publish()">publish</button></p> <!-- 追加 -->
</template>

<script>
import ROSLIB from "roslib"
import HelloWorld from './components/HelloWorld.vue'

const ros =  new ROSLIB.Ros({
    url: 'ws://localhost:9090'
});

// *** 追加 ここから *** //
var cmdVel = new ROSLIB.Topic({
    ros : ros,
    name : '/cmd_vel',
    messageType : 'geometry_msgs/Twist'
});
var twist = new ROSLIB.Message({
    linear : {
      x : 0.1,
      y : 0.2,
      z : 0.3
    },
    angular : {
      x : -0.1,
      y : -0.2,
      z : -0.3
    }
});
// *** 追加 ここまで *** //

export default {
  name: 'App',
  components: {
    HelloWorld
  },

  mounted() {
    this.init();
  },
  methods: {
    init: function () {
      ros.on("connection", function() {
        console.log("connected to websocket server.");
      });
      ros.on("error", function(error) {
        console.log("error connecting to websocket server: ", error);
      });
      ros.on("close", function() {
        console.log("connection to websocket server closed.");
      });
    },
    // *** 追加 ここから *** //
    publish: function () {
      cmdVel.publish(twist);  
    },
    // *** 追加 ここまで *** //
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

実行の結果以下のようになり、ブラウザのボタンを押すと,`/cmd_vel`がpublishされます

![vue-ros-sample1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589906/206c80e1-f7ed-dbbe-a5e3-6ba5020304f9.gif)

## 3.Subscriber

次はSubscriberのサンプルプログラムです。

```src/App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
  <p>sub msg: {{ msg }} </p> <!-- 追加 -->
</template>

<script>
import ROSLIB from "roslib"
import HelloWorld from './components/HelloWorld.vue'

const ros =  new ROSLIB.Ros({
    url: 'ws://localhost:9090'
});

// *** 追加 ここから *** //
var sub = new ROSLIB.Topic({
  ros: ros,
  name: "/listener",
  messageType: "std_msgs/String"
});
// *** 追加 ここまで *** //

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  // *** 追加 ここから *** //
  data: function () {
    return {
      msg: "",
    };
  },
  // *** 追加 ここまで *** //
  mounted() {
    this.init();
  },
  methods: {
    init: function () {
      ros.on("connection", function() {
        console.log("connected to websocket server.");
      });
      ros.on("error", function(error) {
        console.log("error connecting to websocket server: ", error);
      });
      ros.on("close", function() {
        console.log("connection to websocket server closed.");
      });

      // *** 追加 ここから *** //
      var self = this;

      sub.subscribe(function(message) {
        //console.log("callback",message);
        self.msg = message;
      });
      // *** 追加 ここまで *** //
    },
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

実行の結果以下のようになり、`/listener`にデータを送るとブラウザに表示されます

![vue-ros-sample2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589906/0be332d9-a028-0456-f6ee-3bf678198bec.gif)

## 4.Service Client

Service Clientのサンプルです。

```src/Add.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
  <p><button v-on:click="call()">service call</button></p> <!-- 追加 -->
</template>

<script>
import ROSLIB from "roslib"
import HelloWorld from './components/HelloWorld.vue'

const ros =  new ROSLIB.Ros({
    url: 'ws://localhost:9090'
});

// *** 追加 ここから *** //
var addTwoIntsClient = new ROSLIB.Service({
    ros : ros,
    name : '/add_two_ints',
    serviceType : 'rospy_tutorials/AddTwoInts'
});

var request = new ROSLIB.ServiceRequest({
    a : 1,
    b : 2
});
// *** 追加 ここまで *** //

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  mounted() {
    this.init();
  },
  methods: {
    init: function () {
      ros.on("connection", function() {
        console.log("connected to websocket server.");
      });
      ros.on("error", function(error) {
        console.log("error connecting to websocket server: ", error);
      });
      ros.on("close", function() {
        console.log("connection to websocket server closed.");
      });
    },
    // *** 追加 ここから *** //
    call: function () {
      addTwoIntsClient.callService(request, function(result) {
        console.log('Result for service call on ' + addTwoIntsClient.name + ': ' + result.sum);
      });
   },
    // *** 追加 ここまで *** //
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

rospy_tutorialsのadd_two_serverを実行するとサービスのコールを確認できます。標準では入っていないと思うので別途インストールしてください。

![vue-ros-sample3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589906/0ec2a26b-634b-a9fc-becd-0b92709a6de2.gif)


# 4.最後に

vueでroslibjsを使ってrosと通信することができました．

# 5.参考

- スマホアプリでROSのUIを作る場合はFlutterがおすすめらしい(ロボットにスマホ乗せる場合とか良さそう)

https://rb-station.com/blogs/article/flutter-roslib

- roslib JavaScriptでの使用方法

https://qiita.com/hoshianaaa/items/556425cf1a6d1433ed33