2008/09/18

iPodラジコン技術情報

作成したiPodラジコンのハードウェアの技術情報を記しておきます。
ソフトの方は、iPodのOSバージョン1.1.4版にしておきます。
2.1の方はなんかSDKのNDAとかあるし、なんとなく心配なので止めておきます。

買ってきたラジコンはTOMITECHのエアロRC:マツダRX-8(黒)です。
実売価格は1000円程度です。
このラジコンは、普通のラジコンと違って、サーボモータではなく、よくプラモデルで使われるマブチ130モータをステアリング×1、ドライブ×1の計2個を使用しています。ステアリングは1.5V、ドライブは3Vで動作させるようです。
今回の改造は、ステアリングもドライブも同じ3V系にしてしまいました。
Gainer君のアナログ出力のPWMを使ってモータ回転の速度と方向を制御します。



制作過程を下に載せておきます。

1.まず、ラジコンのメイン基板を取り外します。ボス穴3つに通して乗っかっているだけなので、簡単に外れます。もちろんモータケーブルも外してしまいます。この基板はこれで用無しです。ただし電池端子、スイッチ端子も兼ねていますので、それを後で、半田付けから外すことになります。



2.電池端子とスイッチ端子を半田付けから取り外しました。



3.今回は部品がたくさんあるので、1つの基板に収まり切りません。て、いうか載せきれるか?が勝負になりました。なので、2階建ての構造をとることになりました。まず、1階の基板をユニバーサル基板から切り出し、ボス穴に合わせて2mm穴をあけます。それから、電池端子用にも穴を作ります。この穴は丸くないので、ヤスリでしこしこと形状調整していきます。穴を開けたら半田付けしてしまいます。ぴったりとボディに収まりました。
注意点は、電池スイッチの接触をラジオペンチなどで再調整しないとうまくON/OFF出来ません。



4.この1階の基板にはモータドライバICを前後用に2つ載せます。ICはNECのμPD16805のSOP品です。SOPはそのままユニバーサル基板に載せるのは面倒なので、ピッチ変換基板を買ってきてそれに載せます。ちなみにICは秋葉原のツクモロボット館で購入しました。
2階との間の配線も行って、ピンコネクタで接続することになります。
また、この基板を固定するカバーはもともとの基板を固定するやつを使うのですが(前後のモータカバーの押さえも兼ねているので。)、2階基板との接続用に真ん中を切り落としてしまいます。なので、前後に分割してしまいます。これも一品物なのでヤスリで2階部品との干渉を避けながら、しこしこと作っていきます。



5.さて、2階基板の作成ですが、手持ちのユニバーサル基板が片面実装なので素直に部品面を上にすることができません(部品面には半田付けすることができないですから)。あー、両面基板のユニバーサル基板を用意すればよかった・・・と後悔しても始まらないので、スーパーテクニックで部品面を下にして(部品面を向かい合わせにする)、半田面側に部品を載せることにしました。
載せる部品は、
 ①Gainer(生PSoC(CY8C29466)に3.3V版でファームウェアを焼いたもの:秋月電子で500円也)
 ②XBeeモジュール(アルファ電子から通販購入:3480円/1個)
 ③2mm→2.54mmピッチ変換基板+2mmコネクタ(SparkFunから輸入購入:$2.95、$1.25×2)
 ④3V→3.3VDCDCコンバータ(GainerとXBeeが3.3V動作なので必要:ストロベリーリナックス社製AS1322A DC/DCコンバータモジュール(3.3V/5V) を通販購入:840円)
の4つです。すべて結構な面積があるので、RX-8のボディにうまく収まるように配置をよく検討しました。
ちなみにGainer君は部品面側に直接半田付けですので写真では裏に隠れて見えなくなってます。



5.次は、iPod側にXBeeモジュールをくっつけます。iPodドックコネクタ(千石電子:400円)から2mmピッチコネクタ(千石電子:400円)を経由してXBee君を固定します。
ドックコネクタは端子間隔が非常に狭く、また小さいので、配線が非常に大変です!
ピッチコネクタをどうくっつけたかですが、瞬間接着剤です。でもこれだけだと十分な面積が稼げなくてちゃんと付かなかったので、サポートの板を切り出して面積を稼いで貼りました。ふっー。



6.これでハードウェアの準備は出来ました。
ラジコン側の回路図はこんな感じです。



これが完成したiPod+XBeeラジコンです。



ソフトはこんな感じ、あんまり大したことないですよ。
使ってないコードも入っちゃってますけど、ご勘弁を。
あ、もちろん、PyGainerとPySerialが必要です。必要な方はググってみてください。
import objc
from _uicaboodle import UIApplicationMain
from objc import YES, NO, NULL
from PyGainer import PyGainer
import math

textView = None
#killSwitch = None
#killView = None
#speedCtrl = None

STOP = 0
FORWARD = 1
BACKWARD = 2
direction = STOP
STRAIGHT = 0
RIGHT = 1
LEFT = 2
turn = STRAIGHT
count = 0
values = [0, 0, 0, 0]

objc.loadBundle("UIKit", globals(), "/System/Library/Frameworks/UIKit.framework")
#objc.loadBundle("GraphicsServices", globals(), "/System/Library/Frameworks/GraphicsServices.framework")

class radiconApp(UIApplication):

