2019年8月20日火曜日

ロボット用マイコンにESP32はいかが?


1.ESP32入荷

Aliexpressはとても便利なのですが、納期の遅さだけがいただけません。だいたい2~3週間で到着しますので、面白いデバイスを見つけて注文してもすぐには使えません。また、追加でもう一つ欲しいとなったときもまた3週間待ちです。
待ってる間に他の事に興味が移ったりもしますので、なにか面白そうなものを見つけるととりあえず注文! というような運用になってます。

前ふりが長くなりましたが、とりあえず注文して積んであったのがESP32とESP8266。
良くわからずに注文したのですが、ポイント次の通り。

①Arduino IDEで開発出来るマイコンボード
②Arduinoに使われているATMega系のマイコンより処理が早い
③Wi-fiとBluetoothに接続可能なので、無線でロボットをコントロールできるらしい

届いてから2週間ほど放置でしたが、ようやく開発環境を整えるところまでの目処がついたのでまとめてみます。

ESP系のボードとArduinoのスペック比較をするとこんな感じ。
これだけ見ると圧倒的にESPが良いのですが、Arduinoは開発環境や先人の開発例が多くあるので初心者がとっつきやすいと言う大きなメリットがあります。

 
ESP8266
ESP32
Arduino 
Pro 2560
Arduino
Pro Micro
CPU
Tensilica LX106
Tensilica Xtensa
Dual-Core LX6
ATmega2560
ATmega32U4
動作周波数
80/160MHz
160/240MHz
16MHz
16MHz
RAM
36KB
520KB
8KB
2.5KB
Flash
最大16MB
最大64MB
256KB
32KB
WiFi
802.11n 最大65 Mbps
802.11g 
最大54 Mbps
802.11n 最大150 Mbps
802.11g 
最大54 Mbps
無し
無し
BlueTooth
無し
有り
無し
無し
GPIO
最大17
最大32
最大54
最大16
I2C
最大1
最大2
最大1
最大1
PWM
最大4
最大8
最大15
最大5
UART
最大2
最大3
最大3
最大1
※ArduinoはUNOでは無くて私が好むPro 2560とPro Micro

2.ESP32、ESP8266とは

今回はろくにスペックも調べずに買ったのですが、ESP32、ESP8266はどちらもWi-Fiが使えるマイコンです。
改めて調べてみるとESP32はESP8266の進化版で、Bluetoothも使えたり諸々の機能が少しずつ向上していると言う事で、とりあえず手元のESP8266はもう少しお休み頂いて、当面はESP32のみ触ってみます。

ちなみにESP32を調べてると、ESP-WROOM-32とかESP32 DEVKITとかいろんな名前が出てきますが、このシリーズに使われているSoCの名前がESP32でその中に、LX6っていうCPUが入ってるみたいですね。このESP32いい感じで動かすためのフラッシュメモリやらなんやらを基板に並べたパッケージの名前がESP32-WROOM-32で、DEVKITやなんやかんやつくのは開発ボードの名前ってことらしいです。
今回購入したボードはESP32 DEVKIT V1 30PINタイプでした。


こんなイメージ:DEVKIT V1⊃ESP32-WROOM-32⊃ESP32⊃LX6

開発ボードにもたくさんのバージョンがあってややこしいのですが、いつもお世話になっているこちらのサイトに詳しく記載されています。(DEKOのアヤシいお部屋)

3.ESP32の開発環境を整える前に

ESP32にはもともとファームが入ってるらしいです。
ArduinoなりMicroPythonなりのファーム入れる前に試してみるのと、吸い出して保存しておく方が良さそうです。(私は知らずに消してしまいました)
自分では動作確認できてないですがこちらのサイトで紹介されてます。

4.Arduino IDEでの環境作成

Arduino出身の電子工作初心者としてはとてもありがたい事に、ESP32はArduino IDEで開発可能です。
Arduino IDEをインストール済みであれば、新たなボードのライブラリを追加します。

  1. Arduino IDEを開き、[ファイル]→[環境設定]→「追加のボードマネージャのURL:」の入力ボックスの右のアイコンを押して、次のアドレスを追記します。https://dl.espressif.com/dl/package_esp32_index.json
  2. [OK]ボタンで環境設定を抜けて、[ツール]→[ボード:(現在選択しているボード名)]→[ボードマネージャ]→[esp32 by Espressif Systems]→[インストール]→[閉じる]
  3. [ツール]→[ボード:(現在選択しているボード名)]→[ボードマネージャ]→[ESP32 Dev Module]

だいたいこのサイトを参考にしました。これでArduino IDEからESP32が開発可能となりました。
でもね、ESP32ではArduinoのライブラリの多くが使えないらしいです(servo.hとか・・・)マイコンが違うので当然なのかもしれませんが。。。

5.MicroPythonでの環境作成

ESP32用のライブラリもいろいろ出ている様なのですが、ESP32開発するならMicroPythonが良いらしいとのうわさを聞いたので、MicroPythonの環境も整えてみました。

