• Skip to main content
  • Skip to primary sidebar
  • Skip to footer
  • HOME
  • Arduino
    • Arduino初心者編
    • Arduino基礎編
    • Arduino応用編
    • Arduino実践編
  • Raspberry Pi
  • microbit
  • XBee
  • Scratch
  • Contact Us
  • Privacy Policy

STEMSHIP

ものづくりを通して、科学を学ぼう!

現在の場所:ホーム / Arduino / Arduino基礎編 / ArduinoでSTEM教育​ 基礎編:センサのキャリブレーション

ArduinoでSTEM教育​ 基礎編:センサのキャリブレーション

2019年12月15日 by STEMSHIP コメントを書く

センサはいろいろな種類がありますが、どんなものにも誤差が生じます。この誤差をできるだけ小さくするために、キャリブレーション(校正、調整)を行います。例えば、ライントレースカーの場合だと、床材が異なると反射率も異なるので、その環境下でデータを取って、制御閾値を調整する必要があります。このようなセンサデータを処理プログラムする方法を紹介します。

起動時にキャリブレーションする方法

起動時時に実行されるsetup関数内で、基準となるラインを計測して、それをもとに閾値やヒステリシスを設定します。

ヒステリシスについて、簡単に説明します。例えば、照度センサの閾値を500に設定して、500以下だとLEDを点灯、501以上だとLEDを消灯するプログラムを作るとします。照度センサから入力される値は、常に微小に上下しているため、500や501といった値が入ってきます。すると、LEDは点灯と消灯を高速に繰り返してしまいます。街灯はそのような事が起きませんよね?それは、LEDを点灯させる閾値を450、LEDを消灯させる閾値を550というように設定し、センサの値が451~549のときはLED制御をしないように不感帯を設けます。このように、状態によって異なる閾値を持つことをヒステリシスと言います。

1.平均値から決める

1秒間の平均値を計算し、その平均値を閾値とします。ヒステリシスは、0と閾値の距離の10%、または1023と閾値の距離の10%とします。

//www.stemship.com
//2019.12.14

// ピンの定義
#define SENSOR_PIN 0 //照度センサ接続ピン
#define LED_PIN 6    //LED接続ピン

// 状態を示す定数
const int INIT = 0;
const int LEDOFF = 1;
const int LEDON = 2;

// 状態
int curState = INIT;  //現在の状態
int nextState = INIT; //次の状態

// 閾値
int threshold = 0;

// ヒステリシス
int hys = 0;

void setup() {
  //ピンの設定
  pinMode(LED_PIN, OUTPUT);

  //シリアル通信設定
  Serial.begin(9600);

  //キャリブレーション実施
  runCalibration(1000); //1000ms
}

void loop() {
  int sensorVal = analogRead(SENSOR_PIN);

  //状態を遷移させる
  if (sensorVal > (threshold + hys)) {
    nextState = LEDOFF;
  }
  else if (sensorVal < (threshold - hys)) {
    nextState = LEDON;
  }

  //状態が変わったときに、LEDのON/OFFを切り替える
  if ((curState != LEDON) && (nextState ==LEDON)) {
    digitalWrite(LED_PIN, HIGH);
    Serial.print("Switch OFF->ON, ");
    Serial.print("sensorVal = ");
    Serial.println(sensorVal);
  }
  else if ((curState != LEDOFF) && (nextState == LEDOFF)) {
    digitalWrite(LED_PIN, LOW);
    Serial.print("Switch ON->OFF, ");
    Serial.print("sensorVal = ");
    Serial.println(sensorVal);
  }

  //状態を更新する
  curState = nextState;

  //次のループまで100ms待つ
  delay(100);
}

//キャリブレーションを実行する
void runCalibration(int time) {
  int count = 0;
  int sensorVal = 0;
  long sumVal = 0;
  float average = 0;

  // 起動時してから<time>ms間のセンサ値を合計する
  while (millis() < time) {
    sensorVal = analogRead(SENSOR_PIN);
    sumVal += sensorVal;
    count++;
    delay(1);
  }

  //平均値を計算
  average = sumVal / count;

  //平均値から閾値とヒステリシスを決める
  threshold = int(0.9 * average);
  if (threshold > 511) {
    hys = (1023 - threshold) / 10;
  }
  else {
    hys = threshold / 10;
  }

  //計算結果を表示
  Serial.print("count = ");
  Serial.println(count);
  Serial.print("sumVal = ");
  Serial.println(sumVal);
  Serial.print("average = ");
  Serial.println(average);
  Serial.print("threshold = ");
  Serial.println(threshold);
  Serial.print("hys = ");
  Serial.println(hys);
}

2.最小最大値から決める

