ラベル I2C の投稿を表示しています。 すべての投稿を表示
ラベル I2C の投稿を表示しています。 すべての投稿を表示

2016年2月1日月曜日

IchigoJam で遊ぶ その8 IchigoStorms type2 + EV3 Sensor Adapter for NXT

 前回のその7では、Arduinoを使わないでLEGOの制御をやってみようということで、メカナムホイールを使ったこんなのとか、


 オムニホイールを使ったこんなのを、


動かしてみたのですが、センサに関しては、結局NXTのタッチセンサをアナログ入力で拾えた以外は、全然ダメでした。EV3のセンサ類はタッチセンサを除いてUART接続なのではじめから諦めていたのですが、NXTの超音波センサはI2Cという噂?を聞いていたので期待していたのですが、通信速度やプロトコルの仕様が特殊なのか動作させることができませんでした。

 そこで今回は、Mindsensor社の EV3 Sensor Adapter for NXT というのをためしてみることにしました。この EV3 Sensor Adapter for NXT はEV3のUART接続方式のセンサをNXTやArduinoで使えるようにするもので、内部的にはUARTとI2C通信の変換を行うアダプタと思われます。



 当初 Mindsensor社のドキュメントに記述されたI2CアドレスでIchigoJamからI2C通信を試みたのですが、反応がなかったため、一旦Raspberry Pi(BrickPi)に接続して、i2cdetectで調べたところ、I2Cアドレスがドキュメントの記述とは違い、0x19になっていました。改めてIchigoJamで0x19に対して通信を行ったところ、無事データの取得ができました。今回調べたのはEV3用ジャイロセンサとEV3用超音波センサで、ジャイロセンサでは2byteのデータで-32768~32767くらいの範囲の値が、超音波センサでは1byteで30~250くらいの範囲の値が取得できました。超音波センサは本当は2bytで0~2550の範囲で値が取れるという資料があるので、ちょっとその辺と合わないのですが、今回1byteで取得できた30~250の値がちょうどmm単位(10で割ってcm単位)と考えると実際のセンサと障害物の距離に近かったので、これで良しとします。



 今回はIchigoStorm type2 と EV3 Sensor Adapter for NXT と EV3超音波センサの組み合わせでタンクを動かしました。


 プログラムは以下の感じ。行番号が不揃いなのは他のプログラムを流用して作ったためです。

 EV3 Sensor Adaptor for NXT の中身はマイコンが入っていると思うのですが、同様なIchigoJamのI2Cに接続してUARTを拡張できるようなICや安価なマイコンがあれば、UARTの無い(コンソール用にはあるけど)IchigoJamに色々つなぐのに便利なんですが、そういうのは無いのかな?

2015年4月26日日曜日

IchigoJam が楽しい

 一週間ほど前に、(mbed) LPC1114 を試した際にLPC1114つながりで、IchigoJam というLPC1114でBASICが使える子供向けコンピュータ(と言いつつ、これを楽しんでるのは、70後半~80年代にマイコンボードやパソコンをいじっていたおっさんたちだと思う)も入手して触ってみたんですが、これがなかなか楽しいおもちゃです。小学生時代にN-BASICにはじめて触った頃を思い出してしまいました。ところで、このIchigoJamはLPC1114を使っているので、BASICと言いつつマイコンっぽい機能も使えたりします。例えば、デジタルI/Oがいくつかとアナログ入力とI2CとUARTが1chずつあります。となると、当がらくた部屋としては、やはりうごきモノの載せてみたくなるわけです。

 そこで、今回はAmazonで1400円くらいで売っている、これで行ってみようと思います。

 このリモコン昆虫よりも若干大きくなりますが、タミヤのユニバーサルプレートにIchigoJamと電池ボックスを取り付け、このリモコン昆虫の上というか背中の部分に載せます。
 まず、IchigoJam 4つの穴すべてがユニバーサルプレートの穴位置とは揃わないので、内2つをユニバーサルプレートとも固定、残り2つはスペーサを立てているだけで固定はしていません。


 電池ボックス。今回は単4エネループ三本で。


 単純にモータOn/Offができればいいので、Digital出力+Hブリッジでもいいのですが、I2Cを使ってみたかったので、あえて、DRV8830 I2Cモータドライバを使っています。またいつものPSDセンサを前方部に取り付けて、アナログ入力(IN2)に接続しています。


 シールド(ドータボード)の真ん中辺にあるのは、電池から3.3Vを生成するためのDC-DCとモータ側とIchigoJam側を切り分けるためのI2Cレベル変換です。シールドを作ってはじめて気がついたんですが、このジャンパピン用のソケットの間隔って100mil単位になっていないようです。850milとかそんな感じの半端な幅です。なので、差し込むときは、ピンを曲げるような感じで半ば強引に挿し込みました。


 PS/2のコネクタがシールドの裏面をショートさせるので、絶縁テープでカバーをしています。


 みんな大好き、タミヤの楽しい工作シリーズ。リモコン昆虫。


 リンク機構で6脚を動かす仕組みになっています。大人が組立てても十分楽しいキットでした。以下、載せたところ。



 おなか側。


 動かしてみたところ。


 やっていることは、ごく単純。