手順は次の通り。
  1. まずはこのサイトのFlash Download Tools (ESP8266 & ESP32)をダウンロード
  2. 次にこちらのサイトからFirmware for ESP32 boardsをダウンロードします。複数のバージョンがアップされていますので、最新の一つ手前の安定板をダウンロードします。私はesp32-20190529-v1.11.binをダウンロードしました。
  3. 1でダウンロードしたflash_download_tools_v3.6.6を解凍し、flash_download_tools_v3.6.6.exeを実行します。
  4. [ESP32 DownloadTool]を選択し、5~7に従ってDownload(ESP32にファームをDownloadする)条件を入力します。
  5. [...]を選択して、2でダウンロードしたファームを選択
  6. [...]@の右に[0x1000]と入力
  7. CrystalFreq:40M、SPI SPEED:40MHz、SPI MODE:DIO、FLASH SIZE:32Mbit、COM:接続しているポート、BAUD:115200
  8. 今入っているファームを削除するため、[ERASE]を押す。※DEVKIT V1の場合、自動書き込み出来ないので、SYNCと表示されたらボードのBOOTボタンを長押しする。
  9. 新しいファームを入力するため、[START]を押す。※DEVKIT V1の場合、自動書き込み出来ないので、SYNCと表示されたらボードのBOOTボタンを長押しする。
おもにこちらのサイトを参考にしました。

6.うまくファームが入らない場合

・通信速度は115200bpsにしましょう
・CrystalFreqは24Mの方がよいのかも。私はどちらでもいけます。
・DEVKIT V1は、ファームを書きこもうとし始めたらボード上のBOOTボタンを長押し。
 書き込みが始まったら離してOK

7.まとめ

とりあえず開発始めるまでのまとめです。
いまからPythonの勉強を始めます。

2019年7月25日木曜日

四脚歩行ロボットを作る その2 サーボモータ着弾

意外とはやくサーボが届いたので喜び勇んで動作テスト。

1.SG90スペック

購入したサーボモータは小型低価格の定番らしいSG90です。

スペック及び外形寸法はこちら
重量:9g
ストールトルク:1.8kgf・cm(4.8V)
駆動方式:アナログ
ギア材質:POM
回転速度:0.12sec/60°(4.8V)
動作電圧:4.8V
動作角度:180°
PWMサイクル:20msec
制御パルス:0.5~2.4ms
A寸法:34.5mm
B寸法:22.8mm
C寸法:26.7mm
D寸法:12.6mm
E寸法:32.5mm
F寸法:16.0mm

最初はメーカーも知らず購入したのですが、ラベルにはTZTと書いてあります。メーカーサイトを探しても見つからず、ほぼ同じ外観のラベル違いの製品としてTower Pro SG90というものもあります。スペックも同じなのでおそらくどちらかのOEM生産品なのでしょう。
ちなみに私はAli Expressで購入して、一個108円(送料込!!)でした。もちろん国内販売もあり、みんな大好き秋月電子やAmazonにもあります。Amazonは中華業者の出店が多そうなので、Aliで買っても変わらない気はします。
似た製品で、デジタル制御タイプのSG90 Digitalや、360°回転タイプのSG90 360 Degree、金属ギアタイプのMG90S、そのデジタル制御のMG90D、360°回転タイプのMG90D Robot servo 360° Rotation、高トルクタイプのSG92Rなどもあります。

今回はあくまでも制御の練習が目的ですので、Digitalと360は不要、この軽量低トルクで金属ギアは不要ですのでSG90アナログタイプで十分でしょう。
ただし、高トルクタイプのSG92Rは良い選択となる可能性があります。
バランスを崩してから転倒するまでの時間はロボットが小さい程短くなるため、アクチュエーターには素早さが必要とされます。SG90とSG92では、SG92の方が(カタログスペック上は)回転速度が高く設定されています。さらに、負荷時の動作速度はトルクが大きいほど無負荷時に近い速さを実現できるはずですので、高トルクモータの方が有利となるでしょう。

ちなみに重量当たりの出力を計算すると次の通りでした。

SG90:10W/g
SG92:17W/g

2.動作確認① 1モーターでの動作

初サーボモーターですのでとにかく動かしてみましょう。
試験環境は次の通りです。

マイコンボード:Arduino Pro Micro互換機
サーボ:SG90アナログ
サーボ電源:エネループ単四×4本直列(4.8V)



図中では電池2本ですが、実際にはエネループ×4本直列です。
PWM出力はPro Microの5番PINを使用しています。


動画の通り約180°の間を往復運動すれば成功です。
注意点としては、Pro Microの給電はPCからUSBケーブルで行っているのに対し、サーボモータはエネループの電池ボックスから給電している点、電池ボックスのマイナス側とPro MicroのGNDを繋ぐ点ぐらいでしょうか。

テスト動作時のスケッチは以下の通りです。
------------------------------------------------------------------------------
#include <Servo.h>

Servo myservo;

int pos = 0;
void setup() {
  myservo.attach(5);
}

void loop() {
  myservo.write(0);
  delay(500);
  Serial.println("1");
  myservo.write(180);
  delay(500);
}
----------------------------------------------------------------------------

3.動作確認② 複数モーターの同時制御

ロボット製作時には、12個のサーボを異なる速度で、異なる位相へ、同時に動かします。ここでは、脚一本分に相当する3個のサーボを別々に動作させるテストを行いました。
テスト用のスケッチはこちらのサイトを参考にしています。
サーボの一つはロータリーポテンショメータを使って回転速度を変更できる様にしてあります。
サーボ1は5番、サーボ2は6番、サーボ3は9番ピンから出力しています。
ポテンショメータはA0から入力しています。
こちらも電源はエネループ×4個、4.8Vです。


