超音波センサーで物体の距離を測ってみます

〔PICの動かせ方入門に戻る〕



 物体の距離を測るには色々有りますが、先ず近距離(0cm-3cm位)の物体を測ると言いますか
物体の存在を検知する為のセンサーが、
近接センサー(磁気センサー)やリードスイッチに光電センサー(フォトセンサー)です。
近接センサー・フォトセンサーの話はこちらを参照して下さい。

そして中距離(3cm-5m位)の物体を測るのに超音波距離センサーと赤外線距離センサーです。
最後に長距離(5m-数百m)の物体を測るのにはレーザー距離センサーでしょうか。
尚、赤外線距離センサーについてはこちらを参照して下さい。

このページではPIC16F1827を使った超音波距離センサーについて記述します。
また、Arduinoで超音波距離センサーを利用する場合はこちらの記事を参照下さい。

人間に聞こえる音の周波数は、約30Hz〜20kHz位と言われてます、この範囲を可聴周波といます。
そして、20kHz〜数GHzを超音波と言い、今回のセンサーは40KHzの音を出します。
ちなみにぃ、犬は80KHzまでは聞こえるらしい、また、コウモリは20KHz〜100KHzを出すらしい。

また、超音波は色々な分野で利用されています、ソナーや魚群探知機(測深機)に、
医療関係では超音波治療器(ME)や超音波検査などですね。

後ぉ、音波の性質として、電波や光に比べたら伝搬速度は結構遅いです、それに速度はその物質の
状態や温度・湿度・圧力などにより変化します。
なので、何時でも精度のよい測距を求めるなら温度センサーを付けて補正をする事を進めます。

空気中の音の伝搬速度は約340m/sですが、水中では約1530m/sでぇ、鉄では約5km/sです。
超音波の伝搬速度は「気体→液体→固体」の順で伝搬速度(効率)が良くなります。
あ、今回利用するセンサーは空中用ですよ、間違わない様に。

《配線図》

ピン構成図1 今回使用するピン番号は、
14番(VDD)と5番(VSS)の電源と、
18番(C12IN1-)は受信センサーからの
信号をコンパレータに入力します、
12番(P2A)と13番(P2B)で送信センサーへ
PWM出力を行います。

また、距離の表示用に9番(RB3)から、別途作成した 自作のLCDモニターに出力しています。


秋月電子のこちらのセンサーを利用した回路です。
回路はデータシート(部品に付属)に記載の受信回路を手持ちの部品で構成しました。

配線図 16F1827は右上が1番ピンでNJM4580Dは左下が1番ピンです。

下図はLM358NですがNJM4580Dも
同じです。
 ピン構成図2


センサー センサーはブレッドボードには刺さりません、なので、左図の様な物を作りました。

受信センサーの出力をオペアンプNJM4580Dの1回路目で100倍増幅し、
2回路目で22倍増幅なので2200倍の交流増幅(反転増幅)を行っています、
この信号をショットキー・バリア・ダイオード(BAT23)を使用して半波整流(直流)にし
超音波信号を検出する為に検波を行っています。
で、この後はPIC内蔵のコンパレータ回路に信号を入力しています。

回路図

上の配線図には、NJM4580Dの電源接続を省いています注意願います。
また、送信センサー回路で実態配線図と回路図で配線が異なっている事にも注意して下さい。
尚、ゲイン調整を行いたい人は220KΩ抵抗を100k〜330kΩ辺りで行ってみて下さい。

センサー自体は4mまでOKの様ですが、この回路では3cm〜160cmまでしか測定出来ません。
送信側センサーの電圧が20Vp-pまでOKなので送信出力電圧を上げられれば距離を伸ばせると
思いますがぁ....

注意)ショットキー・バリア・ダイオードのBAT23は手に入りにくいそうですのでBAT43を使用ください。

送信側超音波センサーの制御について

送信制御説明

