1. はじめに
キャタピュラ式の車輪を持つ"カムロボ"を "obniz" で制御。
それに、WiFiで映像を送れる Webカメラを構成できる"M5カメラ" を取り付け、遠隔制御ロボットのシステムを実現しました。
まずは、完成形紹介動画をご覧ください。
※動画撮影は便宜上同じ部屋を使ってやりましたが、実際は別の部屋にあるカムロボを制御する形で開発しました。
2階から1階のカムロボを制御してもほぼ同程度の遅延で制御することはできています。
2. システム構成
主に以下の2つを組み合わせたシステム構成となっています。
(1) obniz でロボットの制御をおこなう
(2) M5カメラで映像を飛ばし obniz での操作画面の横に表示する (LAN 内 WiFi)
(1) は図中の オレンジ色の線でデータの流れを示していて、obnizクラウドを通じて通信されます。ここで扱う制御データとは、ブラウザの表示用データや obniz 制御のためのデータとなります。
obniz はカムロボに取り付けられた、モーター、LED、距離センサー等を制御します。
(2) は図中の紫色の線でデータの流れを示していて、WiFi 経由で LAN 内でルータを介して通信されます。
ここで扱う映像データとは、ブラウザ表示用の映像ストリームとなります。実際はM5カメラはWebカメラサーバとなっています。
これらの仕組みにて、図のように2階の部屋にある PC から1階の別の部屋にあるカムロボを遠隔操作できるようなシステムとなります。
3. ハードウェア
3-1. 使用部品
一部組付けてしまっているものもありますが、全部品並べると大体こんな感じとなります。
表にすると以下の通りです。
品名 | 型番 or SPEC | 個数 | 参考価格 | 備考 |
---|---|---|---|---|
obniz基板 | obniz 1Y | 1 | 8,000 | contest提供品を使用 |
カムプログラムロボット | TAMIYA 楽しい工作シリーズ No. 227 | 1 | 3,170 | 通称カムロボ |
M5カメラ | M5STACK-U038 | 1 | 2,050 | |
距離センサ | RCWL-1601 | 1 | 950 | HC-SR04互換 |
白色LED | 不明 | 2 | 50 | |
抵抗 | 100Ω | 2 | 10 | |
モバイルバッテリ | Cheero Canvas 3200mAh | 1 | 2,500 | 手持ちの物で可 ※1 |
M3ビス/ワッシャ/ナット | M3x6 | 9 | 90 | |
M2ナット/ワッシャ/ナット | M2x15 | 2 | 30 | |
スペーサー | M3x25 | 4 | 230 | |
スペーサー | M2x7 | 2 | 30 | |
基板固定スペーサ | 1 | 30 | ||
コネクタセット | GROVE 4pin コネクタ | 1 | 60 | 半分に切って使用 |
ヘッダピン セット | ヘッダピン + コネクタ | 1 | 25 | 距離センサ延長接続用 |
L型金具 | 不明 | 4 | 110 | |
下敷き | PET製が良 | 1 | 110 | (A4サイズをカット) 基板固定用※2 |
参考価格で複数個入りの物は個数で割って必要数を乗算した価格です。
※1モバイルバッテリは、収まるのであれば手持ちのもので大丈夫ですが、ある程度の容量が必要になります。
DAISO で 300円で売っていたこのモバイルバッテリでは容量不足で動きませんでした。
※2下敷きはある程度強度があって、絶縁出来て、加工もしやすい(はさみで切れる、今回は使っていませんがホットボンドも乗りやすい)のでお手軽に使えてお勧めです。
3-2. 組み立て
3-2-1. カムロボ
手順が多く複雑ですが頑張って作っていきます。
カムロボは本来プログラムロボットという位置づけで、プログラムバーという機構で物理的に進行方向を制御する仕組みがあります。
今回は、obniz によりモーターを直接電気的に制御するため、プログラムバーは不要でかつ、スペースの邪魔になる為、その部分は省いて組み立てます。
具体的には、公式組み立て説明書の以下の手順が不要となります。
- ①プログラムバーギアケースの取り付け
- ③サイドフレームサポートの取り付け
⇒obnizを乗せた土台やモバイルバッテリが干渉するので省きます。 - ⑥ステアリングレッグの組み立て
- ⑦ステアリングレッグの取り付け
- ⑧シャフトの取り付け
- ⑬ルーフの組み立て の左半分(電池ボックス部分)
- ⑮配線の前に
- ⑯コードの配線
- ⑰バッテリーの取り付け
3-2-2. ベースボード
obniz や、M5カメラ固定、距離センサを取り付けるためのベースボードを作成します。
あまり重たい部品はなく、可動部ではないため強度も不要です。
最低限部品を支えるだけの強度があればいいので、プラ板(下敷き)に穴を開け切って制作しました。
CADで作成し、両面テープでプラ板に張り付けた状態で穴あけたり切り取ったりすると、寸法もばっちり合うのでお勧めです。(色々試行錯誤したので、記事内写真のベースボードには余分な穴も開いていますが...)
印刷すると実寸になる PDFファイルを提供しようと思いましたが、このサイトは記事に PDFを張り付けれませんでしたので断念して画像にしました。やってみた人は、サイズを合わせ込んで印刷するなり、同じようにCADで描くなりしてください。
3-2-3. ベースボード組み立て
ベースボードにまずは obniz を取り付けます。
一応絶縁の為、下側2本はポリカワッシャーをかましています。透明なので見にくいですが。
次にM5 カメラと距離センサを取り付けます。距離センサーは 1mm の穴が開いていますが 1mm のビスやスペーサーは手に入らなかったので、0.7mm のアルミ針金とスペーサーで固定しました。(力業!?)
3-3. 接続
接続はこんな感じです。
但し、fritzing 用の obniz 部品が 1y 用の物がありませんでしたので、電源用の 3pin ヘッダの部分に強引に部品をくっつけて作画しています。ですので LCD の位置や ESP32の向きなど微妙に異なりますし、線色も決められないところがあって写真とは異なっています。
1y ではない方の obniz を使用する場合は、左側の マイクロB-USBコネクタ横の J1が使用できます。その際は+端子を間違えないようにだけ注意です。
具体的な接続先は下記のとおりです。
obniz端子 | 接続先 | 端子 | 線色 | 備考 |
---|---|---|---|---|
0 | 左モータ | + | 黄 | |
1 | 左モータ | - | 青 | |
2 | 右モータ | + | 黄 | |
3 | 右モータ | - | 青 | |
4 | 左LED | アノード側抵抗 | 白 | |
5 | 左LED | カソード | 黒 | |
6 | 右LED | アノード側抵抗 | 白 | |
7 | 右LED | カソード | 黒 | |
8 | HC-SR04 | GND | 黒 | |
9 | HC-SR04 | ECHO | 灰 | |
10 | HC-SR04 | TRIGGER | 紫 | |
11 | HC-SR04 | vcc | 赤 | |
- | バッテリ | GND | 黒 | GROVEコネクタ経由でM5-Camera |
3.3V | 未接続 | |||
+ | バッテリ | 5V | 赤 | SW, GROVEコネクタ経由でM5-Camera |
表中の線色は、写真に合わせた色となりますが、電源の赤黒以外は手持ちのものを自由に決めてもらっていいと思います。
3-4. 全体組み立て
4. ソフトウェア
プログラムとしては、 M5カメラのプログラム (c/c++) と obniz のプログラム (html + java script) の2段構成です。
それ以外にも、webページ側のアイコンイメージデータが必要です。
4-1. M5カメラプログラム
Arduino環境(ESP32 or M5シリーズ用環境設定済み)で書き込みます。
ボード設定は [ツール] - [ボード:xxxx] - [Esp32 Arduino] - [ESP32 Wrover Module] を選びます。
M5カメラ接続語 [ツール] - [シリアルポート] でポートを選択しておきます。
ソフトは [ファイル] - [スケッチ例] - [ESP32] - [Camera] - [Camera Web Server] で選択したスケッチ例をベースにします。
基本的にサンプルソフトのまま、 WiFi 環境だけ自分の物に書き換えます。
具体的には以下の設定 "ssid" と "password" を自宅のルーターに設定したものに置き換えます。
.CameraWebServer.c
・・・中略・・・
const char* ssid = "*********";
const char* password = "*********";
・・・中略・・・
※ 公式サンプルベースの為、変更が必要な部分だけを抜粋しています。
4-2. obniz 側ソース
obniz の基本制御画面にカメラ画像をインラインフレーム ("iframe" )で埋め込んだ構成となっています。
自分のクラウドスペースにプログラムを作成し、以下を張り付けて編集します。
変更箇所は以下の通り、
-
obniz ID
"// ⇐自分の obniz の ID を入力" と書いている部分を自分の obniz の IDに置き換えます。 -
M5Cemara の IPアドレス
src="http://192.168.xxx.xxx:81/stream"
の部分の xxx のところを、検出されたIPアドレスで置き換えます。
IPアドレスは、M5カメラを PCに繋いで、シリアル通信ソフトや Arduino のシリアルモニター(115200bps設定) で確認できます。赤枠のところに出力されるのが IPアドレスです。
M5のサンプルプログラムは DHCP用なので、自動的にIPが決まりますが、環境によっては毎回変わる可能性もあります。(私のところは毎回同じになりますが)
その場合はM5のプログラム側を静的IPに改造するか、 IP Scanner ソフトで調べるといいです。
CamRobot_WebCam_RemoteControl.html
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
/>
<link rel="stylesheet" href="/css/starter-sample.css" />
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script
src="https://unpkg.com/obniz@3.13.0/obniz.js"
crossorigin="anonymous"
></script>
</head>
<body>
<div id="obniz-debug"></div>
<table border="0">
<tr>
<th>
<div class="Control">
<h3 class="Control">Control</h3>
<table border="0">
<tr>
<td><input type="image" id="go-foreleft" src="./Arrow-ForwardLeft.jpg" width="25%" alt="左前"> </td>
<td><input type="image" id="go-forward" src="./Arrow-Up.jpg" width="25%" alt="前進"> </td>
<td><input type="image" id="go-foreright" src="./Arrow-ForwardRight.jpg" width="25%" alt="右前"> </td>
<td><input type="image" id="led-on" src="./Led-On.jpg" width="25%" alt="LEDオン"> </td>
</tr>
<tr>
<td><input type="image" id="turn-left" src="./Arrow-TurnLeft.jpg" width="25%" alt="左回転"> </td>
<td><img type="image" id="empty1" src="./Empty.jpg" width="25%" alt=""> </td>
<td><input type="image" id="turn-right" src="./Arrow-TurnRight.jpg" width="25%" alt="右回転"> </td>
<td><img type="image" id="empty2" src="./Empty.jpg" width="25%" alt=""> </td>
</tr>
<tr>
<td><input type="image" id="go-backleft" src="./Arrow-BackwardLeft.jpg" width="25%" alt="左後"> </td>
<td><input type="image" id="go-backward" src="./Arrow-Down.jpg" width="25%" alt="後進"> </td>
<td><input type="image" id="go-backright" src="./Arrow-BackwardRight.jpg" width="25%" alt="右後"> </td>
<td><input type="image" id="led-off" src="./Led-Off.jpg" width="25%" alt="LEDオフ"> </td>
</tr>
<tr>
</tr>
</table>
</div>
</th>
<th>
<center>
<iframe id="camera_inline"
width="325"
height="245"
src="http://192.168.xxx.xxx:81/stream"
allow-scripts
>
</iframe>
</center>
</th>
<tr>
</Table>
<script>
var obniz = new Obniz("xxxx-xxxx"); // ⇐自分の obniz の ID を入力
obniz.onconnect = async function() {
// ===== [変数初期化] =====
var stopping_dist = false;
var moving_forward = false;
// ===== [端子初期設定] =====
var motorL = obniz.wired("DCMotor", {forward:1, back:0});
var motorR = obniz.wired("DCMotor", {forward:2, back:3});
var ledL = obniz.wired("LED", { anode: 4, cathode: 5 });
var ledR = obniz.wired("LED", { anode: 6, cathode: 7 });
var dist_sensor = obniz.wired("HC-SR04", {gnd:8, echo:9, trigger:10, vcc:11});
// ===== [LED初期化] =====
ledL.off();
ledR.off();
// ===== [モーター初期化] =====
motorL.power(80);
motorR.power(80);
motorL.move(false);
motorR.move(false);
stop();
// ===== [ディスプレイ初期化] =====
obniz.display.clear();
obniz.display.print("Connected");
// ===== [停止] =====
function stop() {
obniz.display.clear();
obniz.display.print("STOP");
motorL.stop();
motorR.stop();
//ledL.off();
//ledR.off();
}
// ===== [左前] =====
$("#go-foreleft").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("ForwardLeft");
if ( stopping_dist == true ) { // 障害物検知で止める
stop();
}
else {
moving_forward = true;
motorL.power(40);
motorR.power(80);
motorL.forward();
motorR.forward();
}
});
$("#go-foreleft").on("mouseup", function() {
stop();
});
// ===== [前進] =====
$("#go-forward").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("Forward");
if ( stopping_dist == true ) { // 障害物検知で止める
stop();
}
else {
moving_forward = true;
motorL.power(80);
motorR.power(80);
motorL.forward();
motorR.forward();
}
});
$("#go-forward").on("mouseup", function() {
stop();
});
// ===== [右前進] =====
$("#go-foreright").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("ForwardRight");
if ( stopping_dist == true ) { // 障害物検知で止める
stop();
}
else {
moving_forward = true;
motorL.power(80);
motorR.power(40);
motorL.forward();
motorR.forward();
}
});
$("#go-foreright").on("mouseup", function() {
stop();
});
// ===== [左回転] =====
$("#turn-left").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("TurnLeft");
motorL.power(60);
motorR.power(60);
motorL.reverse();
motorR.forward();
});
$("#turn-left").on("mouseup", function() {
stop();
});
// ===== [右回転] =====
$("#turn-right").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("TurnRight");
motorL.power(60);
motorR.power(60);
motorL.forward();
motorR.reverse();
});
$("#turn-right").on("mouseup", function() {
stop();
});
// ===== [左後進] =====
$("#go-backleft").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("Left");
motorL.power(40);
motorR.power(80);
motorL.reverse();
motorR.reverse();
});
$("#go-backleft").on("mouseup", function() {
stop();
});
// ===== [後進] =====
$("#go-backward").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("Back");
motorL.power(80);
motorR.power(80);
motorL.reverse();
motorR.reverse();
});
$("#go-backward").on("mouseup", function() {
stop();
});
// ===== [右後進] =====
$("#go-backright").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("Right");
motorL.power(80);
motorR.power(40);
motorL.reverse();
motorR.reverse();
});
$("#go-backright").on("mouseup", function() {
stop();
});
// ===== [LEDオフ] =====
$("#led-off").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("LedOff");
ledL.off();
ledR.off();
});
// ===== [LEDオン] =====
$("#led-on").on("mousedown", function() {
obniz.display.clear();
obniz.display.print("LedOn");
ledL.on();
ledR.on();
});
// ===== [距離センサーチェック処理] =====
obniz.onloop = async function(){
// 距離を測定
const dist = await dist_sensor.measureWait();
if (dist) {
if (dist < 150) // 15cm 未満になったら
{
stopping_dist = true;
if (moving_forward) { // 前進中なら止める
moving_forward = false;
stop();
}
}
else {
stopping_dist = false;
}
}
await obniz.wait(100);
}
}
</script>
</body>
</html>
4-3. アイコンイメージ
気に入ったアイコンが見つからなかったり使用条件など厳しいところも多かったりと、そんなことに縛られるのも嫌だったため、アイコンイメージは Excel の作図機能で作成して画像処理ソフトで加工(回転サイズ・位置揃え程度)した自作オリジナルのものです。
使ってもらえるように、画像を張り付けてみたのですが記事自体が冗長になってしまいました。(すみません)
記事内の画像を右クリックし、[名前を付けて画像を保存] を選択し、画像の左上部緑バックのキャプションについている名前を付けて保存します。
それらのデータを、obniz クラウドの html ファイルを置いたのと同じ場所に保存します。
但し、これらのアイコンデータをこの記事に関連するもの以外の目的に無断で使用するのはご遠慮ください。
5. 操作説明
5-1. 起動方法
obniz のクラウドに保存している html を開き、
① [実行] の横の ▼ を押して
② [新しいタブで実行] を選ぶと立ち上がります。
5-2. 操作方法
起動すると以下の写真のような操作画面が表示されます。
青枠部分が操作アイコンで、赤枠部分が M5カメラからの映像となります。(カメラ画像には怪しい人物が映り込んでいるのは気にしないでください(゚Д゚;))
直感的に操作できるようなアイコンにはしていますが、番号を振ってそれぞれ説明します。
まずは動かしてみたほうが早いとは思いますが…
① 左前進します
② 前進します
③ 右前進します
④ 左回転(反時計回り)します
⑤ 右回転(時計回り)します
⑥ 左後進します
⑦ 後進します
⑧ 右後進します
⑨ LEDを点けます
⑩ LEDを消します
①~⑧は押し続けている間動作し続け離すと止まります。
離した位置がアイコンから離れてしまうと止まりませんので(課題)ご注意ください。
6. 残課題
今回はまず、obniz を使ってカメラ画像を見ながらロボットの遠隔操作ができるところを目指しました。
基本的な動作は実装・確認できましたが、コンテスト締め切りまでの限られた時間の中で今回は断念した以下の課題があります。
折をみてやってみたいと思いますが、本記事を見た方の方で、色々試行錯誤して挑戦いただくのも面白いかと思います。
① 押しっぱなし状態の件
離した位置がアイコンから離れてしまうと止まらなくなります。
② スマホでの操作
LED オン/オフ 等単発で動作するものはスマホでも動作しましたが、制御アイコンの方は押し続けるとメニューをポップアップしてしまうという、スマホに特化した使用の為制御できませんでした。別途対策が必要です。
③ カメラ映像はローカルネットワーク通信
カメラの映像はローカルLANないでの通信になりますが、外部ネットワークにつなげれると外出先から家の中のロボットを操作できるようになります。(夢!?)
外部ネットワークにもつなぎに行くにはある程度の帯域幅が必要なのとプロトコルをどうするかや、M5 カメラ側のソフトの作り直しが必要です。
④ カメラ映像側は非暗号化通信
最近のブラウザは、どれも同じ html ページ内に SSL 対応(暗号化対応)済みの物と、SSL非対応の物を混在できないようになっています。M5カメラ側のサンプルは SSL非対応の物でしたので、必然的に obniz 側の通信も非対応になっています。対応サウルには M5カメラ側の改造も必要ですが、何よりもSSL対応により暗号化処理の為カメラ側の負荷が高くなる可能性があります。
⑤ 後側センサー
前向きにはセンサーが付いていて、障害物を検知すると止まりますが、後ろ向きだと止まってくれず、色々なものをなぎ倒していきます。後ろ側にもセンサが欲しいところです。まずは、複数ある GND 端子を共通化してピンの空きを作る等しないといけないです。
⑥ 他の車体にも挑戦
色んな車体を試行錯誤する中で、一番安定して動作したカムロボを今回は最終的に選びました。他にも Zoomo や RoverC を使ってもみましたが、そちらも安定した動作が出来たら記事にしてみたいと思います。
⑦ ジョイステックでの操作
もっと簡単に操作したいので、ジョイスティックに対応したいと思っています。もう一台の obniz 使うか、M5StickC あたりを使えばできそうですが、時間が足りませんでしたので、残課題としています。
投稿者の人気記事
-
TakSan0
さんが
2021/05/16
に
編集
をしました。
(メッセージ: 初版)
-
TakSan0
さんが
2021/05/16
に
編集
をしました。
(メッセージ: 図の追加と追加。本文の誤記少し修正)
-
TakSan0
さんが
2021/05/16
に
編集
をしました。
(メッセージ: システム構成への図と説明を追加、他修正)
-
TakSan0
さんが
2022/01/05
に
編集
をしました。
(メッセージ: ライセンス設定等更新)
-
TakSan0
さんが
2022/01/05
に
編集
をしました。
ログインしてコメントを投稿する