2015年1月7日水曜日

サーボモーターをPCやスマートフォンから角度制御する

はじめに

(注)WebIOPiをRaspberry Pi 2やRaspberry Pi 3で用いる場合、必ず「本書発売後の追加情報」の手法でインストールしてください。

本書ではサーボモーターを
  • 8.6 PWM信号によるサーボモーターの角度制御
という節で取り扱いました。

その際「入力部」として
  • 半固定抵抗のつまみ→AD変換→Raspberry Pi
という構成を用い、つまみの角度を読み取りました。そしてその角度に応じて
  • Raspberry Pi上のwiringPiライブラリ→サーボモーター(1個)
という流れの「出力部」により、サーボモーターを精度の良いPWM信号で制御しました。

その様子は公式のYoutube動画「ブルーバックス『RaspberryPiで学ぶ電子工作』」でも見ることができます。

この「入力部」のかわりに、9章で学んだ「WebIOPiを用いたPCやスマートフォンとの連携」の手法を用いると、サーボモーターをPCやスマートフォンから操作することができます。

ページの都合で本書内では紹介できなかった内容ですが、この補足ページではその方法を紹介します。

さて、この方法を身につけると、例えば10章で体験した「カメラつきキャタピラ式模型」を改造することで、
  • カメラの上下の向きのサーボモーターによる制御
がブラウザを通して可能になります。具体的には、以下の動画のような動作が可能になります。まずは御覧ください。


以下、実現方法を順に紹介します。

なお、本ページの内容を学ぶ前に「8.6 PWM信号によるサーボモーターの角度制御」および「9章 WebIOPiを用いたPCやスマートフォンとの連携」をあらかじめ学び、必要なライブラリやソフトウェアをインストールしておく必要があります。

さらに、「カメラつきキャタピラ式模型」の改造まで行う場合は「10.4 キャタピラ式模型にカメラを搭載しよう」まで学んでおく必要があります。

必要な物品

必要な物品は下記のようになります。
  • ブレッドボード
  • ジャンパーワイヤ(オス-メス、オス-オス)
  • 電池ボックス(単3×4)
  • サーボモーター1個
さらに、「カメラつきキャタピラ式模型」の改造まで行う場合は「10.4 キャタピラ式模型にカメラを搭載しよう」で作成した模型が必要になります。


準備

ブラウザからサーボモーターを制御するためには、WebIOPiからwiringPiのPythonモジュールWiringPi2-Pythonを呼び出す必要があります。その際、WebIOPiはPython3で動いていますので、WiringPi2-PythonをPython3用にインストールする必要があります。

本書発売後の追加情報」の「p.214:WiringPi2-Pythonのインストール」に従うと、 Python2 および Python3 用の WiringPi2-Python がインストールできますので、それを実行してから以下に進んでください。

 次に、サーボモーターをブラウザから操作するためのサンプルプログラムをダウンロードし、WebIOPi用のフォルダに移動します。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/webiopi-servo.zip
$ unzip webiopi-servo.zip
$ mv 07 08 /usr/share/webiopi/htdocs/bb/
なお、このコマンドは9.2.5節で解説した本書のWebIOPi用サンプルのコピーを行っていないと失敗しますのでご注意ください。

これが済んだ後、下記のコマンドにより、WebIOPi用のフォルダにあるフォルダを確認すると、下記のように01から08までのディレクトリ名が表示されるはずです。
$ ls /usr/share/webiopi/htdocs/bb/
01 02 03 04 05 06 07 08
これは、 /usr/share/webiopi/htdocs/bb/ ディレクトリにあるファイルやディレクトリを表示する命令です。01~06が本書で用いたサンプルファイルを含むディレクトリ群であり、07がこれから用いるサーボモーター制御用プログラムを含むディレクトリ、08がキャタピラ式模型のカメラを操作するためのプログラムを含むディレクトリです。

以上が確認できたら準備は終了です。

サーボモーターをPCやスマートフォンから角度制御する

まずはサーボモーターを制御する例です。必要な回路は下図のようになります。


これは、本書の図8-13から「入力部」に相当するADコンバータと半固定抵抗を取り除いたものです。サーボモータを2個用いる回路図を示しましたが、サーボモーター1個でも問題なく動作します。

