ラズパイでServo Driver HATを使ってみる。


Raspberry Pi 4とubuntu、ROS2の組み合わせで遊んでみようとAmazonで[PWM Servo Motor Driver](https://www.amazon.co.jp/gp/product/B07K7NK2TJ/)を購入しました。

 * [公式ページ](https://www.waveshare.com/wiki/Servo_Driver_HAT?fbclid=IwAR15uFrKX_ubqwsbWOQjlwzxkZinwKHOoeXQ3DpSVWaXRI7izhdyQTcqQyQ)
* [ユーザマニュアル](https://www.waveshare.com/w/upload/1/1b/Servo_Driver_HAT_User_Manual_EN.pdf)

## Servo Driver HAT仕様

### 仕様
|||
|---|---|
|電源|6V～12V(VIN端子)|
|サーボ電圧|5V|
|ロジック電圧|3.3V|
|ドライバー|PCA9685|
|制御 I/F|I2C|
|寸法|65.0mmx36.0mm|
|取付穴サイズ|3.0mm|

### PIN仕様

白枠で囲んだところ
![hat.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/ca77a396-345e-c15f-6b66-a864e8cf63c8.jpeg)


|PIN 色||
|---|---|
|黒|GND(サーボモータの茶色と接続)|
|赤|5V VCC|
|黄|PWMの信号線|

#### モータ
茶色の線をGNDに接続
![mon1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/790d6ced-a766-dbe3-3322-35d4f4fc061d.jpeg)

こちらは茶色がなくて黒をGNDに接続
![mon2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/3d78000c-039f-7457-d6b7-997708374e74.jpeg)


### I2Cを有効にする

ubuntuなのでraspi-configが使えないので、/boot/firmware/config.txtを直接書き換えます。

#### /dev/firmware/config.txtの末尾に以下を追記。

```/boot/firmware/config.txt
dtparam=i2c_arm=on
```

#### /etc/modulesの末尾に以下を追記。

```/etc/modules
i2c-dev
i2c-bcm2708
```

#### 再起動

```
$ sudo reboot
```

再起動後にi2cが有効になっているか確認

```
$ dmesg | grep i2c
[    2.824095] i2c /dev entries driver
$ lsmod | grep i2c
i2c_bcm2708            20480  0
```

#### ツールをインストール

```
$ sudo apt install i2c-tools
$ sudo apt install python3-smbus
```

#### i2cが有効かどうかの確認

```
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 -- -- -- -- -- -- --
```

### PythonからPCA9685を使えるようにする。

[Adafruit Python PCA9685](https://github.com/adafruit/Adafruit_Python_PCA9685)からライブラリとサンプルを取得

pipがインストールされていなかったので、先にpipをインストール

```
$ sudo apt install python-pip python3-pip
```

次にPCA9685用のライブラリをインストール

```
$ sudo pip install adafruit-pca9685
```


* [Adafruit Library Reference](https://learn.adafruit.com/adafruit-16-channel-servo-driver-with-raspberry-pi/library-reference)

#### FS90Rを使用したの場合
今回はFS90Rを0番に繋いで以下のようなサンプルを動作させてみたところ、モーターがon→offした。

```python:sample.py
#!/usr/bin/python

import Adafruit_PCA9685
import time

pwm = Adafruit_PCA9685.PCA9685()

pwm.set_pwm_freq(60)

pwm.set_pwm(0, 0, 1)

time.sleep(1)

pwm.set_pwm(0, 0, 0)
```

set_pwmの最初に引数は何番目に接続しているかで、2,3番目はもう少し勉強しないと…。

#### SG92Rを使用する場合
PCA9685は、パルス150～650が角度0～180に対応しているので以下の式を使用してパルスを出す。

```
pulse = ( 650 - 150 ) / 180 * 角度 + 150
```

```Python:sample.py
#!/usr/bin/python

import Adafruit_PCA9685
import time

pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq(60)
# 180度
pwm.set_pwm(0, 0, 650)
time.sleep(1)
# 90度
pwm.set_pwm(0, 0, 400)
time.sleep(1)
# 0度
pwm.set_pwm(0, 0, 150)
 ```

#### FS90R(追記)
360度サーボモータは、角度を90を基準に0～180の間で変化させることで、回転方向やスピードをコントロールするらしいので、以下のようなサンプルを作ってみました。

```Python:sample.py
#!/usr/bin/python

import Adafruit_PCA9685
import time

pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq(60)
# 180度
pwm.set_pwm(0, 0, 650)
time.sleep(1)
# 0度
pwm.set_pwm(0, 0, 150)
time.sleep(1)
# 90度
pwm.set_pwm(0, 0, 400)
 ```
を実行してみたら、0度と180度では、同じ速度で逆回転になりました。
でも、90度だと思っていた400では停止しなかった...。
でも372～382くらいだと止まるんだよなー。

もういっこ買ってきて試したら375では止まらなくて380で止まった。
個体差？
パルスは、130～630の間で380くらいで停止が良い感じかなぁ。


秋月のPCA9685の基板の取説にはパルス150～600になっていた。これだと375はちょうど真ん中の値なんだよなー。
http://akizukidenshi.com/download/ds/akizuki/AE-PCA9685.pdf

#### FS90Rとmicro:bit
micro:bitのサーボドライバを使って以下のようなサンプルを動作させたところ90度で止まらなかったので、やっぱりずれていそう。

```JavaScript:sample.js
input.onButtonPressed(Button.A, function () {
    kitronik_i2c_16_servo.servoWrite(kitronik_i2c_16_servo.Servos.Servo1, 0)
})
input.onButtonPressed(Button.AB, function () {
    kitronik_i2c_16_servo.servoWrite(kitronik_i2c_16_servo.Servos.Servo1, 90)
})
input.onButtonPressed(Button.B, function () {
    kitronik_i2c_16_servo.servoWrite(kitronik_i2c_16_servo.Servos.Servo1, 180)
})
```