概要

超音波センサーHC-SR04等を用いてArduinoで距離計測を行いたいときに、Trigピンで超音波を発生させた後にEchoピンで、超音波が反射されてくるまでの時間を計測することで距離を計測できます。その際、pulseInという関数で時間を計測できます。

https://rb-station.com/blogs/article/hc-sr04-arduino

サンプルコードの詳細は上記の記事をご確認ください。

 digitalWrite(TRIG, LOW); 
delayMicroseconds(2);
digitalWrite( TRIG, HIGH );
delayMicroseconds( 10 );
digitalWrite( TRIG, LOW );
duration = pulseIn( ECHO, HIGH ); // 往復にかかった時間が返却される[マイクロ秒]

上記はサンプルコードの抜粋ですが、pulseInで超音波が反射して戻ってくるまでの時間を計測します。しかし、pulseInを実行している間、他の処理は止まってしまいます。

数マイクロ秒なので、気にならないことも多いですが、例えば同時にステッピングモータを動作させているときはモータ動作がpulseInをしている間止まってしまって滑らかでなくなってしまいます。

そこで、pulseInは使わず、他の処理は止めることなく超音波センサでの距離計測ができる方法を紹介します。

関数を自作する

https://arduino.stackexchange.com/questions/28816/how-can-i-replace-pulsein-with-interrupts

上記の記事内容がほぼ答えになっていますが、pulseInのように入力があるのを待っているのではなく、定期的なループごとに入力があるかを毎回チェックする形で対応できます。

/*
 * Non-blocking pulseIn(): returns the pulse length in microseconds
 * when the falling edge is detected. Otherwise returns 0.
 */
unsigned long read_pulse(int pin)
{
    static unsigned long rising_time;  // time of the rising edge
    static int last_state;             // previous pin state
    int state = digitalRead(pin);      // current pin state
    unsigned long pulse_length = 0;    // default return value

    // On rising edge: record current time.
    if (last_state == LOW && state == HIGH) {
        rising_time = micros();
    }

    // On falling edge: report pulse length.
    if (last_state == HIGH && state == LOW) {
        unsigned long falling_time = micros();
        pulse_length = falling_time - rising_time;
    }

    last_state = state;
    return pulse_length;
}

このread_pulse関数を用いると、入力があったタイミングでそれまでの時間を返却してくれるので、ループ処理で毎回呼び出して、0より大きい数値が返されたときに距離計算をします。

例えば、下記のような形で、他の処理をブロックせずに常に距離計測を行う実装ができます。Trigからは400msごとに超音波を発生させ、Echoからの入力値読み取りは100μsごとに行う形にしています。

long nextTrigPulse = 0;
long currentMicros = 0;

void loop() { int duration = read_pulse(ECHO); if(duration > 0){ int distance = (duration / 2) * speedOfSound * 100 / 1000000; Serial.print("Distance:"); Serial.print(distance); Serial.println(" cm"); }
currentMicros = micros();
// 一定時間ごとに超音波センサのTrigをコールする
if(currentMicros > nextTrigPulse){
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite( TRIG, LOW );
nextTrigPulse = currentMicros + 400000;
} // 何かしらの定期処理 delayMicroseconds(100); }

注意点

この方法のデメリットはread_pulseを呼び出す間隔が長くなってしまうと、精度が下がってしまいます。

例えば、1msごとにread_pulseを呼び出すとすると、1ms以内に超音波が反射してきても、次の1msまで気づくことはできません。音速は340.29m/sなので、1msで34cm進みますので、往復34cm = 約17cmの誤差が生じることになります。

正確性の方が重要な場合はpulseIn関数を使うべきかと思われます。

Related Posts

ROSのLaserScanデータの一定角度の範囲を無視する方法
ROSのLaserScanデータの一定角度の範囲を無視する方法
ROSでLidarセンサーを使う時、ロボットの車体など一定角度の範囲に常に障害物があり、これを無視したい時があると思いますが、その方法を紹介します。 以下は、得られた/scan_rawトピックのデータの一定角度の範囲を無視して新しい/...
Read More
ROSのmove_baseで、Pythonからコストマップをクリアする方法
ROSのmove_baseで、Pythonからコストマップをクリアする方法
move_baseを使っていると、障害物が無いのにコストマップで障害物判定されてしまうことがあります。そのようなときは、コストマップを一度クリアしましょう。 コマンドから   move_baseノードには、コストマップをクリアする方法...
Read More
roslaunchでrospy.loginfoでの出力が表示されない場合の対応策
roslaunchでrospy.loginfoでの出力が表示されない場合の対応策
roslaunchで起動したときに、rospy.loginfoで出力しても表示されない場合は下記のように--screenオプションをつけましょう。 コマンドラインで実行する場合 roslaunch your_package your_...
Read More