サーボ1(上段)は低速、サーボ2(中段)は低速、サーボ3(下段)はポテンショメータの入力に従い任意の速度で動きます。


テスト用スケッチは以下の通りです
--------------------------------------------------------------------------------------
#include <Servo.h>

Servo myservo1;
float angle1 = 0;
float vel1 = 0;

Servo myservo2;
float angle2 = 0;
float vel2 = 0;

Servo myservo3;
float angle3 = 0;
float vel3 = 0;

int sensorPin3 = A0; 
int sensorValue1 = 0;
int sensorValue2 = 1023;
int sensorValue3 = 0;

void setup() {
  Serial.begin(9600);
  myservo1.attach(5);
  myservo2.attach(6);
  myservo3.attach(9);
}

void loop() {
  vel1 = mapfloat(sensorValue1, 0, 1023, 0.005, 0.01);
  float n1 = (float) sin(angle1);
  float val1 = n1 * 90.0 + 90.0;
  myservo1.write(val1);
  angle1 += vel1 * 5;

  vel2 = mapfloat(sensorValue2, 0, 1023, 0.005, 0.01);
  float n2 = (float) sin(angle2);
  float val2 = n2 * 90.0 + 90.0;
  myservo2.write(val2);
  angle2 += vel2 * 5;

  sensorValue3 = analogRead(sensorPin3);
  vel3 = mapfloat(sensorValue3, 0, 1023, 0.005, 0.01);
  float n3 = (float) sin(angle3);
  float val3 = n3 * 90.0 + 90.0;
  myservo3.write(val3);
  angle3 += vel3 * 5;

  delay(5);
}

float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
--------------------------------------------------------------------------------

4.サーボ動作範囲の確認

SG90は安価なだけあり、動作バラツキがそれなりにある様です。テスト動作を行っていて気になったのは動作範囲のバラツキです。もっとも気になったのは角度指定で0~180°指定しても実際には180°の動作範囲が得られなかった事です。
気になったので、PWM制御のon時間を直接指定して動作させたところ、3つのサーボだけでも動作範囲が以下の様に異なっていました。

カタログ値 0°:500µsec 180°:2400sec
サーボ1  0°:406µsec 180°:2303sec 動作上限:2374sec
サーボ2  0°:544µsec 180°:2430sec 動作上限:2500sec
サーボ3  0°:479µsec 180°:2368sec 動作上限:2404sec

動作開始位置を0°としていますが、パルス幅はばらばら、可動範囲は180°+αとなっています。パルスの側の精度の可能性もありますが、オシロを持っていないので、確認はできていません。

ロボットに実装する際には、それぞれの可動範囲を測定し、0~180°に相当するパルス範囲を固体ごとに定義する必要がありそうです。





2019年7月24日水曜日

四脚歩行ロボットを作る その1.5 姿勢制御について悩み中

1.姿勢制御の構想中

さて、まだサーボモータがようやく届いたのですが、待ってる間に考えていたことなどを覚書。
4脚制御ロボットの制御方法についていろいろ悩み中です。2足歩行の文献はとても多いのですが、4脚歩行は少ない様です。本流ではないのでしょうか。

2足歩行の文献も全く手におえないものが多く、自分の数学能力の低さに気が遠くなります。
面白いので勉強しようとは思いますが、大学の先生たちと張り合う気にはなれないので、もう少し違う素人ならではアプローチをしたいものです。

2足歩行の重心移動や床反力や加速度云々のあたり、ぱらぱら眺めているだけでも面白い文献が多いのですが、これだけ計算やシミュレーションができるなら、ロボットにムーンウォークさせられるんじゃないでしょうか?
などと思っていたらさすがBoston Dynamicsさん、すでにやってましたね。四脚ですが。。

2.モノマネ開発法

専門的な知識の無い身としては、最新の理論や手法を実装するには超えられない壁がいくつもあります。以前にも述べたように、私が目指すはあくまでもモノマネです。生き物らしい動きをするロボットのモノマネ、まずはSpot miniの動画を観察してどんなところに生き物らしさを感じているのかを探しています。
また、どのようにその制御を実現しているのかを想像し、チープな方法へ置き換えていきたいと考えています。

Spot miniの動きを見てわかるのは、周囲の情報を幅広くセンシングして「理解」しているであろうこと。そしてその状況に対応する方法をいくつも想起して選択し実行しているであろうこと。
前進を始める際には、自分の姿勢を見て周りの障害物を見て、問題がなければ後肢を蹴りだし、前肢を前に繰り出しています。体が動くのは最後の結果であって、目で見て脳で考えるのが先にあるはずです。しかしそこには、高度なセンサーと高速な処理装置と高級な知能が必要で、私にはどれもありません。悩ましいところです。

ところで、どこで読んだか忘れてしまったのですが、人間の脳は意思よりも先に準備を開始しているという話を聞いた気がします。バットでボールを打とうと意図する前に脳の運動野の活動が始まっていると言う話だったかと思います。
脳は無意識の段階で環境へのフィードバックを始めているというのもありそうな話ですが、私はこんな風に妄想しています。
ボールを打つ前には、打つ準備を始めている脳とそうでない脳が量子論的に重ね合わさった状態にある。私は今、「ボールを打った」世界にいるので、観測されるのは「ボールを打つ準備を始めた脳」に収束した姿なのだ。
そう考えると、量子コンピュータが実用化されれば、ロボットの姿勢制御も飛躍的に前進するのかもしれませんね。

