Python Raspberry Pi Pico プログラム ロボット制御 回路 電子工作 高専3年生

【Raspberry Pi Pico】ロボット制御入門③~I2C通信と障害物回避~【Python】

前回はライブラリの作成について学びました。

今回はセンサー入力を使って自動制御の一旦を体験してみましょう。

超音波センサとは

超音波センサーは、その名前の通り、音の一種である「超音波」を使って周囲の物体を感知する電子部品です。

超音波センサーには「発信部(Trigger)」と「受信部(Echo)」があります。

まず、発信部から超音波が発射されます。

この音波が物体にぶつかると跳ね返り、受信部でキャッチされます。

このとき、超音波が発信されてから受信されるまでの時間を計測することで、物体までの距離を測ることができます。

(距離 = 超音波の速度 × 測定時間)

超音波センサの使い方

音波センサーの理論をプログラムで実践してみましょう。

以下のコードをPythonファイルとして保存し、実行してみてください。

from machine import Pin  # machineモジュールからPin機能をインポート
import utime  # 時間を扱うためにutimeモジュールをインポート

# 超音波センサーのトリガー(発信)とエコー(受信)用のピンを設定
trigger = Pin(5, Pin.OUT)  # GPIO 5番ピンをトリガーとして設定
echo = Pin(4, Pin.IN)  # GPIO 4番ピンをエコーとして設定

# 距離を測定する関数を定義
def read_distance():
    trigger.low()  # トリガーをLowにして準備
    utime.sleep_us(2)  # 2マイクロ秒待機
    trigger.high()  # トリガーをHighにして超音波を発信
    utime.sleep(0.00001)  # 10マイクロ秒間超音波を発信
    trigger.low()  # 再度トリガーをLowにする
    
    # エコーが反応を返すまでの時間を計測
    while echo.value() == 0:  # エコー信号が来るのを待つ
        signaloff = utime.ticks_us()  # 反応なしの時間を記録
    
    while echo.value() == 1:  # エコー信号が来るのを待つ
        signalon = utime.ticks_us()  # 反応が返ってきた時間を記録

    # 反応時間を計算し、距離を求める
    timepassed = float(signalon) - float(signaloff)
    distance = (timepassed * 0.0343) / 2  # 距離を計算(音速は約0.0343 cm/μs、片道なので2で割る)
    
    # 測定した距離を表示
    print("Distance: ", distance, "cm")

# メインループで繰り返し距離を測定
while True:
    read_distance()  # 距離を測定
    utime.sleep_ms(50)  # 50ミリ秒待機

RasPiPicoより検出した距離のデータがPCへ送られてくると思います。

解説してゆきます。

超音波センサーのピン設定

trigger = Pin(5, Pin.OUT)  # GPIO 5番ピンをトリガー(出力)として設定
echo = Pin(4, Pin.IN)  # GPIO 4番ピンをエコー(入力)として設定
  • triggerは超音波センサーの発信部を制御するためのピンで、超音波を発信する役割を持っています。
  • echoは超音波が物体に当たって跳ね返ってきた信号を受信するためのピンです。

距離を測定する関数

def read_distance():
    trigger.low()  # トリガーピンをLowに設定して準備
    utime.sleep_us(2)  # 2マイクロ秒待機
    trigger.high()  # トリガーピンをHighにして超音波を発信
    utime.sleep(0.00001)  # 10マイクロ秒間超音波を発信
    trigger.low()  # トリガーピンをLowに戻す
  • 超音波センサーの発信部(トリガー)を制御します。trigger.low()で超音波を出さない状態にした後、trigger.high()で10マイクロ秒間超音波を発信します。その後、trigger.low()に戻すことで発信を停止します。この短い信号で物体に向かって超音波を送信します。
    while echo.value() == 0:  # エコーが0(Low)の間待機
        signaloff = utime.ticks_us()  # エコー信号が来ない間の時間を記録
  • echoが0の間、信号が届かない時間をsignaloffに記録します。これは超音波がまだ反射して返ってきていないことを示します。
    while echo.value() == 1:  # エコーが1(High)の間待機
        signalon = utime.ticks_us()  # エコー信号が来た時間を記録
  • echoが1になると、超音波が物体に反射して戻ってきたことを示します。このときの時間をsignalonに記録します。

