距離を測る、というのは自動運転からロボットなど、コンピュータが実世界に「アプローチ」するときには欠かせぬ行動です。レーザ、ミリ波など大盛り上がりです。ロボット掃除機なども、周囲の物体との距離を測るのにレーザ式のTOFセンサを使っているのだと思っていたら、3角測量の原理で測っている方式もあるのだとか。3角測量、よい響きです。メソポタミアかエジプトか、文明の黎明期からある計測方式。調べてみると「お手頃」で入手可能なセンサが直ぐに見つかりました。
インタフェースの方式でxのところに入る数字が変わるのですが、前回でセットアップ済のI2Cバスに接続できるバージョンがありました。GP2Y0E03です。今回はとりあえず接続して距離の数字がでるところまで実験してみたいと思います。
※「IoT何をいまさら」投稿順Indexはこちら
まずはセンサの写真をアップロードしておきます。
センサそのものは、かなり小さいです。そして、赤外線LEDと赤外線イメージセンサの応用なのでした。ついこないだ勉強したばかりのやつ。まあ、それもあってのセンサの選択ではあるのですが。
IR-LED
IRイメージセンサ
ここのところを強調しておきます。ただし、原理的にいって、3角測量の精度は基線の長さ次第の筈。幅11mmのセンサ本体の中でレンズ間の距離は、5mmほどしかないように見えます。そこからするとそれほどの遠距離を測定するものではなく、ごくごく近傍の距離を測るものに違いありません。スペックによれば
4~50cm
です。(設定項目をいじれば127cmまでの距離数値が一応出力されることを確認しました。ただし、精度的には保証範囲外でしょう。)
まずはセンサのセットアップ
このSharpのセンサのシリーズには、I/Fのバリエーションが
- アナログ出力
- I2C入出力
- アナログ出力+I2C入出力
の3タイプがあります。センサ本体は共通のようです。入手したのは、3のタイプです。アナログ出力も後で実験してみようと思っているのですが、今回はとりあえずI2Cバスに取り付けて実験いたしました。センサ付属のコネクタケーブルの先にピンヘッダを取り付けて前回のボードに接続します。前回3.3V電源、3.3Vインタフェースで準備してあるのでそのまま直結できる筈。その際3.3V電源ならば、VIN(IO)とGPIO1にも3.3Vをそのまま与えてやれば、即、動作モードで立ち上がってくるのでそうしました。接続したところの写真は以下です。
例によって、Nucleo-F072RBボードがUSBでパソコンに接続されており、そこからGNDと3.3V電源、I2Cバスを引き出して使っています。前回の準備でI2C接続のLCDパネルがつなげてあったのですが、配線をいじるのに邪魔なので今回実験では外し(後で、これが失敗の元であったことに気付きましたが。。。)、結果出力はNucleoからUSB経由の仮想シリアル出力といたしました。とりあえず配線はできたので、次はソフトウエアです。これまた例によって
MbedのWeb環境で実行オブジェクトをビルド
します。とりあえずのメインプログラムは、こんな感じ。
#include "mbed.h" #include "GP2Y0E03.h" I2C i2c(I2C_SDA, I2C_SCL); DigitalOut myled(LED2); Serial pc(USBTX, USBRX); int main() { int status; int dist; GP2Y0E03 dms(&i2c); pc.printf("test GP2Y0E03 Distance Measuring Sensor\r\n"); while (1) { dms.writeCommand(DMS_RSTADR, DMS_RSTCMD); wait(0.1); dms.writeCommand(DMS_SCALE, DMS_128); wait(0.1); status = dms.readDistance(&dist); pc.printf("Distance %d[cm] (STAT:%d)\r\n", dist, status); wait(1); myled = !myled; } }
センサ制御のクラスはこちら
#ifndef GP2Y0E03_H #define GP2Y0E03_H #include "mbed.h" #define DMS_ADDR (0x80) #define DMS_DIST (0x5E) #define DMS_RSTADR (0xEE) #define DMS_RSTCMD (0x06) #define DMS_128 (0x1) #define DMS_64 (0x2) #define DMS_SCALE (0x35) class GP2Y0E03 { private: I2C* i2cRef; char wdata[2]; char rdata[2]; int status; public: GP2Y0E03(I2C* ch); int writeCommand(char regadr, char dat); int readData(char regadr, char* dat); int readDistance(int* dist); };
最後にセンサ制御の関数数個。距離の計算は端折ってcm単位に切り捨ててしまっています。
#include "GP2Y0E03.h" // GP2Y0E03::GP2Y0E03(I2C* ch) { i2cRef=ch; wdata[0]=0; wdata[1]=0; rdata[0]=0; rdata[1]=0; } int GP2Y0E03::writeCommand(char regadr, char dat) { status =0; wdata[0] = regadr; wdata[1] = dat; status = i2cRef->write(DMS_ADDR, wdata, 2, 0); return status; } int GP2Y0E03::readData(char regadr, char* dat) { status =0; wdata[0] = regadr; rdata[0] = 0; status = i2cRef->write(DMS_ADDR, wdata, 1, 0); if (status !=0) return status; status = i2cRef->read(DMS_ADDR, rdata, 1, 0); *dat = rdata[0]; return status; } int GP2Y0E03::readDistance(int* dist) { status =0; wdata[0] = DMS_DIST; rdata[0] = 0; rdata[0] = 0; status = i2cRef->write(DMS_ADDR, wdata, 1, 0); if (status !=0) return (status + 100); status = i2cRef->read(DMS_ADDR, rdata, 2, 0); *dist = (rdata[0]*16 + rdata[1]) >> 5; return status; }
何度使っていてもMbedはお手軽です、Webの上でくりくりやっていればマイコン動かすオブジェクトが出来てしまう。早速、ボードを繋いでオブジェクトをダウンロードします。また、出力は仮想COMポートのみなので、Teratermで接続しておきます。
とりあえず方眼紙を引いてその先に「検出対象オブジェクト」を置いてみました。(目の前にあったラズパイの空箱です)
仮想端末を眺めていると、出ました!距離25cm。箱を動かすとそれにつれて距離も変わります。箱を取り除けると127cmと現状設定の最大値が出力されてきます。OKと思ったのですが、どうも様子がおかしい。
STAT:の後で出力させているI2Cバスのステータス(ACK信号)が時々フェイルするのです。どうしたのか??? 気づきました。
I2Cバスのプルアップ抵抗はLCDパネルボード内蔵のものを使っていた
パネルを外していたのでプルアップ無で動いていたのです。一端停止。パネルを再度とりつけたら、今度こそOK。フェイルもせずに安定動作。
しかし、このセンサ、結構設定項目が多いので、精度を出すにはそれなりに設定が必要なように思われます。それにアナログ出力も使ってみたいし。