VL53L0Xのつもりで買った測距センサーのVL53LXXをArduinoで使ってみたい

スポンサーリンク
スポンサーリンク

VL53LXX

今気づいたんですが、VL53L0Xを買ったつもりがVL53LXXって印字されてるやつが届いた。違うじゃん。どうしよう、VL53L0Xと同じような感じで使えんのかな。

超音波使うやつはこの前使ってみたんだけどなんかでっかいので、ちっちゃいやつを探したら見つけた。レーザーですって。すごいじゃん。ただ接続がI2Cなので、超音波のやつを引っこ抜いて代わりにぶっ挿すっていう使い方はできない。できないのね。だから動かしてみて遊んだだけです。

スポンサーリンク

お買い物

1pcs GY-VL53L0XV2 L53L0X TOF Time-Of-Flight Distance Sensor 940nm Laser Ranging | eBay
1pcs CJMCU-06 SPL06-001 Drone Pressure Height Sensor Module Position 5cm. 1pcs GY-9960-3.3 APDS-9960 RGB Infrared IR Gesture Sensor Motion Direction Recog. CJMC...

これ買ったんすよ。でも印字違うの。

VL53L0Xのデータシート

https://www.st.com/resource/en/datasheet/vl53l0x.pdf

スポンサーリンク

接続

Arduino ↔ VL53LXX
5V ↔ VIN
SDA ↔ SDA
SCL ↔ SCL
GND ↔ GND

こんなか。VL53LXXにGPIO1,XSHUTていうピンがあるけど使わない。ていうか何に使うのかよく分からん。

スポンサーリンク

スケッチ例を使ってみる

ライブラリをインストール

これかなぁと思いインストール。VL53L1Xっていうのもあるんだね。GitHubは

pololu/vl53l0x-arduino
Pololu Arduino library for VL53L0X time-of-flight distance sensor - pololu/vl53l0x-arduino

こちら。

スケッチ例を開いて書き込み

これ。ContinuousとSingleの違いはなんじゃろー。データシートから引用すると

1. Single ranging
Ranging is performed only once after the API function is called. System returns to SW standby automatically.
2. Continuous ranging
Ranging is performed in a continuous way after the API function is called. As soon as the measurement is finished, another one is started without delay. User has to stop the ranging to return to SW standby. The last measurement is completed before stopping.

そもこのVL53L0XにアクセスするためにはAPIを使ってアクセスしなきゃならんそうで。Singleの場合、APIをコールするとVL53L0Xは1回だけ測距する。Continuoousの場合、APIをコールしたらstopしない限り測距し続ける。ていうのは分かったんだけど、そうするとどうなるのかがよくわからない。どっちかが精度高くなるとか応答が早いとかあるんだろうか。まぁどっちでもいいので開いて、書き込んでみて、シリアルモニタを開くと、出た!えらいスピードで計測結果が流れていく、ので、1秒ディレイを入れた。おー動く。

スマホカメラで撮るとレーザーが出てるのが分かるね。

スポンサーリンク

実測値と比較してみる

ライブラリのreadmeを見るとmmで出力されるらしいんだけど、どうもズレてるような気がしてならないので、定規を使って現実の値と比較してみる。

こんな。この精度が高くねーじゃんと言いたくなるでしょうが専門的な装置なんて持ってないのでこれで測る。

結果

5cm10cm15cm
1回目50100153
2回目47100160
3回目5099152
4回目45101152
5回目4998150
6回目4897152
7回目4996152
8回目48101152
9回目4897150
10回目4797151

あれ?意外といーじゃん!もっとガッツリ変で、校正入れる感じになるかなーとか思ったけど大丈夫な気がする。

スポンサーリンク

バラツキをなんとかしたい

つっても、数値がバラつく。まぁそんな精度求めてるわけでもないし、取得頻度が高いわけでもないんだけど、なんか気持ち悪いので。こういう時は値を滑らかにするようなフィルタをかければよかったんだった。そういや方位計算する時やったな。移動平均と、ローパスと、中央値を試してみる。

移動平均

#include <Wire.h>
#include <VL53L0X.h>

#define SAMPLE_SIZE 10 //20, 50, 100

int count = 0;
int discardcount = 0;
uint16_t temp[SAMPLE_SIZE];
uint16_t sum = 0;
uint16_t average = 0;