距離の計算

    timepassed = float(signalon) - float(signaloff)  # エコーが届くまでの時間差を計算
    distance = (timepassed * 0.0343) / 2  # 超音波が往復した時間を元に距離を計算(音速: 0.0343 cm/μs)
  • timepassedは、超音波が物体に跳ね返ってエコーに戻ってくるまでの時間を計算します。
  • 距離の計算には、音速(約0.0343 cm/マイクロ秒)を使い、往復した時間を片道の時間にするために2で割ります。

測定結果の表示

    print("Distance: ", distance, "cm")
  • 計算した距離をセンチメートル単位で表示します。

メインループ

while True:
    read_distance()  # 距離を測定する関数を呼び出す
    utime.sleep_ms(50)  # 50ミリ秒待機してから次の測定を行う
  • while Trueループで、read_distance()関数を繰り返し呼び出して距離を測定します。各測定の間に50ミリ秒の待機を入れて、次の測定を行います。

I2C通信

ロボットはセンサー入力を使い、動作を変えてゆく自動制御を行います。

プログラムを作成する過程で、現在どのような値が入力されているのか確認したい場面は多くあります。

今回はPC画面上で値を確認する方法を試しましたが、ロボットを動かしている状態で、PCと配線をする必要が有り、少々不便です。

そこで、I2C通信を使って接続したLCDディスプレイに表示する方法を試します。

使用するLCDはこちらです。

https://akizukidenshi.com/catalog/g/g112031/

このLCDはRaspberry Pi Pico WとI2Cという通信方法でデータの送受信を行います。

I2C(Inter-Integrated Circuit)とは?

I2C(アイ・スクエア・シー、またはアイ・ツー・シー)は、電子部品同士を接続してデータをやり取りするための「シリアル通信方式」の1つです。
シリアル通信とは、データを「1ビットずつ順番に送信」する仕組みで、送信するデータを二進数(0と1)に変換して、1つの線を通じて送る方法です。

I2Cは特に、センサーやディスプレイモジュールなどの電子部品を簡単に接続できるため、マイコンや電子工作で広く使われています。
例えば、Raspberry PiやArduinoを使って温度センサーや液晶ディスプレイを操作する際にも利用されます。

通信手法の種類:シリアル通信とパラレル通信

データを送信する手法には、シリアル通信パラレル通信の2種類があります。

これらは、データを送信する方法に違いがあり、それぞれの特徴があります。

1. シリアル通信

  • データを「1ビットずつ順番に」送る方法。
  • 例え: 一列に並んだ荷物を一つずつトラックで運ぶようなイメージ。
  • I2Cはシリアル通信の一種です。
  • 主に使われる配線は4本(以下の2本が通信に関係):
    • SDA(Serial Data): データを送受信する線。
    • SCL(Serial Clock): 通信のタイミングを合わせるためのクロック信号。

2. パラレル通信

  • 複数のビットを一度にまとめて送信する方法。
  • 例え: 一列の荷物を複数台のトラックで同時に運ぶイメージ。
  • 一度にたくさんのデータを送れるため、高速通信が可能。
  • ただし、多くの信号線が必要になるため、配線が複雑でコストが高くなります。
特徴シリアル通信パラレル通信
コスト配線が少ないため安価多くの信号線が必要なので高価
配線の容易さ簡単(基本的に4本の線:GND、VCC、SDA、SCL)信号線が多く複雑(データ線がビット数に比例して増える)
通信速度1ビットずつ順番に送るため遅い複数ビットを同時に送るため高速