5秒間の最小値と最大値を測定し、それを動作範囲の幅と考えます。例えば、ライントレースカーであれば、ユーザーが起動時に床とラインをセンスさせてあげます。閾値は、最小・最大の中央値とします。ヒステリシスは、閾値から最小または最大値までの距離の10%とします。

このキャリブレーションの場合は少し注意が必要です。ユーザーが起動時のキャリブレーション期間中に、動作範囲を設定してあげないと、正常な閾値設定ができず、動作がおかしくなってしまいます。その場合は、予め設定した値でどうさせたり、動作範囲が狭すぎる場合はキャリブレーションが失敗していることをユーザに音や光で知らせるようにすれば分かりやすいと思います。

//www.stemship.com
//2019.12.14

// ピンの定義
#define SENSOR_PIN 0 //照度センサ接続ピン
#define LED_PIN 6    //LED接続ピン

// 状態を示す定数
const int INIT = 0;
const int LEDOFF = 1;
const int LEDON = 2;

// 状態
int curState = INIT;  //現在の状態
int nextState = INIT; //次の状態

// 閾値
int threshold = 0;

// ヒステリシス
int hys = 0;

void setup() {
  //ピンの設定
  pinMode(LED_PIN, OUTPUT);

  //シリアル通信設定
  Serial.begin(9600);

  //キャリブレーション実施
  runCalibration(5000); //5000ms
}

void loop() {
  int sensorVal = analogRead(SENSOR_PIN);

  //状態を遷移させる
  if (sensorVal > (threshold + hys)) {
    nextState = LEDOFF;
  }
  else if (sensorVal < (threshold - hys)) {
    nextState = LEDON;
  }

  //状態が変わったときに、LEDのON/OFFを切り替える
  if ((curState != LEDON) && (nextState ==LEDON)) {
    digitalWrite(LED_PIN, HIGH);
    Serial.print("Switch OFF->ON, ");
    Serial.print("sensorVal = ");
    Serial.println(sensorVal);
  }
  else if ((curState != LEDOFF) && (nextState == LEDOFF)) {
    digitalWrite(LED_PIN, LOW);
    Serial.print("Switch ON->OFF, ");
    Serial.print("sensorVal = ");
    Serial.println(sensorVal);
  }

  //状態を更新する
  curState = nextState;

  //次のループまで100ms待つ
  delay(100);
}

//キャリブレーションを実行する
void runCalibration(int time) {
  int minimum = 1023;
  int maximum = 0;
  int sensorVal = 0;

  // 起動時してから<time>ms間のmin/maxを取得する
  while (millis() < time) {
    sensorVal = analogRead(SENSOR_PIN);
    minimum = min(minimum, sensorVal);
    maximum = max(maximum, sensorVal);
    Serial.println(sensorVal);
    delay(1);
  }

  //最小値と最大値から閾値ヒステリシスを決める
  threshold = minimum + (maximum - minimum) / 2;
  hys = (maximum - threshold) / 10;

  //計算結果を表示
  Serial.print("minimum = ");
  Serial.println(minimum);
  Serial.print("maximum = ");
  Serial.println(maximum);
  Serial.print("threshold = ");
  Serial.println(threshold);
  Serial.print("hys = ");
  Serial.println(hys);
}

一定時間ごとにキャリブレーションする方法

動作環境が一定であれば、起動時のキャリブレーションだけで問題有りませんが、動作環境が刻々と変化するような場合だと、問題が出てきます。例えば、屋外だと気温や照度は一定では有りませんよね?このような場合は、一定時間ごとにキャリブレーションを実行して、閾値やヒステリシスを更新する必要があります。

//www.stemship.com
//2019.12.14

// ピンの定義
#define SENSOR_PIN 0 //照度センサ接続ピン
#define LED_PIN 6    //LED接続ピン

// 状態を示す定数
const int INIT = 0;
const int LEDOFF = 1;
const int LEDON = 2;

// 状態
int curState = INIT;  //現在の状態
int nextState = INIT; //次の状態

// 閾値
int threshold = 0;

// ヒステリシス
int hys = 0;

// キャリブレーションのインターバル時間[ms]
const unsigned long CAL_INTERVAL = 5000;

//平均値計算用のカウンタ
int count = 0;

//平均値計算用の合計値
long sumVal = 0;

//前回キャリブレーションを実施した時間
unsigned long lastCalTime = 0;

void setup() {
  //ピンの設定
  pinMode(LED_PIN, OUTPUT);

  //シリアル通信設定
  Serial.begin(9600);
}

