RaspberryPiで無限回転サーボを回す

※この記事は[farmtory Advent Calendar](https://adventar.org/calendars/3464)の6日目の記事です。

[Fusion360で作ったラックギア](https://note.mu/satzz21/n/nb19d334752af)を無限回転サーボにつけて回したときのコード、試行錯誤の過程、ハマったことなどを書いておく。


# 作ったもの
[Maker Faire Taipeiに出展した種まきロボット](https://note.mu/satzz21/n/nc9ab432ea2fa?magazine_key=meffc4264a313)。

種をまくプランターを選択して移動する機構として、ラックギアと無限回転サーボを使ったスライダを作った。

#制御
Raspberry Pi 3 Model B+


# 無限回転サーボにした理由
ちゃんと制御しようとするとステッピングモータを選択する気がするが、[micro:bitと一緒に買ったFS90R](https://www.switch-science.com/catalog/3516/)が手元にあって手っ取り早そうだったので。




# 実装概観

## 初期化
BCMモードで初期化、変数など定義


```
import RPi.GPIO as GPIO
import time
import fileinput

GPIO.setmode(GPIO.BCM)
```




```
# setup output pins
sliderPIN = 18 # slider
GPIO.setup(sliderPIN, GPIO.OUT)
```



```
pwmFreq = 50
slider = GPIO.PWM(sliderPIN, pwmFreq)
sliderDutyDefault = 6.85
```


```
end1, end2 = 3,11

duty = end1
sleepTime = 0.5
sliderPosition = 0 
```

```
def initialize():
  slider.start(end1)
  print('initialized')
```

## finalize
一応やっておく

```
def finalize():
  slider.stop()
  GPIO.cleanup()
  print('finalized')
```

## duty変更


``` 
def driveSlider(duty):
  duty = float(duty) # try

  print(['slider duty to:', duty])
  slider.ChangeDutyCycle(duty)
  time.sleep(0.05)
```
これでとりあえずサーボが回るようになる。無限回転サーボなのでどこまでも回る。

## モーターを移動させる

つくったピニオンギアにサーボの軸をホットボンドで固定し、ラックギアと噛み合わせて回せばサーボが移動する機構をつくる。
本当は回転角度を指定したいのだが、適当に時間を決めて回すしかないことにこの辺で気づく。
水平位置(とPiが思っているもの)はとりあえず`sliderPosition`で保持することにする。

```
def _slideTo(destination):
  global sliderPosition

  print('sliding to', destination)
  print ('sleepTime', sleepTime)
  while(sliderPosition < destination):
    time.sleep(0.05)
    sliderPosition += 1
    print ('sliderPosition', sliderPosition)
    driveSlider(6)
  driveSlider(sliderDutyDefault)
```

``` 
def slideTo(destination):
  _slideTo(destination)
```

いろいろあって関数2つにわけたけど結果的には意味はない。



## タクトスイッチを入れる
位置をなんとか補正する。キャリブレーションのためそのへんにあったタクトスイッチをラックギアに設置し、右に移動し続けてスイッチ押したらpositionを0とすることにする。

```
# setup input pins
switchRightPIN = 22
GPIO.setup(switchRightPIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
```

```
def adjustRight():
  global sliderPosition
  print('adjusting right')
  status = True
  while(status):
    status = GPIO.input(switchRightPIN)
    print(['switch right', status])
    driveSlider(8)
  sliderPosition = 0
  driveSlider(sliderDutyDefault)
  print('adjusted right')
```

## integration
だいたいできたのでテストを繰り返してパラメータ調整。
スイッチにぶつかるまで無限回転サーボを回し(`adjustRight`),その後決めた時間だけ逆に回す(`slideTo`)を繰り返しているだけ。

台北持ってく前はこんなかんじであった。
(`rotateTo`)は別のこと(まく種のほうを選択している)をやっている。

````
initialize()
while True:
  print ('sliderPosition', sliderPosition)
  adjustRight()
  print ('sliderPosition', sliderPosition)
  time.sleep(0.1)


  slideTo(0)
  rotateTo(3.8)
  rotateTo(5.8)
  _rotateTo(end1)

  slideTo(8)
  rotateTo(7.8)
  rotateTo(9.8)
  _rotateTo(end1)

  slideTo(16)
  rotateTo(4.8)
  rotateTo(6.8)
  _rotateTo(end1)

  slideTo(24)
  rotateTo(8.8)
  rotateTo(10.8)
  _rotateTo(end1)

  # time.sleep(0.1)

  # slideTo(70)
  time.sleep(0.1)

finalize()


```
早めにintegrationにうつるの、全体の問題点見ながら残り作業時間配分するためにも超大事


# 製作・出展中に起きたこと


## Piが落ちる
最初はPiと同系統でサーボへ電源供給していたが、途中からPiが落ちるようになったので、サーボ稼働用にAC-DCコンバータで別系統で電源供給した。


## ラックギアが変形する
これはサーボ直接関係ないんだけど、UFOキャッチャーのように吊るしているモノを移動させるしくみなので、ラックギアが細すぎたり(MDFなので)湿気で変形したりしてサーボ移動に支障が出ることがあった。


## サーボが壊れる
スイッチが意図通りに押されてPiに入力が入るまでサーボが回り続けるので、スイッチのホットボンド固定やスイッチ配線が外れたり、サーボの移動経路に障害があるとサーボに不要なトルクがかかることになり壊れる。

## dutyが安定しない
上述原因や接触などの理由で、サーボを停止させるためのdutyを安定して出力できず、サーボが停止しているはずのタイミングでゆっくり動いているという状態になっていた。現場では様子見ながらサーボにわたすパラメータをときどき変えていた。


# 感想
片手間で走りながら作っていた割にはデモでは大枠無事稼働続けてくれたのでよかった。あと出展メンバーにサーボ異常時の対応などを伝えておいたのでやってもらえてよかった。

写真など見つけたら追加します。