I2Cの配線

I2C通信の基本構成

I2C 通信では、デバイス同士を接続するために2本のデータ線を使用します:

  • SDA(Serial Data Line): データの送受信を行う線。
  • SCL(Serial Clock Line): 通信のタイミングを同期するためのクロック信号を送る線。

これらに加えて、電源線(VCC)とGND(グランド)を接続する必要があります。

Raspberry Pi Pico W の I2C ポート

Raspberry Pi Pico W では、I2C 通信に使用できる GPIO ピンが 固定されたペア で割り当てられています。この割り当ては自由に変更できませんので、設計時に注意が必要です。

以下が、Raspberry Pi Pico W で使用可能な I2C の SDA と SCL の組み合わせです:

I2CインスタンスSDAピンSCLピン
I2C0GPIO 0GPIO 1
I2C1GPIO 2GPIO 3

注意

  • Raspberry Pi Pico W では、1つの I2C インスタンスに対して SDA と SCL は固定されます。異なるペアを使用する場合は、I2C のインスタンスを切り替えて利用してください。
  • どのデバイスをどのポートに接続するかを設計段階で考慮する必要があります。

配線例

I2C 通信を使ってセンサーやディスプレイなどを Raspberry Pi Pico W に接続する場合、以下のように配線します。

必要なピンの接続

  1. VCC(電源線): 使用するセンサーやデバイスの電源(通常 3.3V または 5V)に接続します。
  2. GND(グランド): Raspberry Pi Pico W の GND とセンサー・デバイスの GND を接続します。
  3. SDA(データ線): Raspberry Pi Pico W の GPIO 0 または 2 に接続(使用する I2C インスタンスに応じて選択)。
  4. SCL(クロック線): Raspberry Pi Pico W の GPIO 1 または 3 に接続(使用する I2C インスタンスに応じて選択)。

I2C Pull-up 抵抗

I2C 通信では、SDA と SCL の両線にプルアップ抵抗が必要です。これにより、通信ラインが正常に動作します。

ポイント

  • プルアップ抵抗の値: 通常は 4.7kΩ〜10kΩ を使用します。
  • 通常、センサーやモジュールにプルアップ抵抗が組み込まれている場合が多いため、外部プルアップ抵抗が不要な場合もあります。

今回はロボットの基板にLCDディスプレイを接続するためのヘッダーピンを準備しているので配線は不要です。

LCD

それでは液晶ディスプレイに文字を表示させてみましょう。

ただ液晶ディスプレイに文字を表示させるのを1からコーディングするのは非常に複雑になってしまいますので、ここではmicropythonに搭載されているライブラリ「ssd1306」を活用しましょう。

まずThonnyの画面左上の「ツール」をクリックして開き、「パッケージを管理」をクリックします。

画面上部の検索バーに「ssd1306」と記入しEnterキーを押します。

画面右側にライブラリ名が表示されますのでクリックします。

中央下部の「インストール」をクリックします。

画面左側の<インストール>の項目に「ssd1306」が追加されていればインストールは完了です。

これでライブラリが使用できるようになりましたので、プログラムを書いていきましょう。

まずライブラリをインポートします。

今回はRaspberry Pi Pico WとLCDをI2C通信で接続しますので、machineライブラリの中からI2Cというライブラリもインポートしましょう。

from machine import Pin, I2C
import ssd1306
import time

次にI2Cの設定をします。

今回はロボットの回路でLCDディスプレイがSDA:GPIO20、SCL:GPIO21につながっていますのでそれを指定します。

そしてLCDが適切に接続できているかをスキャンして確認します。

スキャンが成功すればI2C接続のアドレス(普通は0x3c:10進数で60)をPC画面に表示します。

from machine import Pin, I2C
import ssd1306
import utime

