工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

このチュートリアルでは、Arduino測距儀とデジタル水準器の作り方を学びます。以下の動画をご覧いただくか、書面によるチュートリアルをお読みください。

概要

本デバイスは、超音波センサーで最寄り物体までの距離を測定し、加速度計で地面に対する角度を測定、LCDディスプレイで結果を表示、すべての部品をカスタム設計PCBに実装しています。

単一ボタンで操作可能。電源投入後、まず測定単位を選択します。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

ボタン押下で単位を切り替え、長押し(約0.5秒)で最初のプログラム(距離測定)に入ります。ここでは距離測定と直前の2回分の測定値保存が可能です。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

再度長押しで2番目のプログラム(面積測定)に入り、直交する2つの距離を測って正方形の面積を算出します。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

3番目のプログラムはデジタル水準器で、地面に対する角度を測定します。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

ボタンで2軸(ピッチ/ロール)を切り替えて測定可能です。

Arduino測距儀の回路図

このArduinoプロジェクトの回路図は以下の通り。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

各モジュールの詳細チュートリアルは以下をご覧ください:超音波センサーチュートリアル、LCDチュートリアル、MEMS加速度計チュートリアル。

必要なコンポーネント:

  • 超音波センサー HC-SR04
  • MPU6050 3軸ジャイロスコープ加速度計
  • 16×2キャラクタLCD
  • Arduino Nano

カスタム設計PCB

回路図に基づきEasyEDAでカスタムPCBを設計。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

設計完了後、ガーバーファイルをエクスポート。このプロジェクトのEasyEDAファイルはこちらで確認できます。

JLCPCB(このプロジェクトのスポンサー)でPCBを注文。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

ガーバーファイルをドラッグ&ドロップでアップロード、ビューアーで確認。問題なければ仕様を選んで注文。初回なら最大10枚まで2ドル。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

デバイスの組み立て

数日後、PCB到着。品質は素晴らしく、設計通りの仕上がり。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

電子部品の組み立て開始。ピンヘッダーをPCBにはんだ付け(着脱容易にするため)。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

3つの抵抗を実装。2つはLCDコントラスト用分圧抵抗(R1に1kΩ、R2に220Ω)、1つはボタン用プルアップ抵抗。

ケースは透明アクリルで製作(電子部品の美しさを可視化)。手元にあった5mm厚アクリルを丸ノコでカット。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

上面パネル(4mm厚、LCDにフィットしやすいため)にLCD用開口部を加工。ドリルで2穴を開け、糸ノコで大まかに切り出し、ヤスリで整えてLCDがピタリとはまるようにしました。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

フォースナービットで電源スイッチ、操作ボタン、超音波センサー用の開口部を加工。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

5分硬化型エポキシでケース組み立て。上面はボルト2本を挿入・接着し、上面パネルをナットで固定できるようにしました。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

LCD、電源スイッチ、ボタン、バッテリーコネクターにピンヘッダー/ジャンパーワイヤーをはんだ付け。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

最終組み立て:超音波センサーをPCBと側面パネルの穴に挿入、Arduinoボード、加速度計モジュール、バッテリーコネクターを実装。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

上面パネルにLCD、電源スイッチ、ボタンを固定してPCBに接続。最後に9V電池をコネクターに接続し、上面パネルをナットで固定。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

ハードウェア完成。あとはプログラムの仕組みを解説します。

Arduino測距儀とデジタル水準器のソースコード

コードが長いため、セクションごとに説明を加え、最後に完全なソースコードを掲載します。

まずWire.h(加速度計のI2C通信用)とLiquidCrystal.h(LCD制御用)をインクルード。LCDオブジェクト、MPU6050のI2Cアドレス、超音波センサーピン、プログラム用変数を定義。

#include <Wire.h> // I2C通信ライブラリ
#include <LiquidCrystal.h> // LiquidCrystalライブラリ
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // LCDオブジェクト (rs, enable, d4, d5, d6, d7)
const int MPU = 0x68; // MPU6050加速度計のI2Cアドレス
#define trigPin 8
#define echoPin 9
#define selectButton 10

int16_t AcX, AcY, AcZ;
long duration;
float distance;
int program = 0;
float d = 0, d1 = 0, d2 = 0, area = 0;
int axis = 0, angle = 0;
int unitSelect = 0;
String unit = "cm";