2015年2月22日日曜日

GrovePi お試し。

 本日のガラクタは、GrovePi です。一ヶ月以上も前に Dexter Industries社に直接発注してあったんですが、Dexter社は即日発送してくれたにもかかわらず、その後USPS(アメリカの郵便局?)に荷物が渡った後、トラッキング照会すると、3週間以上前にシカゴに着いたまま荷動きがありません。通関業務で止まってしまっているのか?このまま、どこかへ消滅してしまうのかはわかりませんが、そんなこんなしている間に、スイッチサイエンスさんから GrovePi+ が発売されていたので、まずは、そちらで手に入れました。GrovePi+ は GrovePi の改良版で、GrovePi を Raspberry Pi B+ にスタックするとLANコネクタやUSBコネクタで基盤裏側のパーツのハンダランドがショートしてしまうのですが、GrovePi+ では裏側に部品のランドが出ない(内層)形になっています。それに合わせて、Arduino部分のチップがDIPから表面実装に変わっています。あと、カメラのフラットケーブルを通せるスリットが開いています。それ以外の違いは、自分はわかりませんが、以前のGrovePi (+じゃない)でも、LAN、USBコネクタの位置にGrovePiの裏側にマスキングテープなどを貼れば、問題なく使えるはずです。
 で、GrovePi は、Arduino の Grove System シリーズのセンサーなどをRaspbery Piで利用できるようにするベースシールド的なものですが、通常のベースシールドはArduinoの各ピンと接続されているわけですが、GrovePi ではGrove コネクタがRaspberry Pi にすべて直に接続されているわけではなく(されているコネクタもある。UARTとかI2Cとか。)て、間にArduino相当のマイコンを挟んでいます。
 GrovePi のWikiにある画像を借りると、



 上記のような感じで、 Raspberry Pi から UARTとI2CがGroveコネクタにつながっているほか、Raspberry Pi と GrovePi(上のArduino)がSPIとI2Cで接続されいて、GrovePiのSerial, Digital IO(PWMも), Analog入力を利用できるようになっています。自分がよく使う Raspberry Pi と Arduino (Micro) をUSBかUARTで接続して組み合わせるパターンに似ていますが、GrovePIではI2CでArduinoを制御して、SPI接続でRasperyy PI側からAVRのファームの書き換えも行えるようになっているようです。
 もともと、Grove のセンサやLCD等をいくつか持っているので、このGrovePi も試してみようと思っていました。


 以下、開封したところ。


 裏側。


  Raspberry Pi B+ にスタックしたところ。



 以下、Grove RGB LCD をサンプルソースで動かしてみたところです。GrovePi を利用するためのライブラリは Python で提供されていますので、コードは Python で記述することになりますが、適用されているPythonのサンプルとライブラリのソースを見れば、各GroveモジュールやAVRとのI2Cでのやりとりは解析できそうなので、C/C++などでも利用することは可能ではないでしょうか。
 Grove はハンダ付け無しでブロック感覚で色々遊べるのが楽しいですが、ブレッドボードと併用して、ちょっとした試作にも便利だと思います。









2014年8月9日土曜日

3Dプリンタで多脚ロボット

 3Dプリンタを導入して2週間が経ちましたが、実際に動かせるのは休みの日曜・祝日くらいなので、なかなか思ったように作業が進まないのですが、どうにか初作品のベース部分がそれっぽい形になってきました。









なんだかごちゃごちゃしてますが、六脚ロボットです。サーボはアマゾンで10個入り三千いくらで買ったSG90という小型のものです。動作精度やトルク・早さはもちろん期待できませんが、足の上げ下げ、左・中・右・・・くらいのおおまかな制御ができればいいだろうということで何よりもリーズナブルな価格で採用しました。

 サーボの制御は例によっていつものAdafruitのPCA9685搭載16chPWMコントローラです。まずは、Ardino Unoと接続してサーボ制御プログラムの作成。


 単純に足を上下前後に繰り返し動かすシーケンスですが、なんとなく前に進むことはできそうな動きをしているのでOKとします。

 さらに、3Dプリンタで作った目玉パーツを取り付けます。目玉の中にはスイッチサイエンスさんで入手した Adafruit の I2C接続 8x8 小型マトリックスLED を入れました。




 また、Arduino Uno をArduino Micro に置き換えて、ロボット上に実装しました。電源は搭載が難しそうなので、外部からの有線供給とし、この外部供給 5Vをサーボと制御系で併用するようにしました。ノイズ等での誤動作が心配ですが、パスコンを入れて試してみます。



 全体を組み立てて、



 背中に Arduino Microと