3.ロボット三重苦

はじめに神は天と地とを創造されました。これにより重力加速度が彼をとらえ、すきあらば転倒させようと手ぐすね引いて待ち受けています。また、神が「光あれ」と言われたにも関わらず、彼はそれを感知することができません。なぜなら彼には視細胞はおろか、フォトダイオードもCCDもCMOSも実装されていないからです。
彼は、視覚センサはおろか、音センサもスピーカーも持ちません。触覚や味覚や嗅覚も持っていません。三重苦どころの話じゃないですね。

しかし、かろうじてシリアル通信チャンネルは持っているので、音声によらない通信手段を持っています。下手すれば火田七瀬のテレパシー並みの能力です。
また、9軸ジャイロセンサと四肢の荷重を計測する圧力センサを実装したいと思っています。圧力センサの取付け位置は、形状的にも動体と脚の間に挟み込むくらいしかなさそうですが、調整は難しそうです。出来れば肉球センサみたいなものが開発されればうれしいのですが。
小鹿だって立つんだから、ウチのロボットだって立つくらいはできるんでしょう、タブン。目を閉じてたって、手探りで歩くくらいならできそうなので、高度なセンサーはオミットでよいでしょう。いわば目が開くまえに、三半規管と足裏の触覚だけで立ち上がろうとする生まれたての小鹿さんです。

あと、良いフィードバック回路を作るためには適切なフィルタリングが必要だろうなというあたりは想像しています。特に圧力センサの値なんてバラツキまくるだろうからかなりの補正が必要だと思われます。どうすればいいのかまだ想像もつきませんが、FFTで済むか、カルマンフィルタとか相補フィルタとかあれやこれやをごにょごにょする必要があるのでしょう。

4.集中制御よりも分散制御

想像しているだけでも、生き物らしく動かそうとすると、ボデーのスペック以上に脳のスペックが重要なことが判ってきました。しかし、数学的な理解も、コーディングの技術も、優れたハードウェアもないことはすでに述べた通り。どんなふうにゴマ化してモノマネに徹するかが、腕の見せ所になりそうです。

ボデーの重心位置を推定して理想的な四肢の軌跡を計算するのは大変なので、基本となる歩様に対して、加速度センサーの値から転倒防止のための補正を行う程度でなんとかバランスが取れないかと思っています。
そもそもArduinoの計算能力はそれほど高く無さそうですので、計算量を減らすうえでもこの方法がよさそうです。出来上がる動作は小鹿さんというより虫になりそうな気もしますが。
処理をするのはマイコンなのでハード的には集中制御ですが、ソフト的には脚一本ごとの動作修正を独立して判断させると言う意味で分散制御的な姿をイメージしています。

5.次はサーボの動作テスト

サーボモータを待ちがてらロボットの構造の検討などもしていたので、次回は構造概要についてまとめようかと思っていたのですが、目の前にサーボがあって素通り出来るほど大人では無いので、次回は動作テストなどを。


2019年7月23日火曜日

四脚歩行ロボットを作る その1 初号機構想

前回、ロボット製作に掛ける思いのたけを述べましたが、早速部品をポチりはじめました。順番が逆な気もしますが、初号機の構想の整理を進めたいと思います。

1.構想

  • 4足歩行。
  • いきなり出来るわけないので、初号機で何を勉強すれば良いのかを勉強する。
  • 歩けなくてもいいが、環境への対応が出来る事。環境からの入力をフィードバックさせる。
ざっくりしてますが今決まっているのはこれだけです。
これをもとに初号機の要件を具体化してみます。

2.とりあえずの仕様

  • 一足あたり3自由度
  • アクチュエータはサーボモータ
  • ジャイロセンサーで姿勢感知
  • 感圧センサーで一肢ごとの負荷検出
  • 制御は慣れてるArduino系ボードを使用
犬の歩行をイメージした場合、当然4本脚になるのですが、現実の肢の自由度は前と後ろで異なるようです。SPOT では足首以降の関節を省略しているので、前肢は肘に相当する部分に1自由度と肩に相当する部分に2自由度が与えられています。また、後肢についても前肢と同じ構造としていますが、実際の犬の関節構造を見ると、足首に相当する関節が大きな働きをしているため、ここに1自由度、膝に1自由度、股関節に2自由度なければならないことがわかります。(本当は前後肢ともに捻じれ方向にプラス1自由度ある)
生き物らしさを追求するうえでは後肢に4自由度を与えたいところですが、関節数が増えると制御が複雑になる点と、前肢と後肢を同じ構造にすることはハード面でもソフト面でも大幅なコスト減につながることを勘案し、初号機は前後肢同構造の3自由度とすることとしました。将来的には4自由度を狙いたいところです。

アクチュエータはまず一般的なサーボモータ駆動とします。初号機は一番安く入手性の高いものを選定します。

生き物らしさを追求するうえで欠かせないのは環境への適応だと思っています。歩行を含めた動作について素人ながらにいろいろと考察していますが、環境(外乱)に対するフィードバックはかなり「生き物らしい」動きを見せてくれるはずです。
4脚歩行ロボットの動画の中でも、Spotが蹴とばされても倒れないシーンを見て驚いた方は多いのではないかと思います。
転びそうになったところへとっさに肢を出して支えるこの動作はぜひ再現してみたい機能の一つです。
どうやって制御するかは後から考えるとしますが、体の姿勢や加速度を感知する6軸ジャイロは必須でしょう。また、アクチュエーターの負荷に応じて押し返すような動きが必須かと思いますので、フィードバック回路が必要でしょう。