setup()では加速度計のI2CインターフェースとLCDを初期化、超音波センサーのトリガー/エコーピンとボタンピンのモードを設定。

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  lcd.begin(16, 2);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(selectButton, INPUT_PULLUP);
}

メインループではswitch文でプログラムを切り替え。case 0で測定単位を選択。lcd.print()でテキスト表示、if文で4つの単位を切り替え。

switch (program) {
  case 0: // 測定単位選択
    lcd.setCursor(0, 0);
    lcd.print("Select Unit: ");
    lcd.setCursor(13, 0);
    lcd.print(unit);
    lcd.print(" ");
    delay(10);
    if (digitalRead(selectButton) == 0) {
      if (unitSelect == 0) { unit = "in"; unitSelect = 1; }
      else if (unitSelect == 1) { unit = "m"; unitSelect = 2; }
      else if (unitSelect == 2) { unit = "ft"; unitSelect = 3; }
      else if (unitSelect == 3) { unit = "cm"; unitSelect = 0; }
      // 長押しでプログラム切り替え
      delay(500);
      if (digitalRead(selectButton) == 0) {
        program = 1;
        lcd.clear();
        delay(500);
      }
    }
    break;

unitSelect変数はgetDistance()カスタム関数で機能し、超音波センサーから得られる基本単位(cm)をどの単位に変換するかを指示します。

// 単位変換
if (unitSelect == 1) { distance = distance; unit = "cm"; } // cm→cm
else if (unitSelect == 2) { distance = distance * 0.393701; unit = "in"; } // cm→inch
else if (unitSelect == 3) { distance = distance * 0.01; unit = "m"; } // cm→m
else if (unitSelect == 0) { distance = distance * 0.0328; unit = "ft"; } // cm→ft

ボタンを0.5秒以上長押しするとプログラム1(距離測定)に移行。

delay(500);
if (digitalRead(selectButton) == 0) {
  program = 1;
  lcd.clear();
  delay(500);
}
break;

case 1(距離測定プログラム)ではgetDistance()カスタム関数で最寄り物体までの距離を取得。

case 1: // 距離測定プログラム
  distance = getDistance();
  lcd.setCursor(0, 0);
  lcd.print("Dist: ");
  lcd.print(distance);
  lcd.print(" ");
  lcd.setCursor(14, 0);
  lcd.print(unit);
  delay(10);
  lcd.setCursor(0, 1);
  lcd.print("d:");
  lcd.setCursor(8, 1);
  lcd.print("d:");
  delay(200);
  // 測定値1を保存
  if (digitalRead(selectButton) == 0) {
    if (d == 0) {
      lcd.setCursor(0, 1);
      lcd.print("d: ");
      lcd.setCursor(2, 1);
      lcd.print(distance);
      d = 1;
      delay(100);
    }
    // 測定値2を保存
    else if (d == 1) {
      lcd.setCursor(8, 1);
      lcd.print("d: ");
      lcd.setCursor(10, 1);
      lcd.print(distance);
      d = 0;
      delay(100);
    }
    // 長押しでプログラム切り替え
    delay(500);
    if (digitalRead(selectButton) == 0) {
      program = 2;
      d = 0;
      lcd.clear();
      delay(500);
    }
  }
  break;

getDistance()関数の仕組み:

//===== getDistance - カスタム関数
float getDistance() {
  digitalWrite(trigPin, LOW);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration * 0.034 / 2; // 距離(cm)
  // 単位変換
  if (unitSelect == 1) { distance = distance; unit = "cm"; }
  else if (unitSelect == 2) { distance = distance * 0.393701; unit = "in"; }
  else if (unitSelect == 3) { distance = distance * 0.01; unit = "m"; }
  else if (unitSelect == 0) { distance = distance * 0.0328; unit = "ft"; }
  return distance;
}

トリガーピンで超音波パルスを発信、エコーピンとpulseIn()で往復時間を測定、音速から距離を算出。LCDに距離とテキストを表示、ボタン押下で直前2回の測定値を保存・表示。

Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド

case 2(面積測定プログラム)では同様の方法で直交する2距離を測定し、乗算で面積を算出。

case 2: // 面積測定プログラム
  distance = getDistance();
  lcd.setCursor(0, 0);
  lcd.print("Area: ");
  lcd.print(area);
  lcd.setCursor(12, 0);
  lcd.print(unit);
  lcd.print("^2");
  delay(200);
  if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); delay(200); }
  else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); delay(200); }
  else if (d == 2) { lcd.setCursor(6, 0); lcd.print(area); delay(200); }
  if (digitalRead(selectButton) == 0) {
    if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); d = 1; d1 = distance; delay(100); }
    else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); d = 2; d2 = distance; area = d1 * d2; delay(100); }
    else if (d == 2) { lcd.clear(); d = 0; area = 0; delay(100); }
    delay(500);
    if (digitalRead(selectButton) == 0) { program = 3; d = 0; lcd.clear(); delay(500); }
  }
  break;

