Help us understand the problem. What is going on with this article?

ブラウザからROS Topic、MQTT Topic を送受信しROS nodeと通信する その1

タブレット(iPad上のsafari)上でROS上のシステムと通信し、情報表示するシステムを作ってみたので備忘録として記事にしてみます。

今回の構成は自分でもすぐ再現できるようにdocker環境で作成しています。
以下の内容として、OS はubuntu 16.04(kinetic)をベースに話を進めます。
多分、18.04(melodic)でも変わらないと思っています。
構成的な関係上、slaveはsupervisor をインストールし各パッケージを起動するようにします。

構成

システムダイアログ

plantuml
@startuml
rectangle "Local Area" {

  rectangle "Master PC" {
    [roscore] as roscore
    Interface "Port11311" as ros_port11311
  }

  rectangle "Slave PC" {
    [Web Server1] as wsvr1
    Interface "Port80" as lnet_port80
    Interface "Port9090" as websocket_port9090

    rectangle "ROS node" {
      [rosbridge-server] as rosbridge
      [MQTT publisher] as mqtt_pub
      [MQTT subscriber] as mqtt_sub
    }
  }

  rectangle "Tablet1" {
    rectangle "Browser1" {
      [roslibjs] as roslibjs
    }
  }
}

cloud {
  rectangle "VPS" as vps {
    [MQTT Broker] as mqtt_broker
    [Web Server2] as wsvr2
    wsvr1 -down- lnet_port80
    Interface "Port80" as inet_port80
    Interface "Port1883" as mqtt_port1833
  }
}

rectangle "Tablet2" {
  rectangle "Browser2" {
    [MQTT.js] as mqttjs
  }
}

' layout
mqtt_port1833 -up- mqtt_broker
mqtt_sub -- mqtt_port1833
mqtt_pub -- mqtt_port1833
wsvr2 -- inet_port80
mqtt_port1833 -- mqttjs
inet_port80 -- Browser2
roscore -- ros_port11311
rosbridge -- websocket_port9090
ros_port11311 -- rosbridge
ros_port11311 -- mqtt_pub
ros_port11311 -- mqtt_sub
websocket_port9090 -- roslibjs
lnet_port80 -down- Browser1
@endum

roscoreとブラウザの通信

環境構築

roscore とブラウザが通信する仕組みとして標準的なものが何なのかよくわかっていませんが、rosbridge-serverというパッケージがあり、roscore(rosbridge)とwebsocketで通信できるようになります。

install
$ apt install -y ros-kinetic-rosbridge-server

websocketで通信できる準備ができたら、ブラウザ側が通信するように roslib.js というjavascriptのライブラリを使用して通信します。

install
$ npm install roslib

環境的なセットアップはこの辺で十分かと思います。
フロントエンドとしては、vue.jsを使用しますがvue.jsに関しては大したことをやっていないので細かいことは割愛していきたいと思います。
今回のROS Topic/MQTT Topic を受信する部分のみにフォーカスしていきたいと思います。

ブラウザ側の実装

vue でプロジェクトのセットアップ

適当にプロジェクトを作成します。
プロジェクトの作成には vue ui で作成していきます。
コンテナの外部からアクセスできるようにオプションで「-H 0.0.0.0」を追加して起動します。

$ vue ui -H 0.0.0.0

プロジェクトの作成。
image.png

プリセットは「手動」で。
image.png

ONに変更したのはTypeScriptのみ。
image.png

linterの部分はよくわかっていないけど、とりあえず「ESLint with prevention only」を設定。
image.png

プロジェクト作成後、roslib.jsをインストールするため、左のメニュー(?)の「依存」をクリック。
image.png

「依存をインストール」をクリックして「roslib」を入力しインストール。
image.png

左のメニュー(?)の「タスク」をクリック後、「タスクを実行」をクリック。
image.png

ビルドが実行され、問題なければ以下のようなサンプルページが表示される。
image.png

roscoreとの通信

roscoreと通信するための環境は整ったので以下のようにコードを修正して接続させてみる。

(1) @types/roslib のインストール

これをインストールしないとビルド時のエラーが消えないので追加。

install
$ npm install @types/roslib

(2) App.vueの変更

サンプルのHelloWorld を削除してROS Topic を受信するコンポーネントを追加し変更。

App.vue
@@ -1,17 +1,16 @@
 <template>
   <div id="app">
-    <img alt="Vue logo" src="./assets/logo.png">
-    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
+    <RosClient/>
   </div>
 </template>

 <script lang="ts">
 import { Component, Vue } from 'vue-property-decorator';
-import HelloWorld from './components/HelloWorld.vue';
+import RosClient from './components/RosClient.vue';

 @Component({
   components: {
-    HelloWorld,
+    RosClient,
   },
 })
 export default class App extends Vue {}
RosClient.vue
<template>
  <div class="ros">
    <h1>Received topic: {{ topic }}</h1>
    <button v-on:click="pub">BUTTON</button>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { Ros, Topic, Message } from 'roslib'

@Component
export default class RosClient extends Vue {
  private topic: string = 'unkown';
  private ros: Ros;
  private bridge_server_uri: string;

  constructor() {
    super();
    this.bridge_server_uri = '<<slave pc IP>>';
    this.ros = new Ros({});
  }

  private mounted() {
    console.log('mounted');
    const uri = 'ws://' + this.bridge_server_uri + ':<<rosbridge_server port>>';
    try {
      this.ros.connect(uri);
      this.ros.on('connection', () => {
        console.log('[INFO]: connection');
      });

      this.ros.on('error', err  => {
        console.log('[ERROR]: ' + err);
      });

      this.ros.on('close', () => {
        console.log('[INFO]: close');
      });

      this.start_listen();

    } catch (e) {
      console.log('[ERROR]: ' + e);
    }
  }

  private start_listen() {
    const listener = new Topic({
      ros: this.ros,
      name: '/example',
      messageType: 'std_msgs/Int32'});

    listener.subscribe(message => {
      console.log('[INFO]: topic: ' + listener.name + ', payload: ' + message.data);
      this.topic = listener.name;
    });
  }

  private pub() {
    const topic = new Topic({
      ros: this.ros,
      name: '/example',
      messageType: 'std_msgs/Int32'});

    const msg = new Message({
                  data: 1234
                });
    topic.publish(msg);

  }
}
</script>

以下のようにボタンを押下して受信したtopic名が画面上に表示されれば成功です。

rostopic.gif

その他モロモロはdocker環境を参照。
長くなったので、MQTTの実装はその2に分けて記事にしたいと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした