Raspberry piで2足歩行ロボットの作成②「webiopiでブラウザからi2c制御してPCA9685でサーボを動かしてみる」
2021-07-22
今回は次のステップ、「ブラウザでサーボの操作」をしてみようと思います。
ラズパイには「webopi」というWEBサーバーの機能をインストールすることができるので、それを使って、ブラウザからサーボモーターを動かす仕組みを作ります。
モーターの角度はテスト段階なので適当にしていますが、一応こんな感じで、ちゃんとブラウザからi2c制御でサーボモーターが動いています。↓
はじめに
おおまかな構成
ざくっとした内容ですが、今回は図のような流れでサーボモーターを動かす仕組みを作ります。
①は、ラズパイに立てたWEBサーバー「webopi」になります。これのドキュメントルートに操作画面となるHTMLを作成します。
②は、webiopi独自のライブラリが使えるjavascript(jquery)で、Pythonのマクロを実行させるプログラムを作成します。
③は、PCA9685を制御し、サーボモーターを動作させるプログラムをPythonで作成します。
それでは、順を追ってまとめていきます。
HTML
motor.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>サーボテスト</title>
<meta name="viewport" content="width=
device-width,
initial-scale=1">
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="
/webiopi.js"><
/script>
<script type="text/javascript"
src="motor.js"><
/script>
</head>
<body>
<div class="cam">
<img src=
"http://192.168.11.20:8080/?action=stream"/>
</div>
<main>
<ul>
<div class="a">
<li id="forward" class=
"ledoff">前進</li>
</div>
<div class="bc">
<div class="b">
<li id="left" class=
"ledoff">左旋回</li>
</div>
<div class="n">
<li></li>
</div>
<div class="c">
<li id="right" class=
"ledoff">右旋回</li>
</div>
</div>
<div class="d">
<li id="backward" class=
"ledoff">後退</li>
</div>
</ul>
</main>
</body>
</html>
ラズパイのGPIOや、その他接続したデバイスの制御(i2cなど)は、最終的にPythonで実行する事になります。
つまり、Pythonにブラウザから発生したイベントの情報を伝えなければいけません。
今回の場合は、javascripからPythonの関数を呼び出す事になります。
通常であれば、呼び出しは困難になりますが、webiopi独自のjavascriptライブラリを利用することで、それが可能になります。API的な役割を果たしてくれるんですね。
したがって、head部では、webiopi独自のjsライブラリwebiopi.jsを読み込んでいます。(このライブラリはwebiopiインストール時に自動で生成されています。)
もう1つのmotor.jsは、そのライブラリを使った「イベント発生プログラム」になります。今回は外部スクリプトとして作成します。
body部に上下左右のボタンを配置し、イベント発生の為にIDを振っておきます。
なお、画面上部にカメラ画像(ストリーミング映像)を反映させる予定なので、カメラのスクリプトを埋め込んでいます。
念のため、cssも書いておきます。
CSS
style.css
@charset 'utf-8'; * { margin: 0px; padding: 0px; } body { max-width: 600px; font-size: 25px; width: 100%; } img { width: 100%; } main { height: 40vh; background: skyblue; } ul { display: block; height: 40vh; list-style: none; padding-top: 10px; } .bc { display: flex; } .a ,.bc, .d { height: 12vh; } li { width: 100px ; height: 90% ; margin-left: auto; margin-right: auto; background: yellow; } .b { margin-left: auto; } .c { margin-right: auto; } ul li { text-align: center; } .a li, .b li, .c li, .d li { border: solid 1px; } .ledon { background: #f88888; } .n li { background: skyblue; } a:active { color: #ff2020; }
javascript
motor.js
w().ready(function() { direction = 'STOP'; // 関数:モーターを動かすマクロ呼び出し function change_direction(mode) { direction = mode; if(mode == 'FOWARD') { // 前進 w().callMacro('FW'); } else if(mode == 'BACKWARD') { // 後退 w().callMacro('BK'); } else if(mode == 'RIGHT') { // 右旋回 w().callMacro('RT'); } else if(mode == 'LEFT') { // 左旋回 w().callMacro('LT'); } else { // 停止 w().callMacro('ST'); } } // 「前進」ボタンが押されたときのイベント処理 $('#forward').bind(BUTTON_DOWN, function(event) { // 押されたとき if(direction == 'STOP') { $(this).addClass('ledon'); change_direction('FOWARD'); } }).bind(BUTTON_UP, function(event) { // 離したとき $(this).removeClass('ledon'); change_direction('STOP'); }); // 「後退」ボタンが押されたときのイベント処理 $('#backward').bind(BUTTON_DOWN, function(event) { if(direction == 'STOP') { $(this).addClass('ledon'); change_direction('BACKWARD'); } }).bind(BUTTON_UP, function(event) { $(this).removeClass('ledon'); change_direction('STOP'); }); // 「右」ボタンが押されたときのイベント処理 $('#right').bind(BUTTON_DOWN, function(event) { if(direction == 'STOP') { $(this).addClass('ledon'); change_direction('RIGHT'); } }).bind(BUTTON_UP, function(event) { $(this).removeClass('ledon'); change_direction('STOP'); }); // 「左」ボタンが押されたときのイベント処理 $('#left').bind(BUTTON_DOWN, function(event) { if(direction == 'STOP') { $(this).addClass('ledon'); change_direction('LEFT'); } }).bind(BUTTON_UP, function(event) { $(this).removeClass('ledon'); change_direction('STOP'); }); });
W(){ ... } で囲まれていますが、これがwebiopi独自のライブラリを使用しているという事になります。
記述はjqueryになります。
HTMLボタンを押せば、bind関数で前後左右の、押したボタンの情報がwebiopi().callMacro(引数)によってPythonへ引き渡され、引数の名前がPython側で関数として実行されます。
Python
macro-pwm.py
# coding: utf-8 import webiopi import time from webiopi import deviceInstance pwm0 = deviceInstance('pwm0') @webiopi.macro def FW(): pwm0.pwmWriteAngle(0, 60) time.sleep(1) pwm0.pwmWriteAngle(0, -60) time.sleep(1) @webiopi.macro def BK(): pwm0.pwmWriteAngle(1, 60) time.sleep(1) pwm0.pwmWriteAngle(1, -60) time.sleep(1) @webiopi.macro def RT(): pwm0.pwmWriteAngle(2, 60) time.sleep(1) pwm0.pwmWriteAngle(2, -60) time.sleep(1) @webiopi.macro def LT(): pwm0.pwmWriteAngle(3, 60) time.sleep(1) pwm0.pwmWriteAngle(3, -60) time.sleep(1) @webiopi.macro def ST(): pwm0.pwmWriteAngle(0, 0) pwm0.pwmWriteAngle(1, 0) pwm0.pwmWriteAngle(2, 0) pwm0.pwmWriteAngle(3, 0) time.sleep(1) pwm0.pwmWriteAngle(0, 0) pwm0.pwmWriteAngle(1, 0) pwm0.pwmWriteAngle(2, 0) pwm0.pwmWriteAngle(3, 0) time.sleep(1)
先頭でwebiopiのライブラリをインポートしています。これが無いと正常に動作しないので、必須です。
なお、Adafruit.PCA9685のライブラリを使用していませんが、これはwebiopiでは使えないからです。
webiopiでは、PCA9685デバイスを使用する場合、別途専用のライブラリを用意しているので、それをインポートして使います。deviceInstance ですね。
@webiopi.macroの部分が、javascriptからの呼び出し部分になります。
したがって、この部分に前後左右の関数を作成します。サーボモーターを動かしている部分ですね。
Adafruit.PCA9685のライブラリを使用していないので、記述が前回と異なっています。
ちなみにpwmWriteAngleの引数は、第一引数がサーボのチャンネルで、第二引数が角度になります。今回はとりあえず動作テストなので、設定は適当にしています。
deviceInstanceのライブラリを使用しているので、それに見合った記述が必要ですが、webiopiのチュートリアルに詳細が色々載っているので、そこから引用すれば問題ないかと思います。↓
http://webiopi.trouch.com/PCA9685.html
動作テスト
ターミナルからwebiopiを立ち上げます。
$sudo service webiopi start
後はラズパイのwebサーバにブラウザでアクセスします。↓ラズパイのIP:8000/motor/motor.html
初期パスワードは「webiopi」「raspberry」になります。
問題なければ、サムネイルのような画面になり、操作も可能なはずです。