R_Castle が 2020年03月28日11時52分27秒 に編集
コメント無し
記事種類の変更
セットアップや使用方法
本文の変更
前回記事から時間が空いてしまいましたが、今回はArduinoUnoでサーボモーターを複数動作させる時のポイントなど説明したいと思います。 ラジコンサーボを動かす事についての基本的な説明については前回の記事もご覧いただけると幸いです。 記事リンク:[ラジコンサーボモーターをArduinoで動かしてみる(その1)](https://elchika.com/article/6559c562-aefb-414f-befe-a3ab83ae50ee/) モノを持つロボットアームや二足歩行するロボットを作ろうと思った場合、必ず複数の動作部分が必要になります。 この動作部分にラジコン用サーボモーターを使用する事で、簡単なプログラムで自由に制御する事が出来る様になります。 本題に入る前に話しておきたいと思いますが、最近ではラジコン用のもの以外に「ロボット用」サーボモーターも販売されています。 ロボット用モーターは特殊な形状や強力な力を発生させることが出来たりと言う利点も多いのですが、これらのモーターはラジコン用のパルス制御ではなくI2Cや専用バスを用いたデータ通信で制御する方式が多く採用されている為、今回説明する方式とは制御方法が異なりますので注意して下さい。 まずは入手性も高く取り扱いも容易なラジコン用のサーボモーターを利用した方式を先にご紹介して、ロボット用モーターについての話は実際に複雑なロボットを作る時に別の記事でご紹介したいと思います。 # サーボモーターの動作 一般的なラジコン用サーボモーターの動作について、図でもう一度確認します。  通常は制御命令0度で時計回りに回りきった状態、90度で反時計回りに動作して動作範囲の中央に来ます。 基本的にはこの位置を中央=ニュートラルとして、左右等しく回転する様に取り付けて動かします。そして制御命令180度で反時計回りに回りきった位置になります。 尚、ラジコン用のサーボモーターは模型飛行機や自動車に使用した際のトラブル防止を目的に機械的なストッパーが内部に組み込まれている事が多く、実際の動作範囲は140度~160度位になっている物があります。ロボットに使用する際は事前に確認する等注意して使用してください。 # 簡単な歩行ロボットを想定した使い方 それでは、複数のサーボモーターを使って実際に動くロボットを想定したプログラムを作って見ましょう。 今回は大変申し訳ありませんが3D-CADで作ったCGでご覧ください。  このロボットは、市販の手踊り人形を被せて歩き回るロボットをイメージしてデータを作っています。 先に申し上げた、より高度なサーボ制御の仕方を紹介する時には実物を作って歩かせてみたいと考えています。 画像中の黒い四角い部分がサーボモーターで、全部で4個使用する構成になっています。 また、複数のサーボモーターを動作させる場合は、Arduino他マイコンボードから供給される電力では不足して動作不良を起こしますので、別途用意したバッテリーから給電する様にしましょう。 # マイコンボードの接続 図に接続の仕方について示します。 実機を製作する際にも、空中で電線を直接接続したりなどせず小型の物でも良いのでこういったブレッドボード等を使用する様にしましょう。  ブレッドボードに電池ボックスから+、GNDの電線を接続します。 次に、各サーボモーターの電源線を+、GND線をGNDの共通ラインにそれぞれ接続します。 ==この時電池ボックスは電源スイッチ付きのものを使用する事をお勧めします。この電源スイッチで全体の電源ON/OFFを制御する事が出来ます。==  電源が配線出来たら信号線を接続します。今回はArduinoUnoの8番~11番ピンに順番に接続しています。 これからプログラムの説明に入りますが、各サーボモーターの取り付け位置は下図の①~④の位置にあるものとして進めて行きます。  図を確認したら、サーボモーターを使える様に準備して、4つのサーボモーターが中立=90度の位置になるプログラムを作成してダウンロードします。 ```arduino:4つのサーボモーターを動作させるプログラム(準備) /*****************************************************************/ #include <Servo.h> // サーボ制御のライブラリを読み込みます。 Servo servo1; // 制御するサーボ名1~4を定義します。 Servo servo2; Servo servo3; Servo servo4; void setup() { servo1.attach(8); // 8番ピンの出力をサーボ制御用に宣言します。 servo2.attach(9); // 9番ピン宣言。 servo3.attach(10); // 10番ピン宣言。 servo4.attach(11); // 11番ピン宣言。 } void loop() { walk(90, 90, 90, 90); // 90度(ニュートラル) } void walk(int angle1, int angle2, int angle3, int angle4) { servo1.write(angle1); servo2.write(angle2); servo3.write(angle3); servo4.write(angle4); delay(800); } /*****************************************************************/ ``` プログラムを動作させて各モーターの位置調整を行います。 大きくずれている時はロボットのサーボモーターの取り付け位置を再組立して調整します。 もしズレが1度2度程度であった場合、プログラム中に用意した変数を調整する様にした方が、効率が良い場合もあります。 # 歩行プログラムを作る 今回用意した様なロボットの場合、図中上部にある球の様な部分にそれなりの重量が無いと安定して歩くことが出来ません。目安としてはロボットの重心がサーボモーターよりも上に来る様な重りを乗せます。 (図で説明) 簡単なのは電池ボックスを上に乗せる方式です。バランスの調整が出来たら動作プログラムを作ります。 一番簡単な「一歩」の歩行パターンは次の様になります。 |No|動作パターン| |---|---| |1|左足に重心を乗せる| |2|右足を前に出す| |3|右足に重心を乗せる| |4|左足を前に出す| |5|重心を戻す| これを、4個のサーボモーターの動作に置き換えると下表の様になります。 |No|動作パターン|各サーボモーターの動作| |---|---|---| |1|左足に重心を乗せる| ③のモーターを時計回り、④のモーターを時計回り(少し多め)| |2|右足を前に出す|①のモーターを反時計回り、②のモーターを反時計回り| |3|右足に重心を乗せる(1)|③のモーターを反時計回り、④のモーターを反時計回り(少し多め)| ||右足に重心を乗せる(2)|③のモーターを反時計回り(少し多め)、④のモーターを反時計回り| |4|左足を前に出す|①のモーターを時計回り、②のモーターを時計回り| |5|重心を戻す|③のモーターを時計回り(少し多め)、④のモーターを時計回り| この、全部で5ステップの動作命令をプログラムしてみましょう。 ```arduino:4つのサーボモーターを動作させるプログラム(一歩分) /*****************************************************************/ #include <Servo.h> // サーボ制御のライブラリを読み込みます。 Servo servo1; // 制御するサーボ名1~4を定義します。 Servo servo2; Servo servo3; Servo servo4; void setup() { servo1.attach(8); // 8番ピンの出力をサーボ制御用に宣言します。 servo2.attach(9); // 9番ピン宣言。 servo3.attach(10); // 10番ピン宣言。 servo4.attach(11); // 11番ピン宣言。 } void loop() { // Step.0 又は Step.5「中立位置/重心を戻す」 // 90度(ニュートラル) walk(90, 90, 90, 90); // Step.1「左足に重心を乗せる」 walk( 90, 90, 75, // 時計回り(今回は-15度) 70 // 少し多めの時計回り(今回は-20度) ); // Step.2「右足を前に出す」 walk( 105, // 反時計回り(今回は15度) 105, // 反時計回り 75, 70 ); // Step.3「右足に重心を乗せる」 // (1)重心を中央へ walk( 105, 105, 90, // 中立位置 90 // 中立位置 ); // (2) 重心を右脚へ walk( 105, 105, 110, // 少し多めの反時計回り(今回は+20度) 105 // 反時計回り(今回は+15度) ); // Step.4「左足を前に出す」 walk( 90, // 時計回り(基準位置へ戻す) 90, // 時計回り(基準位置へ戻す) 110, 105 ); // Step.5 はループの先頭に戻る } void walk(int angle1, int angle2, int angle3, int angle4) { servo1.write(angle1); servo2.write(angle2); servo3.write(angle3); servo4.write(angle4); delay(800); } /*****************************************************************/ ``` モーター部分だけのモデルで5ステップの状態を示すと、以下の様になります。        以上のステップを繰り返して動作します。 今回のプログラムのままだとひたすら直進していってしまうので、途中で向きを変えたりバックしてみたりといった動作パターンを作る必要があります。 簡単な構成のロボットであれば、この様に動作命令に直接角度の値を入れてしまえば良いのですが、他にもいくつか記述の手法がありますので、以下に紹介します。 # 中立位置からの増減命令で制御する方法 サーボモーターを中立の位置にする為に、通常は90度を基準の角度にしています。この値を中心に時計回り=値をマイナス、反時計回り=値をプラスする様な命令の仕方でも同様に制御する事ができます。 もっと沢山のサーボモーターを使用する場合は基準角度からの増減ではなく「今の角度」を変数に保管しておき、そこからの増減で命令するやり方でも良いと思います。 ```arduino:4つのサーボモーターを動作させるプログラム(一歩分) /*****************************************************************/ #include <Servo.h> // サーボ制御のライブラリを読み込みます。 #define BASE_ANGLE 90 Servo servo1; // 制御するサーボ名1~4を定義します。 Servo servo2; Servo servo3; Servo servo4; void setup() { servo1.attach(8); // 8番ピンの出力をサーボ制御用に宣言します。 servo2.attach(9); // 9番ピン宣言。 servo3.attach(10); // 10番ピン宣言。 servo4.attach(11); // 11番ピン宣言。 } void loop() { // Step.0 又は Step.5「中立位置/重心を戻す」 // 90度(ニュートラル) walk(0, 0, 0, 0); // Step.1「左足に重心を乗せる」 walk( 0, 0, -15, // 時計回り(今回は-15度) -20 // 少し多めの時計回り(今回は-20度) ); // Step.2「右足を前に出す」 walk( 15, // 反時計回り(今回は15度) 15, // 反時計回り -15, -20 ); // Step.3「右足に重心を乗せる」 // (1)重心を中央へ walk( 15, 15, 0, // 中立位置 0 // 中立位置 ); // (2) 重心を右脚へ walk( 15, 15, 20, // 少し多めの反時計回り(今回は+20度) 15 // 反時計回り(今回は+15度) ); // Step.4「左足を前に出す」 walk( 0, // 時計回り(基準位置へ戻す) 0, // 時計回り(基準位置へ戻す) 20, 15 ); // Step.5 はループの先頭に戻る } void walk(int angle1, int angle2, int angle3, int angle4) { servo1.write(BASE_ANGLE + angle1); servo2.write(BASE_ANGLE + angle2); servo3.write(BASE_ANGLE + angle3); servo4.write(BASE_ANGLE + angle4); delay(800); } /*****************************************************************/ ``` # mapコマンドで回転方向を管理する方法 プログラムに「map」コマンドという物があります、普段はある変数の値を一定の範囲に引き延ばしたり狭めたりする為に使用するのですが、これを応用してサーボモーターの回転方向を管理する事が出来ます。  例えば①、②のサーボモーターを両方とも「中立を0度として、外に開く時をプラス、内側に向く時をマイナス」にしたい場合、mapコマンドの記述は次の様になります。 ```arduino:サーボモーター①、②のmapコマンド Sv11 = map(Sv01,-90,90,180,0); // ①は反転 Sv12 = map(Sv02,-90,90,0,180); // ②は同方向 ``` 次に、③、④のサーボモーターを「中立を0として、外側が下がる時をプラス、上がる時をマイナス」にしたい場合のmapコマンドは次の様な記述になります。 ```arduino:サーボモーター③、④のmapコマンド Sv13 = map(Sv03,-90,90,0,180); // ③は同方向 Sv14 = map(Sv04,-90,90,180,0); // ④は反転 ``` これらを組み込んだ、一歩の動作プログラムは次の様になります。 ```arduino:4つのサーボモーターを動作させるプログラム(mapコマンドによる一歩分) /*****************************************************************/ #include <Servo.h> // サーボ制御のライブラリを読み込みます。 Servo servo1; // 制御するサーボ名1~4を定義します。 Servo servo2; Servo servo3; Servo servo4; void setup() { servo1.attach(8); // 8番ピンの出力をサーボ制御用に宣言します。 servo2.attach(9); // 9番ピン宣言。 servo3.attach(10); // 10番ピン宣言。 servo4.attach(11); // 11番ピン宣言。 } void loop() { // Step.0 又は Step.5「中立位置/重心を戻す」 // 90度(ニュートラル) walk(0, 0, 0, 0); // Step.1「左足に重心を乗せる」 walk(0, 0, -15, 20); // Step.2「右足を前に出す」 walk(15, -15, -15, 20); // Step.3「右足に重心を乗せる」 // (1)重心を中央へ walk(15, -15, 0, 0); // (2) 重心を右脚へ walk(15, -15, 20, -15); // Step.4「左足を前に出す」 walk(0, 0, 20, -15); // Step.5 はループの先頭に戻る }
void walk(int angle1, int angle2, int angle3, int angle4) {
void walk(int Sv01, int Sv02, int Sv03, int Sv04) {
// 回転方向を定義するサブルーチン
int angle11 = map(angle1, -90, 90, 180, 0); // ①は反転 int angle21 = map(angle2, -90, 90, 0, 180); // ②は同方向 int angle31 = map(angle3, -90, 90, 0, 180); // ③は同方向 int angle41 = map(angle4, -90, 90, 180, 0); // ④は反転
int Sv11 = map(Sv01, -90, 90, 180, 0); // ①は反転 int Sv21 = map(Sv02, -90, 90, 0, 180); // ②は同方向 int Sv31 = map(Sv03, -90, 90, 0, 180); // ③は同方向 int Sv41 = map(Sv04, -90, 90, 180, 0); // ④は反転
servo1.write(angle11); servo2.write(angle21); servo3.write(angle31); servo4.write(angle41);
servo1.write(Sv11); servo2.write(Sv21); servo3.write(Sv31); servo4.write(Sv41);
delay(800); } /*****************************************************************/ ``` 左右の腕や足で何個ものサーボモーターが対照的なレイアウトで配置された時など、動作させたい向きと回転方向が異なると混乱の基になります。 そういった際にmapコマンドを利用するのは大変有効ですが、角度命令毎に毎回実行しなければならないという手間も発生しますので注意が必要です。 次回はサーボモーターから一度離れて、模型用のDCモーターをマイコンで制御する方法についてまとめたいと思います。