VL53L0X sensor;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  sensor.setTimeout(500);
  if (!sensor.init())
  {
    Serial.println("Failed to detect and initialize sensor!");
    while (1) {}
  }

  sensor.startContinuous();
}

void loop() {

  temp[count] = sensor.readRangeContinuousMillimeters();
  if (sensor.timeoutOccurred()) {Serial.print(" TIMEOUT");}
  count++;
  
  if (count == SAMPLE_SIZE) {
    count = 0;
  }

  if (discardcount < SAMPLE_SIZE) {
    discardcount++;
    return;
  }

  for (int i = 0; i < SAMPLE_SIZE; i++) {
    sum += temp[i];
  }

  average = sum / SAMPLE_SIZE;
  sum = 0;

  Serial.print(average);
  Serial.println();
}

Continuousのスケッチ例をこんな感じで書き換えた。相変わらずスコープが分からん。実測値を10cmで、標本数を10, 20, 50, 100にして試してみると

102050100
1回目22:08:20.964 -> 9622:08:55.446 -> 10122:09:28.327 -> 9922:10:06.548 -> 99
2回目22:08:20.998 -> 9622:08:55.481 -> 10122:09:28.362 -> 9922:10:06.595 -> 99
3回目22:08:21.032 -> 9722:08:55.481 -> 10122:09:28.396 -> 9922:10:06.595 -> 100
4回目22:08:21.066 -> 9722:08:55.514 -> 10122:09:28.431 -> 9922:10:06.642 -> 100
5回目22:08:21.100 -> 9722:08:55.548 -> 10122:09:28.465 -> 9922:10:06.689 -> 100
6回目22:08:21.134 -> 9722:08:55.581 -> 10122:09:28.499 -> 9922:10:06.689 -> 99
7回目22:08:21.168 -> 9722:08:55.615 -> 10222:09:28.533 -> 9922:10:06.736 -> 99
8回目22:08:21.201 -> 9722:08:55.649 -> 10222:09:28.568 -> 9922:10:06.782 -> 99
9回目22:08:21.235 -> 9722:08:55.682 -> 10122:09:28.604 -> 9922:10:06.782 -> 99
10回目22:08:21.270 -> 9722:08:55.716 -> 10122:09:28.638 -> 9922:10:06.829 -> 99

タイムスタンプついてる。ブレがずいぶん小さくなった。

ローパス(指数加重平均)

#include <Wire.h>
#include <VL53L0X.h>

uint16_t newdata = 0;
uint16_t lastdata = 0;
uint16_t filtereddata = 0;

VL53L0X sensor;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  sensor.setTimeout(500);
  if (!sensor.init())
  {
    Serial.println("Failed to detect and initialize sensor!");
    while (1) {}
  }

  sensor.startContinuous();
}

void loop() {

  float weight = 0.8;

  newdata = sensor.readRangeContinuousMillimeters();
  if (sensor.timeoutOccurred()) {Serial.print(" TIMEOUT");}

  if (lastdata == 0) {
    lastdata = newdata;
    return; 
  }
   
  filtereddata = (1.0 - weight) * lastdata + (newdata * weight);
  lastdata = newdata;

  Serial.print(filtereddata);
  Serial.println();
}

こんな感じで書き換えた。重み付けを0.8, 0.85, 0.9と変えてみる。結果は

0.80.850.9
1回目23:50:33.653 -> 9423:54:09.415 -> 9923:54:39.888 -> 99
2回目23:50:33.687 -> 9423:54:09.449 -> 9923:54:39.923 -> 101
3回目23:50:33.722 -> 9623:54:09.483 -> 10123:54:39.923 -> 101
4回目23:50:33.722 -> 9523:54:09.518 -> 9923:54:39.957 -> 101
5回目23:50:33.756 -> 9423:54:09.551 -> 10023:54:39.991 -> 98
6回目23:50:33.791 -> 9523:54:09.584 -> 10123:54:40.024 -> 102
7回目23:50:33.824 -> 9323:54:09.618 -> 10123:54:40.057 -> 99
8回目23:50:33.859 -> 9723:54:09.652 -> 9723:54:40.091 -> 100
9回目23:50:33.892 -> 9523:54:09.652 -> 10023:54:40.125 -> 101
10回目23:50:33.926 -> 9723:54:09.685 -> 10123:54:40.159 -> 101

