概要
ROSを起動させているロボットに対して、スマートデバイスからROSで通信を行いたい時があるときは、Flutterを用いたネイティブアプリで実装するのがおすすめです。
https://rb-station.com/blogs/article/roslibjs-javascript-ros
手軽にROS通信を行いたい場合は、こちらの記事のように、Webアプリケーションを作って、スマートデバイスのブラウザで表示させれば大丈夫ですが、ブラウザは制限が強く(例えば、iOS Safariはclickなどのユーザー動作起点ではないと音声を再生できない、など)実装したい内容がWebアプリケーションで実現できない場合はネイティブアプリを作った方が良いと思います。
Flutterについて
FlutterとはGoogleが開発している、クロスプラットフォームのアプリ開発フレームワークです。Dartという言語を用いて開発をします。
iOSやAndroid、Webなど複数プラットフォームのアプリケーションをビルドできる他、ホットリロード機能でデバッグがかなり楽だったり、とても簡単にアプリ開発が可能です。
Flutterのアプリケーション開発については色々な記事にて解説がなされていますので、導入は他記事に任せるとして、本記事では、FlutterアプリにROSを載せるところの解説をします。
使用するライブラリ
ROS通信を行うには、roslibというライブラリをインポートする必要があります。
- roslib ( https://pub.dev/packages/roslib )
しかし、このroslib、あまりメンテナンスがされておらず、2021年5月現在、Null-Safetyが実装されていないため、Flutter 2系を使うとエラーになってしまいますので、Flutterのバージョンは1系を使うようにしましょう。
自分の環境はこんな感じです。
$ flutter --version Waiting for another flutter command to release the startup lock... Flutter 1.20.4 • channel stable • https://github.com/flutter/flutter.git Framework • revision fba99f6cf9 (8 months ago) • 2020-09-14 15:32:52 -0700 Engine • revision d1bc06f032 Tools • Dart 2.9.2
ROSが動いているマシンの設定
- rosbridge_server : https://wiki.ros.org/rosbridge_server
roslibとはWebSocketで接続を行います。ROSが動いているマシンでは、rosbridge_serverパッケージのrosbridge_websocket.launchを起動しておくことで接続を受け入れ可能な状態にします。
- コマンドでの起動
roslaunch rosbridge_server rosbridge_websocket.launch
- launchファイルでの起動
<include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch"> </include>
roslibの導入
pubspec.yamlに下記を追加
pubspec.yaml
dependencies: roslib: ^0.0.3
ライブラリのインストール
$ flutter pub get
dartファイル内でroslibライブラリをインポート
main.dart
import 'package:roslib/roslib.dart';
これでライブラリを使う準備は整いました。
ROSトピックをsubscribe/publish
上記のlib/main.dartがメインのコードとなり、一部抜粋して解説をします。
rosのインスタンスを作成
Ros ros;
@override void initState() { ros = Ros(url: 'ws://192.168.11.7:9090');
ros.connect();
上記でrosのインスタンスを作成します。urlは、ROSが起動しているマシンのIPアドレスになります。ifconfig等で、確認をしましょう。次の例では、「wlan0」のinetに記載されている「192.168.11.7」がIPアドレスになっています。
また、FlutterアプリをインストールするデバイスもROSが起動しているマシンと同じWiFiに接続しておきましょう。ポートはrosbridge_serverでwebsocketを起動したポートを指定します。デフォルトでは9090になります。
また、ros.connect()によって、接続を確立します。
トピックのPublish
Topic helloTopic;
helloTopic = Topic( ros: ros, name: '/hello_from_flutter', type: "std_msgs/String", reconnectOnClose: true, queueLength: 10, queueSize: 10);
SubscriberもPublisherも上記のようにTopicのインスタンスをまず作成します。この例では「/hello_from_flutter」という名前でstd_msgs/String型のトピックを表すインスタンス、ということになります。
helloTopic.publish({"data": "Hello from flutter"});
次に、.publish()でPublishができます。型が合っているかは確認しましょう。
トピックのSubscribe
Topic rosOutTopic;
rosOutTopic = Topic( ros: ros, name: '/hello_to_flutter', type: "std_msgs/String", reconnectOnClose: true, queueLength: 10, queueSize: 10);
まずはTopicインスタンスを作成します。Subscribeの方はhello_to_flutterという名前のトピックにします。
void initConnection() async { ros.connect(); await rosOutTopic.subscribe(); setState(() {}); }
Container( child: StreamBuilder( stream: rosOutTopic.subscription, builder: (context, snapshot) { if (snapshot.hasData) { return Text(snapshot.data["msg"]["data"]); } else { return Text(''); } }, )),
ros.connect()で接続をした後にtopic.subscribe()でトピックのSubscribeをすることができます。
データを受信する場合はStreamBuilderで、topic.subscriptionをstreamに指定すると、データを受信した際にsnapshotの変数にデータが入って渡されます。データが含まれる場合はsnapshot.hasDataがtrueになります。データ自体はsnapshot.data["msg"]で取り出すことができます。
ROSトピック通信の確認
受信の確認(Subscribe)
ROSが起動しているマシンでrosbridge_serverを起動し、スマートデバイスでFlutterアプリを起動します。「Connect」ボタンを押すとWebSocketでの接続を行い、下のような状態になります。
続いて、ROSが起動しているマシンで何かしらのメッセージを送ります。
$ rostopic pub /hello_to_flutter std_msgs/String "Hello from Ubuntu"
このように送信したメッセージが表示されていれば成功です。
送信の確認(Publish)
WebSocketで接続するまでは受信の時と同じです。送信の確認をするので、ROSが起動しているマシン上で、rostopicをechoしましょう。
$ rostopic echo /hello_from_flutter
スマートデバイス側で「Hello」ボタンを押したときに、ROSが起動しているマシンで下記のように受信できていれば送信が正常にできています。
data: "Hello from flutter"