@objc.signature("v@:@")
def applicationDidFinishLaunching_(self, unused):
global textView, killView
outer = UIHardware.fullScreenApplicationContentRect()
self.window = UIWindow.alloc().initWithFrame_(outer)
self.window.orderFront_(self)
self.window.makeKey_(self)
self.window._setHidden_(NO)

inner = self.window.bounds()
print inner
self.view = UIView.alloc().initWithFrame_(inner)
self.window.setContentView_(self.view)

textView = UITextView.alloc().initWithFrame_(((0,0),(320,40)))
textView.setEditable_(NO)
textView.setTextSize_(20)
textView.setText_("Sample text.")
self.view.addSubview_(textView)

self.killSwitch = UISwitchControl.alloc().initWithFrame_(((200,100),(320,40)))
self.killSwitch.setValue_(0)
self.killSwitch.addTarget_action_forEvents_(self, "kill:", 0x00000f00)
self.view.addSubview_(self.killSwitch)

self.killView2 = UITextView.alloc().initWithFrame_(((0,140),(100,40)))
self.view.addSubview_(self.killView2)

self.killView = UITextView.alloc().initWithFrame_(((0,100),(100,40)))
self.view.addSubview_(self.killView)

self.speedCtrl = UISliderControl.alloc().initWithFrame_(((0, 200),(320,40)))
self.speedCtrl.setMinValue_(-127)
self.speedCtrl.setMaxValue_(128)
self.speedCtrl.setValue_(0)
self.speedCtrl.setShowValue_(YES)
self.speedCtrl.setContinuous_(1)
self.speedCtrl.addTarget_action_forEvents_(self, "changeSpeed:", 0x00000f00)
self.view.addSubview_(self.speedCtrl)

self.pg = PyGainer()
print "try gainer."
self.pg.open("/dev/cu.iap", PyGainer.MODE1)
print "Gainer open."
self.pg.setHigh(0)
self.pg.setHigh(1)

def kill_(self):
global killView
print "test"
"""
if self.killSwitch.value() == YES:
print "kill"
self.speedCtrl.setValue_(0)
else:
print "valid"
"""

def changeSpeed_(self):
global speedCtrl
#print "change"
#print self.speedCtrl.value()
#speed = self.speedCtrl.value()
print speed

@objc.signature("v@:fff")
def acceleratedInX_Y_Z_(self, x, y, z):
global textView, direction, turn, count, values
print "accel"
str = "x = % -1.3f, y = % -1.3f, z = % -1.3f" % (x, y, z)
textView.setText_(str)
speed = (y * 100 / 50 ) * 255
if speed < 25 and speed > -25:
speed = 0
if speed > 255:
speed = 255
elif speed < -255:
speed = -255
sp = "% -d" % speed
self.killView.setText_(sp)
tilt = (x * 100 / 50 ) * 255
if tilt < 25 and tilt > -25:
tilt = 0
if tilt > 255:
tilt = 255
elif tilt < -255:
tilt = -255
ti = "% -d" % tilt
self.killView2.setText_(ti)
if speed > 25:
if direction == BACKWARD:
direction = FORWARD
values[2] = 0
values[3] = 0
self.pg.analogMultiOutput(values)
sleep(0.01)
values[2] = 0
values[3] = speed
elif speed < -25:
if direction == FORWARD:
direction = BACKWARD
values[2] = 0
values[3] = 0
self.pg.analogMultiOutput(values)
sleep(0.01)
values[2] = -speed
values[3] = 0
else:
direction = STOP
values[2] = 0
values[3] = 0
if tilt > 25:
if turn == LEFT:
turn = RIGHT
values[0] = 0
values[1] = 0
self.pg.analogMultiOutput(values)
sleep(0.01)
values[0] = 0
values[1] = tilt
elif tilt < -25:
if turn == RIGHT:
turn = LEFT
values[0] = 0
values[1] = 0
self.pg.analogMultiOutput(values)
sleep(0.01)
values[0] = -tilt
values[1] = 0
else:
turn = STRAIGHT
values[0] = 0
values[1] = 0
self.pg.analogMultiOutput(values)

@objc.signature("v@:")
def applicationWillTerminate(self):
self.pg.setLow(0)
self.pg.setLow(1)
values = [0, 0, 0, 0]
self.pg.analogMultiOutput(values)
#self.pg.analogOutput(0, 0)
#self.pg.analogOutput(1, 0)
self.pg.terminate()
print "terminating"

UIApplicationMain(["radicon"], radiconApp)

実装はかなり苦労しました。設計なんかよりも実装が大変でしたね。
おい、一体いくら掛かってんだよー、という声が聞こえるようです。
まーいいじゃないですか、結構楽しいですよ。

ウチと近所の子供と戯れるiPodラジコンです。この時のコードだと操縦がかなり難しかったですが、OS2.1バージョン用はもっと楽になりました。
video
ちなみに、これはMAKE: TokyoMeetingに出品する予定です。
iPodではなく、iPhoneで動かしますよ!!

2 コメント:

tomo さんのコメント...

iPodを右左上下と動かして操作できるのは良いね。
子供でも簡単にラジコンを操作できたら、結構いけるんじゃない?
しかし、子供のハゲさが映ってたのはいかがなものか...

Ouch! さんのコメント...

ハゲはヅラじゃないと隠せないからなぁ・・・。こればっかりは仕方がないね。