けっこーバラつく。ある程度は滑らかになってるみたいだけどね。

中央値

つーか中央値ってどう求めるんだ。ちょっと調べてみたらソートして真ん中の値を引っ張ればいいみたい。標本数が偶数の場合は真ん中2つの数字の平均を取るらしい。うーむ。偶数の場合はめんどくさそうなので奇数の場合だけ考えよう。そうすれば要素数は最初に決めてるんだし簡単に引っ張ってこれるはず。問題はソートか…。初めてやるなぁ、とりあえずやってみよう。

配列のソート

バブルソート
uint16_t BubbleSort (uint16_t samples[]) {
  uint16_t tmp = 0;

  for (int j = 0; j < SAMPLE_SIZE - 1; j++) { for (int i = SAMPLE_SIZE - 1; i > j; i--) {
      if (samples[i - 1] > samples[i]) {
        tmp = samples[i - 1];
        samples[i - 1] = samples[i];
        samples[i] = tmp;
      }
    }
  }
  debug(samples);
  return samples[(SAMPLE_SIZE - 1) / 2];
}

こんな、バブルソートは遅いって話だったけどぴょんこぴょんこ数字は出てくる。標本数が11, 51, 101の結果

1151101
1回目13:08:03.561 -> 10113:08:56.342 -> 10413:09:53.361 -> 101
2回目13:08:03.595 -> 10113:08:56.377 -> 10413:09:53.396 -> 101
3回目13:08:03.628 -> 10113:08:56.410 -> 10413:09:53.431 -> 101
4回目13:08:03.662 -> 10113:08:56.444 -> 10413:09:53.466 -> 101
5回目13:08:03.695 -> 10113:08:56.478 -> 10413:09:53.500 -> 101
6回目13:08:03.729 -> 10113:08:56.512 -> 10413:09:53.534 -> 101
7回目13:08:03.762 -> 10013:08:56.546 -> 10413:09:53.568 -> 101
8回目13:08:03.797 -> 10013:08:56.581 -> 10313:09:53.602 -> 101
9回目13:08:03.830 -> 10113:08:56.581 -> 10313:09:53.636 -> 101
10回目13:08:03.864 -> 10113:08:56.615 -> 10313:09:53.669 -> 101

バラツキも少なめ。…クイックソートも試してみようと思ったけど別にいーや。気が向いたらやろう。あとソート済みの配列がこれでできたから、新しく測距した値は選択ソートにすれば処理が早そう。これも気が向いたらにしよう。

注意点

今は静止状態で測距してるからあんまり感じないけど、どのフィルタも標本数が多くなってくると応答性が悪くなる。測距した値が変化すると、即時反映するのではなく緩やかな変化になってた。精度と応答性はトレードオフなのかなーと思いました。

スポンサーリンク

長い距離が測れない時

スケッチ例のSingleに、設定する行がある。

//#define LONG_RANGE

コメントアウトされてるのを復帰させると長い距離も測れるようになる。試したらできた。

スポンサーリンク

精度と速度を変更したい時

これもスケッチ例のSingleに、設定する行がある。

// Uncomment ONE of these two lines to get
// - higher speed at the cost of lower accuracy OR
// - higher accuracy at the cost of lower speed

//#define HIGH_SPEED
//#define HIGH_ACCURACY

どっちかだけなので、どっちかコメントから復帰する。こっちは試してない。

スポンサーリンク

参考

距離を正確に測る その2 レーザーVL53L0X | クックブック
IoT spresense Arduino IDE LED Lチカ
デジタル積雪計を作る③ -距離センサVL53L0Xを動かす-
前回: デジタル積雪計を作る② 12月になりました。 ブログの更新が滞っているせいで進捗が無いように思えますが、ブログが遅いだけで進捗は出まくってます。 今回はESP32を使って 距離センサVL53L0X を動かします。 VL53L0XはマイコンとI...
VL53L0Xで遊んだ - yuqlidの日記
STMicroelectronicsが今年の6月ごろ,世界最小のTime-of-Flight(ToF)方式測距センサとして, VL53L0X を発表しました.
バブルソート : アルゴリズム
バブルソートとは、リストにおいて隣り合うふたつの要素の値を比較して条件に応じた交換を実行しながら整列を行うアルゴリズムです。

コメント

タイトルとURLをコピーしました