ゆるエンジニアはいろいろ遊びたい

FAエンジニアが週末にいろいろ遊ぶブログです

Miuzeiのサーボモーター10個入りについてレビュー

アマゾンでマイクロサーボを探していたら、Miuzeiというメーカーのサーボが10個で2599円ですごく安かったので買ってみました。
メタルギヤと表記されているのですが、先端の取り付け部分はプラスチックだったり、200°制御可能とか書いているけどどう見ても200°開いていない商品画像だったりと非常に怪しい商品です。
最近色々買いすぎて金欠気味で高価なモーターには手が出ないので、とりあえず動けばいいの精神で購入してみました。
パッケージはこちら

仕様は
サイズ:22.4*12.5*22.8
ホーンギアスプライン:20T
重量:10g
直径:4.9mm
負荷電流:90mA
ギアタイプ:5プラスチック
なんと、SG90よりちょっと小さいです。つまり、コンパチできません。
サーボホーンは共通で使えたのでそれはまぁ良かった。
ケースをばらしてみると、

ちゃんとギヤはプラスチックでした。メタルギヤではありません。
動作はどうなのか、サーボドライバPCA9685を使って確認しました。
電源はダイソーのモバイルバッテリーです。

import time
import argparse
import Adafruit_PCA9685


def parse_args():
    p = argparse.ArgumentParser(description='PCA9685 10ch サーボ動作確認')
    p.add_argument('--channels', '-c', nargs='+', type=int, default=list(range(10)),
                   help='テストするチャンネル番号のリスト(デフォルト 0-9)')
    p.add_argument('--min', type=int, default=140, help='サーボ最小パルス(デフォルト150)')
    p.add_argument('--center', type=int, default=350, help='サーボ中央パルス(デフォルト375)')
    p.add_argument('--max', type=int, default=600, help='サーボ最大パルス(デフォルト600)')
    p.add_argument('--delay', type=float, default=0.1, help='各位置での待機秒数')
    p.add_argument('--cycles', type=int, default=3, help='各サーボ繰り返し回数')
    p.add_argument('--address', type=lambda x: int(x,0), default=0x40, help='PCA9685 I2Cアドレス(16進も可)')
    return p.parse_args()


def set_servo(pwm, ch, pulse):
    pwm.set_pwm(ch, 0, int(pulse))


def test_servo_smin(pwm, ch, smin, scenter, smax, delay, cycles):
    print(f"smin Testing channel {ch}: min={smin} center={scenter} max={smax} (cycles={cycles})")
    try:
        for i in range(cycles):
            set_servo(pwm, ch, smin)
            time.sleep(delay)

    except KeyboardInterrupt:
        raise

def test_servo_smax(pwm, ch, smin, scenter, smax, delay, cycles):
    print(f"smax Testing channel {ch}: min={smin} center={scenter} max={smax} (cycles={cycles})")
    try:
        for i in range(cycles):
            set_servo(pwm, ch, smax)
            time.sleep(delay)

    except KeyboardInterrupt:
        raise

def test_servo_center(pwm, ch, smin, scenter, smax, delay, cycles):
    print(f"centerTesting channel {ch}: min={smin} center={scenter} max={smax} (cycles={cycles})")
    try:
        for i in range(cycles):
            set_servo(pwm, ch, scenter)
            time.sleep(delay)

    except KeyboardInterrupt:
        raise

def sweep_servo(pwm, ch, smin, scenter, smax, delay, cycles):
    # 滑らかに往復させる(小刻み)
    steps = 20
    for _ in range(cycles):
        for t in range(steps + 1):
            v = smin + (smax - smin) * (t / steps)
            set_servo(pwm, ch, v)
            time.sleep(delay / steps)
        for t in range(steps + 1):
            v = smax - (smax - smin) * (t / steps)
            set_servo(pwm, ch, v)
            time.sleep(delay / steps)


def main():
    args = parse_args()

    pwm = Adafruit_PCA9685.PCA9685(address=args.address)
    pwm.set_pwm_freq(60)

    try:
        for ch in args.channels:
            # まず中心へ
            set_servo(pwm, ch, args.center)
            time.sleep(0.2)

        # 各チャネル順にテスト
        for ch in args.channels:
            test_servo_smin(pwm, ch, args.min, args.center, args.max, args.delay, args.cycles)
        for ch in args.channels:
            test_servo_smax(pwm, ch, args.min, args.center, args.max, args.delay, args.cycles)
        for ch in args.channels:
            test_servo_center(pwm, ch, args.min, args.center, args.max, args.delay, args.cycles)

        print('Sweep test (all channels)')
        for ch in args.channels:
            sweep_servo(pwm, ch, args.min, args.center, args.max, args.delay, 1)
            time.sleep(0.2)


        print('Done. Returning all servos to center')
        for ch in args.channels:
            set_servo(pwm, ch, args.center)
            time.sleep(0.2)

    except KeyboardInterrupt:
        print('\nInterrupted by user')
    finally:
        # 停止: 0パルスをセットして停止
        for ch in args.channels:
            try:
                pwm.set_pwm(ch, 0, 0)
                time.sleep(0.5)
            except Exception:
                pass


if __name__ == '__main__':
    main()

copilotに書いてもらったら、argparseという今まで使ったことのないモジュールを使ってくれました。
これはコマンドラインで実行する際に、引数を与える事ができるツールらしいです。
まぁ、気にせず実行してみると10個全て動きました。


さすがにダイソーのモバイルバッテリーだと10個一気に動かすとラズパイが落ちましたが、別々なら大丈夫そうです。
これでいろいろ遊べそう。