Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What is going on with this article?
@mnishiguchi

Elixirでサーボ制御

パルス幅変調(PWM)でなめらかにLチカをしたあと、ホビー用サーボモーターを Elixirで動かしたくなりました。いろいろ戸惑った事や気づいた事があったのでメモします。

ハードウエアや電子工作に関してはど素人なもので様々な部分で厳密性がないことを予めご了承お願いします。

2021/3/22(月)開催のautoracex #18での成果です。

[English edition]

やりたいこと

Raspberry Pi Zero WボードとPCA9685 PWM コントローラとをI2Cで接続し、ホビー用デジタルサーボモータSG90Elixirコードで動かしたい。

個人的にElixirコードでのIoTファームウエアの開発を可能にするNervesフレームワークで作ったファームウエアを SDカードに焼いて、Raspberry Piに搭載していますが、そこは今回の内容には直接は関係ありません。

サーボモータについて

世の中には様々なサーボモータがあるようですが、ここで使うのはホビー用のデジタルサーボです。概要についてはネットで調べたら出てくるのでここで説明するまでもないと思います。

Sparkfun のHobby Servo TutorialBasic Servo Control for Beginnersが簡潔にまとまっていてよいです。

大きく分けて 2 種類あります。

標準タイプ(例:SG90)

standard_servo

連続回転タイプ(例:FS90R)

continuous_servo

パルス幅変調(PWM)

一般的なホビー用サーボはパルス幅変調(PWM)により操作します。PWMとはデジタル信号の電圧のハイとローの幅を動かすことです。

なめらかなLチカを実装してみて感覚的にPWMが理解できるようになりました。

servo-driver-pwm-leds

サーボ制御でのPWM

一般的にホビー用サーボモータの周波数は 50Hz で周期は 20ms のようです。問題はその後です。多くの記事や資料には下記の様に書かれていますが、実際のサーボは異なった挙動をするのです。

  • パルス幅 1.0ms -> 時計回りフル
  • パルス幅 1.5ms -> ニュートラル
  • パルス幅 2.0ms -> 反時計回りフル

手元の SG90 はパルス幅1.0msだとちょこっとしか動きません。いくつのパルス幅で何度動くのか? ここが最大の謎です。深く考えると気が狂いそうになるので、細かいことは気にせず個体ごとに補正するのが現実的だと考えています。autoracex主催者@torifukukaiouさんのアドバイスを思い出します。

同じような問題に直面している人もいるようです。

なぜこの問題の答えがネット上にないのか? 製品によりスペックが異なる? Who knows? ¯_(ツ)_/¯

ご存知の方教えて下さい。:bow:

いろいろ調べた結果と手元のサーボを観察した結果をまとめます。

パルス幅 デューティー比 メモ
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

servo-demo

ラズパイの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)

以上

3
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
mnishiguchi
Software Engineer in Washington DC

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
3
Help us understand the problem. What is going on with this article?