以上が終わったらWebIOPiを起動する準備に移ります。

まずは、Raspberry PiでWebIOPiが起動していないことをあらかじめ確認しておきます。その方法の詳細は本書に記述されていますが、Raspberry Piを再起動してしまうのが最も簡単です。

もし、キャタピラ式模型用の設定のように、Raspberry Pi起動時にWebIOPiが自動起動する設定になっている場合、下記のコマンドで自動起動を無効にした後、Raspberry Piを再起動します。
$ sudo systemctl disable webiopi
さて、WebIOPiが起動されていない状態になったら、管理者権限で設定ファイル /etc/webiopi/config を開き、編集します。leafpadを用いるならば下記のコマンドを実行するのでした。
$ sudo leafpad /etc/webiopi/config
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ sudo mousepad /etc/webiopi/config
このファイルのなかで[SCRIPTS]セクションを見つけ、起動するマクロを下記のように記述し、保存します。その後テキストエディタは閉じて構いません。
myscript = /usr/share/webiopi/htdocs/bb/07/script.py
それが済んだら、下記のコマンドでWebIOPiを起動します。
$ sudo service webiopi start
そして、起動が済んだら、PCやスマートフォンのブラウザから下記のアドレスにアクセスします。
http://(Raspberry PiのIPアドレス):8000/bb/07/
「http://」や末尾の「/」の記述を忘れないよう注意するのでした。

ここまでの流れが上手く行けば、ブラウザ上にスライダーが2つ現れます。典型的なブラウザでのスクリーンショットは下図のようになります。このスライダーをスライドすることで、冒頭で紹介した動画のようにサーボモーターの角度が変化するはずです。


なお、このプログラムはArduino Sidekick Basic Kit付属のサーボモーターを対象に記述しました。その他のサーボモーターを用いると、スライダの向きとサーボモーターの回転の向きが逆になる場合があります。

そのような場合、/usr/share/webiopi/htdocs/bb/07/javascript.js にて、下記の2行を見つけます。そして、この2行目の行頭に「//」を追記して無効化してください。
// サーボの回転の向きを逆にしたい場合次の行を無効に
        ratio = 1.0 - ratio;
無効化後、ブラウザでページを再読み込みすると、サーボモーターの向きが逆転しているはずです。

エッセンスの解説

このプログラムのエッセンスを書籍と同様に解説します。他の例と同様、関連するのは、 /usr/share/webiopi/htdocs/bb/07/ ディレクトリにある下記の4ファイルです。
  • index.html
  • javascript.js
  • styles.css
  • script.py

HTMLファイルindex.html

9.5節と同様、jQuery UIのスライダを配置しています。9.5節ではRGBフルカラーLEDの三色を変更するためにスライダが3つ必要でしたが、本ページの例では下記の1つだけです。
<div id="slider_servo"></div>

JavaScriptファイルjavascript.js

スライダの配置はやはり9.5節と同様です。スライダは下記にあるように、0から20の1刻みの値を持ちます。
var sliderMin = 0;
var sliderMax = 20;
var sliderStep = 1;
スライダを動かした時、下記の関数が実行されます。
    var sliderHandler = function(e, ui){
        var ratio = ui.value/sliderMax;
        // サーボの回転の向きを逆にしたい場合次の行を無効に
        ratio = 1.0 - ratio;
        webiopi().callMacro("setHwPWM", [ratio, commandID++]);
    };
関数の最後のあるように、Pythonスクリプト内にあるsetHwPWMマクロが、0.0~1.0に変換されたスライダの位置ratioと、何番目の命令送信かを示すcommandIDという引数付きで送られます。commandIDが必要な理由は、本書269ページを御覧ください。

Pythonスクリプトscript.py

Pythonスクリプト内では、wiringPiの手法でPWMを生成する記述をしています。周波数やデューティ比の設定方法を知るには8.6節の解説が役に立つでしょう。JavaScriptから呼ばれるsetHwPWMマクロは下記のように定義されています。
@webiopi.macro
def setHwPWM(duty, commandID):
    wiringpi.pwmWrite(18, getServoDutyForWebIOPi(float(duty)))