Adafruit 16ch Servo Driverを載せています。


 動かしてみたところ。



 歩き方が雑でぎこちないですが、どうにか動いています。ただ、はじめは、マトリックスLEDのI2Cアドレスを 0x70と0x71にしていたらサーボドライバがなぜか動きませんでした。マトリックスLEDのアドレスを0x71と0x72に変えたところ動くようになりました。別にサーボドライバ(0x40)とアドレスがかぶっているわけでもないですし、プログラム中で指定しているアドレスが間違っているわけでもなかったのですが、不思議です。原因不明。とりあえず今回のソースはかんな感じ。
#include <Wire.h>
#include <TimerOne.h>
#include <Adafruit_PWMServoDriver.h>
#include "Adafruit_16ch_Servo.h"

#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>

double SERVO_CENTER_PULSE_WIDTH = 1520.0; // サーボ信号。センターパルス幅。
double SERVO_MIN_PULSE_WIDTH = 720.0;  // サーボ信号。最小パルス幅。
double SERVO_MAX_PULSE_WIDTH = 2320.0;  // サーボ信号。最大パルス幅。

const int led_pin = LED_BUILTIN; // モニタLED。

// マトリックスLEDのアドレス指定。デフォルトは0x70。
static const uint8_t matrixAddress[] = {0x71, 0x72};

static boolean 
 output = HIGH,    // LED点滅フラグ。
 timerCallbacked = false; // タイマ割り込みフラグ。

static int
 actionSeq = 0,
 keepCount = 0;

uint8_t
 blinkCountdown = 100, // 瞬き用カウント。
 gazeCountdown = 75,  // 目玉向き用カウント。
 gazeFrames = 50;  // 目玉動く早さ。

int8_t
 eyeX = 3, eyeY = 3,  // 現在の目玉位置。
 newX = 3, newY = 3,  // 次の目玉位置。
 dX = 0, dY = 0;   // 位置の差分。

// サーボモータのモーション情報。
static int motion[11][17] = {
//  {ch0, ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9,ch10,ch11,ch12,ch13,ch14,ch15,time(x100ms)}
 {  0,  50,   0,   0,   0,  50,   0,   0,   0, -50,   0,   0,   0,   0,   0,   0, 3},
 { 20,   0, -20,   0,  20,   0,  20,   0, -20,   0,  20,   0,   0,   0,   0,   0, 3},
 {  0,   0,   0,  50,   0,   0,   0, -50,   0,   0,   0, -50,   0,   0,   0,   0, 3},
 {-20,   0,  20,   0, -20,   0, -20,   0,  20,   0, -20,   0,   0,   0,   0,   0, 3},
 {  0,  50,   0,   0,   0,  50,   0,   0,   0, -50,   0,   0,   0,   0,   0,   0, 3},
 { 20,   0, -20,   0,  20,   0,  20,   0, -20,   0,  20,   0,   0,   0,   0,   0, 3},
 {  0,   0,   0,  50,   0,   0,   0, -50,   0,   0,   0, -50,   0,   0,   0,   0, 3},
 {-20,   0,  20,   0, -20,   0, -20,   0,  20,   0, -20,   0,   0,   0,   0,   0, 3},
 {  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, -8},
 {  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0},
 {  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0}
};

// マトリックスLEDの目玉のアニメーションイメージ。開→閉。
static const uint8_t PROGMEM blinkImg[][8] = 
{
 {
  B00111100,   // 目を完全に開いた状態。
  B01111110,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B01111110,
  B00111100
 },
 {
  B00000000,
  B01111110,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B01111110,
  B00111100
 },
 { 
  B00000000,
  B00000000,
  B00111100,
  B11111111,
  B11111111,
  B11111111,
  B00111100,
  B00000000 
 },
 {
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B11111111,
  B01111110,
  B00011000,
  B00000000
 },
 {
  B00000000,         // 目を完全に閉じた状態。
  B00000000,
  B00000000,
  B00000000,
  B10000001,
  B01111110,
  B00000000,
  B00000000
 }
};

// 目をまばたくアニメパターン。全開→閉→全開の間を2段階。
static const uint8_t blinkIndex[] = {1, 2, 3, 4, 3, 2, 1};

// サーボオブジェクト。
Adafruit_16ch_Servo pwm = Adafruit_16ch_Servo();

// マトリックスLEDのオブジェクト。
Adafruit_8x8matrix matrix[2] = {
 Adafruit_8x8matrix(), Adafruit_8x8matrix() };

// タイマ割り込みのコールバック関数。
// フラグ更新を行い、実際の処理はloop()関数内でフラグをチェックして実行。
void timerCallback()
{
 timerCallbacked = true;
}

void setup()
{
 actionSeq = -1;
 keepCount = 0;

 // LED接続ポートを出力に設定。
 pinMode(led_pin, OUTPUT);

 // マトリックスLEDの開始。
 for (int i = 0; i < 2; i++)
 {
  matrix[i].begin(matrixAddress[i]);
 }

 // サーボPWMの開始と初期設定。
 pwm.begin();
 pwm.setPWMFreq(60);
 for (int i = 0; i < 16; i++)
 {
  pwm.setCenterPulseWidth(i, SERVO_CENTER_PULSE_WIDTH);
  pwm.setMinPulseWidth(i, SERVO_MIN_PULSE_WIDTH);
  pwm.setMaxPulseWidth(i, SERVO_MAX_PULSE_WIDTH);
 }
 pwm.setServoPosition(0, 0);
 pwm.setServoPosition(1, 0);
 
 // タイマ割り込みの初期化と開始。
 Timer1.initialize();
 Timer1.attachInterrupt(timerCallback, 100000);
 
}