# I2C通信の初期化 (SDA: ピン20, SCL: ピン21, 通信速度: 400kHz)
i2c = I2C(0, sda=Pin(20), scl=Pin(21), freq=400000)

# 接続されているI2Cデバイスをスキャン
addr = i2c.scan()
if not addr:  # I2Cデバイスが見つからなかった場合
    print("I2Cデバイスが見つかりません。接続を確認してください。")
    raise SystemExit()  # プログラムを終了
else:  # 見つかった場合
    print("I2Cデバイスが見つかりました:", [hex(a) for a in addr])

ディスプレイ表示を設定します。

ディスプレイは定期的に表示する文字を更新してゆくのですが、前回ディスプレイに何か表示されていた場合、画面をリセットする必要があります。

これをしないと変数部分が「■」のように表示されてしまいます。

またこの操作は何度も行うので関数にしておきます。

# OLEDディスプレイを黒で塗りつぶす
def clear():
    utime.sleep(1)  # 1秒待機
    oled.fill(0)  # 0で画面全体を黒にする
    oled.show()  # 表示を更新

これが完了してから表示したい文字列を表示します。

今回は「Hello」という文字列と、その下にプログラムが開始してから1秒ごとに画面を更新し、経過時間(s)を表示させます。

そのために経過時間を記憶するための任意の変数(ここではnow_time)を定義します。

これを+1ずつしていく(now_time = now_time + 1)ことで経過時間を格納します。

そして文字列の表示はoled.textという関数を使います。

oled.text(text, x, y)について、textは文字列もしくは変数を記入することことが出来ます。

変数の場合は、文字列に変換するためにstr(now_time)と記述します。

これは文字列の方を変換する記述です。文字列の場合は''で括る必要があります。

そして表示位置は(x,y)で決定します。表示したい文字列の先頭が画面左端からx, 上端からyの位置に配置することを意味します。

そして1秒ごとにこれを繰り返すことでHelloという文字列と経過時間をリアルタイムで表示できるようになります。

以下に全体のプログラムを示します。

from machine import Pin, I2C
import ssd1306
import utime

# I2C通信の初期化 (SDA: ピン20, SCL: ピン21, 通信速度: 400kHz)
i2c = I2C(0, sda=Pin(20), scl=Pin(21), freq=400000)

# 接続されているI2Cデバイスをスキャン
addr = i2c.scan()
if not addr:  # I2Cデバイスが見つからなかった場合
    print("I2Cデバイスが見つかりません。接続を確認してください。")
    raise SystemExit()  # プログラムを終了
else:  # 見つかった場合
    print("I2Cデバイスが見つかりました:", [hex(a) for a in addr])

# OLEDディスプレイの初期化
try:
    oled = ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)  # ディスプレイサイズ(128x64)とアドレス(0x3C)を指定
except Exception as e:  # エラーが発生した場合
    print("OLEDの初期化に失敗しました:", e)
    raise  # プログラムを終了

# OLEDディスプレイを黒で塗りつぶす
def clear():
    utime.sleep(1) # 1秒待機
    oled.fill(0)  # 0で画面全体を黒にする
    oled.show()  # 表示を更新

now_time = 0 #開始時から経った時間を格納する変数
try:
    while True:
        clear()
        now_time = now_time + 1
        # OLEDディスプレイに文字列を表示
        oled.text('Hello', 0, 0)  # 文字「Hello」を位置(0, 0)に表示
        oled.text(str(now_time), 0, 16)  # 現在時刻を位置(0, 16)に表示
        oled.text('now time:' + str(now_time), 0, 32)  # 現在時刻を位置(0, 32)に表示
        oled.show() # ディスプレイを更新する関数
finally:
    oled.fill(0)  # 0で画面全体を黒にする
    oled.show()

注意点として、アルファベット以外を表記するのは少々手間がかかりますのでご注意ください。

日本語や絵文字は、表記出来ないことはないですがライブラリのインストールやコードの追加が必要になります。