getServoDutyForWebIOPi関数はこのファイル内部で下記のように定義されています。wiringPi用のデューティー比の計算は219~220ページを参考にしてください。
def getServoDutyForWebIOPi(val):
    val_min = 0.0
    val_max = 1.0
    servo_min = 36   # 50Hzで, 0.7ms
    servo_max = 102  # 50Hzで, 2.0ms

    duty = int((servo_max-servo_min)*(val-val_min)/(val_max-val_min) + servo_min)
    return duty

カメラ付きキャタピラ式模型のカメラをサーボモーターで動かす(機体編)

以上でサーボモーターをブラウザから角度制御できるようになりました。その応用として、本書10章の最後に作成した「カメラ付きキャタピラ式模型」のカメラを上下方向に動かせるようにしてみましょう。どのような動作が可能になるのかは本ページ冒頭の動画でご覧頂けます。

模型の概観は下図のようになります。


書籍で紹介したように、この模型は
  • 機体に用いるパーツとして可能な限りTAMIYAの模型キットのものを用いる
  • 搭載する回路は半田付けではなく、ブレッドボードで実現する
  • 用いる工具はニッパとドライバ程度とする
という方針で作成していますので、サーボモーターの取り付けもその方針に従いました。

今回新たに取り付けたサーボモーター部のみを拡大したものが下図です。サーボモーターはArduino Sidekick Basic Kit付属のものを用いました。他のサーボモーターでももちろん可能ですが、サーボモーターの取り付け法は用いるモーターによって変更が必要になるかもしれません。


図中、赤字はTAMIYAの工作キットのパーツ、青字はサーボモーター付属のパーツを表しています。軸受け材からサーボモーターを吊り下げているサーボホーンは、長いものをニッパーで半分に切って用いました。

サーボモーターのサーボホーンをL型アームに取り付ける箇所には2mm径(M2)ワッシャーを用いました。このパーツのみ、TAMIYAの模型キットにもサーボモーターにも付属しないパーツですが、amazonなどの通販サイトで購入することができます。

このように、カメラとサーボモーターの取り付けは、耐久性やメンテナンス性を考えると全てねじ止めするのが理想です。しかし、試してみるとわかりますが、これは工夫が必要な作業です。どうしても難しい場合は両面テープなどによる仮止めでも良いと思います。

なお、2016年4月、Raspberry Piのカメラモジュールのバージョン2が発売になりました。こちらでもここで紹介するプログラムは動作します。

なお、二段構造の上部(Raspberry Piが乗った部分)のプレートですが、カメラと電池ボックスがぶつからないよう、少し前にずらして取り付けなおしています。

カメラ付きキャタピラ式模型のカメラをサーボモーターで動かす(回路編)

カメラを動かせるよう変更したキャタピラ式模型を動かすための回路は下図のようになります。本書の図10-6に、サーボモーターを追加した回路になっています。


準備ができたらWebIOPiを起動する準備を行います。この作業を行う方は、既に本書の10章まで終えているはずですので、簡単に解説します。

まず、管理者権限で設定ファイル /etc/webiopi/config を開き、編集します。leafpadを用いるならば下記のコマンドを実行するのでした。
$ sudo leafpad /etc/webiopi/config
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ sudo mousepad /etc/webiopi/config
このファイルのなかで[SCRIPTS]セクションを見つけ、起動するマクロを下記のように記述し、保存します。その後テキストエディタは閉じて構いません。
myscript = /usr/share/webiopi/htdocs/bb/08/script.py
それが済んだら、WebIOPiを起動します。キャタピラ式模型の場合は、WebIOPiがRaspberry Piの起動とともに自動起動するように設定するのでしたね。

WebIOPiの自動起動が有効になっていない場合、下記コマンドで有効になるのでした。
$ sudo systemctl enable webiopi
その後Raspberry Piを再起動すれば、WebIOPiが自動起動されます。

そして、スマートフォンのブラウザから下記のアドレスにアクセスします。
http://(Raspberry PiのIPアドレス):8000/bb/08/
「http://」や末尾の「/」の記述を忘れないよう注意するのでした。