case 3(角度測定プログラム)。加速度計データ(地球重力場のX,Y,Z各軸成分)を読み取り。各軸の値は2レジスタに格納されるため、計6レジスタを読み取って結合。

case 3: // 角度測定プログラム
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // レジスタ0x3B (ACCEL_XOUT_H) から開始
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // 計6レジスタ読取
  AcX = Wire.read() << 8 | Wire.read(); // X軸
  AcY = Wire.read() << 8 | Wire.read(); // Y軸
  AcZ = Wire.read() << 8 | Wire.read(); // Z軸

  if (axis == 0) {
    // ピッチ角計算(Y軸周りの回転)
    angle = atan(-1 * AcX / sqrt(pow(AcY, 2) + pow(AcZ, 2))) * 180 / PI;
    lcd.setCursor(0, 0);
    lcd.print("Pitch");
  } else if (axis == 1) {
    // ロール角計算(X軸周りの回転)
    angle = atan(-1 * AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2))) * 180 / PI;
    lcd.setCursor(0, 0);
    lcd.print("Roll ");
  }
  lcd.setCursor(0, 1);
  lcd.print("Angle: ");
  lcd.print(abs(angle));
  lcd.print(" ");
  lcd.setCursor(10, 1);
  lcd.print("deg");
  delay(200);
  // 軸切り替え
  if (digitalRead(selectButton) == 0) {
    if (axis == 0) { axis = 1; delay(100); }
    else if (axis == 1) { axis = 0; delay(100); }
    // 長押しでプログラム0に戻る
    delay(500);
    if (digitalRead(selectButton) == 0) { program = 0; lcd.clear(); delay(500); }
  }
  break;
}

X,Y,Z値からピッチ角(Y軸周り回転)とロール角(X軸周り回転)を算出。計算式の詳細はFreescale Semiconductorアプリケーションノート「3軸加速度計を使用した傾斜検知」を参照。LCDに値とテキストを表示、ボタンでピッチ/ロールを切り替え。

完全なソースコード

/* DIY Digital Range Measurer and Spirit Level
   by Dejan Nedelkovski, www.HowToMechatronics.com */
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
const int MPU = 0x68;
#define trigPin 8
#define echoPin 9
#define selectButton 10

int16_t AcX, AcY, AcZ;
long duration;
float distance;
int program = 0;
float d = 0, d1 = 0, d2 = 0, area = 0;
int axis = 0, angle = 0;
int unitSelect = 0;
String unit = "cm";

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  lcd.begin(16, 2);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(selectButton, INPUT_PULLUP);
}