void loop()
{
 
 if (timerCallbacked)
 {
  timerCallbacked = false;

  digitalWrite(led_pin, output);
  output = !output;

  if (keepCount == 0)
  {
   // 次のシーケンスを実行。
   actionSeq++;

   // シーケンス番号が0で停止。負で数だけステップ戻る。
   if (motion[actionSeq][16] == 0)
    while(true);
   if (motion[actionSeq][16] < 0)
   {
    actionSeq = actionSeq + motion[actionSeq][16];

    if (actionSeq < 0) actionSeq = 0;
   }

   // サーボモータ位置設定。
   for (int i = 0; i < 16; i++)
   {
    pwm.setServoPosition(i, (double)motion[actionSeq][i]);
   }
   keepCount = (int)motion[actionSeq][16];
  }
  
  keepCount--;

  
  for (int eye = 0; eye < 2; eye++)
  {
   // マトリックスLEDのクリア。
   matrix[eye].clear();

   // ウィンク動作。
   matrix[eye].drawBitmap(0, 0,
    blinkImg[
     (blinkCountdown < sizeof(blinkIndex)) ?
      blinkIndex[blinkCountdown] : 0
    ], 8, 8, LED_ON);

   // カウンタ減して、0ならランダムに次の瞬きまでのカウントをセット。
   if (--blinkCountdown == 0) blinkCountdown = random(5, 180);

   // 目玉部分の描画。
   if (--gazeCountdown <= gazeFrames)
   {
    matrix[eye].fillRect(
     newX - (dX * gazeCountdown / gazeFrames),
     newY - (dY * gazeCountdown / gazeFrames),
     2, 2, LED_OFF);

    if (gazeCountdown == 0)
    {
     eyeX = newX; eyeY = newY;
     do
     {
      newX = random(7); newY = random(7);
      dX = newX - 3; dY = newY - 3;
     }
     while ((dX * dX + dY +dY) >= 10);
     dX = newX - eyeX;
     dY = newY - eyeY;
     gazeFrames = random(3, 15);
     gazeCountdown = random(gazeFrames, 120);
    }
   }
   else
   {
    matrix[eye].fillRect(eyeX, eyeY, 2, 2, LED_OFF);
   }
  }

  // マトリックスLEDの表示更新。
  for (int i = 0; i < 2; i++)
  {
   matrix[i].writeDisplay();
  }
 }

}


Visual Microでのプロジェクトはこちらにあります。

2014年5月28日水曜日

BrickPi で EV3 Sensor Adaptor for NXT のお試し その2

 以前に、BrickPi で EV3 Senser Adaptor for NXT を使ってEV3用のジャイロと超音波センサを試しましたが、今回は、BrickPi で EV3の赤外線センサを試してみます。EV3の赤外線センサには「近接モード」「ビーコンモード」「リモコンモード」の3つの動作モードがあります。

 「近接モード」は赤外線センサ単体(自分で赤外線を送受信して反射を使って)で近接物との距離を測定するモードです。超音波センサでは、だいたいcm単位での距離の数値がそのまま出力されましたが、赤外線センサの場合は、10cm~50cmの距離の間が10~70の値でほぼ比例的に出力され、それ以上距離がある(何も存在しない)と80~100の値になります。

 「ビーコンモード」は赤外線リモコンが発信する赤外線を受光して、赤外線リモコンとの距離を測定するモードです。ビーコンモードはリモコンとの距離が10cm~2mくらいの間で10~100の値が比較的直線的に出力されます。

 「リモコンモード」では、赤外線リモコンの操作ボタンの押下によって、押したボタンの種類ごとに違う番号が出力されます。

 「ビーコンモード」と「リモコンモード」では赤外線リモコン側のチャネルが4チャネルあり、この切り替えにより値が返されるレジスタが変わります。

 赤外線センサの特性などに関しては、アフレルさんのサイトに詳しい技術情報が掲載されています。

 EV3 Sensor Adaptor for NXT には向き(Host側とセンサ側コネクタ)があるので、注意が必要です。っていうか、はじめ間違えて値が取れなかった・・・。



ソースはこんな感じ。ここにもおいておきます。

// S1ポートにEV3 Sensor Adaptor + 赤外線センサ を接続。

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#include "wiringPi.h"

#include "tick.h"
#include "BrickPi.h"

#define ESA_I2C_ADDRESS    0x32 // EV3 Sensor Adaptor のデフォルトI2Cアドレス
#define ESA_I2C_DEVICE_NO   0x00 // EV3 Sensor Adaptor の I2Cバス上の番号
#define ESA_I2C_REGISTER_COMMAND 0x41 // コマンドレジスタ
#define ESA_I2C_REGISTER_MODE  0x52 // モードレジスタ
#define ESA_I2C_REGESTER_CHANNEL0 0x54 // 0チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL1 0x56 // 1チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL2 0x58 // 2チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL3 0x5A // 3チャネルセンサ値格納

