タブレット(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で通信できるようになります。
$ apt install -y ros-kinetic-rosbridge-server
websocketで通信できる準備ができたら、ブラウザ側が通信するように roslib.js というjavascriptのライブラリを使用して通信します。
$ 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
linterの部分はよくわかっていないけど、とりあえず「ESLint with prevention only」を設定。
プロジェクト作成後、roslib.jsをインストールするため、左のメニュー(?)の「依存」をクリック。
「依存をインストール」をクリックして「roslib」を入力しインストール。
左のメニュー(?)の「タスク」をクリック後、「タスクを実行」をクリック。
ビルドが実行され、問題なければ以下のようなサンプルページが表示される。
roscoreとの通信
roscoreと通信するための環境は整ったので以下のようにコードを修正して接続させてみる。
(1) @types/roslib のインストール
これをインストールしないとビルド時のエラーが消えないので追加。
$ npm install @types/roslib
(2) App.vueの変更
サンプルのHelloWorld を削除してROS Topic を受信するコンポーネントを追加し変更。
@@ -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 {}
<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名が画面上に表示されれば成功です。