void loop() {
  //センサの値を取得
  int sensorVal = analogRead(SENSOR_PIN);

  //現在の時刻を取得
  unsigned long nowTime = millis();

  //センサ取得値を積算してカウンタを更新する。
  sumVal += sensorVal;
  count++;

  //状態を遷移させる
  if (sensorVal > (threshold + hys)) {
    nextState = LEDOFF;
  }
  else if (sensorVal < (threshold - hys)) {
    nextState = LEDON;
  }

  //状態が変わったときに、LEDのON/OFFを切り替える
  if ((curState != LEDON) && (nextState ==LEDON)) {
    digitalWrite(LED_PIN, HIGH);
    Serial.print("Switch OFF->ON, ");
    Serial.print("sensorVal = ");
    Serial.println(sensorVal);
  }
  else if ((curState != LEDOFF) && (nextState == LEDOFF)) {
    digitalWrite(LED_PIN, LOW);
    Serial.print("Switch ON->OFF, ");
    Serial.print("sensorVal = ");
    Serial.println(sensorVal);
  }

  //一定時間が経過したらキャリブレーションを実行する
  if ((nowTime - lastCalTime) > CAL_INTERVAL) {
    runCalibration();
    sumVal = 0;
    count = 0;
    lastCalTime = nowTime;
  }

  //状態を更新する
  curState = nextState;

  //次のループまで100ms待つ
  delay(100);
}

//キャリブレーションを実行する
void runCalibration() {
  //countが0の場合はエラーとなるため、その場合はキャリブレーションをしない。
  if (count < 1) {
    return;
  }

  //平均値を計算
  float average = sumVal / count;

  //平均値から閾値とヒステリシスを決める
  threshold = int(0.9 * average);
  if (threshold > 511) {
    hys = (1023 - threshold) / 10;
  }
  else {
    hys = threshold / 10;
  }

  //計算結果を表示
  Serial.print("count = ");
  Serial.println(count);
  Serial.print("sumVal = ");
  Serial.println(sumVal);
  Serial.print("average = ");
  Serial.println(average);
  Serial.print("threshold = ");
  Serial.println(threshold);
  Serial.print("hys = ");
  Serial.println(hys);
}

Filed Under: Arduino基礎編 関連タグ:キャリブレーション

Reader Interactions

コメントを残す コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。

最初のサイドバー

CONTACT US

  • Facebook
  • Twitter
  • Youtube

More to See

Arduinoベースのセンサノード:XBeeデータ収集ノードの製作

2021年9月27日 By STEMSHIP

Arduinoベースのセンサノード:Arduinoを使った温度センサの作製

2021年9月21日 By STEMSHIP

XBeeモジュールとBMP280センサを使用してデータ収集する

2021年5月31日 By STEMSHIP

XBeeモジュールでMicroPythonを使ってセンサを読み取る

2021年5月24日 By STEMSHIP

XBeeモジュールでセンサを読み取る:センサノードの設定と測定

2021年5月17日 By STEMSHIP

カテゴリー

  • Arduino (44)
    • Arduino初心者編 (15)
    • Arduino基礎編 (11)
    • Arduino実践編 (4)
    • Arduino応用編 (11)
    • Arduino番外編 (3)
  • MakeyMakey (1)
  • microbit (11)
  • Raspberry Pi (13)
  • Scratch (6)
  • STEM教育 (5)
  • XBee (15)
  • ものづくり教育 (2)
  • 子供の教育 (2)
  • 科学 (6)
  • 算数 (5)

Tags

BLE DCモータ EEPROM ELEGOO ESP32 ESP8266 IoT LCD LED makeymakey MQTT Node-RED scratch STEM教育 WiFi XBee アニメーション オンラインゲーム カメラ クローン ゲーム コスチュームの変更 サーボモータ ステッピングモータ ステートマシン スピーカ タイマー ピンポン ブロック定義 ペン ライントレース ラジコン リスト 分数 割り込み 実験 工作 幼児 当たり判定 温度センサ 無線通信 物理 角度制御 重力 音楽

Footer

最近の投稿

  • Arduinoベースのセンサノード:スケッチ(プログラム)の作製
  • Arduinoベースのセンサノード:XBeeデータ収集ノードの製作
  • Arduinoベースのセンサノード:Arduinoを使った温度センサの作製
  • XBeeモジュールとBMP280センサを使用してデータ収集する
  • XBeeモジュールでMicroPythonを使ってセンサを読み取る

タグ

BLE DCモータ EEPROM ELEGOO ESP32 ESP8266 IoT LCD LED makeymakey MQTT Node-RED scratch STEM教育 WiFi XBee アニメーション オンラインゲーム カメラ クローン ゲーム コスチュームの変更 サーボモータ ステッピングモータ ステートマシン スピーカ タイマー ピンポン ブロック定義 ペン ライントレース ラジコン リスト 分数 割り込み 実験 工作 幼児 当たり判定 温度センサ 無線通信 物理 角度制御 重力 音楽

Search

2023年3月
月 火 水 木 金 土 日
 12345
6789101112
13141516171819
20212223242526
2728293031  
« 10月    

Copyright © 2023 · STEMSHIP.COM