文書の過去の版を表示しています。
6-1 サーボモータ(PWM・ライブラリ)
モータについての基礎知識
モータの種類
回転運動をする電子部品といえば「モータ」です。モータにはいくつかの種類があります。
DCモータ
直流電流をかけるとひたすら回転するモータです。
ラジコンカーやミニ四駆などに使われています。
DCモータには2つの端子があり、一方に電源の+側、もう一方に-側を繋ぐと回転します。
接続を逆にすると逆回転する他、電圧が高くなるほど回転が速くなる性質があります。
ステッピングモータ
左:電子工作キットの小型ステッピングモータ
右:3Dプリンタから外したステッピングモータ
電気信号を入力することで、角度や回転数を指定して高精度に回転させられるモータです。
特に低速時のトルク(ねじろうとする力)が非常に高く、静止状態でもトルクを維持できる特長があります。
身近(?)なものではFDM式3Dプリンタなどに使われています。
電子工作用途ではサーボモータの方が種類が多く、一般的に使われている印象があります。
サーボモータ
電気信号で制御できるモータです。
回転「角度」が制御できるタイプと、回転「速度」が制御できるタイプがあります(製品により決まっています)
360度連続回転可能な製品以外は、完全に回転し続けることはできません。
トルクが高いものほど動作電圧が高くなります。
電子工作では、ロボットやアニマトロニクスの制作などでよく使われています。
SG90サーボモータについて
本項で使用する「SG90」は電子工作では定番のサーボモータです。
小さい分パワーはそれなりですが、入手しやすく5Vのマイコンで扱いやすいサーボです。
初めて使う電子パーツは、まずデータシートで仕様を確認しましょう。
SG90は秋月電子通商の販売ページにデータシートがありますので、こちらで仕様を確認します。
(下記に販売ページのリンクを掲載しておきますので、リンク先にあるデータシートのPDFをご確認ください)
マイクロサーボSG-90の販売ページ(秋月電子通商):
https://akizukidenshi.com/catalog/g/g108761/
2024年6月現在、こちらのページに掲載されているデータシートには「Operating voltage: 3.3 V (~6V)」の記載があります。
なので最小動作電圧は3.3V、最大6Vまでの電圧をかけられるようです。
また「Servo can rotate approximately180degrees(90 in each direction)」の記載があります。
このサーボは左右90度ずつ、計180度の回転が可能とわかります。
6-1-1_Servo_PWM
配線図
SG90から出ているワイヤには、メス型のコネクタがついているかと思います。
両端がオスピンになっているワイヤを挿し込むことで、Arduinoと接続することができます。
3本出ているワイヤのうち、橙色はD11へ、赤色は5Vへ、茶色はGNDへ繋いでください。
完全に余談ですけどSG90のワイヤの配色、P型・D型色覚の人には判別が大変なのではないかと思うんですがどうなんでしょうね。
ちなみに正規品のSG90であれば3本の線のうち真ん中が必ず5Vになるようです。
とはいえロット違いや将来的に変更が生じる可能性がないとも言えないので、参考程度に…
PWMの仕組み
実際に動かす前に、サーボモータを動かすのに必要な仕組み、「PWM」について説明します。
先程の第5章では「デジタル入力」「デジタル出力」「アナログ入力」の3つを説明しました。
この時「アナログ出力」はないの?と思った方もいらっしゃると思います。
Arduinoには電圧を自在に調整して出す「アナログ出力」の機能はありません。
その代わりとして、PWM(パルス変調(Pulse Width Modulation))という出力方式を利用して、「疑似的なアナログ出力」を可能にしています。
(厳密に言うとR4シリーズにはアナログ出力の機能があります(後述)が、他のシリーズのArduinoでは使えない機能なので、PWMの使い方も履修しておいた方がいいかと思います)
PWMの基本
Arduinoが出力できる電圧は0Vか5Vのどちらかです(いわゆるデジタル出力です)
PWMでは、この0Vと5Vの電圧を高速で切り替えて出すことによって、“0Vと5Vの中間の電圧”を疑似的に作り出します。
“中間の電圧”の値は、0Vと5Vを出力する長さの割合によって決まります。
上の図はPWMのモデル図です。
HIGH(5V)とLOW(0V)を3:2の割合の長さで出すことによって、3Vの出力を疑似的に作り出しています。
以上がPWMの基本です。
PWMを利用することで、電圧の変化によって動作が変化する部品を操作することができます。
例えばLEDの明るさを変化させたり、DCモータの回転速度を変えたりすることができます。
本項ではanalogWriteのサンプルコードもご用意しておりますので、後ほどご覧ください。
PWM:サーボモータの場合
さて、サーボモータもPWMによって制御可能な部品ですが、こちらはもう少々複雑な仕組みです。
説明の為に、先程のSG90のデータシートの図を掲載します。
こちらはデータシート2枚目の図に、講師が説明を書き加えたものです。
図の中央には少し盛り上がってから下がり、右端で再度隆起しているグラフがあります。
まず一番下の薄青緑で塗られた「20ms(50Hz) PWM Period」はPWM信号の周期を表します。
色付けされた1周期分の幅が20ms(=20ミリ秒)です。
この20ミリ秒の周期の中で、何ミリ秒の間HIGHにするかでサーボの動きを指定することができます。
この“何ミリ秒の間HIGHにするか”を「パルス幅」と呼び、その「パルス幅」にあたる部分が、グラフ最初の盛り上がり部分、薄赤色に色付けした「0.5~2.4ms DutyCycle」です。
0.5msは最小動作のパルス幅、2.4msは最大動作のパルス幅を示します。
つまり、20msのPWMの周期うち、HIGHにする時間を0.5msにすると最小角度の0度、2.4msにすると最大角度の180度になるように、内部のギアが動く仕様になっています。
ちなみに90度にしたい場合は、0.5msと2.4msの中間の1.45msの間HIGHにすればOKです。
サンプルコード解説
サンプルコード6-1-1_Servo_PWM.inoを開いてください。
delay(20); //20ms待機する
これ、正しくは
delayMicroseconds(10000-pulse_WIDTH);
delayMicroseconds(10000);
じゃないですかね。20ms周期ならその分引かないと正確にならない気がします…
delayMicroseconds(20000-pulse_WIDTH);としないのは、delayMicrosecondsは16383usまでしか動作しないためです
https://kimudiary.com/arduino/arduino%E3%81%A7%E3%82%B5%E3%83%BC%E3%83%9C%E3%83%A2%E3%83%BC%E3%82%BF%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99/
私は試してみたときこのコンパイルエラーに引っかかりました。
This library only supports boards with an AVR, SAM, SAMD, NRF52 or STM32F4 processor.
サーボライブラリが古かったみたいで、新しくIDEインストールしてれば起きないのかもしれませんが…
https://python-beginner.blog/arduinounor4-sg90-error/
int servo_PIN = 11; //サーボのPWM通信線はD11に接続 int pulse_WIDTH = 1450; // 任意のパルス幅を入れる。サンプルでは1.45ms(=90度) void setup() { pinMode(Servo_PIN, OUTPUT); //D11をデジタル出力に設定する } void loop() { digitalWrite(servo_PIN, HIGH); //D11をHIGHにする delayMicroseconds(pulse_WIDTH);//pulse_WIDTHで設定したミリ秒待機する digitalWrite(servo_PIN, LOW); //D11をLOWにする delay(20); //20ms待機する }
サンプルコード6-1-1では、ベタにパルス幅を指定する方法でサーボを動かします。
仕組みとしては、デジタル出力とdelayを組み合わせて任意のパルス幅を作り出します。
今回初登場のdelayMicrosecondsは、delayと同じ“任意の時間分待機する”関数です。
両者の違いは時間の単位のみです。 delayが“ミリ秒”で時間を指定するのに対し、
delayMicrosecondsは更に短い単位の“マイクロ秒”で待機時間を指定することができます。
1行目
int servo_PIN = 11; //サーボのPWM通信線はD11に接続
PWMを使って任意の電圧を出力したい場合は「~」の付いたデジタル出力端子を使います。
が!、今回は普通のデジタル出力にdelayを組み合わせてパルス幅を作り出すので、「~」がついていないソケットでも大丈夫です。
(~の付いている11にしたのは後の都合のためです…)
2行目
int pulse_WIDTH = 1450; // 任意のパルス幅を入れる。サンプルでは1.45ms(=90度)
パルス幅をマイクロ秒で指定します。90度に動かすには1.45ミリ秒のパルス幅が必要なので、
マイクロ秒換算した1450の数値を入れています。
180度まで回したい時は2.4ミリ秒=2400マイクロ秒、の数値を、原点の0度に戻したい時は0.5ミリ秒=500マイクロ秒の数値を入れます。
それ以外の角度は…パルス幅を計算して求めてください…
5行目
pinMode(Servo_PIN, OUTPUT); //D11をデジタル出力に設定する
D11を出力モードにします。何の変哲もないデジタル出力の設定です。
9行目
digitalWrite(servo_PIN, HIGH); //D11をHIGHにする
パルス幅を作り出すため、まずはD11をHIGHにします。
10行目
delayMicroseconds(pulse_WIDTH);//pulse_WIDTHで設定したミリ秒待機する
delayMicrosecondsによってpulse_WIDTHに入れた数値のマイクロ秒分待機します。
11行目
digitalWrite(servo_PIN, LOW); //D11をLOWにする
パルス幅の時間分HIGHにしたので、D11をLOWにします。
12行目
delay(20); //20ms待機する
11行目を終えてすぐloop文の先頭に戻ると、PWMの1周期が終わらないうちにHIGHが再度出力されてしまいます。
なので、delayを使ってPWMの1周期に相当する20ミリ秒の間、ここで待機します。
6-1-2_Servo_library
サーボモータはパルス幅を指定して動かすだけでなく、「ライブラリ」を使うことでもっと簡単に、直感的に動作させることができます。
センサやディスプレイ、モジュールなどは、第5章までに扱ってきたLEDの球やタクトスイッチなどと比べると、非常に複雑な電子部品です。
その機能を使うためには、本来であれば難解で膨大な命令を記述しなければなりません。
そういった複雑な電子部品をなるべく簡単に扱えるように作られたコードの集合体を、「ライブラリ」と呼びます。
ArduinoIDEのインストール時に、基礎的な「ライブラリ」はデフォルトでインストールされています。今回使うServoライブラリもその一つです。
それ以外の「ライブラリ」は、電子部品のメーカーが提供していることが多いです。
また有志のすごい人が自作しているライブラリもあります。
そのため、有名な電子部品や人気な電子部品は、リリース元の異なる複数のライブラリがある場合があります(LEDテープなんかがそうですね)
あるライブラリのルールは、そのライブラリを使っているプログラムの中でしか使えないので、注意が必要です。
配線図
サンプルコード解説
サンプルコード6-1-2_Servo_library.inoを開いてください。
#include <Servo.h> //サーボモータ制御用の標準ライブラリをインクルードします int servo_PIN = 11; //サーボのPWM通信線はD11に接続 Servo myservo; //スケッチ内で使うサーボのインスタンス名を宣言 void setup() { myservo.attach(servo_PIN,500,2400); //サーボのピンをD11で指定/パルス幅の最小/同左最大 } void loop() { myservo.write(0); //サーボを0度にする delay(500); myservo.write(45); //サーボを45度にする delay(500); myservo.write(90); //サーボを90度にする delay(500); myservo.write(135); //サーボを135度にする delay(500); myservo.write(180); //サーボを180度にする delay(500); }
1行目
#include <Servo.h> //サーボモータ制御用の標準ライブラリをインクルードします
早速「ライブラリ」の登場です。今回使うのは「Servo」ライブラリです。
Servoライブラリは、ArduinoIDEに最初からインストールされているライブラリです。
ライブラリをスケッチの中で使いたい場合は、ライブラリのインクルードが必要です。
「スケッチ」→「ライブラリをインクルード」→「Servo」を押すと、宣言部に #include <Servo.h> の一文が入ります。
これによりServoライブラリを読み込んで、このスケッチの中で使用できるようにします。
(本サンプルコードでは既にインクルードされているため、この処理は不要です)
3行目
int servo_PIN = 11; //サーボのPWM通信線はD11に接続
デジタル出力とdelayを使ってパルス幅を指定した6-1-1では、やってることは実質デジタル出力でしたので、どのデジタル出力ソケットを使ってもOKでした。
今回はPWM出力が可能なデジタルソケット、つまり「~」がついている「D3/D5/D6/D9/D10/D11」のどれかのソケットを使う必要があります。
5行目
Servo myservo; //スケッチ内で使うサーボのインスタンス名を宣言
スケッチ内で使うサーボのインスタンス名を作成します。
この先サーボの設定や動作の制御をするときは、このインスタンス名を指定して命令を書きます。
インスタンスの命名も、変数の命名と同じような要領でやるのが望ましいです。つまり、「英語を使った説明的な名前」です。
ざっくり言うと「どんなライブラリで何を動かすのか」という観点で命名するのがよいでしょう。
今回はサーボモータを動かすのでmyservoにしました。
8行目
myservo.attach(servo_PIN,500,2400); //サーボのピンをD11で指定/パルス幅の最小/同左最大
先ほど作成したインスタンス名「myservo」を使って、サーボの設定をします!
今回引数で指定するのは、 (サーボを繋いだソケット番号 , 最小のパルス幅 , 最大のパルス幅) です。
このうち後ろ二つのパルス幅については省略可能です。今回は律儀に「,500,2400」を入れます。
一番先頭のサーボを繋いだソケット番号は必須となります。今回は変数servo_PINの中身「11」が代入されます。
12行目
myservo.write(0); //サーボを0度にする
サーボの角度を0度にします。命令はたったのmyservo.write(0); だけです。
引数には動かしたい「角度」の数値を入れます。今回は0度なので(0)となります。
角度を指定する → [その角度分動かすのに必要なPWM信号を生成して送信する] → サーボが動く
という動作のうち、[ ]の部分をライブラリが勝手にやってくれるイメージです。
なので、「69度」や「154度」のようなめんどくさい角度も計算不要で一撃で動かせます。楽ですね。
13行目
delay(500);
delayで待機します。
14~21行目
myservo.write(45); //サーボを45度にする delay(500); myservo.write(90); //サーボを90度にする delay(500); myservo.write(135); //サーボを135度にする delay(500); myservo.write(180); //サーボを180度にする delay(500);
12・13行目のセットが数回繰り返されます。違いは角度です。
0度→45度→90度→135度→180度 と、delayを挟みながら段階的にサーボが動き、180度まで達するとloop文の先頭に戻りまた0度から動作を繰り返します。
サーボをArduinoにたくさん繋げて動かしたいぜ!!!!と思ったそこのアナタ!
サーボをたくさん繋げる方法は第8章で出てくるのでそっちも見てくれよな!!!!!
Tips:R4シリーズの特権・DACによるアナログ出力機能
Arduinoにはアナログ出力機能がないため、疑似的アナログ出力のPWMを使う必要がある
…と丸く収めようとしたかったのですが、なんとR4シリーズにはアナログ出力機能が追加されていたため、このコーナーを書く羽目になってしまいました。トホホ。
R4シリーズには、DAC(デジタルアナログコンバータ。アナログ入力で出てきた、アナログ信号をデジタル信号に変換するADCの逆バージョンみたいなやつ)が搭載されており、A0ソケットでのみで扱うことができるようです。
本資料ではDACの扱い方の詳細は割愛しますが、興味のある方は下記リンクを参考に、チャレンジしてみるといいと思います。
Arduino UNO R4 Minima Digital-to-Analog Converter (DAC)(Arduino公式):
https://docs.arduino.cc/tutorials/uno-r4-minima/dac/
6-1-3_analogWrite
PWMを使った疑似的なアナログ入力で、LEDの明るさを調節してみましょう!
https://curiouser.sakura.ne.jp/lutamesta/doku.php/gimmickkouza/electronic_basic/6/1_servo_pwm#pwmno%E5%9F%BA%E6%9C%AC
こことの関連性も書きたい
配線図
今回使う関数はanalogWriteですが、あくまで“PWMを使い疑似的にアナログ出力をする”ものなので、アナログソケットではなくPWM対応の「~」がついたデジタルソケットを使います。
またいつものことですが、LEDの抵抗はお使いになるLEDの仕様に合わせたものを計算してお選びください。
サンプルコード解説
サンプルコード6-1-3_analogWrite.inoを開いてください。
int LED = 10; //LEDはD10に挿す int DUTY = 255;//デューティサイクルを設定(0~255) void setup() { pinMode(LED,OUTPUT);//(PWMの場合pinModeは実は省略可能) } void loop() { analogWrite(LED,DUTY);//D10にDUTYの値を出力 }
2行目
int DUTY = 255;//デューティサイクルを設定(0~255)
PWMのデューティサイクル(=HIGHを出力する時間)を0~255の数値で設定します。
数値が大きいほどLEDは明るく、小さいほど暗く光ります。
5行目
pinMode(LED,OUTPUT);//(PWMの場合pinModeは実は省略可能)
PWMは出力しかしないので、実はpinModeの記述は省略することができます。
もちろん書いても問題ありません。今回は律儀に書いてみました。
9行目
analogWrite(LED,DUTY);//D10にDUTYの値を出力
PWM出力をするときに使うanalogWrite関数です。
引数の一番目には出力するピン番号を(今回は変数LEDの中身「10」)、二番目には出力するアナログ値(今回ならDUTYの中身「255」)を書いておきます。
変数DUTYが255の状態でLEDは最も強く光ります。
DUTYの値を減らして書き込むと、明るさが弱くなっているのがわかるかと思います(ぜひ試してみてください)
6-1-4_analogWrite_firefly
analogWriteを使うことで、LEDの明るさをスイッチで切り替えたり、CdSセルで取得した明るさに連動させて可変させる…なんてこともできるようになります。
またコードでDUTYの変化を細かく制御することで、発光パターンを作り出すこともできます。
サンプルコード6-1-4では、“蛍のようにゆっくりと明滅する”発光パターンを作成しています。
配線図
サンプルコード解説
サンプルコード6-1-4_analogWrite_firefly.inoを開いてください。
SPEEDはdelay(SPEED)とするのはdelayは遅延させる意味のためちょっと違和感あるので、duarationとかにした方がいいと思います。
あと単位書いたほうがいいと思います。[ms]とか。
int ledPin = 10; LEDはD10に挿す
int duaration = 5;[ms]delayの待ち時間です。明滅する速度に関係します。
int LED = 10; //LEDはD10に挿す int SPEED = 5;//delayの待ち時間です。明滅する速度に関係します。 void setup() {//(今回pinModeは省略しちゃいま~す) } void loop() { for(int i = 0; i < 255; i++){//iに+1をし続け0から255になるまでの間ループ analogWrite(LED,i);//D10にiの値を出力 delay(SPEED);//次の出力までSPEED分待機する } delay(500);//完全に明るくなったら少し停止するといい感じに見える for(int i = 255; i > 0; i--){//iから-1をし続け255から0になるまでの間ループ analogWrite(LED,i);//D10にiの値を出力 delay(SPEED);//次の出力までSPEED分待機する } delay(500);//完全に暗くなったら少し停止するといい感じに見える }
2行目
int DUTY = 255;//デューティサイクルを設定(0~255)
このスケッチでは、LEDに送るアナログ値を1段階ずつ増減させ点灯することで、連続的に明るさが変化するように見せます。
この“1段階ずつ増減”の合間にdelay(SPEED);を挟むことにより、明滅の速度を調整します。
(SPEEDを増やすと、よりゆっくり明滅するようになります)
8行目
for(int i = 0; i < 255; i++){//iに+1をし続け0から255になるまでの間ループ
8~11行目と13~16行目に2つのfor文があります。8~11の方はLEDを徐々に明るくする
for文で、13~16の方はLEDを徐々に暗くするfor文です。
引数の中身を見ていきます。第一引数(1番目の引数って意味だよ)では初期値0の変数iを設定
します。第二引数で、“このiが255より小さい間このfor文をループする”条件を指定し、
第三引数では“このfor文を1回ループするごとにiに+1をする”ことを指定します。
9行目
analogWrite(LED,i);//D10にiの値を出力
analogWrite関数で、LEDにアナログ値を出力します。
出力する値の指定には、for文を回すために作られた変数iを使い回し、その数値を出力します。
10行目
delay(SPEED);//次の出力までSPEED分待機する
変数SPEEDに設定した数値分待機します。
12行目
delay(500);//完全に明るくなったら少し停止するといい感じに見える
一つ目のfor文を抜けた時点で、LEDの明るさは最大値になっています。
明るさが最大の時にその状態を少し継続させるとちょっといい感じに見える(個人の感想)ので、delay(500);で待機します。
17行目のdelay(500);も同じ目的(LEDが完全に暗くなった時に、いい感じに見せる為に待機させている)です。
この2行は完全に個人の好みなので、増減削除はご自由に。