PICから40KHzのPWM信号を図1の様に出力制御を行った場合は70cm程しか測定出来ませんが、
PICのシングルPWMステアリング制御を利用して図2の様に40KHzのPWM信号を出力した場合は
160cmまで測定可能です。
これは図2の様に12番ピンがHIGH時は13番ピンはLOWを出力する様に反転させます(差動出力)、
これを相補PWM出力制御と言います、これにより図1(5Vp-p)より図2(10Vp-p)の方がセンサーへの
送信出力電圧(p-p)を約2倍高める事が出来ます。

《サンプルプログラム》

このプログラムにはデバッグモニタープログラム「skMonitorLCD.c」「skMonitorLCD.h」が必要です。
デバッグLCDモニターについてはこちらを参照して下さい。
「skMonitorLCD.c」は送信データをTimer2のタイミングで送る様に変更しました、
その為に下記プログラムを変更しています。
 *1)

また、LCDモニターの出力はRB3から行っているので「skMonitorLCD.h」を下記の様に変更します。
#define _XTAL_FREQ 8000000  // 使用するPIC等により動作周波数値を設定する
#define MONITOR_PIN RB3      // モニタ出力するピンの番号を設定する

@上記配線図画面の様に配線しましょう。
AMPLAB X(v2.15)を起動させます。
B下記がサンプルのプログラムソースです、
  MPLAB(R) XC8 C Compiler Version 1.32コンパイラを使用しています。
  プロジェクトを作成して新規ファイルにコピーペーストして貼り付けて下さい。
  プログラムソースをダウンロードしてプロジェクトに取込む事も出来ます。 *3)
---------------------------------------------------------------------
#include <xc.h>
#include "skMonitorLCD.h"

#define _XTAL_FREQ 8000000    // delay用に必要(クロック8MHzを指定)
#define TIME_OVER  30000      // 超音波センサーから無返答時のタイムアウト時間(30ms)
#define T1COUT     35536      // タイマー1用カウントの初期値(65536-30000:30msカウント)

unsigned int UMS_info ;

// コンフィギュレーション1の設定
#pragma config FOSC = INTOSC    // 内部クロックを使用する(INTOSC)
#pragma config WDTE = OFF       // ウオッチドッグタイマー無し(OFF)
#pragma config PWRTE = ON       // 電源ONから64ms後にプログラムを開始する(ON)
#pragma config MCLRE = OFF      // 外部リセット信号は使用せずにデジタル入力(RA5)ピンとする(OFF)
#pragma config CP = OFF         // プログラムメモリーを保護しない(OFF)
#pragma config CPD = OFF        // データメモリーを保護しない(OFF)
#pragma config BOREN = ON       // 電源電圧降下常時監視機能ON(ON)
#pragma config CLKOUTEN = OFF   // CLKOUTピンをRA6ピンで使用する(OFF)
#pragma config IESO = OFF       // 外部・内部クロックの切替えでの起動はなし(OFF)
#pragma config FCMEN = OFF      // 外部クロック監視しない(FCMEN_OFF)

// コンフィギュレーション2の設定
#pragma config WRT = OFF        // Flashメモリーを保護しない(OFF)
#pragma config PLLEN = OFF      // 動作クロックを32MHzでは動作させない(OFF)
#pragma config STVREN = ON      // スタックがオーバフローやアンダーフローしたらリセットをする(ON)
#pragma config BORV = HI        // 電源電圧降下常時監視電圧(2.5V)設定(HI)
#pragma config LVP = OFF        // 低電圧プログラミング機能使用しない(OFF)

