- 記事公開日
ハンドエレキを改造!コントローラーで操縦できるようにしてみた
「エレキがリモコン操作できたら便利だよなぁ~」と思い、作ってみました。
十字キーの左または右を押している間、アームが動き、進行方向を変えられます。
ボタンはスピードの切り替え。
一段ずつアップ・ダウンするだけでなく、一気に最高スピードまでアップできるところがポイントです。
ハンドエレキを直接手で操作していたときは、後にのけ反る姿勢を取らないといけなかったので、ずいぶんと操縦がラクになりました。
ただ、構想段階では、もっとカンタンにできると思っていたんですけどね・・・
思いのほか、ロボットになってしまいました。
掛かった費用・使用したもの
同じものを作ろうと思う人はいないでしょうが、何か作るときのヒントになればと思うので、情報を残しておきます。
総費用:約10,000円
制御のコアとなる手のひらサイズのラズパイ
メインの制御システムに、Raspberry Pi(通称:ラズパイ)を使いました。
ラズパイは、小さいパソコンです。
なんと、手のひらに収まるサイズ!
しかも、1,000円ちょっとで買えちゃいます。
Raspberry Pi Zero W – スイッチサイエンス
値段も安いので、ラズパイの処理能力はそれほど高くありません。
しかし、無線通信やモーター制御などの面倒な処理を担ってくれるので、必要なパーツやプログラムもぐーんと少なくて済みます。
そしてもう一つ、ラズパイを使うことでの大きなメリットがあります。
それは、『操縦プログラム』が可能になること。
現段階ではまだ組み込んでいませんが、GPSデータを使って、目的のポイントまで自動操縦することだって可能です。
スーファミにそっくりなワイヤレス・コントローラー
普通にリモコン付けるだけじゃあ、フットコンと同じで面白くない。
そこで、ワイヤレスタイプのコントローラーを使用しました。
2.4GHz帯の無線コントローラーなので、最大約10m(障害物があるときは約3m)まで電波が届きます。
アルミボートの後部座席からでも操縦が可能ですよ。
ただし、ワイヤレスコントローラーには、一つ注意点があります。
それは、一定時間操作しないと『スリープ状態』になってしまうこと。
このコントローラーの場合、約3分間、何もボタン操作が無いと、スリープしてしまいます。
STARTボタンを押せば、すぐに再接続されますが、やや面倒です。
充電ケーブルを使って有線にしておけばスリープにはなりませんので、フローターの場合、有線タイプのコントローラーで十分かもしれませんね。
なお、このコントローラーは、中国の通販サイト『AliExpress(通称:アリエク)』で購入しました。
スーパーファミコン ワイヤレスゲームパッド – AliExpress
やっぱスーファミ世代の僕には、このコントローラーが手に馴染みます。
モバイルバッテリー
今回、電源にはモバイルバッテリーを2つ使ってます。
一つはラズパイ用(黒)、もう一つがモーター用(白)です。
- 黒:Anker Anker PowerCore II 6700 2A / 5V
- 白:ELECOM DE-M01L-6400 6400mAh 2.6A / 5V
本当はモバイルバッテリー1つだけで補いたかったのですが、手持ちのモバイルバッテリーでは出力電流が足りなかったので、今の段階では2つ使ってます。
※ラズパイとそれぞれのモーターで、各1.2Aぐらいの電流が必要です。
本来、モバイルバッテリーはスマホなどの”充電”に使用するものなので、少しでも電流が流れていない時間が続くと、出力が止まってしまう仕様になっています。
そこで、ずっとある程度の電流を引っぱり出しておく回路が必要でした。
「どれくらいの電流があれば、ずっと出力し続けるか?」はモバイルバッテリーの種類によって違うので、可変抵抗によって引っ張る電流を変更できるようにしています。
ちなみに、今回使用しているモーター用のモバイルバッテリー(白)では、100mA以上の電流を流し続ける必要があった為、可変抵抗の値は「40Ω」に設定しています。
5V ÷ 40Ω= 0.125A = 125mA
なお、ラズパイ用のモバイルバッテリーは(黒)、もう少し低い電流で出力するので、ラズパイ用としました(ラズパイの待機時消費電流:80mA)
もしかしたら、電源はモバイルバッテリーではなく、エレキを動かすための鉛バッテリーから電気を引っ張ってくる方が簡略化できたかもしれませんね。
※鉛バッテリーを使用する場合は、12V→5Vに降圧するための回路が必要になります(使用してるエレキの定格電圧が12Vの場合)
サーボモーター
『スピードを切り替えるためのモーター』と『方向を変えるためのアームのモーター』、両方ともサーボモーターという指定した位置で止められる特殊なモーターを使用しています。
基本的に、値段が高いほど、回す力(トルク)が大きいです。
今回は『10kgf』のトルクを持つサーボモーターを使用しました。
しかし、単純に力のあるモーターを使えば良いわけではなく、”力の働き”を考えないといけないのが大変ですね。
トルクがあるモーターは重いので、その分、フレームに掛かる負担も大きくなってしまいます。
すると、フレームが歪む・・・
じゃあ、フレームの負担を減らすために、モーターの取り付け位置を変えようとすると、今度は回転軸がズレるから、ギアやプーリーが必要になってしまう。
しかも、ギアやプーリーを使うと、もっと大きな動力が必要になってきます。
結局、軸の位置も変えずに、重量もできるだけ軽いモーターを選び、バランスを取って、この形になりました。
フレーム用の資材(アルミ・木材・ネジ等)
フレームに使用したアルミ・木材などの資材は、すべてハンズマン(ホームセンター)で揃えました。
一個のアルミパーツが10円~30円ぐらいで変えるので、パズル感覚で組み立てられます。
あまり加工は得意でないので、基本的には穴を空けるとか、アルミ板を折り曲げる程度のことしかしてません。
ただ、「エレキ本体に穴はあけたくない」というこだわりがあったので、クランプで固定したり、機構を工夫する必要がありました。
この装置、防水性は全くないのですからね・・・
雨の日の釣行のために、ネジ数本で取り外しができるようになってます↓
ボタンを押して、エレキを水面から持ち上げる部分はよく稼働させるので、突っ張り棒方式で固定しました↓
水中でのモーターの高さを調整させる部分は、手動で回せるネジを使用しました↓
電源スイッチ
電源スイッチとしてトグルスイッチを取り付けました。
- スイッチを上にすると、通電(各モバイルバッテリーで電源ON)
- スイッチを下にすると、モーターとラズパイの電源がOFF
- スイッチを真ん中にすると、モーターのみ電源がオフ
『モーターのみ電源オフ』の目的は、手動操作を可能にするためです。
モーターに通電していると、その位置でモーターが固定されてしまうため、手で動かすことができません。
入り組んだ狭いところでは、手動で操作する方がやりやすいかもしれないので、一応、余地を残しておきました。
回路図
LEDはステータス確認用です。
通電しているときは、緑が点灯。
プログラムが正常に動いているときは、黄色が点灯。
コントローラーからの信号を受信したときは、黄色が点滅します。
電源スイッチとラズパイのGPIO16間にある1KΩの抵抗は、ラズパイに電源オフを伝えるための信号に使っています。
ラズパイに流していい電流は上限20mAと決まっているので、抵抗によって5mA程度だけ流れるよう制御しています。
今回、サーボモーターを動かすために『ハードウェアPWM』という制御方法を使用しました。
ラズパイでPWM信号を出力できるGPIOピンは、12Pin・13Pin(もしくは、18Pin・19Pin)と決まっています。
プログラム(Python)
#!/usr/bin/python
import time
import threading
import pigpio
import struct
import subprocess
# Device Input Directory
device_path = "/dev/input/js0"
# L(time) : unsigned long
# h(value) : unsigned short
# B(type) : unsigned char
# B(number) : unsigned char
EVENT_FORMAT = "LhBB";
EVENT_SIZE = struct.calcsize("LhBB")
# PWM Pin Set
gpio_pin0 = 12
gpio_pin1 = 13
# Make Instance : pigpio.pi
servo = pigpio.pi()
# GPIO12 : ALT0(PWM0)
servo.set_mode(gpio_pin0, pigpio.ALT0)
# GPIO13 : ALT0(PWM1)
servo.set_mode(gpio_pin1, pigpio.ALT0)
# Initial Speed
speed = 0
# Save Previous Speed
pre_speed = 0
# Initial Pulse
pulse = 50000
# Default Motor Status
flag = False
# Power Off (Switch)
sw_pin = 16
servo.set_mode(sw_pin, pigpio.INPUT)
servo.set_pull_up_down(sw_pin, pigpio.PUD_DOWN)
def power_off(gpio, level, tick):
print(gpio, level, tick)
if level == 1:
#subprocess.call("sudo shutdown -h now", shell=True)
subprocess.call("sudo poweroff", shell=True)
servo.callback(sw_pin, pigpio.EITHER_EDGE, power_off)
# Test LED (Yellow)
chk_pin = 4
servo.set_mode(chk_pin, pigpio.OUTPUT)
servo.write(chk_pin, 1)
# Controller Input Func
def con_input():
# Device Path Open
with open(device_path, "rb") as device:
# Device Input Data Read (1st Time)
event = device.read(EVENT_SIZE)
# Exists [event] Data
while event:
# Fromat Convert
(con_time, con_val, con_type, con_num) = struct.unpack(EVENT_FORMAT, event)
print( "{0}, {1}, {2}, {3}".format(con_time, con_val, con_type, con_num ) )
global flag
global pre_speed
global speed
global pulse
# CROSS KEY Push
if con_type == 2:
# LEFT / RIGHT KEY
if con_num == 0:
# INITIAL
if con_val == 0:
flag = False
# LEFT (MAX:-32767)
elif con_val < 0:
flag = 1
# RIGHT (MAX:32767)
elif con_val > 0:
flag = 2
# BUTTON Push
elif con_type == 1:
# ---------------------------
# BUTTON Setting
# ---------------------------
# X (Speed:4)
if con_num == 0:
speed = 4
# A (Speed:1)
elif con_num == 1:
speed = 1
# B (Speed:2)
elif con_num == 2:
speed = 2
# Y (Speed:3)
elif con_num == 3:
speed = 3
# L (Speed:-1)
elif con_num == 4:
if con_val == 1:
speed = pre_speed -1
if speed < -1:
speed = pre_speed
# R (Speed:+1)
elif con_num == 5:
if con_val == 1:
speed = pre_speed +1
if speed > 4:
speed = pre_speed
# SELECT (Speed:0)
elif con_num == 8:
speed = 0
# START
#elif con_num == 9:
# speed = 0
# ---------------------------
# Pulse Setting
# ---------------------------
# Speed:0
if speed == 0:
pulse_up = 60000
pulse_down = 44000
# Speed:1
elif speed == 1:
pulse_up = 85000
pulse_down = 60000
# Speed:2
elif speed == 2:
pulse_up = 95000
pulse_down = 75000
# Speed:3
elif speed == 3:
pulse_up = 110000
pulse_down = 90000
# Speed:4
elif speed == 4:
pulse_up = 125000
pulse_down = 105000
# Back:1
elif speed == -1:
pulse_up = 23000
pulse_down = 23000
# ---------------------------
# Send Pulse
# ---------------------------
if con_val == 1:
if speed > pre_speed:
pulse = pulse_up
elif speed < pre_speed:
pulse = pulse_down
pre_speed = speed
servo.hardware_PWM(gpio_pin1, 50, pulse)
print (speed)
# Device Input Data Read (2nd Time)
event = device.read(EVENT_SIZE)
# Parallel Process Command
th = threading.Thread(target=con_input)
th.start()
# Default Motor Pulse
i = 80000
# Speed Adjust (Motor Step)
step = 200
# Speed Adjust (Wait Interval)
interval = 0.00001
# Motor Max Pulse
left_max = 40000
right_max = 107000
# Infinite loop
while True:
# Controlle BUTTON ON
if flag:
# Test LED OFF (Yellow)
servo.write(chk_pin, 0)
# LEFT KEY ON
if flag == 1:
i = i - step
if i < left_max:
i = left_max
# RIGHT KEY ON
elif flag == 2:
i = i + step
if i > right_max:
i = right_max
# Motor Pulse Set
servo.hardware_PWM(gpio_pin0, 50, i)
# Motor Pulse Data
print(i)
# Speed Adjust
time.sleep(interval)
# Test LED ON (Yellow)
servo.write(chk_pin, 1)
# Parallel Process End
th.join()
exit()
実際、触ってみないと分からないかもしれませんが、スピードを切り替えるとき、アップするときとダウンするときでは、モーターの停止位置を変える必要があります。
よって、アップとダウンでサーボに送るパルスが異なるコードとなっています。