制御は使い慣れている Arduino系ボードを使用する予定です。処理能力が足りなければ2号機以降で変更していくことでしょう。

3.部品選定

部品選定の強い味方、Aliexpress。安価な部品を適当に目についたものからカートに放り込みました。初号機なのでトルクが足りないとかセンサ感度が悪いとかそもそも使用出来ないとか、トラブル上等でいきます。失敗は成功のマザー。
サーボモーターは一番安いものを選んだだけです。生き物らしさを出すにはパワーウェイトレシオが不足していると思われますが、よしとします。

Pro Microを使っているのは前に買った在庫があるからです。小さいのが良ければNanoとか、PWM増設ボードが面倒ならMegaでもいいです。Pro MicroではPWM出力ピンが足りませんので、増設ボードとかマイコンを注文しました。使い方はまだ判りません。
センサー類は適当に選択。

サーボブラケットや構造体が決まってませんが、部品、特にサーボモーターの現物を見てから考えます。

4.まとめ

部品選定しましたが、初めて扱うデバイスばかりで出たとこ勝負感が否めません。ただ、社会人になって技術者続けてますが、ある程度構想したら予算をつけてプレ試作ぐらいしないと事業化の可否も判断出来ません。

現物を目の前にして改良を重ねる方が手っ取り早い事が多いですし、結果に重きを置かない趣味の世界に於いては、その過程こそが悦楽です。

次回はサーボモーターが届いてからになるでしょうか。
到着までにサーボモータの制御方法の予習をして待つことにします。

2019年7月20日土曜日

電子工作はじめたらロボット作りたくなった

1.時間が無くなる夢が広がる電子工作

フラシム用のコントローラを作りたくて始めた電子工作ですが、手段が目的化するというか、電子工作自体が楽しくなってしまいました。

使いもしないスイッチやボード類を買い漁ったり、仕様書眺めてどんなことが出来るのか調べてると夢が広がります。とてもすべてやる時間は無いので、やりたいことの方向性を整理してみるとおおよそ次の内容に集約されます。

  • 光りとか音とかアクチュエータとかちょこちょこ使ったインスタレーション的なな何か。
  • IoTでライフハックとかなんとか
  • VRなんかで使うマンマシンインターフェース
  • ロボット



2.どんなロボットが作りたい?

と言うわけで、ロボットにもいろいろジャンルがありますが、一番魅力を感じるのは「生き物と見分けのつかないロボット」です。



これまで見た中で一番「生き物らしい」ロボットと言えばこれです。動きの生々しさはもとより、困ったときにお友達呼んでくるとか、社会性まで想像させますよね。
すげーよBoston Dynamics。

こんなロボットうちにも欲しいし、出来るものなら作ってみたい。

3.生き物らしさとは?

どんなロボット(無生物)を作ろうかと悩むと、生き物や生き物らしさとは何か、という疑問にたどり着くのは逆説的で面白いですね。

私たちがロボットを見て「生き物らしい」と感じるポイントはどこにあるのでしょうか。宝塚の男役や歌舞伎の女形をみて男らしい、女らしいと感じる様に、無意識にパターニングして判別するアルゴリズムが我々の脳にあるのでしょう。

また、生き物らしさと言っても、動き、社会性、コミュニケーション、群れ、知能、自己保存、自己複製。。。いろんな側面が有ります。それぞれをテーマにしたいろんな映画や小説のワンシーンが頭をよぎります。

考え始めるとやりたいことや妄想がどんどん膨らむのですが、ロボットを通じて表現するならば、やはり「動き」の再現が第一のテーマです。ロボットをしこしこ作りながら、コードを書きながら、電気羊の夢について思いを馳せるのが第二のテーマでになるでしょう。

十数年前、2足歩行ロボットのブームがありました。おそらく手ごろなサーボモーターやマイコンなどが手に入りやすくなったという背景があったのでしょう。多くのキットが発売され、2足歩行ロボットが身近になりました。それまでの2足歩行の難しさを知っている人たちには感動を持って迎えられた反面、まだまだ生き物らしさの足りない動きに「この程度か」と思われてしまった部分もあった様に感じます。

ボストンダイナミクスのロボットたちを見ているとASIMOの時代よりもより強く「生き物らしさ」を感じます。センシングや計算能力、アルゴリズムの進歩により、ロボットも次の時代に入ったと感じます。そしてその生き物らしさは、今後ますます強く臭い立つようになるでしょう。

4.要件定義要望書

ロボットをつくった事がないどころか、基礎知識皆無、サーボモーターも触ったことが無いので要件定義など出来るはずもなく。。。
まずはただの要望書です。
  • 高望みしないが、嘘でもいいので生き物らしさを動きで表現したい。
  • Boston DynamicsのSPOT MINIみたいな4足歩行がしたい。
  • 高度な情報処理は出来ないので見た目だけでいい。人工無脳、あるいは中国語の部屋みたいなイメージで、外から見て生きていればいい。
  • ただし動きの出力だけでは生き物らしさはおそらく表現できないので、なんらかなのセンシングとフィードバックはいるだろう。