//  割り込みの処理
void interrupt InterFunction( void )
{
     // コンパレータ関連の割込み処理
     // センサーから返答があった場合の処理(物体からの反射有り)
     if (C2IF == 1) {
          TMR1ON = 0 ;                       // TMR1カウント停止
          UMS_info = TMR1L ;                 // カウント値を記録する
          UMS_info = UMS_info | (TMR1H << 8) ;
          UMS_info = UMS_info - T1COUT ;
          TMR1H = (T1COUT >> 8) ;            // カウント値の再設定
          TMR1L = (T1COUT & 0x00ff) ;
          C2IF = 0 ;                         // コンパレータ割込フラグをリセット
     }
     // タイマー1の割込み処理
     // センサーから返答がない場合の処理(物体からの反射がない、近くに物体が無い)
     if (TMR1IF == 1) {
          TMR1ON = 0 ;                       // TMR1カウント停止
          UMS_info = TIME_OVER ;             // カウント値は時間切れ
          TMR1H = (T1COUT >> 8) ;            // カウント値の再設定
          TMR1L = (T1COUT & 0x00ff) ;
          TMR1IF = 0 ;                       // タイマー1割込フラグをリセット
     }
}
// 超音波センサーから距離を読込む処理
// temp:現在の温度を設定する  correction:距離補正
int UsonicMeasurRead(int temp,int correction)
{
     unsigned long t ;
     int ans ;

     ans = 0 ;
     // 超音波センサーの送信側に40KHzPWM信号を200us出力を行う
     TMR4ON = 1 ;                       // TMR4(PWM)スタート
     __delay_us(200) ;
     TMR4ON = 0 ;                       // TMR4(PWM)ストップ
     // 超音波センサーの受信側からの返答を待つ
     TMR1ON   = 1 ;                     // TMR1カウント開始
     UMS_info = 0 ;
     while(UMS_info == 0) ;             // 反射波の受信を待つ
     // 返答時間から距離を求める
     if (UMS_info < TIME_OVER) {
          t = 331500 + (600 * temp) ;   // 音波の伝搬する速度を求める
          t = (t * UMS_info) / 1000000 ;// 距離の計算
          ans = t / 2 ;                 // 往復なので÷2
          ans = ans + correction ;      // 距離の補正値を加える
     }
     return ans ;                       // mmの距離を返す
}
// メインの処理関数
void main()
{
     int val ;

     OSCCON = 0b01110010 ;    // 内部クロックは8MHzとする
     ANSELA = 0b00000010 ;    // AN0-AN4はAN1のみ使用、他は全てデジタルI/Oとする
     ANSELB = 0b00000000 ;    // AN5-AN11は使用しない全てデジタルI/Oとする
     TRISA  = 0b00000010 ;    // ピン(RA)はRA1のみ入力、他は全て出力に割当てる(RA5は入力専用)
     TRISB  = 0b00000000 ;    // ピン(RB)は全て出力に割当てる
     PORTA  = 0b00000000 ;    // RA出力ピンの初期化(全てLOWにする)
     PORTB  = 0b00000000 ;    // RB出力ピンの初期化(全てLOWにする)
     // 反射波時刻カウント用タイマー1の設定(1カウントは1us)
     T1CON   = 0b01110000 ;   // クロックソースはFosc、プリスケーラ1/8、
     TMR1H   = (T1COUT >> 8) ;// 30000(30ms)までカウントさせる
     TMR1L   = (T1COUT & 0x00ff) ;
     TMR1IF  = 0 ;            // TIMER1のフラグを0にする
     TMR1IE  = 1 ;            // TIMER1の割込みを許可する
     // PWMの設定(CCP2でシングル出力:タイマー4使用)
     CCP2CON = 0b00001101 ;   // PWM機能(シングル出力:相補を行う)を使用
     CCP2SEL = 0 ;            // P2Aは12番ピンのRB6を使用する
     P2BSEL  = 0 ;            // P2Bは13番ピンのRB7を使用する
     PSTR2CON= 0b00000011 ;   // P2AをPWM出力する、P2Bは反転出力
     CCPTMRS = 0b00000100 ;   // ECCP2機能はTimer4を使用する
     T4CON   = 0b00000000 ;   // TMR4プリスケーラ値を1倍に設定
     CCPR2L  = 24 ;           // デューティ値はは50%ほどで初期化
     CCPR2H  = 0 ;
     TMR4    = 0 ;            // タイマー4カウンターを初期化
     PR4     = 49 ;           // PWMの周期を設定(40KHzで設定)
     // DACの設定(約2.0Vをコンパレータの閾値とする)
     DACCON0 = 0b11000000 ;   // VDD/VSSを使用、DACOUTピン(RA2)使わない
     DACCON1 = 13 ;           // 約2.0Vを出力( 5V*(13/2^5)=2.03125 )
     // コンパレータ2の設定(割込みで利用)
     CM2CON0 = 0b10010110 ;   // −>+でON、高速モード、出力は反転、ヒステリシス有効
     CM2CON1 = 0b10010001 ;   // 立上りで割込み利用、+はDAC入力、−はRA1から入力
     C2IF    = 0 ;            // コンパレータ2割込フラグを0にする
     C2IE    = 1 ;            // コンパレータ2割込みを許可する
     // 周辺装置全体の割り込みを許可
     PEIE    = 1 ;            // 周辺装置割り込み有効
     GIE     = 1 ;            // 全割込み処理を許可する
     // LCDモニターに送信出来る様に初期化する
     MonitorInit() ;

     while(1) {
          // LCDモニター画面の消去を行う
          MonitorPutc(0x11) ;
          MonitorPuts("                ") ;
          MonitorPutc(0x11) ;
          // 20℃と言う事でセンサーから距離を読込む、20mm程誤差が有るので足して置く
          val = UsonicMeasurRead(20,20) ;
          // モニターにcmで表示を行う
          MonitorPutd((unsigned char)(val/10)) ;
          MonitorPuts("cm") ;
          // 1秒後に繰り返す(連続で行う場合でも最低50msは空ける事)
          __delay_ms(1000) ;
     }
}
---------------------------------------------------------------------
CコンパイルPIC書き込みを実行して下さい。

