この Book では、Raspberry Pi と PiRT-Unit を組み合わせてOpenRTM-aist から利用する方法を解説します。
PiRT-Unit は産総研で開発された、Raspberry Pi用 IO拡張ボードです。 ウィン電子工業から発売中です。
PiRT-Unit からは、AD (4ch)、DA (2ch)、PWM (1ch)、I2C (1ch)、RS232C/XBee (1ch) がそれぞれ利用できます。
Raspberry Pi拡張IOボード | |
ADコンバータ | 10bit, 4ch チップ: ADC104S021 サンプリング 200kHz |
DAコンバータ | 12bit, 2ch チップ MCP4822 |
PWM出力 | 1ch, RCサーボモータードライブ用 フォトカプラ絶縁 |
RS232C | D-SUB 9pinコネクタ XBee とジャンパにて切り替え |
XBee | XBee接続コネクタ XBee: Digi International 製 Zigbeeモジュール XBee とジャンパにて切り替え |
電源入力 | 5V DC入力 Raspberry Piに電源供給可能 Raspberry Piからの電源供給でも動作 |
Raspberry Pi のデフォルトの設定 (raspbian armhf) では、SPI デバイスなどは利用できません。 ここで、PiRT-Unit を利用するために必要なデバイスの設定やプログラミング環境の構築を行います。
以下の内容をスクリプト化したものがこちらにあります。
Raspberry Pi のサイトからダウンロードしたイメージの適当な場所にダウンロードして以下のように実行します。
$ wget http://svn.openrtm.org/Embedded/trunk/RaspberryPi/tools/rpi.sh $ chmod 755 rpi.sh $ sudo rpi.sh rtunit0 --type rtunit $ sudo rpi.sh rtunit0 --type rtunit_examples
以上で、以下の PiRT-Unit を利用するために行っている設定、パッケージのインストール、サンプルのインストールが自動で行われます。 バージョンアップ、ファイル配置の変更などによってエラーが出た場合にはメーリングリストなどへお知らせください。
Usage: rpi.sh hostname --type <TYPE> TYPE are: basic kobuki kobuki_only rtunit rtunit_only basic: Installing avahi, cmake, subversion/git and OpenRTM kobuki: Installing basic + Kobuki RTC kobuki_only: Installing Kobuki RTC only rtunit: Installing basic + spi/i2c tools and modules rtunit_only: Installing spi/i2c tools and modules only rtunit_examples: Installing basic + PiRT-Unit examples EXAMPLE: 1) Just change hostname # rpi.sh kobuki0 2) Basic setup: Installing OpenRTM-aist (C++/Python) # rpi.sh kobuki --type basic " 3) Kobuki setup: Installing OpenRTM-aist (C++/Python) and Kobuki RTC # rpi.sh kobuki --type kobuki
spi と i2c のデバイスモジュールをロードする方法は、カーネルの3.18から変更になりました。Raspberry Pi用 OS Raspbian が2015年のバージョンから該当するようですが、カーネルのバージョンを確認して判断してください。
spi と i2c を利用するためには、raspi-config で Enable に設定します。Raspberry Pi の初期設定 のページをご覧ください。
これより古いバージョンでは、以下のファイルを設定します。
/etc/modprobe.d/raspi-blacklist.conf に
blacklist spi-bcm2708 blacklist i2c-bcm2708
という2行が設定されていますが、これをコメントアウトします。
# blacklist spi and i2c by default (many users don't need them) #blacklist spi-bcm2708 #blacklist i2c-bcm2708
これで、spi と i2c のデバイスモジュールがロードされるようになります。
spi や i2c デバイスモジュールがロードされても、デフォルトでは一般ユーザーからアクセスできないようなパーミッションに設定されています。 少々不便なので、デバイス作成時に誰でもアクセスできるように設定しておきます。 新たに、/etc/udev/rules.d/50-udev.rules というファイルを作成し、中に以下の1行を追記します。
KERNEL=="spidev*", SUBSYSTEM=="spidev", GROUP="spi", MODE="0666"
以上で準備は終了です。
PiRT-Unit では AD および DA は SPI経由で接続されており、SPI デバイスを制御する Python モジュールをインストールすることで、Python から手軽に AD および DA を利用することができます。
Python から SPI経由で AD、DA を利用するには、以下の拡張モジュールをインストールします。
下準備のため、以下のパッケージをインストールします。
sudo apt-get update sudo apt-get upgrade sudo apt-get install python-dev git-core i2c-tools python-smbus
py-spidev は github https://raw.github.com/doceme/py-spidev にあります。以下のようにしてインストールします。
$ cd ~ (or 適当なディレクトリー) $ git clone git://github.com/doceme/py-spidev $ cd py-spidev $ chmod 755 setup.py $ sudo ./setup.py install
これで、Python モジュール py-spidev が利用できるようになります。 試しに、spidev モジュールを利用してみます。
$ sudo python sudo: unable to resolve host raspbian-armhf Python 2.7.3 (default, Jan 13 2013, 11:20:46) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import spidev >>> spi = spidev.SpiDev() >>> spi.open(0,0) >>> print spi.xfer2([0x00,0,0,0])
これで、CN2のAD変換器の値を読みだしてプリントしています。AD変換器 (ADC104S021) は10bitのシングルエンド型で、サンプリングレートは最大200kHzです。(Linux上で200kHzのサンプリングレートを保証するものではありません。) 実際には、"spi.xfer2([0x00],0,0,0])" で出力される値を 1024 (bit) で割って、5.0 (V) を掛けた値が計測された電圧になります。
>>> r = spi.xfer2([0x00,0,0,0]) >>> print r[0] * 5.0 / 1024.0, " [V]"
ここまで、エラーなく実行できれば、spidev モジュールが正しくインストールされています。
WiringPi-Python は GPIO を制御するツール: WiringPi を Python から利用するためのモジュールです。 まずは WiringPi をインストールします。
$ git clone git://git.drogon.net/wiringPi $ cd cd wiringPi $ git pull origin $ ./build
次に、WiringPi-Python 本体を以下のようにインストールします。
$ sudo apt-get install python-dev $ git clone https://github.com/WiringPi/WiringPi-Python.git $ cd WiringPi-Python $ git submodule update --init $ sudo python setup.py install
以上で、必要なモジュールのインストールは終了です。
ここでは、PiRT-Unitの入出力をそれぞれ簡単にテストしてみます。
PiRT-Unit の CN2-CN5 がADコンバータのピンです。 ピンアサインは以下の図のようになっており、GND-5V間を分圧したものを入力ピンに加えることでセンサ値などを読み取ることができるようになっています。
ADコンバータをテストするもっとも簡単な方法は、Phidgetsのセンサを接続することです。 Phidgets は Phidgets Inc. から発売されている、IO拡張ボードとセンサ群製品です。 日本では、ぷらっとほーむなどで購入することができます。
PCにUSB接続の拡張IOボードを接続し、様々なセンサ、アクチュエータユニットを追加して、プログラム等から計測・制御することができるキットです。
PCと接続する場合は、Interface Kitが必要ですが、PiRT-Unitでのみ使用する場合は、Sensor Kitのみでよいでしょう。
PIRT-UnitのADピンは、Phidgetのセンサが接続可能なピンアサインとなっており、Phidgetデバイスを接続することで、容易に拡張することができるようになっています。
adc_test.py
#!/usr/bin/env python # -*- coding: euc-jp -*- import sys import time import spidev class ADC: def __init__(self): self.spi = spidev.SpiDev() self.spi.open(0, 0) def get_value(self, channel): sned_ch = [0x00,0x08,0x10,0x18] if ((channel > 3) or (channel < 0)): return -1 r = self.spi.xfer2([sned_ch[channel],0,0,0]) ret = ((r[2] << 6 ) & 0x300) | ((r[2] << 6) & 0xc0) | ((r[3] >> 2) & 0x3f) return ret def get_voltage(self, channel): ret = self.get_value(channel) * 5.0 / 1024 return ret def main(): adc = ADC() while 1: adc1 = adc.get_value(0) msg1 = "%1.5fV(%04x)" % ((float(adc1)*5/1024),adc1) print msg1, adc1 = adc.get_value(1) msg1 = "%1.5fV(%04x)" % ((float(adc1)*5/1024),adc1) print msg1, adc2 = adc.get_value(2) msg2 = "%1.5fV(%04x)" % ((float(adc2)*5/1024),adc2) print msg2, adc3 = adc.get_value(3) msg3 = "%1.5fV(%04x)" % ((float(adc3)*5/1024),adc3) print msg3, sys.stdout.write("\n") time.sleep(0.5) if __name__ == '__main__': main()
ADコンバータにピンに適当な可変抵抗やPhidgetセンサなどを接続して、adc_test.py を実行すると、下図のようにセンサ値を読み取ることができます。
dac_test.py
#!/usr/bin/env python # -*- coding: euc-jp -*- import spidev from time import sleep spi = spidev.SpiDev() spi.open(0,1) def changeLevel(ch, onOff, percent): bit7 = ch << 7 bit6 = 0 << 6 bit5 = 1 << 5 bit4 = onOff << 4 size=12 number=(2**size-1)*percent/100 number= number<<(12-size) number= number<<(12-size) bottomPart= number % 256 topPart=(number-bottomPart)>>8 firstByte=bit7+bit6+bit5+bit4+topPart secondByte=bottomPart return spi.xfer2([firstByte,secondByte]) def which_channel(): channel = raw_input("Which channel do you want to test? Type 0 or 1.\n") while not channel.isdigit(): channel = raw_input("Try again - just numbers 0 or 1 please!\n") return channel def main(): channel = 3 while not (channel == 1 or channel == 0): channel = int(which_channel()) print "These are the connections for the digital to analogue test:" print "jumper connecting GP11 to SCLK" print "jumper connecting GP10 to MOSI" print "jumper connecting GP9 to MISO" print "jumper connecting GP7 to CSnB" print "Multimeter connections (set your meter to read V DC):" print " connect black probe to GND" print " connect red probe to DA%d on J29" % channel raw_input("When ready hit enter.\n") percent=[0,25,75,100] for p in percent: r = changeLevel(channel,1,p) print "Your meter should read about {0:.2f}V".format(p*2.048/100.0) raw_input("When ready hit enter.\n") r = changeLevel(0,0,0) r = changeLevel(1,0,0) if __name__ == '__main__': main()
i2c_test.py
#!/usr/bin/env python # -*- coding: euc-jp -*- import smbus import time def main(): # LCD initialize i2c = smbus.SMBus(1) addr = 0x3e contrast = 42 # 0-63 i2c.write_byte_data(addr, 0, 0x38) # function set(IS=0) i2c.write_byte_data(addr, 0, 0x39) # function set(IS=1) i2c.write_byte_data(addr, 0, 0x14) # internal osc i2c.write_byte_data(addr, 0,(0x70 | (contrast & 0x0f))) # contrast i2c.write_byte_data(addr, 0,(0x54 | ((contrast >> 4) & 0x03))) # contrast/icon/power i2c.write_byte_data(addr, 0, 0x6c) # follower control time.sleep(0.2) i2c.write_byte_data(addr, 0, 0x38) # function set(IS=0) i2c.write_byte_data(addr, 0, 0x0C) # Display On i2c.write_byte_data(addr, 0, 0x01) # Clear Display i2c.write_byte_data(addr, 0, 0x06) # Entry Mode Set time.sleep(0.2) # LCD Clear i2c.write_byte_data(addr, 0, 0x38) # function set(IS=0) i2c.write_byte_data(addr, 0, 0x0C) # Display On i2c.write_byte_data(addr, 0, 0x01) # Clear Display i2c.write_byte_data(addr, 0, 0x06) # Entry Mode Set time.sleep(0.2) # Send to LCD line1 = '__test__' for c in line1: i2c.write_byte_data(addr, 0x40, ord(c)) i2c.write_byte_data(addr, 0, 0xc0) # 2nd line line2 = '__!(^^)!__' for c in line2: i2c.write_byte_data(addr, 0x40, ord(c)) if __name__ == '__main__': main()
Phidgets は Phidgets Inc. から発売されている、IO拡張ボードとセンサ群製品です。 日本では、ぷらっとほーむなどで購入することができます。
PCにUSB接続の拡張IOボードを接続し、様々なセンサ、アクチュエータユニットを追加して、プログラム等から計測・制御することができるキットです。
PCと接続する場合は、Interface Kitが必要ですが、PiRT-Unitでのみ使用する場合は、Sensor Kitのみでよいでしょう。
PIRT-UnitのADピンは、Phidgetのセンサが接続可能なピンアサインとなっており、Phidgetデバイスを接続することで、容易に拡張することができるようになっています。
ここでは、Phidget の Ministick sensorを利用して、移動ロボットを制御してみます。 Ministick sensor は Phidget Sensor Kit #2 に含まれています。
Ministick sensorの出力ピンとPiRT-Unitを以下のように接続します。
X軸方向 (横向き) がADのCH0に、Y軸方向 (縦向き) がADのCH1に対応します。
このデータを読むプログラムをPythonで書いてみます。
#!/usr/bin/env python # -*- coding: euc-jp -*- import sys import time import spidev class ADC: def __init__(self): self.spi = spidev.SpiDev() self.spi.open(0, 0) def get_value(self, channel): sned_ch = [0x00,0x08,0x10,0x18] if ((channel > 3) or (channel < 0)): return -1 r = self.spi.xfer2([sned_ch[channel],0,0,0]) ret = ((r[2] << 6 ) & 0x300) | ((r[2] << 6) & 0xc0) | ((r[3] >> 2) & 0x3f) return ret def get_voltage(self, channel): ret = self.get_value(channel) * 5.0 / 1024 return ret def main(): adc = ADC() while 1: adc1 = adc.get_value(0) msg1 = "%1.5fV(%04x)" % ((float(adc1)*5/1024),adc1) print msg1, adc1 = adc.get_value(1) msg1 = "%1.5fV(%04x)" % ((float(adc1)*5/1024),adc1) print msg1, adc2 = adc.get_value(2) msg2 = "%1.5fV(%04x)" % ((float(adc2)*5/1024),adc2) print msg2, adc3 = adc.get_value(3) msg3 = "%1.5fV(%04x)" % ((float(adc3)*5/1024),adc3) print msg3, sys.stdout.write("\n") time.sleep(0.5) if __name__ == '__main__': main()
このようなプログラムを作成します。TeraTermなどでRaspberry Piにログインして、サンプルプログラムを作成、テストします。
Linux raspbian-armhf 3.2.27+ #307 PREEMPT Mon Nov 26 23:22:29 GMT 2012 armv6l The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Tue May 14 07:07:04 2013 from dhcpe2078.a02.aist.go.jp pi@raspbian-armhf ~ $ vi adc_test.py pi@raspbian-armhf ~ $ chmod 755 adc_test.py pi@raspbian-armhf ~ $ ./adc_test.py
サンプルプログラムを実行すると以下のような画面になります。ジョイスティックを倒してみると、Ch0,Ch1のデータが変化します。
それぞれ、X軸は左に倒すと、Y軸は上に倒すと電圧値が上がり、逆に倒すと電圧値が下がることがわかります。 ministick sensorの座標の関係を図に表わすと以下のようになります。
これは、一般的な座標系とはX軸が逆向きになっていますので、センサ値を処理する際には注意が必要です。
また、中心位置では、それぞれ5.0Vの半分、2.5V程度を指していることもわかると思います。 ただし、この値はぴったり2.5Vではなく、状況によっても変化するので、使用する前にキャリブレーションが必要なこともわかります。
ministick sensorを利用したジョイスティックコンポーネントを作成します。 仕様としては、以下のようにします。
基本プロファイル | |
コンポーネント名 | Ministick |
モジュール概要 | Phidget ministick sensor component |
バージョン | 1.0.0 |
ベンダ名 | AIST |
モジュールカテゴリ | Input Device |
アクティビティ | |
onInitialize, onFinalize, onActivated, onDeactivated, onExecute | |
データポート | |
[out] pos | |
概要 | ジョイスティックのX-Y位置データ |
データ型 | TimedFloatSeq |
詳細 | data[0]: x位置, data[1]: y位置 |
単位 | 無し |
[out] vel | |
概要 | 移動ロボットの速度ベクトル |
データ型 | TimedVelocity2D |
詳細 | vx: 並進速度, vy: 0.0, va: 角速度 |
単位 | vx [m/s], va [rad/s] |
[out] wheel_vel | |
概要 | 車輪速度 |
データ型 | TimedFloatSeq |
詳細 | data[0]: 左車輪角速度, data[1]: 右車輪角速度 |
単位 | [rad/s] |
コンフィギュレーション | |
scaling | |
概要 | スケーリングファクタ |
データ型 | double |
GUIコントロール | slider.0.1 |
制約条件 | 0.0<=x<=10.0 |
tread | |
概要 | 移動ロボットのトレッド幅 |
データ型 | double |
GUIコントロール | slider.0.01 |
制約条件 | 0.0<=x<=1.0 |
print_xy | |
概要 | XYデータプリントのデバッグフラグ |
データ型 | string |
GUIコントロール | radio |
制約条件 | (YES,NO) |
print_vel | |
概要 | velデータのデバッグプリントフラグ |
データ型 | string |
GUIコントロール | radio |
制約条件 | (YES,NO) |
print_wvel | |
概要 | wheel_velデータのデバッグプリントフラグ |
データ型 | string |
GUIコントロール | radio |
制約条件 | (YES,NO) |
この仕様に従い、RTCBuilderでPythonのテンプレートコードを生成します。
コンポーネントにADCの読み込みなどの機能を追加していきます。
まず、コンポーネントのコンストラクタでSPIオブジェクトを生成し初期化します。
他の import 文の近くに、spidevをimportする一文を追加します。計算などで使用するのでmathモジュールもインポートします。
# Import RTM module import RTC import OpenRTM_aist import math import spidev
さらに、コンストラクタで、必要な変数を初期化子、SPIオブジェクトを生成します。
class Ministick(OpenRTM_aist.DataFlowComponentBase): def __init__(self, manager): OpenRTM_aist.DataFlowComponentBase.__init__(self, manager) self._scaling = [1.0] self._tread = [0.2] self._print_xy = ["NO"] self._print_vel = ["NO"] self._print_wvel = ["NO"] self.x = 0.0 self.y = 0.0 self.spi = spidev.SpiDev() self.spi.open(0, 0)
AD変換器からデータを読む関数を Ministickクラスに追加します。 init() 関数の次あたりに、以下の関数を追記します。
def get_adc(self, channel): sned_ch = [0x00,0x08,0x10,0x18] if ((channel > 3) or (channel < 0)): return -1 r = self.spi.xfer2([sned_ch[channel],0,0,0]) ret = ((r[2] << 6 ) & 0x300) | ((r[2] << 6) & 0xc0) | ((r[3] >> 2) & 0x3f) return ret
X-Yの位置から車輪の速度へ変換する関数をMinistickクラスに追加します。
dev xy_to_wvel(self, x, y): th = math.atan2(y, x) v = math.hypot(x, y) vl = v * math.cos(th - (math.pi/4.0)) vr = v * math.sin(th - (math.pi/4.0)) return (vl, vr)
車輪速度から速度ベクトルへ変換する関数をMinistickクラスに追加します。
def wvel_to_vel2d(self, vl, vr): v = (vr + vl) / 2.0 if v < 0.0: w = - (vr - vl) / self._tread[0] else: w = (vr - vl) / self._tread[0] return RTC.Velocity2D(v, 0.0, w)
コンポーネント初期化時に、ジョイスティックのニュートラル位置をキャリブレーションします。 AD変換器からデータを100回程度読み込み平均し、オフセットデータとして保存します。
def onInitialize(self): : 中略 self.x_offset_v = 0.0 self.y_offset_v = 0.0 for i in range(1, 100): self.x_offset_v += self.get_adc(0) self.y_offset_v += self.get_adc(1) self.x_offset_v = self.x_offset_v / 100.0 self.y_offset_v = self.y_offset_v / 100.0 return RTC.RTC_OK
最後に、onExecute関数を実装します。
def onExecute(self, ec_id): self.x = - (self.get_adc(0) - self.x_offset_v) * self._scaling[0] / 1000.0 self.y = (self.get_adc(1) - self.y_offset_v) * self._scaling[0] / 1000.0 if self._print_xy[0] != "NO": print "(x, y) = ", self.x, self.y self._d_pos.data = [self.x, self.y] self._d_wvel.data = self.xy_to_wvel(self.x, self.y) if self._print_wvel[0] != "NO": print "(vl, vr) = ", self._d_wvel.data[0], self._d_wvel.data[1] self._d_vel.data = self.wvel_to_vel2d(self._d_wvel.data[0], self._d_wvel.data[1]) if self._print_vel[0] != "NO": print "(vx, va) = ", self._d_vel.data.vx, self._d_vel.data.va self._posOut.write() self._velOut.write() self._wvelOut.write() return RTC.RTC_OK
Ministickコンポーネントと、いろいろなものを接続してテストしてみます。
TkMobileRobotCanvasと接続してみます。
Windows上のインストールされているOpenRTM-aist-Pythonのサンプルから TkMobileRobotCanvasを起動します。 同時に、ネームサービスとRTSystemEditorも起動してください。
Createボタンを押し、移動ロボットを一つ生成します。 ネームサーバにコンポーネントが一つ現れるので、SystemEditor上にドラッグアンドドロップします。
次に、Raspberry Pi上でネームサーバとMinistickコンポーネントを起動します。
$ rtm-naming $ ./Ministick.py
RTSystemEditorから、RaspberryPiのネームサーバに接続すると、Ministickコンポーネントが見えますので、SystemEditor上にドラッグアンドドロップします。
Ministick の wvel データポートから先ほど生成した移動ロボットコンポーネントへポートを接続します。
アクティベートすると、両方のコンポーネントが緑色になり操作可能になります。
前述のKobukiの制御、の節に従ってKobukiコンポーネントをRaspberryPi上で起動します。
Ministickコンポーネントを別のRaspberry Pi上で起動します。
それぞれのコンポーネントはローカルのネームサーバ(デフォルト)に参照を登録させて、別のPCからこれら2つのネームサーバにRTSystemEditorで接続します。 Name Service Viewに2つのコンポーネントが見えるはずですので、これら (Ministick の vel と KobukiAIST の targetVelocity) を接続します。(下図参照)
RTSystemEditorのメニューバーの緑色の再生ボタン(全活性化)を押すと、システムが動き出しMinistickでKobukiを操作できます。
Ministickがニュートラル状態でも、TkMobileRobotのロボットやKobukiが少しずつ動く場合があります。これは、ニュートラル状態のキャリブレーションが十分ではないためです。 Ministickコンポーネントを工夫して、使いやすいコンポーネントにしてみましょう。
例えば、キャリブレーションをonActivatedで行うようにすれば、一旦Deactivateして再度Activateすれば零点をリセットできます。 また、onDeactivatedの時には速度0を出力するようにすれば、JyoistickのDeactivateによりロボットが安全に停止します。 あるいは、零点付近に不感帯を設けることで、多少キャリブレーションがずれても、ニュートラル状態で必ず0が出力されるようにできます。
以下のようなパーミッション関係のエラーが出ることがあります。 上述のudevの設定が正しく行われていない可能性がありますので、見直してください。
pi@raspbian-armhf ~ $ ./adc_text.py Traceback (most recent call last): File "./adc_text.py", line 47, in <module> main() File "./adc_text.py", line 25, in main adc = ADC() File "./adc_text.py", line 10, in __init__ self.spi.open(0, 0) IOError: [Errno 13] Permission denied
また、sudo を利用しても実行可能です。
PiRT-UnitにはZigBeeモジュールXBeeを接続するためのコネクタがあります。 RaspberryPiからシリアルデバイス経由で利用して、他のZigBeeモジュールとの通信に利用したり、シリアルコンソールを無線化するのにも利用できます。 Raspbian Wheesyではデフォルトでシリアルコンソールに設定されています。 この解説では、シリアルコンソールをXBeeで無線化する方法を説明します。
XBeeモジュールとPCを接続するにはXBee-USBエクスプローラを利用する必要があります。 XBeeモジュール接続コネクタとUSBコネクタがついており、PCに接続してPCからXBeeの各種設定を行ったり、XBeeをシリアルポートとして利用することができます。
XBee-USBエクスプローラは、様々なメーカーから発売されています。
商品名 | メーカー | 価格 | URL |
AE-XBEE-USB | 秋月電子通商 | 1,280円 | http://akizukidenshi.com/catalog/g/gK-06188/ |
SFE-WRL-08687 | Sparkfun Switch Science |
2,619 円 | http://www.switch-science.com/catalog/30/ http://strawberry-linux.com/catalog/items?code=18128 でも入手可能 |
SFE-WRL-09819 | Sparkfun Switch Science |
2,619 円 | http://www.switch-science.com/catalog/344/ |
デバイスマネージャを開いてください。 Windows7では、「コントロールパネル」→「システムとセキュリティ」→「システム」→「デバイスマネージャー」から開くことができます。 デスクトップに「コンピュータ」がある場合、右クリック→「プロパティー(R)」→「デバイスマネージャー」から開くのが最も早いでしょう。
XBee-USBエクスプローラをPCのUSBポートに接続します。 初めて接続する場合は、デバイスの認識とデバイスドライバのインストールでしばらく時間がかかります。 デバイスドライバのインストールが終了すると、以下のようにデバイスマネージャにCOMポートとして現れます。 この時、どのCOMポートに割り当てられたかを覚えておいてください。(下の例ではCOM8に割り当てられた。)
運悪くドライバが自動でインストールされない場合、FTDI社のチップ(FT232B等)を使ったXBee-USBエクスプローラの場合はFTDI者から直接ドライバをダウンロードしてインストールしてください。
なお、秋月およびSparkfunのXBee-USBエクスプローラでは、Windows7はドライバのインストールは不要でした。
XBeeモジュールは大きく分けて親機と子機に分かれており、そのXBeeモジュールを親機にするか子機にするかはXBee-USBエクスプローラ経由でPCから設定します。
一つのXBeeネットワーク内には必ず1台の親機"Coordinator"が必要で、親機に対して複数の子機がぶら下がる形になります。 一方、購入直後のXBeeモジュールは子機"Router"に設定されており、初めてXBeeネットワークを構成する際には、どれか一つを親機"Coordinator"にしてあげる必要があります。
XBeeモジュールを設定するには、DigiのWebページからX-CTUという設定ソフトウエアをダウンロードしPCにインストールする必要があります。
DigiのX-CTUのダウンロードサイトへ行きます(更新等によるリンク切れがあった場合はMLなどでお知らせいただければ幸いです)。
ページの Diagnostics, Utilities and MIBs の項目をクリックすると、X-CTUのインストーラへのリンクが現れるので、クリックしてダウンロードします。(63MB程度あります。)
ダウンロードした実行ファイル 40003002_C.exe (このファイル名はバージョンアップなどにより変更されるかもしれません) をクリックして実行すると、インストーラが開始されます。指示に従ってインストールを完了してください。
最後に、frimwareのバージョンアップをするか聞いてくることがありますが、特に必要がなければスキップしてください。(結構時間がかかります。)
インストール完了後、X-CTUを起動します。起動後は以下のような画面が表示されます。
X-CTUの「PC-Settings」を選択します。 タブ内のSelect Com PortでXBee-USBエクスプローラデバイスが接続されているCOMポートを選択します。(この例ではCOM8です。) XBee-USBエクスプローラデバイスが接続されているCOMポートが不明な場合は、一旦デバイスをPCから取り外し、デバイスマネージャでなくなるCOMポートを観察するか、X-CTUを再度起動してなくなったCOMポートを見つけるなどして対応するCOMポートを特定してください。
Select Com Portで対象となるCOMポートを選択したら、右下のTest/Queryボタンを押してください。XBeeと通信を行い、以下のようにシリアルナンバーなどを表示します。
次に接続されているXBeeが現在どのような設定になっているか、ファームウエアの情報を読み込みます。 X-CTUの「Modem Configuration」タブをクリックし、下のModem Parameter and Firmwareのエリアの「Read」ボタンをクリックします。 すると、XBeeとの通信が開始され、少し経つと以下のようにXBeeの設定情報が下のエリアにツリー表示されます。
また、「Modem」の部分にXBeeモジュールの種類、「Function Set」の部分にファームウェアの種類、「Version」にファームウェアのバージョンが表示されます。
「Function Set」の部分はおそらく ZIGBEE ROUTER AT となっているはずですが、これはこのXBeeモジュールが"Router"すなわち子機として設定されていることを意味します。
ここで子機"Router"を親機"Coordinator"に変更してみます。 親機すなわり「Coordinator」に変更するために、プルダウンメニューからZIGBEE COORDINATOR ATを選択します。
「Write」ボタンを押すとXBeeへの書き込みが開始されます。1から2分程度で書き込みが終了します。
先ほどのファームウエアの選択時に、ZIGBEE COORDINATOR AT のほかに ZIGBEE COORDINATOR API というファームウエアがあったことに気付かれたかもしれません。 AT とつくものは、ATコマンドでXBeeの設定を行うタイプのファームウエアで、一方APIとつくものは、API経由でXBeeの設定を行うタイプのファームウエアを意味します。
もしXBeeが全く応答しなくなったら、強制的にファームウエアを上書きし出荷状態に戻します。 以下の手順に従って、工場出荷状態に戻してください。
Raspberry Pi上に存在する GPIO ピンのうちのI2C用ピン(2本)を用いると、複数の I2C デバイスを操作することができます。 ここでは、I2C を利用するためのセットアップ、C言語によるプログラム記述の実例、RTコンポーネント化するためのヒントを示します。
を取り上げます。
I2C は2線を用いるシリアルバス通信の一種です。クロック線のエッジを用いてデータ線上で値の授受行い、バス上にある複数デバイスを制御することができます。 バス上にはマスターとスレーブがあり、規格では複数のマスター/スレーブの存在を許しているようですが、実際には単一マスター/複数スレーブによる使用が簡単です。 マイコンなどから利用する場合は、マスターによるクロック信号の送信と、データの通信プロトコル実装などを考慮して実装しなければなりませんが、Raspberry Pi を用いると既存のライブラリを使用して非常に簡単に I2C デバイスを利用することができます。
Raspberry Pi の GPIOポート(26pin)のうち、もともと I2C 通信専用に設定されている GPIO が2本存在します。(下図参照) GPIO 0 が I2C のデータ用ポート、GPIO 1 が I2C のクロック用ポートとしてあらかじめ設定されており、これらを利用することで簡単に I2C 通信を行うことができます。
この2本のポートを用いて I2C デバイスを直接接続するだけで、クロック線のドライブ・通信プロトコルの解釈などはすべて Raspberry Pi上の Linux で行われます。 ユーザーはデバイスファイル (/dev/i2c-*) の読み書きを行うことでデバイスからのデータ取得/デバイスへのデータ出力を実現できます。
複数のデバイスの接続はバス接続(下図)で実現することができます。 デバイス間の調停も Raspberry Pi側である程度は行うことになっています。 なお、上記GPIO 0、GPIO 1ポートは Raspberry Pi 基板上でプルアップ抵抗によりプルアップされていますので、別途プルアップする必要はありません。
Raspberry Pi で I2C 通信を使うためにはいくつのかの設定ファイルを変更する必要があります。 ファイルは root 権限で編集してください。
/etc/modules に以下の一行を追加します。
i2c-dev
これにより、/dev/i2c-* が有効になります。
/etc/modprobe.d/raspi-blacklist.conf の以下の一行をコメントアウトします。
# blacklist i2c-bcm2708
以上の変更が終了したら、変更を反映するために再起動してください。
i2c-tools はコマンドラインから I2C デバイスへアクセスするためのツールです。 実際に開発する際には、コマンドラインから I2C デバイスの動作を確認したいことがたびたびありますので、事前に i2c-tools をインストールしておいてください。
sudo apt-get install i2c-tools
として、インストールします。
上記のブロック図では、I2C デバイスは SDA、SCL の2線のみが配線されていましたが、実際には当然各デバイスに電源を供給してあげる必要があります。 それほど消費電力の大きくない I2C デバイスであれば、Raspberry Pi の GPIO ピンヘッダ 26pin のうち、2,3pin(5V)、1,17pin(3.3V)からでている電源を利用することができます。 I2C デバイスの説明書・データシートを参照した上で、5V ないし 3.3V のうち適切な電源を供給するようにしてください。 また、PiRT-Unit には I2C用の4ピンのコネクタが付属しており、SDA、SCL、3.3V、GND が配線可能です。
今回用いる I2C デバイスはすべて 3.3V で動作するため、Raspberry Pi の 1,17pin(3.3V)から各デバイスに電源を供給します。 以下にブレッドボード上に上記の4つのデバイスを配置した際の配線図を示します。
配線の際にはデバイスの説明書/データシートをよく読んで間違いのないよう配線してください。配線を間違うとデバイスが認識されないだけでなく、場合によってはデバイスが破壊される可能性もあります。
デバイスの種類によっては、使用方法によって適宜ジャンパが必要なものや、I2Cの2本の信号線以外に割り込み、リセット信号などをが必要なものもあります。その場合は、Raspberry Pi の GPIO ピンを適宜利用し、プログラム側から制御する必要があります。
上記の配線が完了したら、Raspberry Pi上から i2c-tools を利用してデバイスを操作することができます。 まずは、i2cdetect コマンドで、I2C バスに接続されているすべてのデバイスのアドレスを確認してみましょう。以下のようにタイプすると、下図のような表示が現れます。
$ sudo i2cdetect 1
図では4個のデバイスが存在していることが確認できます。-(マイナス)以外に16進数の数字が表示されていますが、それぞれが各デバイスのアドレスを表しています。内訳は、
となっています。このコマンドと各デバイスの説明書/データシートを突き合わせることで、Raspberry Pi が I2C デバイスを認識できているかを確認することができます。 なお、Raspberry Pi のリビジョンによって、コマンドの後ろに入れるバス番号が0の場合と1の場合がありますので注意してください。
デバイスに実際にデータを書き込むには i2cset コマンドを、デバイスからデータを読み込むにはi2cgetコマンドを使用します。(下図参照)
// LSM303DLHCの地磁気センサーへアクセス $ sudo i2cset -y 1 0x1e 0x00 0x14 $ sudo i2cset -y 1 0x1e 0x01 0x20 $ sudo i2cset -y 1 0x1e 0x02 0x00 $ sudo i2cget -y 1 0x1e 0x32 $ sudo i2cget -y 1 0x1e 0x31 $ sudo i2cget -y 1 0x1e 0x03 $ sudo i2cget -y 1 0x1e 0x04 $ sudo i2cget -y 1 0x1e 0x05 $ sudo i2cget -y 1 0x1e 0x06 $ sudo i2cget -y 1 0x1e 0x07 $ sudo i2cget -y 1 0x1e 0x08 $ sudo i2cget -y 1 0x1e 0x09 // LSM303DLHCの加速度センサーへアクセス $ sudo i2cset -y 1 0x19 0x20 0x27 $ sudo i2cget -y 1 0x19 0x28 $ sudo i2cget -y 1 0x19 0x29 $ sudo i2cget -y 1 0x19 0x2a $ sudo i2cget -y 1 0x1e 0x2b $ sudo i2cget -y 1 0x1e 0x2c $ sudo i2cget -y 1 0x1e 0x2d
この例では、LSM303DLHCの説明書に従って、まず地磁気センサを動作させるために0x1eのアドレスのデバイスに対して内部レジスタ0x00~0x02に所定の数値を書き込み、内部レジスタ0x31~32、0x03~0x09を読み出しています。 その後、LSM303DLHCの加速度センサを動作させるため、0x19のアドレスのデバイスに対して内部レジスタ0x20に所定の数値を書き込み、0x28~0x2dまでの数値を読み込んでいます。
i2c-toolsのコマンド群はルート権限でのみ実行可能であるのでsudoを使用するか、sudo bashとしてrootになって実行する必要がありますので注意してください。
プログラムからこれらのデバイスにアクセスするには、デバイスファイル (/dev/i2c-0 または /dev/i2c-1) をオープンし、読み書きすることでデバイスのレジスタ操作および値の読み出しを行います。
ただし、I2Cデバイスの種類によっては特有の初期化シーケンスを持っていたり、デバイスのデータの読み書きに一定の手順を要求するものがあります。各デバイスの説明書やデータシートを確認しながらプログラムを作成する必要があります。 (インターネット上には、RaspberryPi/AVRマイコン/ArduinoなどからのI2Cデバイスの制御方法に関する情報が色々あるようですので、そちらも参考にしてください。)
以下に、L3GD20デジタル3軸ジャイロセンサモジュールを用いたサンプルプログラムを掲載します。
このモジュールはI2CおよびSPIでの通信が可能なセンサモジュールで、I2Cデバイスとして利用する場合、アドレスを0x6a、0x6bの2種類から選択可能です。ここではアドレスを0x6aとして使用しています。
内部レジスタは、
0x0f | 常に0xd4を出力 |
0x20 | 0x0fを書き込むことで動作開始 |
0x28~2d | 3軸ジャイロデータが格納される |
このほか、検出レンジ、サンプリングレート等の設定も可能ですが、ここでは触れません。
以下にプログラムを示します。 なお、こちらからダウンロードすることもできます。また、I2CセンサをRTコンポーネント化した、データ取得コンポーネントおよびディスプレイコンポーネントもサンプルとして示します。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <linux/i2c-dev.h> // I2C用インクルード #include <fcntl.h> #include <sys/ioctl.h> #include <wiringPi.h> // delay関数用にインクルード // プロトタイプ宣言 void L3GD20_readData(int *gyrodata, int fd); void L3GD20_write(unsigned char address, unsigned char data, int fd); unsigned char L3GD20_read(unsigned char address, int fd); void L3GD20_init(int fd); int main(int argc, char **argv) { int i2c_fd; // デバイスファイル用ファイルディスクリプタ // char *i2cFileName = "/dev/i2c-0"; // I2Cデバイスファイル名 char *i2cFileName = "/dev/i2c-1"; // RaspberryPiのリビジョンに合わせて変更 int i2cAddress = 0x6a; // L3GD20のI2Cアドレス int gyroData[3]; // ジャイロ3軸(x,y,z)データ格納用 printf("i2c Gyro(L3GD20) test program\n"); delay(500); // I2Cデバイスファイルをオープン if ((i2c_fd = open(i2cFileName, O_RDWR)) < 0) { printf("Faild to open i2c port\n"); exit(1); } // L3GD20用にセット if (ioctl(i2c_fd, I2C_SLAVE, i2cAddress) < 0) { printf("Unable to get bus access to talk to slave\n"); exit(1); } //デバイス初期化 L3GD20_init(i2c_fd); // 1秒ごとに20回ジャイロデータを取得、表示 int i; for(i=0; i<20; i++){ // デバイスからデータ取得 L3GD20_readData(gyroData, rtc); // 取得したデータを校正して表示、1秒待ち printf("x, y, z : %5.2f, %5.2f, %5.2f\n", (float)gyroData[0]*0.00875,(float)gyroData[1]*0.00875, (float)gyroData[2]*0.00875); delay(1000); } return; } // L3GD20用 1バイト書き込みルーチン:addressで示すレジスタにdataを書き込む void L3GD20_write(unsigned char address, unsigned char data, int fd) { unsigned char buf[2]; buf[0] = address; buf[1] = data; if((write(fd,buf,2))!=2){ printf("Error writing to i2c slave\n"); exit(1); } return; } // L3GD20用 1バイト読み出しルーチン: addressで示すレジスタの値を読み出す // 戻り値がレジスタ値 unsigned char L3GD20_read(unsigned char address, int fd) { unsigned char buf[1]; buf[0] = address; if((write(fd,buf,1))!= 1){ // addressを一度書き込む所に注意 printf("Error writing to i2c slave\n"); exit(1);} if(read(fd,buf,1)!=1){ printf("Error reading from i2c slave\n"); exit(1);} return buf[0]; } // L3GD20用 ジャイロデータ読み出しルーチン: // 整数値配列へのポインタを使ってデータを受け渡す void L3GD20_readData(int *gyrodata, int fd) { unsigned char data[6]; // センサから3軸に対して2バイトずつデータを読み出す int i; for(i=0; i<6; i++){ data[i]=L3GD20_read(0x28+i,fd); } // 各数値を32bit幅の整数に整形する // センサの数値精度が16bit・2の補数表現での出力のため、シフトで加工 gyrodata[0]=((int)data[1]<<24|(int)data[0]<<16)>>16; gyrodata[1]=((int)data[3]<<24|(int)data[2]<<16)>>16; gyrodata[2]=((int)data[5]<<24|(int)data[4]<<16)>>16; return; } // L3GD20用 ジャイロデータイニシャライズルーチン void L3GD20_init(int fd) { unsigned char Data; printf("L3GD20 init seq. start\n"); // L3GD20 動作確認 // L3GD20の0x0fレジスタは常に0xd4にセットされているため動作確認ができる Data = L3GD20_read(0x0f,fd); if(Data != 0xd4){ printf("L3GD20 is not working\n"); exit(1);} delay(10); // レジスタへの書き込みチェックとイニシャライズを同時に行う printf("read OK, Now writing check...\n"); // 0x20レジスタに0x0fを書き込むことで動作させる L3GD20_write(0x20, 0x0f, fd); // 0x20レジスタに実際に0x0fが書かれたか確認 Data = L3GD20_read(0x20,fd); if(Data != 0x0f){ printf("Writing miss\n"); exit(1); } printf("Writing OK\n"); delay(10); return; }
RaspberryPi上でI2Cデバイスにアクセスするプログラムは以上のように記述することができます。上記のコードをRTコンポーネント化する場合には、例えば初期化部分をRTコンポーネントのonInitializeまたはonActivatedに実装、デバイスからデータを取得して表示するルーチンを onExecute に実装し、OutPortから出力するようにすれば、センサデータを出力するコンポーネントが出来上がります。
実行コンテキストの周期は、I2Cデバイスおよびバスの速度と、バスに接続されているI2Cデバイスの数、それぞれのI2Cデバイスのサンプリングレートなどから決める必要があります。
複数のデバイスをI2C接続した状態でこれらをコンポーネント化する場合、各デバイス間でデータの読み書きのスケジューリングを考慮する必要があります。 実現手法として考えられるのは、