#define MODE_Color_ReflectedLight 0x00 // カラーセンサ反射光モード
#define MODE_Color_AmbientLight  0x01 // カラーセンサ周囲明るさモード
#define MODE_Color_MeasureColor  0x02 // カラーセンサ色モード
#define MODE_Gyro_Angle    0x00 // ジャイロセンサ角度モード
#define MODE_Gyro_Rate    0x01 // ジャイロセンサ角速度モード
#define MODE_InfraRed_Proximity  0x00 // 赤外線センサ近接度モード
#define MODE_InfraRed_Beacon  0x01 // 赤外線センサビーコンモード
#define MODE_InfraRed_Remote  0x02 // 赤外線センサ遠隔モード
#define MODE_Sonar_CM    0x00 // 超音波センサcmモード
#define MODE_Sonar_Inches   0x01 // 超音波センサインチモード
#define MODE_Sonar_Presence   0x02 // 超音波センサ存在検出モード

int main(int argc, char *argv[])
{
 int result;
 uint8_t mode, p0, p1, p2, p3, v0, v1, v2, v3;
 char h0, h1, h2, h3;
 int value;
 unsigned int tmp;

 // モード選択。
 //mode = MODE_InfraRed_Proximity; // 近接
 //mode = MODE_InfraRed_Beacon;  // ビーコン
 mode = MODE_InfraRed_Remote;  // リモート

 // -- BrickPiの初期化。
 // タイマのクリア。
 ClearTick();
 
 // BricPi初期化(ポートオープン)。
 result = BrickPiSetup();
 if (result) return 0;

 // 通信アドレス設定。
 BrickPi.Address[0] = 1;
 BrickPi.Address[1] = 2;

 // -- センサポートの設定。
 // S1ポートをI2Cセンサに設定。
 BrickPi.SensorType[PORT_1] = TYPE_SENSOR_I2C;
 // S1ポートのI2Cスピードを100kbpsに設定。
 BrickPi.SensorI2CSpeed[PORT_1] = 0;
 // S1ポートのI2C接続デバイス数を1に設定。
 BrickPi.SensorI2CDevices[PORT_1] = 1;

 // アドレス設定。
 BrickPi.SensorSettings[PORT_1][ESA_I2C_DEVICE_NO] = 0;
 BrickPi.SensorI2CAddr[PORT_1][ESA_I2C_DEVICE_NO] = ESA_I2C_ADDRESS;

 if (BrickPiSetupSensors())
  return 0;

 // 赤外線センサのモード設定。
 BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 書込バイト数
 BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 読込バイト数
 BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGISTER_MODE; 
 BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][1] = mode;
 
 result = BrickPiUpdateValues();
 if (!result)
 {
  while (1)
  {
   switch (mode)
   {
   case MODE_InfraRed_Proximity: // 近接センサモード
    // センサー値取得
    BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
    BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
    BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL0; // Ch0
    result = BrickPiUpdateValues();
    if (!result)
    {
     // int値取得算出。
     tmp = (int)BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
     tmp += ((int)BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1]) << 8;
     if (tmp > 32767)
     {
      value = (int)tmp - 65536;
     }
     else
     {
      value = (int)tmp;
     }

     printf("IR: %d\n", value);
    }
    else
    {
     printf ("IR: Get result Error!!\n");
    }

    break;
   case MODE_InfraRed_Beacon:  // ビーコンモード
    h0 = 0; h1 = 0; h2 = 0; h3 = 0; p0 = 0; p1 = 0; p2 = 0; p3 = 0;

    // センサー値取得
    BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
    BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
    BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL0; // Ch0
    result = BrickPiUpdateValues();
    if (!result)
    {
     // 方向(1バイト目)と近接(2バイト目)値取得。
     h0 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
     p0 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1];

    }

    // センサー値取得
    BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
    BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
    BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL1; // Ch1
    result = BrickPiUpdateValues();
    if (!result)
    {
     // 方向(1バイト目)と近接(2バイト目)値取得。
     h1 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
     p1 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1];

    }

    // センサー値取得
    BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
    BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
    BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL2; // Ch2
    result = BrickPiUpdateValues();
    if (!result)
    {
     // 方向(1バイト目)と近接(2バイト目)値取得。
     h2 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
     p2 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1];

    }

    // センサー値取得
    BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
    BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
    BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL3; // Ch3
    result = BrickPiUpdateValues();
    if (!result)
    {
     // 方向(1バイト目)と近接(2バイト目)値取得。
     h3 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
     p3 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1];

    }

    //printf("h0=%d, h1=%d, h2=%d, h3=%d, p0=%d, p1=%d, p2=%d, p3=%d\n", h0, h1, h2, h3, p0, p1, p2, p3);
    printf("p0 = %d, p1=%d, p2=%d, p3=%d\n", p0, p1, p2, p3);

    break;
   case MODE_InfraRed_Remote:  // リモートモード
    v0 = 0; v1 = 0; v2 = 0; v3 = 0;

    // センサー値取得
    BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
    BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
    BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL0; // Ch0
    result = BrickPiUpdateValues();
    if (!result)
    {
     // 方向(1バイト目)と近接(2バイト目)値取得。
     v0 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
     v1 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1];

    }

    // センサー値取得
    BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
    BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
    BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL1; // Ch1
    result = BrickPiUpdateValues();
    if (!result)
    {
     // 方向(1バイト目)と近接(2バイト目)値取得。
     v2 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
     v3 = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1];

    }

    printf("v0=%d, v1=%d, v2=%d, v3=%d\n", v0, v1, v2, v3);

    break;
   default:
    break;
   }

   usleep(100000);
  }
 }

 return 0;
}