DPICをブレッドボードに取付けて動かせば、1秒毎にLCDモニターに距離(cm)を表示します。


送信器センサーに40KHzの信号を200us出力します、40KHz信号はPICのCCP2の
シングルPWM機能(相補PWM制御)を利用しP2A(12番ピン)とP2B(13番ピン)から出力しています。

制御図

その後、C12IN1-(RA1:18番ピン)より受信器センサの値をPIC内蔵のコンパレータに入力しています。
コンパレータの+側はPIC内蔵のDAC回路より2.0V電圧を生成して入力し閾値としています、
なのでぇ、受信器センサー値が2.0Vより上の値であれば反射波が有ったと判断します、
でぇ、反射波が有れば割込みが発生します。(送信器から回り込む直接波が有る為に2.0Vとしています)
また、その間の時刻カウントはタイマー1を利用し、30ms以上反射波が無かった場合はタイムアウトです。

超音波センサーから距離を得る関数の説明
ans = UsonicMeasurRead(temp,correction)
  temp   :超音波周辺の温度を指定します(今回は20℃を設定)
  correction:距離の補正値を指定します(今回は20mmを設定)
  ans    :距離をmmで返します、反射波が無かった場合は0を返します。
  何時でも何処でも正確な距離を測りたい人は温度センサーを付けて補正を行って下さい。
  また、今回のこの回路ではどの距離でも常に約20mm程短く測量されました、
  なので距離補正値20mm入れています。

タイマー1機能
タイマー1についてはこちらの記事を参考にして下さい。

シングルPWM機能
今回はCCP2とTimer4を使用しています、他のTimerやCCP1を利用する場合はこちらの記事を参考に
して下さい。