いまのところのざっくりしたイメージはこんな感じ。
どこまで出来るかわかりませんが、まずは初号機を作って何を勉強すれば良いのかを考えます。

2019年7月19日金曜日

P-51D武装関連コントローラ その5 回路設計とコーディング

DCS側の設定とProMicroのコードも出来たので、これに合わせて回路設計も行います。気をつけるのはなるべく小さく収めたいけど、はんだ付けが難しくない程度に。。。くらいです。

1.ユニバーサル基板の実装図

取扱いの利便性を考え、Arduino Pro Micro及び各スイッチ類への接続はQIコネクタを通して行います。よって基板上にはコネクタと分圧抵抗用回路用お抵抗しか載っていません。
表面
 各コネクタの接続先は次の通り。
・2,3,GNDのコネクタ
 TS3A(ON-OFF-ONトグル)
・4,5,GNDのコネクタ
 TS3B(ON-OFF-ONトグル)
・6,7,GNDのコネクタ
 TS3C(ON-OFF-ONトグル)
・8,GNDのコネクタ
 TS2A(ON-OFFトグル)
・9,GNDのコネクタ
 TS2B(ON-OFFトグル)
・10,Vcc.GNDの5PINコネクタ
 RS4A(4ポジションロータリスイッチ)
・16,GNDのコネクタ
 PBA(リセット用プッシュボタン)
・14,15,GNDのコネクタ
 RS3A(3ポジションロータリスイッチ)
・A0,A1,GNDのコネクタ
 RS3B(3ポジションロータリスイッチ)
裏面
・A2,Vcc,GNDのコネクタ
 VOLA(アナログボリューム)
・A3,Vcc,GNDのコネクタ
 VOLB(アナログボリューム)















デバイス名対応表

2.スイッチ類の配線処理

スイッチ類ヘはリード線をはんだ付けし、収縮チューブで保護。なんとなくフラットケーブル状になる様にして仕舞い易くしています。基板側にはQIコネクタのオスを加締め。2ピン、3ピン等のソケットを使い分けるとより便利。

3.コーディング

その3で動作確認したコードを組み合わせてコーディングします。関数化とかしてませんが、許してください。


#include "Joystick.h"

//シリアルモニタ出力 mon = 1 出力オン/ mon = 0 出力オフ
int mon = 0;

int P1 = 0;
int P2 = 0;
int i = 0;

//アナログ軸数値の初期化
int VOLAValue = 0;
int currentVOLAValue = 0;

int VOLBValue = 0;
int currentVOLBValue = 0;

//仮想ボタンの初期化
int TS3APosition = 0;
int currentTS3APosition = 0;
int PIN2State = 0;
int PIN3State = 0;

int TS3BPosition = 0;
int currentTS3BPosition = 0;
int PIN4State = 0;
int PIN5State = 0;

int TS3CPosition = 0;
int currentTS3CPosition = 0;
int PIN6State = 0;
int PIN7State = 0;

int TS2APosition = 0;
int currentTS2APosition = 0;
int PIN8State = 0;

int TS2BPosition = 0;
int currentTS2BPosition = 0;
int PIN9State = 0;

int RS3APosition = 0;
int currentRS3APosition = 0;
int PINA1State = 0;
int PINA0State = 0;

int RS3BPosition = 0;
int currentRS3BPosition =0;
int PIN15State = 0;
int PIN14State = 0;

int PBAState = 0;
int currentPBAState = 0;

int RS4APosition = 0;
int currentRS4APosition = 0;
int RS4AValue = 0;

//ジョイスティックの開始
Joystick_ Joystick;

void setup() {

//シリアルモニタ開始
  Serial.begin(9600);
  
//ジョイスティックの初期化
  Joystick.begin();

//デジタルピン設定
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);  
  pinMode(5, INPUT_PULLUP);  
  pinMode(6, INPUT_PULLUP);  
  pinMode(7, INPUT_PULLUP);  
  pinMode(8, INPUT_PULLUP);  
  pinMode(9, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);  
  pinMode(A2, INPUT_PULLUP);  
  pinMode(15, INPUT_PULLUP);  
  pinMode(14, INPUT_PULLUP);  
  pinMode(16, INPUT_PULLUP);  
}

void loop() {

//-----------------------------------------------------TS3Aの処理-------------------------------------------------------

//TS3Aの処理
  PIN2State = !digitalRead(2);
  PIN3State = !digitalRead(3);

  if(PIN2State == HIGH){
    currentTS3APosition = 0;
  } else if (PIN3State == HIGH){
    currentTS3APosition = 2;    
  } else {
    currentTS3APosition = 1;
  } 

  if ( currentTS3APosition != TS3APosition){
    for (i=0 ; i<=2 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS3APosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS3APosition);
      Serial.print(" to ");
      Serial.println(currentTS3APosition);
    }

    TS3APosition = currentTS3APosition;
  }

//-----------------------------------------------------TS3Bの処理-------------------------------------------------------

//TS3Bの処理
  PIN4State = !digitalRead(4);
  PIN5State = !digitalRead(5);

  if(PIN4State == HIGH){
    currentTS3BPosition = 3;
  } else if (PIN5State == HIGH){
    currentTS3BPosition = 5;    
  } else {
    currentTS3BPosition = 4;
  } 

  if ( currentTS3BPosition != TS3BPosition){
    for (i=3 ; i<=5 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS3BPosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS3BPosition);
      Serial.print(" to ");
      Serial.println(currentTS3BPosition);
    }

    TS3BPosition = currentTS3BPosition;
  }

