Raspberry Piで戦艦大和をラジコン化してみた 3 ~システム編~
第1回:Raspberry Piで戦艦大和をラジコン化してみた 1 ~背景・準備編~: EeePCの軌跡
第2回:Raspberry Piで戦艦大和をラジコン化してみた 2 ~大和製作編~: EeePCの軌跡
前回、起動に失敗した大和。
一夜明けて土曜日の朝、早速トラブルチェックにかかります。
Raspberry Piが起動しない原因はこのHC-SR04の配線ミスでした。
VCC、Trig、Echo、GNDの順番を完全に逆にしてることが判明。
こいつを抜くと起動するのでおかしいと思ったんですが、やっぱり突貫工事でやるとだめですね。
いまさらはんだ付けしたところは取れないため、Raspberry Piに挿したピンを挿し替えます。
Raspberry Piは無事起動する事を確認。
このあと、カメラモジュールも接続して起動。
出力120%!無線LAN接続!大和(のコンピュータ部分だけ)始動!!
が、ここでトラブル第2弾発生!
古代「Webiopiが完全に沈黙!ブラウザでの接続不能!」
沖田「副長!システムはどうなっているか!?」
真田「無線LANの接続を確認。SSHでの接続正常。手動での起動に切り替えます。」
で、EeePCからSSHで”大和”に接続、コマンドで
sudo webiopi -c /etc/webiopi/config -d
とWebiopiをデバッグモードで起動。
・・・したんですが、これがWebiopi標準モジュールで起動失敗している模様。
エラーメッセージでググると、
「そいつはSDメモリ内のファイルが逝っちまってるぜ!ヒャッハー!」
というような結論が。
HC-SR04の配線ミスによる影響でしょうか、SDメモリー内のファイルが一部壊れたようです。
・・・万事休すか・・・
と思ったそのとき
Raspberry Piの起動ディスクSDメモリをバックアップしてたことを思い出しまして。
さっそく、別のSDメモリカードにバックアップイメージをWrite。
正常に起動。Webiopiも問題なし!
バックアップは偉大です。
カメラの映像もちょうどいい感じ。第2主砲塔の先端まで見えてます。
森「レーダーに感!前方!12時の方向!距離160!」
たまたま前においてあった物体(買い物袋)までの距離を測定してました。測るとたしかにこれくらいの距離。
ところでこのレーダー(正確には超音波測定センサー)、測定範囲は大体4~5m程度。
250分の1ですから、スケール値で1000~1250mということに。
おまけに計測値は最大10秒ほどの遅延あり。あまり役に立つ装備ではありませんね。
実際、昭和18年改修時につけられた大和のレーダーもあまり大して役に立たなかったらしいですから、いかにも大和らしい装備といえばその通りですが。
とりあえず、現状のままサーボチェックに入ります。
機関、舵用サーボは後ろ側にあるため、PCA9685のそれぞれ14、15番ピンにつけました。
が、またまたここでトラブル発生。
島「舵正常!機関用スイッチ始動!」
徳川「機関のスイッチが入りません!このままでは航行不能!」
舵はいいんですが、機関用サーボがスイッチを動かせません。
原因は単純、サーボが両面テープで止めただけなので動いちゃうようです。
そこで、大和の余った部品と、百均でなんとなく買ったボンドを投入。
このボンド、キャップを開けただけでだらしなくたれてくるため、ティッシュが欠かせません。あまりのだらしなさに思わず私は漏れボンド、略して「漏れボン」と呼んでました。
しかしこのこの漏れボン、このあと意外なほど活躍してくれます。
さて、いよいよ主砲、副砲とサーボをつなぎます。
いろいろ考えたんですが、これまたなんとなく買ったこの6mm径のアクリルパイプを使用。
これとサーボアームを切り取って各砲軸につなげたんですが、アロンアルファではちょっと心もとない。
なにかいいものはないか!?
砲雷長「このままでは主砲に動力が伝達しません!破断する恐れ大!!」
艦長「”漏れボン”を使え!」
てことで、根元の結合部に先ほどの”漏れボン”を使います。
しばし乾燥タイム。
30分ほどするとかなり固まります。
固まったら穴に通します。
穴に通すときに、余分な漏れボンを削っておかないとうまく通りません。
あとはここにサーボを固定するだけ。
で、サーボ固定用台座として準備したのは
このボード。
1cmほどの厚みがあるこのボード。固すぎず、やわらかすぎず、ちょうどいい硬さかと。
こいつをこんな感じに切っておき
隙に合わせて1~2段で引っ付けます。
こんな感じにちょうどよい高さにしておきます。
この土台とサーボは2mm径×8mm長さの木ネジでとめます。
微妙な高さの場合はこのボードとプラ板の組み合わせで取り付けました。
例によってこの土台もアロンアルファで引っ付けてたんですが。
やはり心もとないので
艦長「”漏れボン”を使え!」
との命令を頂き
”漏れボン”注入です。すっかり漏れボン中毒ですね。
なお、上は後ろの2基の砲塔。
こちらは前側の3基の砲塔。しばらく乾かします。
Raspberry Piですが、最初は艦橋の下の甲板に無理やりテープでつけてたんですが。
最終的には艦橋に引っ付けてしまいました。
USBケーブルが引っかかるため、甲板に載せるときはここだけスライドするようにつけないといけません。
サーボとバッテリーを切り離せば、このとおり持ち運べます。
こいつも最初テープで固定してたんですが、あまりにもあっさりと剥がれてしまうため
艦長「”漏れボン”を使え!」
ということで、またまた活躍の”漏れボン”でした。
波動砲並みに便利です。
固まったところでサーボをPCA9685に取り付け、フタを締めようとしたんですが。。。
第1主砲塔のサーボケーブルが短すぎて、うまく閉まりません。
仕方がないので、第1主砲のみ切り離しました。
既にサーボ用延長ケーブルを注文してるんですが、なかなか届きません。これが来るまで4基の砲塔のみになります。
ボタン配置も作り変えました。
一番上はLED発光信号「トラトラトラ」ボタン。
2段目はモーター(前、停止、後)と舵(右、中央、左)用ボタン。
3段目は5基それぞれの砲塔制御(90度、0度、-90度)ボタン。
4段目は全砲塔いっぺんに制御できるボタン。
一番上にストリーミング画像が、下には距離計の数値が出ております。
iPad miniの画面にちょうどの大きさです。
発光信号はかの有名な「トラトラトラ(我奇襲に成功せり)」ですが、これは大和から発信された電文ではなく(まして発光信号ではなく)、深い意味はありません。単に短いから採用しただけです。
発光信号を出している間は他のボタンが制御不能になるという波動砲バリの仕様のため、あまり長い信号を送信できないという事情もあります。
全砲門ニュートラル時の配置。
明らかに前の副砲の向きが変です。あとで直しておきました。
とりあえず、砲塔はちゃんと動いてくれました。
これで第1主砲塔も動いてくれれば・・・
舵も問題なく動作。
サーボの固定策が功を奏して、機関前後スイッチもちゃんと入るようになりました。
が
ここで再びトラブル発生。
スイッチをオンにするとサーボが制御不能になるという厄介な事実が発覚。
サーボとは別のスイッチをつけてあるので、このスイッチでモーターを止めると再び制御可能になります。
原因はおそらくモーターの出すノイズじゃないかと思われます。
上の写真を見てもらうと分かりますが、ただでさえPCA9685が基板むき出しで置いてあります。
こりゃノイズ拾うなというのが無理な相談。
対策は下記記事の中ほどにあるように、モーターにコンデンサをつけなきゃいけないようです。
早速Amazonに注文しました。
それだけでいいかどうかは、やってみないとわかりませんが。
また、取り回しの自由度を上げたり、第1主砲塔の動作にはサーボの延長ケーブルが必要です。
というわけで、今回はこれでテスト終了。
テスト動画をYouTubeに上げておきました。
まだ水に浮かべたわけではありませんが、砲塔が動くディスプレイモデルくらいとしてなら使えるようになったわけで。
というわけで、ラジコン大和 With Raspberry Pi、まだまだ続きます。
しばらくは仕事が忙しいため、まったりと取り組んでいくつもり。ロビもまだ完成してませんし。
実はこの大和、他にも問題を抱えていまして。
見ていただくと分かりますが、無線LANで動いてます。
つまり今のところ自宅の周辺でしか制御できないということに。
以前に買った超小型無線LANルーター購入を使えば外でも制御できるんですが、こいつ経由で制御すると時々応答がなくなるんですよね…現在こちらも問題解決模索中。
それよりも緊急で馬鹿馬鹿しい問題が一つあります。
それは
置き場所をどうしよう \(´Д`)/
というやつで。
10インチサイズのEeePCと比べてこのでかさ。
箱の状態なら縦に置いたり出来たんですが・・・完成してからのこと考えてませんでした。
そもそもアリイ製の250分の1モデルを完成後の置き場に困るという理由で手放しておきながら、そんなこと忘れてこれを買ってしまったわけですから、学習能力なさすぎです。
メインPCの上においてみた。
震度1でも揺れようものなら多分倒れてきます。
結局、物置部屋と化している8畳の部屋の片隅に置くことで解決。
子供は大喜びですが、ちょっとおっかないですね。
未完成品ながら、複数サーボの制御、LEDや各種センサー(カメラ、距離計)の動作をブラウザで遠隔操作するという部分についてはほぼ目的を達成。Raspberry Piを使った電子工作という観点では完成形といったところでしょうか。
今回は"大和のラジコン化"でしたが、応用次第ではいろいろなものが作れそうです。
今後、アイデア次第ではもう一つ買うかもしれません、Raspberry Pi。
ということで、一旦大和ラジコン化については一区切りということで、以上で3回シリーズは終わり。
…なんですが。
この最後の記事を書きながら気づいたことが一つあります。
この大和、カタパルトがありません。
すっかり作るの忘れてました、というか、そんな部品あったっけ?
しかし時既に遅し・・・余った部品は全部捨てちゃいました・・・
問題だらけの大和ですが、今後の活躍に乞うご期待!
Raspberry Pi Type B 512MB ケースセット Pi Tin for the Raspberry Pi - Clear |
(おまけ)
ソースコードを追記しておきます。( )内は私のつけたファイル名
○ ブラウザHTML (test.html) ○
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
<script type="text/javascript" src="/webiopi.js"></script>
<script>
window.onload=function(){
setInterval("document.getElementById('usonic').src='test.txt'",5000);
}
</script>
<script type="text/javascript">
webiopi().ready( function()
{
var content, button;
content = $("#content");
// create a button which output a bit sequence on GPIO 25 with a 100ms period
button = webiopi().createSequenceButton("sos", "トラトラトラ", 25, 100, "01010011010100101010101001101010010101001010011010100101010");
content.append(button); // append button to content div
var content1, button1;
content1 = $("#content1");
button1 = webiopi().createMacroButton("macro1", "engine-Max", "Servo0_H_Max");
content1.append(button1);
button1 = webiopi().createMacroButton("macro2", "engine-Mid", "Servo0_H_Mid");
content1.append(button1);
button1 = webiopi().createMacroButton("macro3", "engine-Min", "Servo0_H_Min");
content1.append(button1);
var content2, button2;
content2 = $("#content2");
button2 = webiopi().createMacroButton("macro4", "rudder-Max", "Servo1_H_Max");
content2.append(button2);
button2 = webiopi().createMacroButton("macro5", "rudder-Mid", "Servo1_H_Mid");
content2.append(button2);
button2 = webiopi().createMacroButton("macro6", "rudder-Min", "Servo1_H_Min");
content2.append(button2);
var content3, button3;
content3 = $("#content3");
button3 = webiopi().createMacroButton("macro7", "Servo 0-Max", "Servo2_H_Max");
content3.append(button3);
button3 = webiopi().createMacroButton("macro8", "Servo 0-Mid", "Servo2_H_Mid");
content3.append(button3);
button3 = webiopi().createMacroButton("macro9", "Servo 0-Min", "Servo2_H_Min");
content3.append(button3);
var content4, button4;
content4 = $("#content4");
button4 = webiopi().createMacroButton("macro10", "Servo 1-Max", "Servo3_H_Max");
content4.append(button4);
button4 = webiopi().createMacroButton("macro11", "Servo 1-Mid", "Servo3_H_Mid");
content4.append(button4);
button4 = webiopi().createMacroButton("macro12", "Servo 1-Min", "Servo3_H_Min");
content4.append(button4);
var content5, button5;
content5 = $("#content5");
button5 = webiopi().createMacroButton("macro13", "Servo 2-Max", "Servo4_H_Max");
content5.append(button5);
button5 = webiopi().createMacroButton("macro14", "Servo 2-Mid", "Servo4_H_Mid");
content5.append(button5);
button5 = webiopi().createMacroButton("macro15", "Servo 2-Min", "Servo4_H_Min");
content5.append(button5);
var content6, button6;
content6 = $("#content6");
button6 = webiopi().createMacroButton("macro16", "Servo 3-Max", "Servo5_H_Max");
content6.append(button6);
button6 = webiopi().createMacroButton("macro17", "Servo 3-Mid", "Servo5_H_Mid");
content6.append(button6);
var content7, button7;
content7 = $("#content7");
button7 = webiopi().createMacroButton("macro19", "Servo 4-Max", "Servo6_H_Max");
content7.append(button7);
button7 = webiopi().createMacroButton("macro20", "Servo 4-Mid", "Servo6_H_Mid");
content7.append(button7);
button7 = webiopi().createMacroButton("macro21", "Servo 4-Min", "Servo6_H_Min");
content7.append(button7);
var content8, button8;
content8 = $("#content8");
button8 = webiopi().createMacroButton("macro22", "Servo All-Max", "Servoa_H_Max");
content8.append(button8);
button8 = webiopi().createMacroButton("macro23", "Servo All-Mid", "Servoa_H_Mid");
content8.append(button8);
button8 = webiopi().createMacroButton("macro24", "Servo All-Min", "Servoa_H_Min");
content8.append(button8);
} );
function mousedown() {
webiopi().setValue(25, 1);
}
function mouseup() {
webiopi().setValue(25, 0);
}
function outputSequence() {
var sequence = "01010011010100101010101001101010010101001010011010100101010" // Tora tra tra morse code or whatever you want
// output sequence on gpio 7 with a 100ms period
webiopi().outputSequence(25, 100, sequence, sequenceCallback);
}
function sequenceCallback(gpio, data) {
alert("sequence on " + gpio + " finished with " + data);
}
</script>
<style type="text/css">
button {
display: block;
margin: 5px 5px 5px 5px;
width: 160px;
height: 45px;
font-size: 14pt;
font-weight: bold;
color: black;
}
#gpio25.LOW {
background-color: White;
}
#gpio25.HIGH {
background-color: Red;
}
</style>
</head>
<body>
<img width="352" height="288" src="http://192.168.13.2:8080/?action=stream"><br>
■ 発光信号
<div id="content" align="left"></div>
■ 舵・機関
<table id="table">
<tr>
<th> <div id="content1" align="center"></div></th>
<th> <div id="content2" align="center"></div></th>
</tr>
</table>
■ 主砲・副砲
<table id="table">
<tr>
<th> <div id="content3" align="center"></div></th>
<th> <div id="content4" align="center"></div></th>
<th> <div id="content5" align="center"></div></th>
<th> <div id="content6" align="center"></div></th>
<th> <div id="content7" align="center"></div></th>
</tr>
</table>
■ 全サーボ制御
<div id="content8" align="left"></div></br>
<br>
■ 超音波距離計[cm] : <iframe frameborder="0" scrolling="no" src="test.txt" id="sonic" width="150" height="23"></iframe><br>
</body>
</html>
○ pythonコード (test.py) ○
import webiopi
from webiopi import deviceInstance
webiopi.setDebug()
GPIO = webiopi.GPIO
pwm0 = deviceInstance("pwm0")
def setup():
GPIO.setFunction( 25, GPIO.OUT )
GPIO.output(25, GPIO.HIGH)
def loop():
GPIO.output(25, not GPIO.input(25))
time.sleep(5)
@webiopi.macro
def Servo0_H_Min():
servo0_h_min()
def servo0_h_min():
pwm0.pwmWriteAngle(15, -30)
@webiopi.macro
def Servo0_H_Max():
servo0_h_max()
def servo0_h_max():
pwm0.pwmWriteAngle(15, 35)
@webiopi.macro
def Servo0_H_Mid():
servo0_h_mid()
def servo0_h_mid():
pwm0.pwmWriteAngle(15, 0)
@webiopi.macro
def Servo1_H_Min():
servo1_h_min()
def servo1_h_min():
pwm0.pwmWriteAngle(14, -50)
@webiopi.macro
def Servo1_H_Max():
pwm0.pwmWriteAngle(14, -50)
@webiopi.macro
def Servo1_H_Max():
servo1_h_max()
def servo1_h_max():
pwm0.pwmWriteAngle(14, 50)
@webiopi.macro
def Servo1_H_Mid():
servo1_h_mid()
def servo1_h_mid():
pwm0.pwmWriteAngle(14, 0)
@webiopi.macro
def Servo2_H_Min():
servo2_h_min()
def servo2_h_min():
pwm0.pwmWriteAngle(0, -90)
@webiopi.macro
def Servo2_H_Max():
servo2_h_max()
def servo2_h_max():
pwm0.pwmWriteAngle(0, 90)
@webiopi.macro
def Servo2_H_Mid():
servo2_h_mid()
def servo2_h_mid():
pwm0.pwmWriteAngle(0, 0)
@webiopi.macro
def Servo3_H_Min():
servo3_h_min()
def servo3_h_min():
pwm0.pwmWriteAngle(1, -90)
@webiopi.macro
def Servo3_H_Max():
def servo3_h_max():
pwm0.pwmWriteAngle(1, 90)
@webiopi.macro
def Servo3_H_Mid():
servo3_h_mid()
def servo3_h_mid():
pwm0.pwmWriteAngle(1, 0)
@webiopi.macro
def Servo4_H_Min():
servo4_h_min()
def servo4_h_min():
pwm0.pwmWriteAngle(2, -90)
@webiopi.macro
def Servo4_H_Max():
servo4_h_max()
def servo4_h_max():
pwm0.pwmWriteAngle(2, 90)
@webiopi.macro
def Servo4_H_Mid():
servo4_h_mid()
def servo4_h_mid():
pwm0.pwmWriteAngle(2, 0)
@webiopi.macro
def Servo5_H_Min():
servo5_h_min()
def servo5_h_min():
pwm0.pwmWriteAngle(12, -90)
@webiopi.macro
def Servo5_H_Max():
servo5_h_max()
def servo5_h_max():
pwm0.pwmWriteAngle(12, 90)
@webiopi.macro
def Servo5_H_Mid():
servo5_h_mid()
def servo5_h_mid():
pwm0.pwmWriteAngle(12, 0)
@webiopi.macro
def Servo6_H_Min():
servo6_h_min()
def servo6_h_min():
pwm0.pwmWriteAngle(13, -90)
@webiopi.macro
def Servo6_H_Max():
servo6_h_max()
def servo6_h_max():
pwm0.pwmWriteAngle(13, 90)
@webiopi.macro
def Servo6_H_Mid():
servo6_h_mid()
def servo6_h_mid():
pwm0.pwmWriteAngle(13, 0)
#
@webiopi.macro
def Servoa_H_Min():
servoa_h_min()
def servoa_h_min():
pwm0.pwmWriteAngle(0, -90)
pwm0.pwmWriteAngle(1, -90)
pwm0.pwmWriteAngle(2, -90)
pwm0.pwmWriteAngle(12, 90)
pwm0.pwmWriteAngle(13, 90)
@webiopi.macro
def Servoa_H_Max():
servoa_h_max()
def servoa_h_max():
pwm0.pwmWriteAngle(0, 90)
pwm0.pwmWriteAngle(1, 90)
pwm0.pwmWriteAngle(2, 90)
pwm0.pwmWriteAngle(12, -90)
pwm0.pwmWriteAngle(13, -90)
@webiopi.macro
def Servoa_H_Mid():
servoa_h_mid()
def servoa_h_mid():
pwm0.pwmWriteAngle(0, 0)
pwm0.pwmWriteAngle(1, 0)
pwm0.pwmWriteAngle(2, 0)
pwm0.pwmWriteAngle(12, 0)
pwm0.pwmWriteAngle(13, 0)
pwm0.pwmWriteAngle(14, 0)
○ カメラ起動用(m.sh) ○
mkdir /tmp/stream
raspistill --nopreview -w 352 -h 288 -q 5 -o /tmp/stream/pic.jpg -tl 100 -t 9999999 -th 0:0:0 &
LD_LIBRARY_PATH=/usr/local/lib mjpg_streamer -i "input_file.so -f /tmp/stream -n pic.jpg" -o "output_http.so -w /usr/local/www" &
« Raspberry Piで戦艦大和をラジコン化してみた 2 ~大和製作編~ | トップページ | ”生活を便利にするアプリ10選”は意外に便利そうなアプリばかりです »
「おもちゃ系」カテゴリの記事
- 最近買った小物類(2024.06.23)
- 東京オートサロン、CARGO BASE & 秋葉原へ行ってきました(2024.01.13)
- 毎年恒例のヴィレッジヴァンガード福袋を買ってきました(2024.01.02)
- 大須に行ってきました(2023.12.23)
- 人生初、幕張メッセに行ってきた(2023.10.28)
「Raspberry Pi・Arduino・電子工作」カテゴリの記事
- 名古屋 大須へ行ってきました(2024.04.28)
- Raspberry Pi 5用電源購入(2024.04.19)
- Interface 2024年5月号はRaspberry Pi 5特集(2024.03.26)
- Raspberry Pi 5とPCがつながらなかった理由は「プライバシーセパレーター機能」のせいでした(2024.03.12)
- Raspberry Pi 5に日本語LLM(ELYZA-Japanese-Llama-2-7b-fast-Instruct)を入れてみた(2024.03.10)
« Raspberry Piで戦艦大和をラジコン化してみた 2 ~大和製作編~ | トップページ | ”生活を便利にするアプリ10選”は意外に便利そうなアプリばかりです »
コメント