編集履歴一覧に戻る
norippyのアイコン画像

norippy が 2021年02月28日14時28分56秒 に編集

初版

タイトルの変更

+

LEDマトリクスを搭載した置き型ロボット

タグの変更

+

ESP32

+

Neopixel

+

LEDマトリクス

+

サーボモーター

+

ロボット

+

秋葉原2021

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

製作品

本文の変更

+

# はじめに LEDマトリクスって面白いですよね。LEDのドットで色々な表現ができて、LEDの集まりだけど、一つのインターフェースとして使えます。LEDならではの味があって個人的に好きです。 また最近のロボットを見ていると、高解像度のディスプレイでロボットの表情を表現しているものがいくつもあり、なんだか自分もそんな表情を変える事ができるロボットが作りたくなりました。 そこでサーボモータによる動きと、LEDマトリクスによる味のある表現でユーザーを楽しませる置き型ロボットを作ることにしました。 ちなみに、私は筐体設計を主に行ってきたエンジニアなので、独学で基板やソフトウェアの開発をしてきました。 その点を踏まえて温かい目でみてもらえればと思います。 # 1. 回路設計をする まずはどんなロボットが作りたいかシステムを構想し、それを回路図に起こしていきます。 最初のステップとなるシステム構成は以下になります。 ![キャプションを入力できます](https://camo.elchika.com/d9be3a5f209b94ebbbe2a0e8e6fd9cfdc8634a74/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f64396339373432342d316163652d343463352d623038302d333765353933373834326363/) システムの構想ではUSBから電源を給電し、3.3Vの生成や、USBシリアル変換を使ってESP32と通信ができるようにたり、 5Vを生かして大量のNeoPixelを動かしたり、サーボを動かしたりすることを考えました。そしてオーディオの信号を入力とし、その信号の振幅によってインジケータLEDを動かすと言う事も考えています。 これを元に回路図を作ります。 ![制御基板](https://camo.elchika.com/c5620ff9b7131108a93ee6106a3f3832676c4b42/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f30643465373830322d346139382d343533612d616330392d396535313233303236663266/) ![NeoPixel基板(全体)](https://camo.elchika.com/2dbfa6bb53f1edcfb6f61f1e40d5ccc815b5c7fe/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f33303738306135362d613337612d343437612d396533332d633536363531643934666637/) ![NeoPixel基板(拡大)](https://camo.elchika.com/f13195538d6910e7d72026fc5ed2222ca8fdd8d5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f30383539363236362d383964312d343836312d623039642d633837313338313537333863/) 回路図は画像にすると潰れてしまって見えないと思うので、githubにてPDFを公開しております。(一番下にリンクあります) ## 1-1.制御回路の詳細な構成 回路図ではシステム構成通りの回路を作っています。電源はUSB type Cでとることにしました。type CですがUSB2として使用する想定だったので、キチンと設計できていません。(反省) 本当はプルダウン抵抗などをつける必要があります。 自分もあとで見直しできるようにこちらに参考情報を貼っておきます https://www.atmarkit.co.jp/ait/articles/1809/04/news016_2.html 使用しているUSBシリアル変換ICは、ESP32のDevKitに使われているCP2102ではなく、秋月電子などでも売られているFT231XSを使用しました。基板を作る時に、違うものを使うと動かないんじゃないかと心配になるかもしれませんが、こちらのICでも問題なく動作します。 ちなみに今までいくつかCP2102を使って基板を作ってきましたが、そこそこの頻度でレギュレーター部分が壊れます。ICは火傷するくらい熱々だけど通信ができるといった事が発生するので、そういったリスクを避けるためにFT231XSにしたのもあります。 ESP32は3.3V系ですが、サーボモーターやNeoPixelは5V系です。各部品への電源はUSBから直接5Vを送るようにしました。しかし制御は3.3V系。もちろん3.3V系の制御信号を送る事で理論上動くはずですが、リスクを減らすためにに制御信号はFETをスイッチングして5V系で動くようにしました。 音声信号はオーディオのLまたはRの信号とGNDをAD変換し、I2CでESP32に振幅をデジタル信号として送るようにしました。 またこのロボットでは将来的にCAN通信の勉強がしたいと思い回路を作っています。しかしとりあえず実装した機能のため、特に開発をしていないので、無視してください。 ## 1-2.NeoPixel基板について 実際に回路図を見てもらうとわかるのですが・・・LEDが大変なことになっています! 拡大したNeoPixel基板の回路図にあるように直列でNeopixelを280個並べています。この回路図を作るだけでも大変です。 LEDマトリクスは市販のものを使わず、あえて自作することにしました。理由は、目の細かいLEDマトリクスが欲しかったのと、表現の幅を広げるために16x16サイズで作りたかったからです。 LEDはとりあえずNeopixelを並べて、電流を使わなかったら十分イケると判断し、Neopixelで実現することにしました。ちなみにLEDマトリクスと一緒に音声信号の入力があると点灯するインジケーターLEDもつけることに。トータルでNeopixelを280個使いました。 使用したNeopixelは **WS2812B-2020の16mA仕様**。同じサイズのLEDはworldsemi以外の会社もコンパチ品を出していたり、電流が違うモデルがあったりと複数存在していいますが、それら全ての動作確認を行った結果、**WS2812B-2020の16mAがベスト**という結論に至りました。 ちなぜこんな事が分かったかというと、ロボットを完成させるにあたり、各LED280個を実装したPCBAを作り、同じ制御信号を流す実験をしました。 その実験でWS2812B-2020の16mA以外のLEDは、200個以上繋げると、LEDが点灯しなかったり、途中から全く制御が出来ず、デモモードみたいな動作をするということが分かりました。 このベストに行き着くために、複数のLEDマトリクスを作りました。すごく時間かかりました・・・ これが実際に起きた事の一例です(WS2182Bの5mAモデルの基板)。 @[twitter](https://twitter.com/Norio_Delux/status/1307511225933160448?s=20) # 2.モデリングをする ロボットをつくるなら、ちゃんと筐体も作ろうということで、3D CADを使ってモデリングを行いました。ここで先に描いた回路図の部品が実装できるような基板の形状もセットで検討します。 図のように、実際に使用する部品を3Dモデルで置きながら干渉がないか確認し、設計を進めます。3D CADではコネクタや LED,ICなど大きい部品を基板パターンに無理がないか想像しながらおくように心がけています。 今回はちょっと前に購入したレーザーカッターを活用したいと考え、アクリルパーツを使うようなデザインにしました。 ![キャプションを入力できます](https://camo.elchika.com/42a91232bc1e0eed043b05e542cbe0990ab0b803/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f38663763363761642d353365622d343035342d383836372d653136303866393761313262/) ![キャプションを入力できます](https://camo.elchika.com/c8d1acdb888073a7140e9a1c0e7af29a77571a8a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f64336538663063632d633132662d346335392d393736312d383861646366643937333737/) ロボットの形はあくまで参考です。こちらのページ参考に開発をされる場合は自由にモデリングして、自分だけのロボットを作ってみましょう。 もちろん参考にできるよう、3DデータをSTEPにしてgithubに公開しております。ご興味ありましたらご覧ください。 # 3.部品の製造、基板の実装をする 今回勉強がてら、基板を作って自分で実装するのではなく、elecrowでPCBAまでをお願いしてみました。つまり部品の調達もお任せしています。私の方ではgerberや部品の位置情報、部品の型番なども含めて書かれた部品表(BOM)を提供し、製造をお願いしています。(BOM詳細はgitを参照)。 部品番号やスペックだけではなく、どこで売っているか、部品はSMDかTHDか、またコンパチ部品を使用することが可能かどうかも記述しておきます。elecrowはこれを見て、部品を集め、基板の製造〜実装まで行ってくれます。基板の製造だけではなく、面倒な時はPCBAまでお願いするのはアリです。 ![キャプションを入力できます](https://camo.elchika.com/6bd447ce5fa0d7f833d6bfdf4d7d1128d1a45a66/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f35383336623836322d616465612d346635642d626662662d366338633265373561373263/) 発注したら、出来上がりを待つだけ。PCBAはこんな感じでできてきます。PCBAを作る時は捨て基板をつけるようにしています。これは基板のリフローをする時に捨て基板を利用して固定したり、実装が容易になる、置く向きを間違えないなど製造時にメリットがあるという理由で存在しています。 ![PCBA](https://camo.elchika.com/7ba60cd4a5150a3bd94a3287f7e9c1c55b6231f5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f32306631336562612d646365352d343362632d396631652d326163386461663438336461/) 自分が新卒で電子機器の筐体設計をしていた時に基板の外形をモデリングしていました。会社の文化なのか、こういった複数の基板を1つにまとめる集合基板のレイアウトを考えるのも筐体設計をする機械設計者の仕事だったため、覚えました。1つの基板にまとめることで基板の製造をコストを削る事ができますし、自分で実装する時にクリームハンダを使う場合、1回で全てのランドにハンダをのせる事が出来、時間短縮になります。 こうやってPCBAを製造してもらったものの、LEDは前述のとおりトラブルがあり、作ってもらったものの結局自分で実装することに。 LEDマトリクス用のLEDと、基板意外に必要なサーボモーターはaliexpressで購入しました。 サーボモーター:K-power製 DMM0090 https://ja.aliexpress.com/item/33033826351.html?spm=a2g0s.9042311.0.0.27424c4doktrKk このサーボモーターを選んだ理由は、そこそこパワーがあるのと、たまたま以前使用した事があったというだけです。 SG-90でも良かったのですが、重さもあるので、トルクが欲しいと思いこちらを選んでいます。 LEDマトリクス用LED:WS2812 12mA 0.24W / chip モデル https://ja.aliexpress.com/item/32876864437.html?spm=a2g0s.9042311.0.0.27424c4dzJKh1l LEDマトリクスで使用するLEDはaliexpressで購入し、自分で実装しました。 実装もトースターリフローで行っており、クリームハンダを塗った基板に部品を置いていくだけにしました。 ![LEDマトリクス実装途中の写真](https://camo.elchika.com/5b81c73086d8957caeb05c94bdb7e487264840b8/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f31346165313533332d663064332d343766322d613639612d663832323933373164663438/) 実装するには実体顕微鏡必須です。そして、280個のLEDを実装するので、相当時間がかかります。写真はLEDマトリクスを置き終えた達成感から撮影したものですが、置くだけで1時間以上かかりました。修行です笑 ちなみにLEDマトリクスを作る時は、リフロー必須です。1つずつ半田付けすると時間もかかりますし、周りの既に実装したLEDを変形させたり傷をつける恐れがあるからです。 ![パーツを並べた写真](https://camo.elchika.com/b3de2d358eaf761e1534a41afeec32356ecf4696/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f65373535663534382d343234622d343333322d623231312d393039633538353339353631/) モデリングした筐体部品はalibabaで見つけたサプライヤーに製造をお願いして作ってもらっています。(一部内部部品は家にある光造形の3Dプリンタで出力しました。) 今回はABSライクの材料で製造をしました。ナイロンのSLSより安いよとお勧めされてこちらを使いました。 調べてみるとマテリアルジェッティングという3Dプリントの方式になるようです。表面はとても滑らかです。 アクリルの部品は黒の半透明と、赤のアクリルを使っています。これらは既にモデリングした3Dデータから2DのDXFを作り、レーザーカッターで加工して部品を作っています。 そしてロボットを組み立てるために必要なネジ、これはM2 x6のBタイト、M3x8のBタイトというセルフタッピングネジを使っています。このネジは家電製品などでよく使われているネジです。これらのネジはwilcoで購入する事が出来ます。 どんなネジを使おうかと悩んだらwilcoをチェックしてみてください。Bタイトなどは下穴のサイズなどの情報もしっかり描かれているため、設計に困ることはありません。 https://wilco.jp/ これで全ての部品が揃ったので、いよいよ組み立てです。モデリングデータをみてもわかるように基本的にネジ締めだけで組み立てができるようにしました。とはいえ左右のインジケータLEDだけアクリルと筐体を接着剤で固定しています。 ![キャプションを入力できます](https://camo.elchika.com/42b613b7e9cb7c58ae94c7f88f8123e490b2272b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f61616666353265652d623762302d343238332d383461632d656334353338363130313232/) ![キャプションを入力できます](https://camo.elchika.com/6ec690ec66eb7db3ae2aefede03b57487e6aa543/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f66616463356331632d343663332d346238332d393365642d633037666533613866356261/) ![キャプションを入力できます](https://camo.elchika.com/afb46dda7fa44abadb297d733800d51c4302ae6a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f65356532646137352d383366322d346231662d616563332d393136633430316138383833/) ロボットができました!!! # 4.ロボットの制御ソフトウェア開発 基板が出来たらいよいよ組み込みソフトウェアの開発です。今回はArduino IDEを使ってArduinoで開発をしています。 ライブラリも豊富ですし、多少下のレイヤーがさわれなくても動くようなものだと判断したためです。ESP-IDFで開発できるのが理想ですが、Arduinoのライブラリが充実している事もあり、プロトタイプを開発するにはArduinoで丁度良かったりもします。 ロボットを動かすための制御ソフトウェアは大きく3つに分かれています。 ・LEDマトリクスを制御する(LEDを点灯!) ・オーディオ信号をセンシングし、LEDを光らせる ・サーボモーターを制御する これらの制御について1つずつ説明します。コードもGitHubに公開しています。 ## 4-1.LEDマトリクス制御 市販のLEDマトリクスではなく、NeoPixelを256個直列に接続して作ったLEDマトリクス、この制御にはFastLEDライブラリを利用しました。 LEDマトリクスはNeoPixelという事で、1つのGPIOのみで制御をしています。 回路図を見るととてもシンプルですね。 LEDに関係するコードを以下にまとめました。説明はコメントに記載しております。 コードでは直列のLEDをLEDマトリクスに見立てる変換をしています。 ```arduino:ledControl.ino #include <FastLED.h> #include "picture_preset.h" //FAST LED SETTING #define COLOR_ORDER RGB //RGB表記なのかGRB表記なのか #define CHIPSET WS2812B //使用するLEDの種類 #define BRIGHTNESS 30 //基準となる明るさ #define NUM_LEDS 280 //LEDの個数 //LEDの個数 #define L_LEVEL 5 //インジケータがLの時に光るLEDの個数 #define M_LEVEL 9 //インジケータがMの時に光るLEDの個数 #define H_LEVEL 12 //インジケータがHの時に光るLEDの個数 #define MATRIX_X 16 //LEDマトリクスのX軸の方向の個数 #define MATRIX_Y 16 //LEDマトリクスのY軸方向の個数 void showVoiceLED(int val) { int startLeft = 256;//256番目のLEDがLEDだと int startRight = 268; //268番目から int level = 0; if (val == 0) { for (int i = 0; i < H_LEVEL; i++) { leds[startLeft + i] = CRGB::Black; leds[startRight + i] = CRGB::Black; } } else { switch (val) { case 1: level = L_LEVEL; break; case 2: level = M_LEVEL; break; case 3: level = H_LEVEL; break; } for (int i = 0; i < level; i++) { leds[startLeft + i] = CRGB::White; leds[startRight + i] = CRGB::White; } for (int i = level; i < H_LEVEL; i++) { leds[startLeft + i] = CRGB::Black; leds[startRight + i] = CRGB::Black; } } FastLED.show(); // FastLED.delay(5); } //LEDマトリクスに表示する絵を呼び出す(picture_preset.h)をチェック void showPicture(uint32_t array[]) { for (int i = 0; i < (MATRIX_X * MATRIX_Y); i++) { leds[i] = CRGB(byte(array[i] >> 16), byte(array[i] >> 8), byte(array[i])); } FastLED.show(); } //すべてのLEDをOFFにする void resetLED() { for (int i = 0; i < NUM_LEDS; i++) { leds[i] = CRGB::Black; } FastLED.show(); delay(300); } //プリセットのPictureを呼び出す void CheckPicture(int num) { Serial.print("call here and icon number is :"); Serial.println(num); switch (num) { case ASRADA: showPicture(asrada); break; default: break; } } ``` LEDマトリクス の関数を利用すれば、ドット絵を描く事が出来ます。 でもこれ、真面目に1つずつドットを定義して、色を指定してとやっていたら大変ですよね。ですがこれを簡単に実現できるアプリケーションが存在します。 私はLED Matrix Studioを利用しました http://maximumoctopus.com/electronics/builder.htm こちらのアプリを使えばドット絵から、各座標のパラメータが並んだ配列を作る事が出来ます。 下のソースは一例のangryという配列です。よく見ると、絵になっているのが見えるかと思います。 ```arduino:picture_preset.h uint32_t angry[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x0000FF00, 0x0000FF00, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000FF00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; ``` この配列を読み込むと、こんな感じで出力されます ![ドット絵を表示](https://camo.elchika.com/a241253bc9e1d159b1769891f704e44b304ac0b7/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33656464613963382d313761642d343261342d393139372d6165623338643933333138642f32336630333639302d663362322d343161652d383961332d633638343231656233393166/) これでLEDマトリクスは完成。ちなみにちょっと頑張ってこれを作ってみました。 @[twitter](https://twitter.com/Norio_Delux/status/1314957991255236608?s=20) こんな感じで直列のLEDで作るLEDマトリクスでもプログラムで文字スクロールなんて事もできるようになります。 ## 4-2.オーディオ信号をセンシングし、LEDを光らせる オーディオ信号はI2Cでマイコンと通信するADコンバーターを使いました。 VIN+にオーディオのRight/Left,そしてVIN-にオーディオのGNDを繋いでいます。 これでオーディオの振幅を取得する事が出来ます。 動きとしてはこんな感じです。 @[twitter](https://twitter.com/Norio_Delux/status/1306611948084064256?s=20) オーディオ信号なので、出力元の大きさをあげれば振幅は大きくなるため、より信号が取りやすくなります。信号が取れたらそれに合わせてインジケーターLEDを転倒させます。このLEDはマトリクスLEDの後ろに接続しているので、257個目からのLEDを制御するようにコードを作成します。 この機能のコードは以下になります。 ```arduino:AD control.ino //MCP3425(AD変換)の設定 #define MCP3425 0x68 #define REGISTOR 0b10011000 #define ALPHA 0.1 float before = 0; byte wingLevel = 0; bool firstTime = true; #define DOWN -1 #define UP 1 #define ERROR_SIZE 50000 /*I2Cを使ったオーディオ信号取得〜LED点灯のやりかた void setup(){ //I2Cの設定 ADsetup(); } //時間管理 #define AUDIO_INTERVAL 50 //50msecごとにチェックする unsigned long audioCheckTime = 0; void loop(){ if (millis() - audioCheckTime >= AUDIO_INTERVAL) { checkAudioSignal(); //ここで信号をチェック→LED点灯処理までを行う audioCheckTime = millis(); } } */ //I2Cのセットアップ関数 void ADsetup() { //I2Cの設定 Wire.begin(); Wire.beginTransmission(MCP3425); Wire.write(REGISTOR); Wire.endTransmission(); } float checkAudioSignal() { Wire.requestFrom(MCP3425, 2);//2byte分のデータを取得する。 while (Wire.available() < 2) {} float value = (Wire.read() << 8) + Wire.read(); if (value >= ERROR_SIZE) { return 0; } float res = ALPHA * before + (1 - ALPHA) * value; if (firstTime) { //初回時のLED点灯回避 before = value; firstTime = false; } ConvertLEDLevel(res, before); //ここでLEDのレベルを分類する。 before = value; Serial.println(res); } void ConvertLEDLevel(float value, float before) { float sound_res = value - before; if (sound_res <= DOWN) { if (wingLevel > 0 ) { wingLevel--; if(wingLevel == 0 && value > 0){ wingLevel = 1; } } } else if (sound_res >= UP) { if (wingLevel < 3) { wingLevel++; } } else if (sound_res == 0) { if (wingLevel > 0) { wingLevel--; } } showVoiceLED(wingLevel); //LEDの点灯をする } ``` ## 4-3.サーボモーターを制御する LEDだけではなくロボットとして首を左右に触れるようになっているのが、このロボットの特徴。 ただサーボモーターを制御するだけなのもつまらないので、首の動き始めと動き終わりをゆっくりにする制御をしてみました。 サーボモータの制御といえばシンプルなのは角度を指定するだけで、最速で動かすということをやられている方が多いかと思います。 ですが、こちらも制御の仕方を工夫すればこんなふうに動かす事ができるんです。 @[twitter](https://twitter.com/Norio_Delux/status/1305872660593868800?s=20) 実際のコードはこちらになります。 ```arduino:servo_control.ino //SERVO SETTING #define MIN 900 // サーボの仕様書に記載されている最小 #define MAX 2100 //サーボの仕様書に記載されている最大パルス幅 #define INI_POSITION 100 //初期の座標 #define INTERVAL 20 //サーボモーターの制御パルスを送る間隔 //サーボモーターの動かし方 /* moveServoSigmoid(目標角度、目標角度まで移動にかける時間(msec)) これで基本的に動かす事が出来ます。また、関数の中で現在の角度も読むようになっているので、現在角度を気にしなくてもOKです */ //servo control void moveServoSigmoid(int servo, int motionTime) { if (myservo.attached() == false) { //1つでもattachされてなかったら全部されていない AttachServo(); } if (motionTime == 0) { myservo.write(servo); return; } //リミッターを通す servo = CheckDeg(servo); int targetPulsemyservo = map(servo, 0, 180, MIN, MAX); int pulseNowmyservo = myservo.readMicroseconds(); // Serial.println(pulseNowmyservo); //ここで必要なパルスの幅を計算する int msPulsemyservo = abs(targetPulsemyservo - pulseNowmyservo); //シグモイド関数 //-100から100の決められた数字の中でモーションを間引く //式は result = 1/(1 + (exp(-0.12 * x))); //それぞれのステップ数を決める double motionStep = 200 / (motionTime / INTERVAL); //ゴールまでのステップが決まっていて、それに対してintervalで割ってる double alpha = -0.12; for (double i = -100; i < (motionTime / INTERVAL); i = i + motionStep) { //各サーボの制御を行う //Head_Yaw if (pulseNowmyservo > targetPulsemyservo) { myservo.writeMicroseconds(pulseNowmyservo - msPulsemyservo / (1 + (exp(alpha * i)))); } else if (pulseNowmyservo < targetPulsemyservo) { myservo.writeMicroseconds(pulseNowmyservo + msPulsemyservo / (1 + (exp(alpha * i)))); } else { //do nothing } //最後にdelay(1パルス分の20msec)する。 delay(INTERVAL); } // DetachServo(); } // servo method void AttachServo() { myservo.attach(SERVO, MIN, MAX); } void DetachServo() { delay(300);// myservo.detach(); } int CheckDeg(int ServoDeg) { if (ServoDeg > 180) { ServoDeg = 180;//一旦初期値の変更をする } else if (ServoDeg < 0) { ServoDeg = 0; } return ServoDeg; } void resetPosition() { moveServoSigmoid(INI_POSITION, 1500); } void presetMotion(int val) { switch (val) { case SMILE: resetPosition(); break; case SAD: moveServoSigmoid(INI_POSITION + 80, 1000); moveServoSigmoid(INI_POSITION - 80, 1500); moveServoSigmoid(INI_POSITION + 80, 1000); resetPosition(); break; case ANGRY: moveServoSigmoid(INI_POSITION + 30, 300); moveServoSigmoid(INI_POSITION - 30, 600); moveServoSigmoid(INI_POSITION + 30, 600); moveServoSigmoid(INI_POSITION - 30, 600); moveServoSigmoid(INI_POSITION + 30, 600); moveServoSigmoid(INI_POSITION - 30, 600); resetPosition(); break; case SHOCK: moveServoSigmoid(INI_POSITION + 60, 800); moveServoSigmoid(INI_POSITION - 60, 1600); moveServoSigmoid(INI_POSITION + 60, 1600); moveServoSigmoid(INI_POSITION - 60, 1600); moveServoSigmoid(INI_POSITION + 60, 1600); moveServoSigmoid(INI_POSITION - 60, 1600); resetPosition(); break; default: break; } } ``` # 最後に LEDマトリクスの制御やサーボモーターも変わった動きをさせたりと、今までやった事が無かったソフトウェアの開発も新しいことに挑戦できる面白いロボットを今回作ってみました。 ただこのロボットはどう使うか探りながら作ったところがあるため、作ってみるとやはり仕様面で考えなければならない事が多々あることに気付きました。 このロボットで実現した機能は他の形のロボットや、そもそもLEDマトリクスを作ってみたい方の参考になるかと思います。 記事にもあるようにこちらの開発資料はGitHubにも公開していますので、参考にしていただければ幸いです。 GitHub : https://github.com/norioike/rino-Original-Robot.git