2014年5月18日日曜日

BrickPi で EV3 Sensor Adaptor for NXT のお試し

 本日のがらくたは、Mindsensors社の「EV3 Sensor Adaptor for NXT」です。いつもこの手のものをお世話いただいている ROBO Product さんより購入しました。

EV3 Sensor Adapter for NXT or Arduino

 このアダプタは、EV3用のセンサをNXT コントローラで利用できるようにするための変換アダプタです。中身としては、EV3のUART(シリアル)信号をNXT コントローラで利用できる100kbpsのI2C通信と変換しているのだと思われます。Mindstorms社のサイトを見ると、EV3のセンサをNXTコントローラだけでなく、NXShieldやBrickPiでも使えるようになると書いてありますが、ライブラリやサンプルが提示されているのは、2014年05月中旬現在では、NXTブロックでのグラフィックプログラミングとNXCだけのようです。

 そこで、EV3 のセンサをこのアダプタを使って、BrickPi で利用できるか試してみようと思います。BrickPi は基本NXTコントローラ互換のセンサポートなので、EV3のセンサも使えるようになれば、手持ちのEV3センサが活用できるようになります。今回は、NXTのセットにはなくEV3(の教育版)で追加された「ジャイロ」とNXT版よりも距離精度が上がっている「超音波センサ」をターゲットにしてみます。


 ジャイロセンサをBrickPi の S1ポートに接続しました。


 超音波センサはBrickPi の S2ポートに接続しました。


 参考にした資料は、Mindsensors社のサイトに有るマニュアルとNXC用のライブラリのソースです。

 いきなりですが、動かしてみた結果です。ジャイロは「角度モード」で水平に回転させたときに回転した角度が直接読み取れます。初期位置から相対的に時計回りに+、反時計回りに-の角度が出力されます。
 超音波センサは「距離cmモード」にするとmm単位で対象との距離が出力されます。


 ジャイロ、超音波センサともに角度や距離がそのまま割りとリニアリティもよく出力されるので、非常に使いやすいです。ただ、BrickPiからアダプタを使ってのアクセスでは、I2Cデバイスとしてのアクセスなので、手順は一手間あります。今回は、関数化などをしていない状態ですが、ソースをこちらに載せておきます。

// S1ポートにEV3 Sensor Adaptor + EV3 ジャイロを接続。
// S3ポートにEV3 Sensor Adaptor + EV3 超音波センサを接続。

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#include "wiringPi.h"

#include "tick.h"
#include "BrickPi.h"

#define ESA_I2C_ADDRESS    0x32 // EV3 Sensor Adaptor のデフォルトI2Cアドレス
#define ESA_I2C_DEVICE_NO   0x00 // EV3 Sensor Adaptor の I2Cバス上の番号
#define ESA_I2C_REGISTER_COMMAND 0x41 // コマンドレジスタ
#define ESA_I2C_REGISTER_MODE  0x52 // モードレジスタ
#define ESA_I2C_REGESTER_CHANNEL0 0x54 // 0チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL1 0x56 // 0チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL2 0x58 // 0チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL3 0x5A // 0チャネルセンサ値格納

#define MODE_Color_ReflectedLight 0x00 // カラーセンサ反射光モード
#define MODE_Color_AmbientLight  0x01 // カラーセンサ周囲明るさモード
#define MODE_Color_MeasureColor  0x02 // カラーセンサ色モード
#define MODE_Gyro_Angle    0x00 // ジャイロセンサ角度モード
#define MODE_Gyro_Rate    0x01 // ジャイロセンサ角速度モード
#define MODE_InfraRed_Proximity  0x00 // 赤外線センサ近接度モード
#define MODE_InfraRed_Beacon  0x01 // 赤外線センサビーコンモード
#define MODE_InfraRed_Remote  0x02 // 赤外線センサ遠隔モード
#define MODE_Sonar_CM    0x00 // 超音波センサcmモード
#define MODE_Sonar_Inches   0x01 // 超音波センサインチモード
#define MODE_Sonar_Presence   0x02 // 超音波センサ存在検出モード