//-----------------------------------------------------TS3Cの処理-------------------------------------------------------

//TS3Cの処理
  PIN6State = !digitalRead(6);
  PIN7State = !digitalRead(7);

  if(PIN6State == HIGH){
    currentTS3CPosition = 6;
  } else if (PIN7State == HIGH){
    currentTS3CPosition = 8;    
  } else {
    currentTS3CPosition = 7;
  } 

  if ( currentTS3CPosition != TS3CPosition){
    for (i=6 ; i<=8 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS3CPosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS3CPosition);
      Serial.print(" to ");
      Serial.println(currentTS3CPosition);
    }

    TS3CPosition = currentTS3CPosition;
  }

//-----------------------------------------------------TS2Aの処理-------------------------------------------------------

//TS2Aの処理
  PIN8State = !digitalRead(8);

  if(PIN8State == HIGH){
    currentTS2APosition = 10;
  } else {
    currentTS2APosition = 9;
  } 

  if ( currentTS2APosition != TS2APosition){
    for (i=9 ; i<=10 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS2APosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS2APosition);
      Serial.print(" to ");
      Serial.println(currentTS2APosition);
    }

    TS2APosition = currentTS2APosition;
  }

//-----------------------------------------------------TS2Bの処理-------------------------------------------------------

//TS2Bの処理
  PIN9State = !digitalRead(9);

  if(PIN9State == HIGH){
    currentTS2BPosition = 12;
  } else {
    currentTS2BPosition = 11;
  } 

  if ( currentTS2BPosition != TS2BPosition){
    for (i=11 ; i<=12 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentTS2BPosition,HIGH);

   if (mon == 3){
      Serial.print("Position ");
      Serial.print(TS2BPosition);
      Serial.print(" to ");
      Serial.println(currentTS2BPosition);
    }

    TS2BPosition = currentTS2BPosition;
  }

  
//-----------------------------------------------------VOLAの処理-------------------------------------------------------

//VOLAの処理
  currentVOLAValue = 1023 - analogRead(A3);
  if (currentVOLAValue < VOLAValue - 1 || currentVOLAValue > VOLAValue + 1){
    if (mon == 1){
      Serial.print("VOLA = ");
      Serial.println(currentVOLAValue);
    }
    Joystick.setXAxis(currentVOLAValue);
    VOLAValue = currentVOLAValue;
  }

//VOLBの処理
  currentVOLBValue = 1023 - analogRead(A2);
  currentVOLBValue = currentVOLBValue * 1.014;
  
  if (currentVOLBValue < VOLBValue - 1 || currentVOLBValue > VOLBValue + 1){
    if (mon == 1){
      Serial.print("VOLB = ");
      Serial.println(currentVOLBValue);
    }
    Joystick.setYAxis(currentVOLBValue);
    VOLBValue = currentVOLBValue;
  }

//-----------------------------------------------------RS3Aの処理-------------------------------------------------------

//RS3Aの処理
  PINA1State = !digitalRead(A1);
  PINA0State = !digitalRead(A0);

  if(PINA1State == HIGH){
    currentRS3APosition = 13;
  } else if (PINA0State == HIGH){
    currentRS3APosition = 15;    
  } else {
    currentRS3APosition = 14;
  } 

  if ( currentRS3APosition != RS3APosition){
    for (i=13 ; i<=15 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS3APosition,HIGH);

   if (mon == 2){
      Serial.print("Position ");
      Serial.print(RS3APosition);
      Serial.print(" to ");
      Serial.println(currentRS3APosition);
    }

    RS3APosition = currentRS3APosition;
  }


//-----------------------------------------------------RS3Bの処理-------------------------------------------------------

//RS3Bの処理
  PIN15State = !digitalRead(15);
  PIN14State = !digitalRead(14);

  if(PIN15State == HIGH){
    currentRS3BPosition = 16;
  } else if (PIN14State == HIGH){
    currentRS3BPosition = 18;    
  } else {
    currentRS3BPosition = 17;
  }

  if ( currentRS3BPosition != RS3BPosition){
    for (i=16 ; i<=18 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS3BPosition,HIGH);

   if (mon == 2){
      Serial.print("Position ");
      Serial.print(RS3BPosition);
      Serial.print(" to ");
      Serial.println(currentRS3BPosition);
    }

    RS3BPosition = currentRS3BPosition;
  }

//-----------------------------------------------------PBAの処理-------------------------------------------------------
  currentPBAState = digitalRead(16);
  if (currentPBAState != PBAState){
    if (currentPBAState == LOW){
//      Joystick.sendState();
      Serial.println("reset!");
    }
  } 
  PBAState = currentPBAState;  

//-----------------------------------------------------RS4Aのanalog処理-----------------------------------------------------

//anarog値読み取り
  RS4AValue = analogRead(10);

  if( RS4AValue >=0 && RS4AValue <= 80 ){
    P1 = 23;
  }
  if( RS4AValue > 160 && RS4AValue <= 330){
    P1 = 22;
  }
  if( RS4AValue > 400 && RS4AValue <= 590){
    P1 = 21;
  }
  if( RS4AValue > 650 && RS4AValue <= 850){
    P1 = 20;
  }

  if (P1 != P2) {
    P2 = P1;
    delay(50);
  } else {
    currentRS4APosition = P2;
  }

  if (currentRS4APosition != RS4APosition){
    for (i=20 ; i<=23 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS4APosition,HIGH);

    if (mon == 1){
      Serial.print("Position ");
      Serial.print(RS4APosition);
      Serial.print(" to ");
      Serial.println(currentRS4APosition);
    }

    RS4APosition = currentRS4APosition;
  }
  delay(50);
}


