Maker Faire Tokyo 2016 の裏側

「Maker Faire Tokyo 2016 に出展してきました」の裏側を紹介します。

展示中の内容は「こちら」を見ていただくとして、
今回はその中で缶デバイスの中身についてです。

缶デバイス画像

構成

中身はラブベリーパイをベースとした、簡易的な姿勢計測装置のようなものでした。

PCとの通信はブルートゥースを利用、ちなみに2缶作成したのですが、会場ではブルートゥースが飛びすぎてて1缶上手く繋がらなかったです。

部品リスト

  • ラズベリーパイ
  • 9軸センサ(MPU9250)
  • 圧力センサ 円形
  • アナログデジタル変換
  • Bluetoothドングル
  • 3400mAh 筒状バッテリー

中身の写真
img_5683

* MPU9250とラズベリーパイはI2C通信
* 圧力センサはアナログ・デジタル変換を挟んでSPI通信
* 電源はUSBのコネクタ部分が収まりきらなかったためGPIOから供給

センサ情報アクセスについて

MPU9250はインターフェースとしてI2CとSPI両方対応しているため、両方共試してみました。
ただ、SPIではアクセス速度が早過ぎるためか、地磁気センサのアクセスにて値がとれなくなる現象が合ったため途中で断念ました。
I2Cではアドレスが2種類選べる(0x68, 0x69)ので標準で2つまでなら動作できます。
地磁気センサスレーブアドレスとして0x0cを利用するのでアドレスが被るので、交互に読み取るような形でないと情報がとれなかったです。
素直にマルチプレクサでチップ丸ごと切り替えるような方法のほうが良いかもしれないです。

raspberry pi の I2Cの設定、pythonの準備

他の方も沢山記事として書いているの割愛

実践:I2Cを通してMPU9250の値をpythonで取る

MPU9250の初期化部分を間違うと値取れても変な値だったり、そもそも値が取れなかったりします。


class MPU9250:
"""MPU9250ハンドリング用クラス"""
def __init__(self, address=0x68, CHANNEL=1):
"""チップの確認、初期化"""
self.address = address
self.bus = smbus.SMBus(CHANNEL)
data = self.bus.read_i2c_block_data(self.address, WHO_AM_I, 1)
if data[0] == 113:
self.init_MPU9150()
else:
raise Exception
def init_MPU9150():
"""初期化"""
SMPLRT_DIV = 0x19
CONFIG = 0x1A
GYRO_CONFIG = 0x1B
ACCEL_CONFIG = 0x1C
INT_PIN_CFG = 0x37
INT_ENABLE = 0x38
PWR_MGMT_1 = 0x6B
AK8975A_ADDRESS = 0x0C
AK8975A_CNTL = 0x0A
# reset
self.bus.write_byte_data(self.address, PWR_MGMT_1, 0x80)
self.bus.write_byte_data(self.address, PWR_MGMT_1, 0x00)
# clock-source set up bits 2:0 (0x01)
self.bus.write_byte_data(self.address, PWR_MGMT_1, 0x01)
# set up Gyro and Accel (1 kHz)
self.bus.write_byte_data(self.address, CONFIG, 0x03)
# set sample rate = gyroscope output rate/(1 + SMPLRT_DIV)
self.bus.write_byte_data(self.address, SMPLRT_DIV, 0x04)
# set gyroscope range (2000)
self.bus.write_byte_data(self.address, GYRO_CONFIG, 0x18)
# set accell range (16g)
self.bus.write_byte_data(self.address, ACCEL_CONFIG, 0x16)
# set up mag
self.bus.write_byte_data(self.address, INT_PIN_CFG, 0x22)
self.bus.write_byte_data(self.address, INT_ENABLE, 0x01)
# set mag range 100Hz and 16bit
self.bus.write_byte_data(AK8975A_ADDRESS, AK8975A_CNTL, 0x06 | 0x10 )
#
self.accel_coefficient = 8/float(0x8000) # 16G
self.gyro_coefficient = 1000/float(0x8000) # 2000
self.mag_coefficient = 4912 / 32760.0 # 16bit
def read_all_data(self):
"""全データの取得"""
ACCEL_XOUT_H = 0x3B
"""加速度、ジャイロはHLで並ぶ、アドレス的に連続しているため一度に取得可能"""
data = self.bus.read_i2c_block_data(self.address, ACCEL_XOUT_H, 12)
ax = self.bytes_toint(data[0], data[1]) * self.accel_coefficient
ay = self.bytes_toint(data[2], data[3]) * self.accel_coefficient
az = self.bytes_toint(data[4], data[5]) * self.accel_coefficient
gx = bytes_toint(data[6], data[7]) * self.gyro_coefficient
gy = bytes_toint(data[8], data[9]) * self.gyro_coefficient
gz = bytes_toint(data[10], data[11]) * self.gyro_coefficient
# ジャイロ、加速度読み込み終わり
AK8975A_ADDRESS = 0x0C
AK8975A_XOUT_L = 0x03
"""地磁気はLHで並ぶ、7バイト読み込まないと更新されない"""
data = self.bus.read_i2c_block_data(AK8975A_ADDRESS, AK8975A_XOUT_L, 7)
mx = bytes_toint(data[1], data[0]) * self.mag_coefficient
my = bytes_toint(data[3], data[2]) * self.mag_coefficient
mz = bytes_toint(data[5], data[4]) * self.mag_coefficient
return [ax, ay, az, gx, gy, gz, mx, my, mz]
def stop_mag(self):
"""地磁気センサの停止"""
self.bus.write_byte_data(self.address, INT_PIN_CFG, 0x20)
def start_mag(self):
"""地磁気センサの開始"""
self.bus.write_byte_data(self.address, INT_PIN_CFG, 0x22)
def bytes_toint(msb, lsb):
if not msb & 0x80:
return msb << 8 | lsb # +ve return - (((msb ^ 255) << 8) | (lsb ^ 255) + 1)

うまくいけば、下記の用なコードで生データが取れます


mpu = MPU9250()
print mpu.read_all_data()

コマンドベースで繋がっているか確認するために、ラズパイとMPU9250をつなげた段階では下記のようになっているはずです。
(アドレスを69にしている場合は69が立ちます)
つなげた段階で表示されていない場合は、何かしら間違っています。


i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

地磁気センサも初期化した段階では、0cも使われるため、下記のようにならないと値が取れません。


i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- 0c -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

あとはこのデータを元に、ジャイロの値から回転角求めたり自力でやるのもよいですし、既に公開されているオープンソースの下記を利用したりするのもいいかもしれません。固定値のチューニングは必要です。

測定のレンジについては上記のpythonでは下記のようになっています。
・加速度 ±16g
・角速度 2000dps
・地磁気 16bit 4912μT

雑感

書くの忘れたのですが、圧力センサは、途中にアナログ・デジタル変換挟んでSPI接続です。

初めて作成した感想としては、
想像以上にセンサの性能が上がっているのだなぁというのを感じ、自分が思っている以上に精度が出ました。

You may also like...