2021/07/11
長らく作りかけで放置していたマークIVの続き記事を更新しようと思います!
今回は主にプログラムの修正がメインになります。記事では色々と説明しているのですが、記事の後半でソースコードをまとめて紹介しているので、読み飛ばしてしまって問題ありません。
砲塔の制御
前回、カメラを起動した状態で左右の履帯を動かす所までやりました。しかし、砲塔をスマホから制御する部分が出来ていなったので、そちらをやっていこうと思います!
今までは上記のようなボタン配置でした。左右の履帯を回転させるためのボタンと、カメラを起動するためのボタンを用意していました(カメラのボタンは「仮」ボタンなので、押しても何も起きません)。
ここに、左右の砲塔を動かすためのボタンを配置します。
こんな感じです。左右履帯のボタンの下に、砲塔用のボタンを配置しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
btn = webiopi().createButton("f-rt", "f_rt", push_f_rt_button, release_rt_button); ct1.append(btn); btn = webiopi().createButton("b-rt", "b_rt", push_b_rt_button, release_rt_button); ct1.append(btn); btn = webiopi().createButton("f-lt", "f_lt", push_f_lt_button, release_lt_button); ct0.append(btn); btn = webiopi().createButton("b-lt", "b_lt", push_b_lt_button, release_lt_button); ct0.append(btn); ~中略~ function push_f_rt_button() { webiopi().callMacro("right_servo_forward"); } function push_b_rt_button() { webiopi().callMacro("right_servo_back"); } function release_rt_button() { webiopi().callMacro("right_servo_stop"); } function push_f_lt_button() { webiopi().callMacro("left_servo_forward"); } function push_b_lt_button() { webiopi().callMacro("left_servo_back"); } function release_lt_button() { webiopi().callMacro("left_servo_stop"); } |
ボタンの文字列”l_r”等ではわかり辛いので、画像や”▲””▼”に変更したいのですが、とりあえずこのまま進めようと思います!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
def loop(): global r_servo_state global l_servo_state webiopi.sleep(0.1) if r_servo_state != 0: global r_servo_val r_servo_val = r_servo_val + r_servo_state if r_servo_val < MIN_SERVO_PULSE_WIDH or r_servo_val > MAX_SERVO_PULSE_WIDH: r_servo_val = r_servo_val - r_servo_state else: pwm.set_pwm(R_SERVO_CH, 0, r_servo_val) if l_servo_state != 0: global l_servo_val l_servo_val = l_servo_val + l_servo_state if l_servo_val < MIN_SERVO_PULSE_WIDH or l_servo_val > MAX_SERVO_PULSE_WIDH: l_servo_val = l_servo_val - l_servo_state else: pwm.set_pwm(L_SERVO_CH, 0, l_servo_val) ~中略~ @webiopi.macro def right_servo_forward(): global r_servo_state r_servo_state = -10 @webiopi.macro def right_servo_back(): global r_servo_state r_servo_state = 10 @webiopi.macro def right_servo_stop(): global r_servo_state r_servo_state = 0 @webiopi.macro def left_servo_forward(): global l_servo_state l_servo_state = -10 @webiopi.macro def left_servo_back(): global l_servo_state l_servo_state = 10 @webiopi.macro def left_servo_stop(): global l_servo_state l_servo_state = 0 |
moter.pyには左右の砲塔を動かす処理を追加しました。
履帯のDCモーターは「ボタンを押している間、一定のPWMを出力する」という処理をすれば良かったのですが、砲塔に使っているサーボモータは「ボタンを押している間、PWM値を徐々に上げるor下げる」しなくてはなりません。そのため、ボタンを押した時にグローバル変数の値を変更するようにして、loop関数内でグローバル変数を参照し、値が変わっていればPWM値を変更するようにしています。
※Pythonのglobalって処理が結構重いらしいのですが、他に良い案が浮かばなかったので、こんな書き方になっています。今後変えるかもです。
カメラを起動させる方法
現在カメラを起動する為には、Teratermからコマンドを使って”mjpg-streamer”を起動させています。
マークIVで遊ぶ度、Teratermを起動してコマンドを実行するのは面倒くさいので、ボタンを押したらカメラが起動するようにしたいと思います。
1 2 3 4 5 6 |
btn = webiopi().createButton("cam-srt", "cam_srt", push_camera_start); ct1.append(btn); ~中略~ @webiopi.macro def camera_start(): subprocess.Popen(['/home/pi/camera.sh', 'start']) |
index.htmlに”cam_srt”ボタンを追加し、ボタンを押すとシェルスクリプトが呼ばれるようにしました。
1 2 3 4 5 6 7 |
#!/bin/sh if [ $1 = "start" ]; then ps -ef | grep mjpg_streamer | grep -v grep || /home/pi/mjpg-streamer/mjpg-streamer-experimental/mjpg_streamer -o "/home/pi/mjpg-streamer/mjpg-streamer-experimental/output_http.so -w /home/pi/www" -i "/home/pi/mjpg-streamer/mjpg-streamer-experimental/input_raspicam.so -x 640 -y 480 -fps 30 -q 10" & else pkill mjpg_streamer fi |
こちらが追加した”camera.sh”という名前のシェルスクリプトです。このシェルスクリプトを”camera.sh start”とか”camera.sh stop”みたいな感じで呼べばカメラの起動/終了ができます。
あと、”cam_srt”を何度も押してしまって、バックグランドプロセスが何個も立ち上がってしまうと、エラーが発生する(はず)なので、psコマンドで”mjpg-streamer”が起動していない事を確認してから、起動させるようになっています。
当初”cam_stp”も追加していたのですが、ボタンが多くなり過ぎて他のボタンを追加する余裕が無くなってきたので、一旦消す事にしました。
Raspberry Piの電源を切る方法
今回のマークIVには、メインのユニットにRaspberry Pi Zeroを使っています。他のラジコンのように、いきなり電源を切ってしまうと、ファイルシステムの破損等を引き起こしてしまう恐れがあるため、お行儀良く電源を切る必要があります。
なので、電源を切るためのボタンを用意しました。
1 2 3 4 5 6 |
btn = webiopi().createButton("pwr-off", "pwr_off", push_power_off); ct0.append(btn); ~中略~ function push_power_off() { webiopi().callMacro("power_off"); } |
内部的には”poweroff”コマンドを実行しているだけです。Windowsで言う所のシャットダウンと同じような事をやっています。
1 2 3 |
@webiopi.macro def power_off(): subprocess.Popen(['poweroff']) |
これでOSをシャットダウンできますが、物理的には電力供給され続けているため、少し時間を置いてから物理スイッチの電源を切る必要があります。
タイマー処理の追加
今回はカメラ起動ボタンやシャットダウンボタンを追加しましたが、まだ問題があります。
例えばカメラを起動した場合は、カメラが起動した後スマホのブラウザ側の画面をリロードしなくてはなりませんし、シャットダウンボタンを押した後、ユーザーはどのタイミングで電源ボタンをOFFにすれば良いかわかりません。その問題を解決する為にsleep処理を追加してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function sleep(waitSec, callbackFunc) { var spanedSec = 0; var waitFunc = function () { spanedSec++; if (spanedSec >= waitSec) { if (callbackFunc) callbackFunc(); return; } clearTimeout(id); id = setTimeout(waitFunc, 1000); }; var id = setTimeout(waitFunc, 1000); } |
他のサイトを参考にして、index.htmlにjavascriptの関数を追加しました。このsleep処理を使う事で、任意の時間経過後に、画面をリロードしたり、「シャットダウンが完了しました」というようなメッセージを表示させる事も出来ます。
1 2 3 |
sleep(1, function () { // 1秒スリープする // 1秒後に実行したい処理を書く }); |
こんな感じで使う事が出来ます。とっても便利。
カメラ起動
カメラ起動ボタンを押した後、数秒後にindex.htmlをリロードするように修正してみました。
まず、システムを起動した直後はカメラが起動していないので、ボタンを押す事で起動できる旨の画像を用意してみました。(テスト画像なので、後で差し替えると思います)
1 2 3 4 5 |
<div style="float:left; width:60%;"> <div id="streaming" align ="center"> <img id="stremer_image" src="http://192.168.10.13:8080/?action=stream" onError="this.onerror=null;this.src='images/default_image.jpg';" width="80%" /> </div> </div> |
画像はこんな感じで、mjpg-streamerの画像取得失敗した場合に表示させるようにしています。
そのほか、「読み込み中…」等の画像も用意してみました!カメラ起動ボタンを押したら、その画像を非表示から表示に切り替えるようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 |
function push_camera_start() { let stremer_image_ID = document.getElementById("stremer_image"); let loading_image_ID = document.getElementById("loading_image"); let loading_animation_ID = document.getElementById("loading_animation"); stremer_image_ID.style.display ="none"; loading_image_ID.style.display ="block"; loading_animation_ID.style.display ="block"; webiopi().callMacro("camera_start"); sleep(3, function () { document.location.reload(); }); } |
“push_camera_start”に色々と追加してみました。やっている事は「cam_srtでカメラが起動するよ」画像を非表示にして、「読み込み中…」画像を表示させて、3秒後にindex.htmlを再読み込みしているだけです!
シャットダウン
Raspberry Piのシャットダウンを実行すると、systemdで管理されたUnitを順番に停止させていくため、完全に電源が切れるまで少し時間がかかります。マークIVには物理的な電源スイッチが付いている為、そちらをOFFにするためには10秒程度時間を置いてからスイッチを切る必要があります。
なので、シャットダウンボタン処理も同様にタイマーを使って画像差し替え等を行おうと思います。
(本来は完全にOSのpoweroff処理が完了したトリガーを取れれば良いのですが、難しいのでタイマーで実現する事にしました。)
追加したのは以下の画像です。
こちらの画像に差し替える事で、ユーザーに電源スイッチを切るタイミングを通知できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function push_power_off() { let stremer_image_ID = document.getElementById("stremer_image"); let shutdown_001_image_ID = document.getElementById("shutdown_001_image"); let shutdown_002_image_ID = document.getElementById("shutdown_002_image"); let loading_animation_ID = document.getElementById("loading_animation"); stremer_image_ID.style.display ="none"; shutdown_001_image_ID.style.display ="block"; loading_animation_ID.style.display ="block"; webiopi().callMacro("power_off"); sleep(10, function () { shutdown_001_image_ID.style.display ="none"; shutdown_002_image_ID.style.display ="block"; loading_animation_ID.style.display ="none"; }); } |
あと、静止画だけでは処理が正常に動いているのか止まっているのか判別し辛いので、フリー素材のローディング中のgitアニメーションも追加しました。
このgifアニメーションを一番上に表示させています。これで直感的に処理中である事が分かるはずです。
動画
以下動作試験した時の動画です
ずっと放置していたタミヤ1/35スケールMk4の作業を進めました!スマホで操作している動画です💡
カメラを起動すると電池を食うのでデフォルトでは切ってありますが、ボタンを押せば起動出来ます🙃
色々と粗がありますが、それっぽく動いているので良しとします(^ω^) pic.twitter.com/kqUTRpJVcy
— おりくらしげる (@shigeruorikura) April 30, 2021
問題なく動いています!良かった良かった!
※一応、ローディング中のgitアニメーションは表示させているのですが、小さすぎて見え辛いですw
ソースコード
今回のソースコードはgithubで公開しています。
まだ、綺麗な状態ではありませんが、今後ソースコードを修正する場合は、githubを更新して行こうと思っています。
あと、まっさらなRaspberry Piで導入試験していないので、情報に不足等があるかと思いますが。見つけ次第修正して行こうと思います。
やりたかったけど出来ていない事
詳細(読まなくても大丈夫です)
当初は、左右履帯の速度変更が出来るようにしたいと思っていたので、index.htmlファイルに”slider”を配置して、それに合わせて履帯の速度を可変できるようにしようとしていました。
※index.htmlにスライダーを配置している図。(中央の画像は、マークIVのカメラの出力では無く、サンプルとして画像を埋め込んでいるだけです。)
他の方のブログを参考に実装してみたのですが、残念な事に上手く動かず…他の方はGPIOから直接PWMを出している実装だったのですが、私の場合はI2C経由でPWMドライバを動かしているので、その辺が原因なのかも知れません。
かなり時間がかかっても実現できなかったので、一旦あきらめて現在の実装で進めて行こうと思います。※塗装が終わってから再調査しようかなと思います。
おわりに
という訳で、今回は久しぶりのMarkⅣの記事でした!
ソフトウェアって「やっている事の説明は不要だ!コードを見ろ!」というのが一般的なので、こうやってブログに長々書くのは結構ナンセンスな行為なのですが、今までの記事の流れ的にいきなりすっ飛ばすのも違和感があったので、まとめてみました!
今後は「コード修正は全部githubで確認してください。修正する場合はcloneしてより良い実装があったらプルリク投げて下さい!」って言えるので楽になるハズです!
あと、今回でプログラムはこれくらいにして、塗装の続きを行おうと思います!塗装に入るまで長かった…
今回はこれで終わりです!また、どこかのタイミングで記事を更新しようと思います!ではでは~
関連URL
関連記事
・続き↓
タミヤ 1/35 マークIV カメラ付きラジコン化計画 (その22) ~車体の下地塗装~
・前回↓
タミヤ 1/35 マークIV カメラ付きラジコン化計画 (その20) ~サーボモータの追加~
・一覧↓
カメラ付きラジコン化計画シリーズ