2019年7月17日水曜日

P-51D武装関連コントローラ その4 分圧抵抗回路でスイッチ数節約

前回予告した分圧抵抗回路の説明です。

分圧抵抗回路についてはこちらのサイトで詳しく解説されていますが、今回は簡易版の回路構成で十分ですので、少し改変して以下の通りの回路としました。

1.分圧抵抗回路によるスイッチング読み取り

分圧抵抗回路の考え方は、可変抵抗器とほぼ同じです。VccとGNDの間に任意の数の抵抗を配置し、それぞれの部位の電圧を図る事で、何個めのポジションにあるのかを推定します。回路図にすると次の通りです。
分圧抵抗回路
抵抗値の数を増やせばスイッチングのポジションも増やせます。
ここでスイッチが切り替わった際のアナログ入力の読込値を推定すると、このようになります。

  • スイッチが最上段の場合、1024×3/4=768
  • 上から2番目の場合、1024×2/4=512
  • 上から3段目の場合、1024×1/4=256
  • 上から4段目(最下段)、1024×0/4=0
すでにこの回路で実装しているのですが、、、良く考えたら一番上の抵抗いらなかったんじゃまいか!
まぁいいです。忘れましょう。

この様に、スイッチングによってアナログ入力値が段階的に変化するため、それぞれのポジションに対応したスイッチが押された事にしておくというのが分圧抵抗スイッチの考え方です。


2.分圧抵抗回路の限界

ただし、この方法には限界もあります。
一つ目は、ボタンの同時押しに対応出来ないと言う事。どれか一つなら簡単ですが、複数のボタンが押された場合、合成抵抗になってしまうのでどのボタンとどのボタンが押されたのかが識別できません。
今回は、ロータリースイッチへつなぐため、二つ以上のスイッチが同時にONにならないと言う条件がありますので問題ありません。

二つ目は、スイッチングの瞬間が不安定と言う事です。ロータリースイッチの特性上、スイッチが切り替わる瞬間、どこにもつながっていない状態、もしくは2ポジション同時につながっている場合などが発生します。今回は、切り替直後の不安定な時間帯の入力をソフト的にオミットしています。

三つ目はデジタル処理よりも時間がかかるらしいこと。
かかるらしいのですが、今回の用途では気にならないし、感じもしません。

3.分圧抵抗回路用のコーディング

このコードではPINナンバー10でアナログ値を読み取り、それぞれのポジションに応じてボタン20,21,22,23のON/OFFを送信しています。PC側のボタン認識は21,22,23,24です。

ポイントはポジション認識の為のアナログ値の閾値に一定の幅を持たせている点と、ポジションが変わった際にはdelayコマンドで50ms待たせて再確認している点です。スイッチ切り替え時のノイズはこれでほぼキャンセルされます。

#include "Joystick.h"
//シリアルモニタ出力 mon = 1 出力オン/ mon = 0 出力オフ
int mon = 0;

int RS4APosition = 0;
int currentRS4APosition = 0;
int RS4AValue = 0;
int P1 = 0;
int P2 = 0;
int i = 0;


//ジョイスティックの開始
Joystick_ Joystick;

void setup() {

//シリアルモニタ開始
  Serial.begin(9600);
  
//ジョイスティックの初期化

  Joystick.begin();

}


void loop() {
//anarog値読み取り
  RS4AValue = analogRead(10);

  if( RS4AValue >=0 && RS4AValue <= 80 ){
    P1 = 23;
  }
  if( RS4AValue > 160 && RS4AValue <= 330){
    P1 = 22;
  }
  if( RS4AValue > 400 && RS4AValue <= 590){
    P1 = 21;
  }
  if( RS4AValue > 650 && RS4AValue <= 850){
    P1 = 20;
  }

  if (P1 != P2) {
    P2 = P1;
    delay(50);
  } else {
    currentRS4APosition = P2;
  }

  if (currentRS4APosition != RS4APosition){
    for (i=20 ; i<=23 ; i++){
      Joystick.setButton(i,LOW);
    }
    Joystick.setButton(currentRS4APosition,HIGH);

    if (mon == 1){
      Serial.print("Position ");
      Serial.print(RS4APosition);
      Serial.print(" to ");
      Serial.println(currentRS4APosition);
    }

    RS4APosition = currentRS4APosition;
  }
  delay(50);
}


4.まとめ

Arduino Pro Microのスイッチ不足解消法は他にもいくつかあるのですが、今回はお試しで実装が一番楽と思われる方法を選択してみました。
思った通りの結果が得られたのでかなり満足です。

次回は全体のコーディングと基板実装を行い、いよいよ完成です。(もう完成してるけど)

ESP32でスマホとシリアル通信 その2 BluetoothでLチカ

1.BluetoothでLチカ制御 前回、Bluetoothでの双方向通信に成功しましたので、ついでに少しスケッチを修正してスマホからESP32のLチカを制御した様子が以下の動画です。 撮影が悪くて画面が良く見えませんが、"1"を送信...