int main(int argc, char *argv[])
{
 int result;
 uint8_t mode;
 int value;
 unsigned int tmp;

 // -- BrickPiの初期化。
 // タイマのクリア。
 ClearTick();
 
 // BricPi初期化(ポートオープン)。
 result = BrickPiSetup();
 if (result) return 0;

 // 通信アドレス設定。
 BrickPi.Address[0] = 1;
 BrickPi.Address[1] = 2;

 // -- センサポートの設定。
 // S1ポートをI2Cセンサに設定。
 BrickPi.SensorType[PORT_1] = TYPE_SENSOR_I2C;
 // S1ポートのI2Cスピードを100kbpsに設定。
 BrickPi.SensorI2CSpeed[PORT_1] = 0;
 // S1ポートのI2C接続デバイス数を1に設定。
 BrickPi.SensorI2CDevices[PORT_1] = 1;
 // S3ポートをI2Cセンサに設定。
 BrickPi.SensorType[PORT_3] = TYPE_SENSOR_I2C;
 // S3ポートのI2Cスピードを100kbpsに設定。
 BrickPi.SensorI2CSpeed[PORT_3] = 0;
 // S3ポートのI2C接続デバイス数を1に設定。
 BrickPi.SensorI2CDevices[PORT_3] = 1;

 BrickPi.SensorSettings[PORT_1][ESA_I2C_DEVICE_NO] = 0;
 BrickPi.SensorI2CAddr[PORT_1][ESA_I2C_DEVICE_NO] = ESA_I2C_ADDRESS;
 BrickPi.SensorSettings[PORT_3][ESA_I2C_DEVICE_NO] = 0;
 BrickPi.SensorI2CAddr[PORT_3][ESA_I2C_DEVICE_NO] = ESA_I2C_ADDRESS;

 if (BrickPiSetupSensors())
  return 0;

 // S1ジャイロセンサのモード設定。
 BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 書込バイト数
 BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 読込バイト数
 BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGISTER_MODE; 
 BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][1] = MODE_Gyro_Angle;
 //BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][1] = MODE_Gyro_Rate;
 // S3超音波センサのモード設定。
 BrickPi.SensorI2CWrite[PORT_3][ESA_I2C_DEVICE_NO] = 2; // 書込バイト数
 BrickPi.SensorI2CRead[PORT_3][ESA_I2C_DEVICE_NO] = 1; // 読込バイト数
 BrickPi.SensorI2COut[PORT_3][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGISTER_MODE; 
 BrickPi.SensorI2COut[PORT_3][ESA_I2C_DEVICE_NO][1] = MODE_Sonar_CM;
 result = BrickPiUpdateValues();
 if (!result)
 {
  mode = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];

  while (1)
  {
   // センサー値取得
   BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
   BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
   BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL0; // Ch0
   BrickPi.SensorI2CWrite[PORT_3][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
   BrickPi.SensorI2CRead[PORT_3][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
   BrickPi.SensorI2COut[PORT_3][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL0; // Ch0
   result = BrickPiUpdateValues();
   if (!result)
   {
    // S1ポート、ジャイロ値算出。
    tmp = (int)BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
    tmp += ((int)BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1]) << 8;
    if (tmp > 32767)
    {
     value = (int)tmp - 65536;
    }
    else
    {
     value = (int)tmp;
    }

    switch (mode)
    {
    case 0:
     printf("Gyro angle: %d\n", value);
     break;
    case 1:
     printf("Gyro rate: %d\n", value);
     break;
    case 2:
     printf("Gyro N/A\n");
     break;
    default:
     break;
    }

    // S3ポート、超音波センサ値算出。
    value = (int)BrickPi.SensorI2CIn[PORT_3][ESA_I2C_DEVICE_NO][0];
    value += ((int)BrickPi.SensorI2CIn[PORT_3][ESA_I2C_DEVICE_NO][1]) << 8;

    printf("Ultrasonic distance: %d\n", value);
   }

   usleep(100000);
  }
 }

 return 0;
}


 関数化やクラス化をすれば、NXTのセンサとおなじ感覚で、もっと楽に使えるようになると思いますが、それは、また今度。



2014年2月27日木曜日

タミヤの楽しい工作シリーズ レスキュークローラ で BBクローラ

 Google+ でタミヤの楽しい工作シリーズを作った人の投稿を見て、久々に作りたくなったので、何種類か買ったまま、積んである在庫の中から レスキュークローラを引っ張り出してきて作ることにしました。みんな大好きタミヤの楽しい工作シリーズは、自分も大好き。


  モータ3つを有線リモコンでコントロールできるようになっていて、うち2つは左右のクローラを前後に回転、残り1つが車体前部のアームを上下にコントロールできるようになっています。リモコンでアームを上下にコントロールしながら上手く障害物を乗り越えるように操作するということでしょうか。

 箱を開けてみます。結構色々なパーツが入っていて、楽しそう。


 まずは、下準備でアーム用の棒状のパーツと本体ベースになるプレート状のユニバーサルプレートを指定のサイズにカットしていきます。



 次にアーム上下を操作するクランクギアボックスを組み立てます。


 続いて、クローラを駆動する両輪型のギアボックスを組み立て。




 クランクギアボックスをベースプレートに取り付け。


 クローラ駆動用のギアボックスをクランクギアを取り付けたベースプレートの反対面に取り付けるのですが、ここで、ちょっと気が変わって、説明書通りに作るのを放棄しました・・・。
 手持ちのユニバーサルプレートをもう一枚加工して、そちらにクローラ駆動用のギアボックスを取り付けることに。


 タミヤの楽しい工作シリーズで 同じような機構をもった アームクローラ というのがあって、こちらはアーム部分の上下をモータで制御せずに、成り行き任せになっているんですが、動画等をみるとその方法で十分というかなまじモータでコントロールするよりもいい感じで動いているように見えるので、そちらの方式を採用することにしました。前進中に障害物に乗り上げると、アーム部分のクローラの回転で障害物を下側に巻き込もうとして、結果アームが上方向にせり上がって、障害物の上に乗り上げて行くような動作をするはずです。

 と、いうわけで、足回りを組み立てていきます。


 モータを取り付けて、


 クローラ(履帯)を取り付けます。


 裏側。


 続いて、アーム部を組み立てます。



 履帯をはめたところ。


 本体と合体させて、間の履帯も取り付けました。


 かっこいい。こういう感じ、好きです。

 上部のクランクアームをやめて、別のユニバーサルプレートを追加したのは、元々有線リモコン式でリモコン側から有線経由でパワー供給する仕組みなんですが、これを、バッテリー搭載して無線化してみようと思ったためです。試しに小型のモバイルバッテリと小型の無線LANルータを載せてみたところ、ピッタリサイズ。ただし、実際にはバッテリに接続するUSBコネクタや無線LANルータへの電源供給コネクタやLANケーブルコネクタに挿さるものが出っ張るので、それぞれを斜めに配置して左右に中心をずらすように配置してやろうと思っています。


 モバイルバッテリの高さ(厚み)が23mm~24mm位なので、10mmと14mmのプラ支柱を連結してベースプレートに立てます。



 せっかくバッテリを積んだので、マイコンを積みたくなります。今回は Beaglebone Black で行ってみようと思います。ただ、BBB はユニバーサルプレートには上手く載ってくれません。これが Raspberry Pi だとネジ穴がいい位置(5mm単位の穴と重なる位置)にあり、そのまま支柱を立てて載せられるのですが、BBBはダメです。そこで、間にアクリルの板でマウンタを作成してネジ穴位置を調整します。


 支柱とネジにはプラスティック製を使用しました。これは、絶縁の問題もあるんですが、今回マウンタの切り出しや穴あけは採寸せずに現物合わせで適当にやっているので、かなり位置がずれています。金属の硬いネジだとずれてしまうと当然ハマりませんが、プラスティック製は弾力で吸収させて、位置ずれをごまかすことがある程度できるためです。


 キットに元々入っていたベースプレートからクランクギアを外してしまい、BBBのマウンタを取り付けました。


 あとは、無理やり?ナットで止めて、はみ出したプラネジの部分をニッパで切り落として、ネジの長さも調整します。

 レスキュークローラのベースプレートの支柱に固定して、バッテリ・無線LANとBBBの2階建て構造にしました。
 ただ、モバイルバッテリに挿したUSBコネクタや無線LANに挿した電源供給用マイクロUSBとBBBへ接続するLANケーブルが予想以上にかさばってしまい、非常に収まりの悪い配置になってしまいました。




 さて、次に、モータ駆動周りを考えます。BeagleBone BlackなのでPWMも複数利用できますが、コントローラをRaspberry Piに変えたりすることも考えて、I2Cモータドライバを使ってみようと思います。プログラムもそのほうが楽なんじゃないかな?


 まずは、BeagleBone Black とI2Cモータドライバ単体で動かしてみます。


 I2Cモータドライバは例によってDRV8830です。部品棚にはストリナのモジュールと秋月のモジュールが転がっているんですが、お試しは秋月版で行ってみます。コネクタや配線は違いますが、制御方法は当然共通なので、実装するときは現物合わせでどちらかを選定すればいいやということで、秋月版で試しましたが、実際に基板に載せるときは、実装ステペース的にストリナ版を使うことになりました。



 まずは、モータを動かすところからですが、一応、I2Cコンパスとジャイロも載せられるようにしました。BeageleBone 単体でとりあえず、モータが回転することを確認。


 クローラに載せて実装します。





 正面から見ると綺麗に収まっているようにみえるんですが・・・・


 実は後ろ側がUSBケーブルでグチャグチャ・・・


 なにか綺麗に収める、または、配線できる方法があるといいんですが・・・

 とりあえず、クローラをテスト稼働。


 現時点では特にセンサ情報のフィードバックや制御は行っていませんが、とりあえず、プリセット的にプログラムして、走行テスト。


 このくらいの山は乗り越えられるようです。今後、センサ(コンパス、ジャイロ)等を使えたらいいなと考えています。