注釈

こんにちは!SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Communityへようこそ!Raspberry Pi、Arduino、ESP32について、他の愛好者と一緒にさらに深く学んでいきましょう。

参加する理由

  • 専門家のサポート: 購入後の問題や技術的な課題を、コミュニティやチームのサポートを通じて解決できます。

  • 学びと共有: ヒントやチュートリアルを交換し、スキルを向上させましょう。

  • 特別なプレビュー: 新製品の発表や先行公開に早期アクセスできます。

  • 特別割引: 新しい製品に対して限定割引をお楽しみください。

  • 祝祭プロモーションとプレゼント: プレゼントや祝祭プロモーションに参加できます。

👉 私たちと一緒に探求し、創造する準備はできましたか?[ここ]をクリックして、今すぐ参加しましょう!

4. 障害物回避

このプロジェクトでは、PiCrawlerが超音波モジュールを使用して前方の障害物を検出します。 PiCrawlerが障害物を検出すると、信号を送信し、別の方向に進むために方向転換を行います。

コードを実行する

cd ~/picrawler/examples
sudo python3 4_avoid.py

プログラムが開始されると、PiCrawler は立ち上がります。

超音波センサーを使用して距離を継続的に測定し、 その値をターミナルに表示します。

15 cm以内に障害物が検出された場合: - 警告音が再生されます。 - ロボットは小さく左に旋回します。

進行方向に障害物がない場合: - ロボットは前進します。

Ctrl+C を押すまで、ロボットは自動的に障害物回避を続けます。

終了する前に、安全に座る姿勢に戻ります。

コード

注釈

下記のコードは 修正/リセット/コピー/実行/停止 できます。ただし、まずはソースコードのパス(例えば picrawler\examples )に移動してください。コードを修正した後、直接実行して効果を確認できます。

from picrawler import Picrawler
from robot_hat import Music, Ultrasonic, Pin
import time
import signal

music = Music()
crawler = Picrawler()
sonar = Ultrasonic(Pin("D2"), Pin("D3"))  # Ultrasonic trigger/echo pins

music.music_set_volume(100)  # Set speaker volume

alert_distance = 15  # Obstacle warning distance (cm)
speed = 80           # Movement speed

# ----------------------------
# Add hardware timeout to sonar.read()
# Prevent program from freezing
# ----------------------------
class Timeout(Exception):
    pass

def _alarm_handler(signum, frame):
    raise Timeout()

signal.signal(signal.SIGALRM, _alarm_handler)

# Read distance once with timeout protection
def safe_read_once(timeout_s=1):
    try:
        signal.alarm(timeout_s)
        d = sonar.read()
        signal.alarm(0)
        return d
    except Timeout:
        signal.alarm(0)
        return None
    except Exception:
        signal.alarm(0)
        return None

# Read multiple times and return median value (anti-noise)
def read_distance_filtered(n=5, gap=0.03, timeout_s=1):
    vals = []
    for _ in range(n):
        d = safe_read_once(timeout_s=timeout_s)
        if d is not None and d > 0:
            vals.append(d)
        time.sleep(gap)

    if not vals:
        return None

    vals.sort()
    return vals[len(vals)//2]  # Median filter

def main():
    distance = read_distance_filtered(n=5, gap=0.03, timeout_s=1)
    print("distance:", distance)

    if distance is None:
        time.sleep(0.15)  # Wait if read failed
        return

    if distance <= alert_distance:
        # Obstacle detected → play sound and turn
        try:
            music.sound_play_threading('./sounds/sign.wav', volume=100)
        except Exception as e:
            print("sound error:", e)

        crawler.do_action('turn left angle', 1, speed)
        time.sleep(0.5)  # Quiet window after movement
    else:
        # Path clear → move forward
        crawler.do_action('forward', 1, speed)
        time.sleep(0.4)

if __name__ == "__main__":
    try:
        crawler.do_step('stand', 40)  # Stand before starting
        time.sleep(1.0)

        while True:
            main()

    except KeyboardInterrupt:
        print("\nStop.")
    finally:
        try:
            crawler.do_step('sit', 40)  # Sit before exit
            time.sleep(1.0)
        except Exception:
            pass

どのように動作するのか?

  1. 初期化ブロック

    music = Music()
    crawler = Picrawler()
    sonar = Ultrasonic(Pin("D2"), Pin("D3"))
    
    music.music_set_volume(100)
    alert_distance = 15
    speed = 80
    

    このブロックでは、3つの主要モジュールを初期化します: - music:音声の再生を制御します。 - crawler:PiCrawler の動作を制御します。 - sonar:超音波センサーを使用して距離を読み取ります。

    また、スピーカーの音量、障害物検出のしきい値(cm)、 および移動速度を設定します。

  2. タイムアウト設定ブロック(sonar.read() が停止するのを防ぐ)

    class Timeout(Exception):
        pass
    
    def _alarm_handler(signum, frame):
        raise Timeout()
    
    signal.signal(signal.SIGALRM, _alarm_handler)
    

    超音波センサーのドライバは、エコー信号を待つ間に 処理がブロックされる可能性があります。 このブロックではシグナルハンドラを設定し、停止した sonar.read() の呼び出しをプログラム側で中断できるようにします。

  3. 関数:safe_read_once()

    def safe_read_once(timeout_s=1):
        try:
            signal.alarm(timeout_s)
            d = sonar.read()
            signal.alarm(0)
            return d
        except Timeout:
            signal.alarm(0)
            return None
        except Exception:
            signal.alarm(0)
            return None
    

    この関数は、タイムアウト保護付きで超音波センサーを1回読み取ります。 - 読み取りが成功した場合は、距離の値を返します。 - タイムアウトまたはエラーが発生した場合は、処理が停止する代わりに None を返します。

  4. 関数:read_distance_filtered()

    def read_distance_filtered(n=5, gap=0.03, timeout_s=1):
        vals = []
        for _ in range(n):
            d = safe_read_once(timeout_s=timeout_s)
            if d is not None and d > 0:
                vals.append(d)
            time.sleep(gap)
    
        if not vals:
            return None
    
        vals.sort()
        return vals[len(vals)//2]
    

    この関数は、複数回のサンプルを取得することで信頼性を向上させます: - 無効な値(None または <= 0)は無視されます。 - 残った値をソートします。 - ノイズの影響を減らすために中央値を返します。

  5. 関数:main()(主要な判断と動作)

    def main():
        distance = read_distance_filtered(...)
        if distance is None:
            return
    
        if distance <= alert_distance:
            music.sound_play_threading(...)
            crawler.do_action('turn left angle', 1, speed)
        else:
            crawler.do_action('forward', 1, speed)
    

    これはメインの制御ロジックです:

    • フィルタ処理された距離値を読み取ります。

    • 読み取りに失敗した場合、このサイクルはスキップされます。

    • 障害物が alert_distance より近い場合、警告音を再生し、左に旋回します。

    • それ以外の場合は前進します。

  6. プログラムエントリーブロック(連続ループ + 安全終了)

    if __name__ == "__main__":
        try:
            crawler.do_step('stand', 40)
            while True:
                main()
        except KeyboardInterrupt:
            print("\nStop.")
        finally:
            crawler.do_step('sit', 40)
    

    このブロックは、プログラム全体の動作フローを制御します: - 開始前に PiCrawler を立ち上がらせます。 - プログラムは無限ループで main() を繰り返し実行します。 - Ctrl+C を押すとループが停止します。 - プログラム終了前に、PiCrawler は座る姿勢に戻ります。