パルス幅変調(PWM)でなめらかにLチカをしたあと、ホビー用サーボモーターを Elixirで動かしたくなりました。いろいろ戸惑った事や気づいた事があったのでメモします。
ハードウエアや電子工作に関してはど素人なもので様々な部分で厳密性がないことを予めご了承お願いします。
2021/3/22(月)開催のautoracex #18での成果です。
やりたいこと
Raspberry Pi Zero WボードとPCA9685 PWM コントローラとをI2Cで接続し、ホビー用デジタルサーボモータSG90をElixirコードで動かしたい。
個人的にElixirコードでのIoTファームウエアの開発を可能にするNervesフレームワークで作ったファームウエアを SDカードに焼いて、Raspberry Piに搭載していますが、そこは今回の内容には直接は関係ありません。
サーボモータについて
世の中には様々なサーボモータがあるようですが、ここで使うのはホビー用のデジタルサーボです。概要についてはネットで調べたら出てくるのでここで説明するまでもないと思います。
Sparkfun のHobby Servo TutorialとBasic Servo Control for Beginnersが簡潔にまとまっていてよいです。
大きく分けて 2 種類あります。
標準タイプ(例:SG90)
連続回転タイプ(例:FS90R)
パルス幅変調(PWM)
一般的なホビー用サーボはパルス幅変調(PWM)により操作します。PWMとはデジタル信号の電圧のハイとローの幅を動かすことです。
なめらかなLチカを実装してみて感覚的にPWMが理解できるようになりました。
サーボ制御でのPWM
一般的にホビー用サーボモータの周波数は 50Hz で周期は 20ms のようです。問題はその後です。多くの記事や資料には下記の様に書かれていますが、実際のサーボは異なった挙動をするのです。
- パルス幅 1.0ms -> 時計回りフル
- パルス幅 1.5ms -> ニュートラル
- パルス幅 2.0ms -> 反時計回りフル
手元の SG90 はパルス幅1.0msだとちょこっとしか動きません。いくつのパルス幅で何度動くのか? ここが最大の謎です。深く考えると気が狂いそうになるので、細かいことは気にせず個体ごとに補正するのが現実的だと考えています。autoracex主催者@torifukukaiouさんのアドバイスを思い出します。
同じような問題に直面している人もいるようです。
なぜこの問題の答えがネット上にないのか? 製品によりスペックが異なる? Who knows? ¯_(ツ)_/¯
ご存知の方教えて下さい。
いろいろ調べた結果と手元のサーボを観察した結果をまとめます。
パルス幅 | デューティー比 | メモ |
---|---|---|
400µs | 2% | 手元のSG90の反応する下限 |
500µs | 2.5% | 僕はこれを0度としています。 |
1500µs | 7.5% | 一般的にここがニュートラルとされるらしい (90度) |
2500µs | 12.5% | 僕はこれを180度としています。若干角度が足らないので170ぐらいかも |
2800µs | 14% | 手元のSG90の反応する上限 |
いくつかの資料ではSG90の定格パルス幅が500..2400µsとなってます。手元のSG90は最大で400..2800µs動きます。
サーボテスター
おそらくプロは波形を測定したりするのでしょうが、そこまで本格的にやるつもりはないので、簡易的なサーボテスターをアマゾンで買いました。これがサーボの特性を理解する上で大いに役に立ちました。確か10USドルくらいだったと思います。パルス幅を800..2200µsの間で動かせて、サーボがどう動くのかが簡単に確認できます。サーボテスターの値が正しいのかどうかしりませんが、正しいと信じるようにしています。最低限サーボの挙動が見えてきます。
PWMコントローラー
どうやったらElixirでPWMを操作できるのかよくわからなかったので、Elixir/Nervesコミュニティに質問してみました。何人か親切に教えて下さいました。ラズパイのビルトインPWMより外付けのPWMボードをI2Cで接続したほうが良いとのことでした。Nerves project共同作者のFrank Hunlethさんも自身の経験上、LinuxのPWMサポートがあまり良くないといってました。
最終的にPCA9685: 16-channel 12-bit PWM controllerをラズパイにI2Cでつなげることにしました。16チャネルあるので16個のサーボを同時に操作することができます。ロボットとか作るとチャンネルが多く必要になるのですかね?
ライブラリ
Elixirでサーボを動かすための既存のライブラリは見つけられませんでした。jimsynz/pca9685.exがありましたが、残念ながら依存性が更新されておらずメンテもされてません。ですので自作することにしました。詳しいことはよく理解してませんが、PCA9685データシート片手に他の言語で書かれた既存ライブラリを参考に、最低限動くものはできました。
ラズパイとPCA9685とのI2C通信にはelixir-circuits/circuits_i2cがあるので、それを信頼して使用しています。
デモ
hardware
firmware
software
- ServoKit - Use PCA9685 PWM/Servo Controller in Elixir
ラズパイのIExからこんなElixirスクリプトでサーボを動かすことができました!
# Start a pwm control process
ServoKit.start_link()
# Define a function that changes duty cycle and delays a little
set_pwm_duty_cycle = fn x, ch ->
ServoKit.set_pwm_duty_cycle(x, ch: ch)
Process.sleep(50)
end
# Iterate changing duty cycle with 0.5 step betweem 2.5 and 12.5 for channel 15
list1 = 2.5 |> Stream.iterate(&(&1 + 0.5)) |> Enum.take(21)
list2 = 12.5 |> Stream.iterate(&(&1 - 0.5)) |> Enum.take(21)
0..99 |> Enum.each(fn _ ->
list1 |> Enum.each(&set_pwm_duty_cycle.(&1, 15))
list2 |> Enum.each(&set_pwm_duty_cycle.(&1, 15))
end)
以上