M5Stack + サーボモーターSG90
以前Raspberry piで遊んだ時のサーボモーターSG90を、M5Stackとつないで動かしてみました。
実は全然うまくいかなくて試行錯誤して場当たり的に解決したのですが、まぁそのへんはそれはそれということで……
SG90
サーボモーターというのは、モーターの一種で、回転しつつ角度を決めて止まることができるものです。首振りのような動作ができるので、車のハンドル、ロボットの関節などで使われます。PWM(pulse width modulation)という信号を使って回転する位置を指定します。データシートにはこんな感じで書いてあります。
つまり、20ミリ秒(1/50秒)のうち、0.5ミリ秒だけGPIOをHIGHにしておけばサーボモーターは-90度の位置に固定され、2.4ミリ秒だけHIGHにすれば90度の位置に固定されるので、その間でほしい角度の分だけHIGHになるように調整してね、という意味です(たぶんあっているはず)。SG90はおよそ180度の範囲で動かすことができます。
Raspberry piのときにはモータードライバーのICを使って間接的に操作したのですが、M5Stackの場合はGPIOのポートを直接使えます。
配線
今回は、信号線としてGPIO 16を使ってみました。このへん、いろいろ試したのですが結局どれが適切なのかわからず…… あちこちつないでみたのですが画面が乱れたり音がなったりわけがわかりません。あとの2つはGroundと5Vにつなぎます。
USBケーブルでPCと接続しながら開発していたのですが、サーボを動かしているうちにM5Stackが再起動してしまい、GPIOの使い方が良くないのかとずいぶん悩んでしまいました。しかしよく考えるとUSBケーブルの電源品質が悪かったようで、電源不安定のため再起動していたようです。Arduino IDEからツール⇒シリアルポートモニタを開いてみていると、Brownoutエラーが記録されていました。
コーディング
試行錯誤の結果、SG90の仕様に合わせてPWMを50Hzに指定、指定する値の範囲は16bit(0~65535)にしてみました。この場合-90度(0.5ms)は1638、90度(2.4ms)は7864…… に相当する計算だと思うのですが、実機で試行錯誤したところ2300~9000くらいで首振りすることになりました。どうしてデータシートの計算と合わないのかなぁ。不思議ですが、こんなものですかね?
#include <M5Stack.h> int led1 = 16; //PWMの設定 const double PWM_Hz = 50; //PWM周波数 const uint8_t PWM_level = 16; //PWM 16bit(0~65535) void setup() { Serial.begin(115200); m5.begin(); pinMode(led1, OUTPUT); //モータのPWMのチャンネル、周波数の設定 ledcSetup((uint8_t)1, PWM_Hz, PWM_level); //モータのピンとチャンネルの設定 ledcAttachPin(led1, 1); } void loop() { for (int i = 2300; i <= 9000; i=i+100) { ledcWrite(1, i); delay(30); Serial.printf("%d\n", i); } for (int i = 9000; i > 2300; i=i-100) { ledcWrite(1, i); delay(30); Serial.printf("%d\n", i); } }
追記
MicroPythonでも書いてみたのですが、MicroPythonのPWMは1%刻みでしかコントロールできない様子。PWMのマニュアルではdutyは0~1023の間で指定することになっているのだけれど、100以上の値を指定するとエラーになるみたい。実際のdutyの定義域は0~100なのかな。サーボがカクついてしまいます。
from m5stack import * import machine import utime p16 = machine.Pin(16) pwm16 = machine.PWM(p16) pwm16.freq(50) while True: # dutyは 0.5/20=0.025 ~ 2.4/20=0.12 なので、これを100倍して3~12の間。 # ……なのだけど、実機では4から15くらいで動作している。 # MicroPythonではPWMは0~100%の1%刻みでしか操作できない? # サーボモーターの動作としては結構ぎくしゃくしてしまう感じだ。 for d in range(4, 15): pwm16.duty(d) # dutyは0から100の間 utime.sleep(1) for d in reversed(range(4, 15)): pwm16.duty(d) # dutyは0から100の間 utime.sleep(1)
- 出版社/メーカー: 梅本合同会社
- メディア: おもちゃ&ホビー
- この商品を含むブログを見る