問題1

LCDディスプレイに超音波センサの値を表示するプログラムを作成してください。

解答例はこちら
from machine import Pin, I2C
import ssd1306
import utime

# I2C通信の初期化 (SDA: ピン20, SCL: ピン21, 通信速度: 400kHz)
i2c = I2C(0, sda=Pin(20), scl=Pin(21), freq=400000)
now_time = 0
# 接続されているI2Cデバイスをスキャン
addr = i2c.scan()
if not addr:  # I2Cデバイスが見つからなかった場合
    print("I2Cデバイスが見つかりません。接続を確認してください。")
    raise SystemExit()  # プログラムを終了
else:  # 見つかった場合
    print("I2Cデバイスが見つかりました:", [hex(a) for a in addr])

# OLEDディスプレイの初期化
try:
    oled = ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)  # ディスプレイサイズ(128x64)とアドレス(0x3C)を指定
except Exception as e:  # エラーが発生した場合
    print("OLEDの初期化に失敗しました:", e)
    raise  # プログラムを終了

# OLEDディスプレイを黒で塗りつぶす
def clear():
    utime.sleep(1) # 1秒待機
    oled.fill(0)  # 0で画面全体を黒にする
    oled.show()  # 表示を更新

# 超音波センサーのトリガー(発信)とエコー(受信)用のピンを設定
trigger = Pin(5, Pin.OUT)  # GPIO 5番ピンをトリガーとして設定
echo = Pin(4, Pin.IN)  # GPIO 4番ピンをエコーとして設定

# 距離を測定する関数を定義
def read_distance():
    trigger.low()  # トリガーをLowにして準備
    utime.sleep_us(2)  # 2マイクロ秒待機
    trigger.high()  # トリガーをHighにして超音波を発信
    utime.sleep(0.00001)  # 10マイクロ秒間超音波を発信
    trigger.low()  # 再度トリガーをLowにする
    
    # エコーが反応を返すまでの時間を計測
    while echo.value() == 0:  # エコー信号が来るのを待つ
        signaloff = utime.ticks_us()  # 反応なしの時間を記録
    
    while echo.value() == 1:  # エコー信号が来るのを待つ
        signalon = utime.ticks_us()  # 反応が返ってきた時間を記録

    # 反応時間を計算し、距離を求める
    timepassed = float(signalon) - float(signaloff)
    distance = (timepassed * 0.0343) / 2  # 距離を計算(音速は約0.0343 cm/μs、片道なので2で割る)
    
    # 測定した距離を表示
    return(distance)

try:
    while True:
        distance = read_distance()  # 距離を測定
        clear()
        now_time = now_time + 1
        oled.text('now time:' + str(now_time), 0, 0)  # 現在時刻を位置(0, 32)に表示
        oled.text('distance:' + str(distance), 0, 16)  # 現在時刻を位置(0, 32)に表示
        oled.show() # ディスプレイを更新する関数
finally:
    # OLEDディスプレイを黒で塗りつぶす
    clear()

障害物回避

今回は超音波センサを使った、ロボットの障害物回避に挑戦してみましょう。

実現するにはどうすればいいでしょうか。

まずは物体との距離が一定以上に近づいたことを検知できるようにしてみましょう。

以下のようにWhileの中身に検出するための条件分岐を追加します。

from machine import Pin
import utime

# トリガーとエコーのピン設定
trigger = Pin(5, Pin.OUT)
echo = Pin(4, Pin.IN)

def read_distance():
    # トリガーを操作して距離を測定するコード
    trigger.low()
    utime.sleep_us(2)
    trigger.high()
    utime.sleep(0.00001)
    trigger.low()

    while echo.value() == 0:
        signaloff = utime.ticks_us()
    while echo.value() == 1:
        signalon = utime.ticks_us()

    timepassed = float(signalon) - float(signaloff)
    distance = (timepassed * 0.0343) / 2  # 距離計算(cm単位)

    print("Distance: ", distance, "cm")
    return distance