void loop() {
  switch (program) {
    case 0:
      lcd.setCursor(0, 0);
      lcd.print("Select Unit: ");
      lcd.setCursor(13, 0);
      lcd.print(unit);
      lcd.print(" ");
      delay(10);
      if (digitalRead(selectButton) == 0) {
        if (unitSelect == 0) { unit = "in"; unitSelect = 1; }
        else if (unitSelect == 1) { unit = "m"; unitSelect = 2; }
        else if (unitSelect == 2) { unit = "ft"; unitSelect = 3; }
        else if (unitSelect == 3) { unit = "cm"; unitSelect = 0; }
        delay(500);
        if (digitalRead(selectButton) == 0) { program = 1; lcd.clear(); delay(500); }
      }
      break;
    case 1:
      distance = getDistance();
      lcd.setCursor(0, 0);
      lcd.print("Dist: ");
      lcd.print(distance);
      lcd.print(" ");
      lcd.setCursor(14, 0);
      lcd.print(unit);
      delay(10);
      lcd.setCursor(0, 1);
      lcd.print("d:");
      lcd.setCursor(8, 1);
      lcd.print("d:");
      delay(200);
      if (digitalRead(selectButton) == 0) {
        if (d == 0) { lcd.setCursor(0, 1); lcd.print("d: "); lcd.setCursor(2, 1); lcd.print(distance); d = 1; delay(100); }
        else if (d == 1) { lcd.setCursor(8, 1); lcd.print("d: "); lcd.setCursor(10, 1); lcd.print(distance); d = 0; delay(100); }
        delay(500);
        if (digitalRead(selectButton) == 0) { program = 2; d = 0; lcd.clear(); delay(500); }
      }
      break;
    case 2:
      distance = getDistance();
      lcd.setCursor(0, 0);
      lcd.print("Area: ");
      lcd.print(area);
      lcd.setCursor(12, 0);
      lcd.print(unit);
      lcd.print("^2");
      delay(200);
      if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); delay(200); }
      else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); delay(200); }
      else if (d == 2) { lcd.setCursor(6, 0); lcd.print(area); delay(200); }
      if (digitalRead(selectButton) == 0) {
        if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); d = 1; d1 = distance; delay(100); }
        else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); d = 2; d2 = distance; area = d1 * d2; delay(100); }
        else if (d == 2) { lcd.clear(); d = 0; area = 0; delay(100); }
        delay(500);
        if (digitalRead(selectButton) == 0) { program = 3; d = 0; lcd.clear(); delay(500); }
      }
      break;
    case 3:
      Wire.beginTransmission(MPU);
      Wire.write(0x3B);
      Wire.endTransmission(false);
      Wire.requestFrom(MPU, 6, true);
      AcX = Wire.read() << 8 | Wire.read();
      AcY = Wire.read() << 8 | Wire.read();
      AcZ = Wire.read() << 8 | Wire.read();
      if (axis == 0) {
        angle = atan(-1 * AcX / sqrt(pow(AcY, 2) + pow(AcZ, 2))) * 180 / PI;
        lcd.setCursor(0, 0);
        lcd.print("Pitch");
      } else if (axis == 1) {
        angle = atan(-1 * AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2))) * 180 / PI;
        lcd.setCursor(0, 0);
        lcd.print("Roll ");
      }
      lcd.setCursor(0, 1);
      lcd.print("Angle: ");
      lcd.print(abs(angle));
      lcd.print(" ");
      lcd.setCursor(10, 1);
      lcd.print("deg");
      delay(200);
      if (digitalRead(selectButton) == 0) {
        if (axis == 0) { axis = 1; delay(100); }
        else if (axis == 1) { axis = 0; delay(100); }
        delay(500);
        if (digitalRead(selectButton) == 0) { program = 0; lcd.clear(); delay(500); }
      }
      break;
  }
}

float getDistance() {
  digitalWrite(trigPin, LOW);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration * 0.034 / 2;
  if (unitSelect == 1) { distance = distance; unit = "cm"; }
  else if (unitSelect == 2) { distance = distance * 0.393701; unit = "in"; }
  else if (unitSelect == 3) { distance = distance * 0.01; unit = "m"; }
  else if (unitSelect == 0) { distance = distance * 0.0328; unit = "ft"; }
  return distance;
}

このArduinoプロジェクトをお楽しみいただけたら幸いです。ご質問はコメント欄でお気軽にお尋ねください。

製造プロセス

  1. Arduinoで作る模型鉄道DCCコマンドステーション 〜Bluetooth制御・無料アプリ付き〜
  2. Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
  3. Arduino UNOで作るLCDアニメーション&ゲーム入門
  4. Arduinoオーディオ周波数検出器 – 高感度サウンドモジュールで最大音量ピークの周波数を測定
  5. Arduinoで作る自作デジタルペット「Tamaguino」─ 3Dプリンター製ケース付き完全ガイド
  6. Arduino三目並べ:MAX7219 LEDマトリクスと段ボールケースで自作
  7. ArduinoとNokia 5110ディスプレイで自作電圧計:ステップバイステップガイド
  8. SparkFun 7セグメントディスプレイで作る信頼性の高いArduinoカウントダウンタイマー
  9. Python 3からArduino UNOを制御 – シリアル通信でLED点滅デモ
  10. Arduino NanoとRDA5807Mで作るコンパクトFMラジオ
  11. DIY集水槽水位モニター - Arduino Nano、MPXV5010DP、OLEDディスプレイ