🎮

ラズパイでサーボモータを制御(PCA9685)

2022/01/15に公開

はじめに

電子工作ではサーボモータを制御する機会が結構あります。そこで今回はラズパイでサーボモータを制御したいと思います。

ただ、ラズパイはサーボモータを制御するためのPWMが使えるGPIOの数が少ないためI2Cでサーボモータを制御できるサーボモータドライバ(PCA9685)を利用したいと思います。

PCA9685を使うと16個のサーボモータをI2Cで制御できるようになります。価格もAmazonで2個で1,000円台と安価で入手できます。

さらに今回は前回の記事「ラズパイでゲームパッドの入力を取得 🎮 」と組み合わせて、ゲームパッドでサーボモータを制御できるようにします。

今回使うもの

ラズパイ - Raspberry Pi 3 Model B
ワイヤレスゲームパッド - JC-U4113SBK
サーボモータドライバ - PCA9685
サーボモータ - TG9e
Python 3.9.x

Adafruit_PCA9685のインストール

PCA9685をPythonで使うためにライブラリをインストールします。

aptでインストール

$ sudo pip3 install adafruit-pca9685

回路図

「ラズパイ」→「PCA9685」→「サーボモータ」の順に信号が流れます。

ラズパイ と PCA9685

PIN1(3.3V PWR) - VCC
PIN2(5V PWR) - V+
PIN3(I2C SDA) - SDA
PIN4(I2C SCL) - SCL
PIN6(GND) - GND

PCA9685 と サーボモータ

PWM - PWM
V+ - V+
GND - GND

ラズパイのピン配列参考↗️

PCA9685でサーボモータを制御するプログラム

まずはサーボモータを単体で動かしてみます。

関数の説明

pwm.set_pwm()

pwm.set_pwm(チャンネル, 0, パルス幅) のように使います。

チャンネル:PCA9685の0〜15あるチャンネルを指定します。今回は0チャンネルにサーボモータをつないでいるので0を渡す。

0:長くなるので割愛します。とりあえず0を渡せばOK!

パルス幅:サーボモータの種類によっても変わるのですが今回はパルス幅150〜650(サーボモータの角度0〜180)を渡す。

test.py
import Adafruit_PCA9685
import time

# PCA9685初期設定
pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq(60)


def main():
    while True:
        pwm.set_pwm(0, 0, 150)
        time.sleep(1)
        pwm.set_pwm(0, 0, 650)
        time.sleep(1)


if __name__ == '__main__':
    main()

プログラム実行

$ sudo python3 test.py

【応用】ゲームパッドでサーボモータを制御

ゲームパッドのジョイスティックでサーボモータを制御してみます。

プログラムは「ゲームパッドの入力を取得」→「ジョイスティックの値を変換」→「変換した値をPCA9685に渡す」という流れになっています。

※ゲームパッドの入力を取得する方法は前回の記事「ラズパイでゲームパッドの入力を取得 🎮 」をご覧ください。

test.py
import pygame
import Adafruit_PCA9685

# pygame初期設定
pygame.init()
joystick = pygame.joystick.Joystick(0)
joystick.init()


# PCA9685初期設定
pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq(60)


# ジョイスティックの出力範囲をサーボモーターの出力範囲に変換
def map_pca9685(val):
    in_min = -100
    in_max = 100
    out_min = 150
    out_max = 650
    return int((val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)


# ジョイスティックの出力範囲を調整
def map_axis(val):
    val = round(val, 2)
    in_min = -1
    in_max = 1
    out_min = -100
    out_max = 100
    return int((val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)


# ジョイスティックの出力範囲を調整(L2 R2ボタン)
def map_axis_t(val):
    val = map_axis(val)
    if val <= 0 and val >= -100:
        in_min = -100
        in_max = 0
        out_min = 0
        out_max = 50
        return int((val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
    else:
        in_min = 0
        in_max = 100
        out_min = 50
        out_max = 100
        return int((val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)


def main():
    while True:
        # イベントチェック
        if pygame.event.get():
            gamepad_data = {
                "joy_lx": map_axis(joystick.get_axis(0)),
                "joy_ly": -map_axis(joystick.get_axis(1)),
                "joy_rx": map_axis(joystick.get_axis(3)),
                "joy_ry": -map_axis(joystick.get_axis(4)),
                "joy_lt": map_axis_t(joystick.get_axis(2)),
                "joy_rt": map_axis_t(joystick.get_axis(5)),
                "hat_x": joystick.get_hat(0)[0],
                "hat_y": joystick.get_hat(0)[1],
                "btn_a": joystick.get_button(0),
                "btn_b": joystick.get_button(1),
                "btn_x": joystick.get_button(2),
                "btn_y": joystick.get_button(3),
                "btn_lb": joystick.get_button(4),
                "btn_rb": joystick.get_button(5),
                "btn_back": joystick.get_button(6),
                "btn_start": joystick.get_button(7),
                "btn_guide": joystick.get_button(8),
                "btn_joyl": joystick.get_button(9),
                "btn_joyr": joystick.get_button(10)
            }
            
            # ゲームパッド(左ジョイスティックのx軸)を取り出しPCA9685に渡す。
            pulse = map_pca9685(gamepad_data["joy_lx"])
            print(pulse)
            pwm.set_pwm(0, 0, pulse)


if __name__ == '__main__':
    main()

プログラム実行

$ sudo python3 test.py

終わりに

ゲームパッドでサーボモータを制御できるようになりました!そうするとラジコンカーのステアリングはこれで制御できますね!

次回はゲームパッドでDCモータを動かしてみたいと思います。

Discussion