# 一定距離のしきい値を設定
const_distance = 20  # 単位はセンチメートル

while True:
    distance = read_distance()  # 距離を測定
    utime.sleep_ms(50)
    # 条件分岐を追加
    if distance < const_distance:
        print("近づきすぎです!")

説明

  1. 定数の設定
    • const_distanceという変数を用意し、しきい値として距離を設定しました。これにより、コード内で距離を変更したい場合はこの変数だけを変更すれば済みます。
    • 定数を使うことで、コードが長くなっても値を見つけやすく、変更もしやすくなります。
  2. 条件分岐
    • if distance < const_distance:の部分で、物体が一定の距離以内に近づいた場合に「近づきすぎです!」と出力します。これにより、ロボットが障害物に接近しすぎた場合に警告を出すことができます。

それではロボットが近くに障害物を検出した場合、90度旋回するプログラムを作ってゆきます。

90度回転するためにロボットを制御しよう

ロボットを90度回転させるためには、どれくらいモーターを回せば良いのでしょうか?まずは基本的な考え方を説明します。

ロボットが回転する際には、左右のモーターの動きによって動作が変わります。

以下の図のように、90度回転するためには左側のモーターがある距離だけ進む必要があります。

具体的な計算としては、左側のモーターが2π * (x/2) / 4 [mm]、右側のモーターが-πx/4 [cm]だけ動く必要があります。

しかし、実際にプログラムで距離を直接制御することはできません。

私たちが制御できるのは、PWM(モーターの速度を調整するもの)や動かす時間の設定です。

そのため、まずはロボットが1秒間でどれくらい進むかを調べてみましょう。

これによって、必要な距離を動かすための時間がわかります。

1秒間に進む距離を測るプログラム

次のプログラムを使って、ロボットが1秒間にどれくらい進むかを測定してみましょう。

以前作成したRobotクラスを使用してロボットを前進させます。

# 作成したライブラリからRobotクラスをインポート
from motor import Robot
import utime

# Robotクラスのインスタンスを作成
robot = Robot()

# 計測のための時間を初期化
init_time = utime.ticks_us()
now_time = utime.ticks_us()

try:
    while now_time - init_time < 1000000:  # 1秒間(1000000マイクロ秒)だけ動作
        now_time = utime.ticks_us()
        # ロボットを前進させる
        robot.forward()
finally:
    # 動作終了時にロボットを停止
    robot.stop()

測定の手順

  1. このプログラムを実行すると、ロボットは1秒間だけ前進します。
  2. 実行後に、ロボットが進んだ距離を定規やメジャーで測定してください。単位はミリメートル(mm)で記録すると便利です。
  3. 測定した距離から、1秒間にどれくらい進むかを知ることができます。

測定結果を使った計算

  • 測定した距離を使って、ロボットが90度回転するために必要な時間を計算できます。
  • 例えば、1秒で100mm進んだ場合、90度回転に必要な距離が50mmであれば、0.5秒(500ms)でその距離を進むことがわかります。

このようにして、ロボットが正確に回転するように時間やモーターの動作を調整していきます。

ここで使っているutime.ticks_us()という関数は、RPi Picoに電源が入ってからの経過時間をマイクロ秒単位で返してくれるものです。

以前の距離を測定する関数でも使っていました。

この関数の目的は経過時間を測ることで、今回は1秒(=1000000マイクロ秒)を測るために使いました。

もしticks_us()ticks_ms()に変えると、ミリ秒単位で経過時間を測定するようになります。

距離と時間の関係を使った計算

ロボットが1秒間に進んだ距離をyとします。これは次の式で表されます。

1 [秒] × A [mm/s] = y [mm]
⇔ A = y [mm] / 1 [秒]

ここで、Aはロボットが1秒間に進む速度です。