コンパレータの設定
 コンパレータとは、二つのソース電圧(+-チャンネル)を比較し、
 どちらが大きいかで出力の極性 HIGH(1)/LOW(0)状態が切替わる回路です。
 16F1827にはコンパレータ1と2で2個使用出来ます、下記は2での設定です。
 CM2CON0 = 0b10010110 ;
  (ちなみに、右から左へbit0〜bit7の順で数えますよ)
  bit1:1 = コンパレータのヒステリシス機能は有効
     0 = コンパレータのヒステリシス機能は無効
  bit2:1 = コンパレータは、標準電力の高速モードで動作する
     0 = コンパレータは、低電力の低速モードで動作する
  bit4:1 = コンパレータ出力は反転
     0 = コンパレータ出力は非反転
  bit5:1 = コンパレータの出力を外部ピンに出す
     0 = コンパレータの出力は内部で使用する
  bit6:コンパレータの入力に対する出力極性を決める
     bit4 = 1 の場合
     + < − 時に出力は1
     + > − 時に出力は0
     bit4 = 0 の場合
     + < − 時に出力は0
     + > − 時に出力は1
  bit7:1 = コンパレータは有効
     0 = コンパレータは無効
 CM2CON1 = 0b10010001 ;
  bit0-1:コンパレータの−側入力チャネル選択ビット
      00 = C12IN0-(RA0/17番ピン)からソース信号を入力する
      01 = C12IN1-(RA1/18番ピン)からソース信号を入力する
      10 = C12IN2-(RA2/ 1番ピン)からソース信号を入力する
      11 = C12IN3-(RA3/ 2番ピン)からソース信号を入力する
      今回はC12IN1-(18番ピン)を利用ですが、他のピンを使用する場合該当ピンを
      ANSELA/TRISAレジスターでアナログ入力で設定する必要が有ります。
  bit2-3:コンパレータの+側入力チャネル選択ビット
      00 = C12IN+((RA2/ 1番ピン)からの電圧を閾値入力する (注1)
      01 = PIC内蔵DACからの電圧を閾値入力する
      10 = PIC内蔵の固定電圧 FVR(1.024V/2.048V/4.096V)からの電圧を閾値入力する
      11 = GNDに接続する
  bit6:出力の立下りで割込みを利用する場合は1とする、未利用なら0です
  bit7:出力の立上りで割込みを利用する場合は1とする、未利用なら0です

 (注1) コンパレータ1の時はC1IN+(RA3/ 2番ピン)となります、
     また、レジスター名はそれぞれCM1CON0/CM1CON1に変更して下さい。

DACの設定
 DACはPIC内部でVDDに比例したさまざまな電圧リファレンスを出力できる機能です。
 DACCON0 = 0b11000000 ;
  bit0:−側のソース電圧選択ビット(VSOURCE-)
     1 = VREF-(RA2/ 1番ピン)端子接続を選択する
     0 = PIC接続のGND(VSS)を選択する
  bit2-3:+側のソース電圧選択ビット(VSOURCE+)
     00= PIC接続の+電源(VDD)を選択する
     01= VREF+(RA3/ 2番ピン)端子接続を選択する
     10= PIC内蔵の固定電圧 FVR(1.024V/2.048V/4.096V)を選択する
  bit5:1 = DACOUT(RA2/ 1番ピン)端子から電圧リファレンスを出力する
     0 = DACOUTピン出力不可
  bit6:1 = 低電力状態時に+側のソース電圧を選択し、−側は切る
     0 = 低電力状態時に−側のソース電圧を選択し、+側は切る
  bit7:1 = DACは有効
     0 = DACは無効
 DACCON1 = 13 ;
  出力する電圧を設定します。
  出力電圧 = ((VSOURCE+) - (VSOURCE-))*(DACCON1/(2^5)) + VSOURCE-
  VSOURCE+ = 5V  VSOURCE- = 0V  DACCON1 = 13 ならば 5V*(13/2^5)=2.03125
  DACCON1は0〜31の範囲とする。

《その他》

実験

実験風景です。
赤外線距離センサーよりは超音波センサーの方が距離は正確に測れます。

送信出力電圧を上げられれば距離を伸ばせす事が出来ます。
9V〜12Vの別電源を用意してトランジスターかFETを使って出力するかぁ、
後は5V電源ならMAX232のRS232C用レベル変換ICを利用するかぁ、でしょうか。

基板化する場合は、送信センサーからの直接波が回り込まない様な工夫が必要かもね。
また、基板化すると雑音も小さくなり距離が稼げるかもね。
まぁ、色々トライ&調整してみてみて。

それとぉ、超音波の反射はガラス・紙・ゴム・木・金属・コンクリート等は100%反射しますが、
布類・綿・グラスウールなどの物体は音を吸収します、なのでぇ物体検出は無理っぽいですよ。



MPLAB X用に記事変更(*3) 2015/10/10
回路図の追加(*2) 2014/02/06
"skMonitorLCD.c"変更によりサンプルプログラム変更(*1) 2014/02/01


【きむ茶工房ガレージハウス】
Copyright (C) 2006-2015 Shigehiro Kimura All Rights Reserved.