日記とか、工作記録とか

自分に書けることを何でも書いてゆきます。作った物、買ったもの、コンピュータ系の話題が多くなるかもしれません。

M5Stack + サーボモーターSG90

以前Raspberry piで遊んだ時のサーボモーターSG90を、M5Stackとつないで動かしてみました。
実は全然うまくいかなくて試行錯誤して場当たり的に解決したのですが、まぁそのへんはそれはそれということで……

SG90

サーボモーターというのは、モーターの一種で、回転しつつ角度を決めて止まることができるものです。首振りのような動作ができるので、車のハンドル、ロボットの関節などで使われます。PWM(pulse width modulation)という信号を使って回転する位置を指定します。データシートにはこんな感じで書いてあります。

f:id:WindVoice:20190914233011p:plain

つまり、20ミリ秒(1/50秒)のうち、0.5ミリ秒だけGPIOをHIGHにしておけばサーボモーターは-90度の位置に固定され、2.4ミリ秒だけHIGHにすれば90度の位置に固定されるので、その間でほしい角度の分だけHIGHになるように調整してね、という意味です(たぶんあっているはず)。SG90はおよそ180度の範囲で動かすことができます。

Raspberry piのときにはモータードライバーのICを使って間接的に操作したのですが、M5Stackの場合はGPIOのポートを直接使えます。

今回はArduino IDEを使ってみていますので、ledcWrite()という関数を使えばいいようです。

配線

今回は、信号線としてGPIO 16を使ってみました。このへん、いろいろ試したのですが結局どれが適切なのかわからず…… あちこちつないでみたのですが画面が乱れたり音がなったりわけがわかりません。あとの2つはGroundと5Vにつなぎます。

f:id:WindVoice:20190914233758j:plain

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);
  }
}

結果

動作結果は下のようになりました。片道2秒で右、左に首を振ります。それ以外には特段面白いこともなく申し訳ない。首振りで面白くなる何かを考えなければ。


M5stack + サーボモーターSG90

追記

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)

デジタル・マイクロサーボ SG90 (5個)

デジタル・マイクロサーボ SG90 (5個)