この速度Aを使って、必要な回転時間tを計算します。

A × t = πx / 4
⇔ t = (πx / 4) / A

この計算から得られた時間だけ、right()もしくはleft()の関数を使ってロボットを動かすことで、90度回転を実現できます。

計算が正しいかどうか確認するために、次のサンプルプログラムを実行してみましょう。

xxxxxxxの部分には、先ほど求めたtをマイクロ秒に変換した値(t * 10^6)を入れてください。

# 作成したライブラリからRobotクラスをインポート
from motor import Robot
import utime

# Robotクラスのインスタンスを作成
robot = Robot()

# 時間を初期化
init_time = utime.ticks_us()
now_time = utime.ticks_us()
print(init_time)

try:
    while now_time - init_time < xxxxxxxx:  # xxxxxxxxに計算した時間(マイクロ秒単位)を入れる
        now_time = utime.ticks_us()
        # 右回転にする場合はrobot.right()に変更してもOK
        robot.left()
finally:
    # 停止
    robot.stop()

それではこれを障害物検知と組み合わせてみましょう。やることはプログラムを合体させるだけです。

障害物検知と90度回転を組み合わせたロボット制御プログラム

このプログラムでは、ロボットが超音波センサーを使って前方の障害物までの距離を測定し、一定の距離よりも近づいた場合に90度回転して障害物を避けるように動作します。

from machine import Pin  # RPi Picoのピンを制御するためのモジュール
from motor import Robot  # ロボットを制御するための自作ライブラリ
import utime, time  # 時間管理用のモジュール

# 超音波センサーの設定
trigger = Pin(5, Pin.OUT)  # トリガーピン(超音波の発信)
echo = Pin(4, Pin.IN)  # エコーピン(超音波の受信)

# 障害物検知のしきい値(距離:20cm)
const_distance = 20
time.sleep(1)  # 初期化のための1秒待機
print("システム準備完了")

# ロボットのインスタンスを作成
robot = Robot()

# 超音波センサーで距離を測定する関数
def read_distance():
    trigger.low()  # トリガーをLowに設定
    utime.sleep_us(2)  # 2マイクロ秒待機
    trigger.high()  # トリガーをHighに設定して超音波を発信
    utime.sleep(0.00001)  # 10マイクロ秒間超音波を発信
    trigger.low()  # トリガーをLowに戻す

    # 反射した超音波を受け取る時間を記録
    while echo.value() == 0:  # 反射波が来るまで待機
        signaloff = utime.ticks_us()
    while echo.value() == 1:  # 反射波を受け取るまでの時間を記録
        signalon = utime.ticks_us()

    # 距離を計算(音速に基づく)
    timepassed = float(signalon) - float(signaloff)
    distance = (timepassed * 0.0343) / 2  # 距離を計算(単位: cm)
    print("距離: ", distance, "cm")
    return distance

# メインループ
try:
    while True:
        utime.sleep_ms(50)  # 少し待機
        robot.forward()  # ロボットを前進させる
        distance = read_distance()  # 距離を測定する

        # 距離がしきい値より小さい場合(障害物が近い場合)
        if distance < const_distance:
            print("障害物を検知!")
            robot.stop()  # ロボットを停止
            utime.sleep_ms(10)  # 少し待機

            # ロボットを左に90度回転
            init_time = utime.ticks_us()
            now_time = utime.ticks_us()
            while now_time - init_time < 1104000:  # 1.104秒間回転
                now_time = utime.ticks_us()
                robot.left()  # 左回転

finally:
    robot.stop()  # プログラム終了時にロボットを停止

問題2

障害物を検出したら90度右旋回し、1秒進んだら90度左旋回をし、元の進行方向へ戻るプログラムを作成してください。

次回はこちら

Follow me!

-Python, Raspberry Pi Pico, プログラム, ロボット制御, 回路, 電子工作, 高専3年生
-, , , , , ,

PAGE TOP