ここまでの流れが上手く行けば、下図のようにカメラからの映像の右側に、カメラの上下を操作するスライダが現れます。このスライダーをスライドすることでカメラの上下方向の向きが変化します。


キャタピラ式模型を動作させるとき、回路を取り付けたサーボモータが不安定に変動する場合があります。そんなとき、サーボモーターへの電源(サーボモーターの赤色ケーブルへの接続)を電池の+極からとるのではなく、 Raspberry Piの 5V 端子(3.3V 端子の隣)から取るのも手です。

モーター類をRaspberry Piに直接接続するのはあまりよくありませんが、サーボモーター1個動かす程度ならばRaspberry Piは不安定にならないだろう、という期待に基づいています。
もしそうすることで Raspberry Pi の動作が不安定になるのならば、やはりサーボモーターへの電源は電池からとるように戻すべきです。

なお、このプログラムをディスプレイをつながない状態で自動起動しようとすると、NOOBS 1.9.1以降のRaspbianでは「キャタピラは動作するが、サーボモーターは動作しない」という状態になり得ます。この問題に直面した場合、以下のように「Raspbianがコンソールで起動する」状態にしてください。

まず、Jessieで導入されたGUIの設定アプリケーションを起動し、「システム(System)」タブの「ブート(Boot)」項目にある「CLI(To CLI)」にチェックを入れ、再起動してください。画面が黒い「コンソール」状態でRaspbianが起動し、ここで紹介したプログラムが自動起動で正常動作するようになります。
なお、Raspbianが黒いコンソールで起動する状態から元に戻したい場合、コンソールで「startx 」というコマンドを実行してデスクトップを起動してから、設定アプリケーションの「ブート(Boot)」項目で「デスクトップ(To Desktop)」を選択し、再起動してください。

もし、この「CLI起動によるWebIOPiの自動起動」でもサーボモーターが正常動作しない場合、以下を試してください。 まず、以下のコマンドを実行し、WebIOPiの自動起動を無効にします。
sudo systemctrl disable webiopi
次に、以下のコマンドで、/etc/rc.localファイルを管理者権限で編集できる状態にします。
sudo leafpad /etc/rc.local
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
sudo mousepad /etc/rc.local
そして、「exit 0」の上の行に、以下の2行を追記して保存し、再起動してください。
sleep 10
sudo systemctl start webiopi
以上で、/etc/rc.localからWebIOPiが起動され、サーボモーターが正常動作することを期待しています。 なお、「sleep 10」は「10秒待機する」の意味です。 安全のために「10秒」と大きな数値を選びましたが、 私の環境では「sleep 3」(3秒待機)程度でも正常動作しました。 さらに、この方法ではCLI起動ではなくGUI起動のままでもサーボモーターが正常動作しました。

さて、このプログラムもArduino Sidekick Basic Kit付属のサーボモーターを対象に記述しています。その他のサーボモーターを用いると、スライダの向きとサーボモーターの向きが上下逆になる場合があります。

そのような場合、/usr/share/webiopi/htdocs/bb/08/javascript.js にて、下記の2行を見つけます。そして、この2行目の行頭に「//」を追記して無効化してください。
// サーボの回転の向きを逆にしたい場合次の行を無効に
        ratio = 1.0 - ratio;
無効化後、ブラウザでページを再読み込みすると、サーボモーターの向きが逆転しているはずです。

さて、このプログラムは、10章のプログラムと、本ページで紹介したサーボモーター操作のプログラムを合体させたものです。

ただし、サーボモーターの動く範囲はscript.py内で下記のように設定し、元の範囲(36~102)よりも狭めています(どちらも中心は69)。これは、サーボモーターの動く範囲を広げすぎると、カメラを支えているアームが機体とぶつかるためです。
    servo_min = 48
    servo_max = 90
また、スライダを縦に配置するには、下記の変更を行っています。

1. index.html内でスライダをspan要素に変更
    <span id="slider_servo"></span>
2. styles.css内で下記の設定
#slider_servo{
    display: inline-block;
}
3. javascript.js内でスライダをverticalに設定
$( "#slider_servo" ).slider({
        orientation: "vertical",
(略)
4. javascript.js内でスライダの高さを設定
$( "#slider_servo" ).height(mHeight);

以上です。お疲れ様でした。