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

【Poliviabot】【Raspberry Pi Pico】ロボット制御入門⑧ラインセンサ(アナログ入力)とライントレース【Python】

本サイトでは様々な環境で気軽に本格的なロボット教育を行ってもらうべく、独自のロボット教材を開発しました。

現在、一般販売へ向けて準備をしております。

本Webページではβ版の先行体験をしていただく方向けに公開している資料になっております。

一般の方は販売をお待ちください。

前回はこちらです

1 この章の目的

この章では、PoliviaBot UMEのラインセンサ(左・中央・右)を使って、ライントレースを実装します。
Python初心者でも追えるように、①読み取り → ②しきい値 → ③制御(左右差) → ④改良の順で進めます。

  • ADC(アナログ入力)の基本を理解する
  • ラインセンサの値(白・黒)を数値として読み取れる
  • しきい値で「黒/白」を判定できる
  • 左右のモータ出力を変えてライントレースできる

2 使うピン(PoliviaBot仕様)

ラインセンサはアナログ入力(ADC)です。

  • Line_L:GP26 (ADC0)
  • Line_C:GP27 (ADC1)
  • Line_R:GP28 (ADC2)

モータ(PWM制御を使う前提で章を進めます)

  • 左:Motor_L+ GP0(PWM), Motor_L- GP1
  • 右:Motor_R+ GP2(PWM), Motor_R- GP3

3 アナログ入力(ADC)とは

デジタル入力は 0 / 1 でしたが、アナログ入力は「連続値」です。

MicroPythonのADCは read_u16() で読みます。

  • 0〜65535 の数値で返ってくる
  • 値が大きい/小さいで反射の強さが分かる

4 ラインセンサ値を表示して観察

実験:白と黒で値がどう変わるか

床の「白」「黒」にセンサを当てて、数値の違いを観察します。

from machine import ADC
import time

L = ADC(26)  # Line_L
C = ADC(27)  # Line_C
R = ADC(28)  # Line_R

while True:
    lv = L.read_u16()
    cv = C.read_u16()
    rv = R.read_u16()

    print("L:", lv, " C:", cv, " R:", rv)
    time.sleep(0.2)

観察ポイント

  • 黒のときに大きい?小さい?(センサ回路によって逆もある)
  • 値の差が十分あるか(例:白=1000、黒=40000など)

5 しきい値で「黒/白」を判定する

観察した値を使って、しきい値 TH を決めます。

例)黒が 40000、白が 1000 なら
中間の 20000 あたりをしきい値にします。

TH = 20000

def is_black(v):
    return v > TH

6 モータPWM制御の準備(ライントレース用)

ライントレースは「左右で速さを変える」のでPWMを使います。

from machine import Pin, PWM

# 左モータ
L_P = PWM(Pin(0))
L_M = Pin(1, Pin.OUT)

# 右モータ
R_P = PWM(Pin(2))
R_M = Pin(3, Pin.OUT)

L_P.freq(1000)
R_P.freq(1000)

def drive(left_speed, right_speed):
    # 正転のみ(この教材の基本形)
    L_M.value(0)
    R_M.value(0)
    L_P.duty_u16(left_speed)
    R_P.duty_u16(right_speed)

def stop():
    L_P.duty_u16(0)
    R_P.duty_u16(0)
    L_M.value(0)
    R_M.value(0)

7 いちばん簡単なライントレース(3値判定)

まずは「中央が黒なら直進、左右が黒なら曲がる」の超基本です。

コースは以下のようなシンプルなコースを使います。

A4かA3用紙に印刷してお使いください。

ルール例(黒線を追う)

  • Cが黒:直進
  • Lが黒:左に寄っている → 左旋回(左遅く、右速く)
  • Rが黒:右に寄っている → 右旋回(左速く、右遅く)
  • 全部白:見失い → 停止 or 探索(後述)
from machine import ADC, Pin, PWM
import time

# --- ラインセンサ ---
L = ADC(26)  # Line_L
C = ADC(27)  # Line_C
R = ADC(28)  # Line_R

# --- モータ(PWM) ---
L_P = PWM(Pin(0))          # Motor_L+
L_M = PWM(Pin(1))          # Motor_L-  ← PWMにする(逆回転で使うため)

R_P = PWM(Pin(2))          # Motor_R+
R_M = PWM(Pin(3))          # Motor_R-  ← PWMにする

L_P.freq(1000)
L_M.freq(1000)
R_P.freq(1000)
R_M.freq(1000)

def motor_left(speed):
    """speed: -65535〜+65535(+前進、-後退、0停止)"""
    if speed > 0:
        L_P.duty_u16(speed)
        L_M.duty_u16(0)
    elif speed < 0:
        L_P.duty_u16(0)
        L_M.duty_u16(-speed)
    else:
        L_P.duty_u16(0)
        L_M.duty_u16(0)

def motor_right(speed):
    """speed: -65535〜+65535(+前進、-後退、0停止)"""
    if speed > 0:
        R_P.duty_u16(speed)
        R_M.duty_u16(0)
    elif speed < 0:
        R_P.duty_u16(0)
        R_M.duty_u16(-speed)
    else:
        R_P.duty_u16(0)
        R_M.duty_u16(0)

def drive_lr(left_speed, right_speed):
    motor_left(left_speed)
    motor_right(right_speed)

def stop():
    drive_lr(0, 0)

# --- パラメータ ---
TH = 20000
BASE = 25000     # 直進速度
SPIN = 22000     # 強旋回の強さ(大きいほど強い)

def is_black(v):
    return v > TH

# --- メインループ ---
while True:
    lv = L.read_u16()
    cv = C.read_u16()
    rv = R.read_u16()

    bl = is_black(lv)
    bc = is_black(cv)
    br = is_black(rv)

    if bc:
        # 中央が黒 → 直進
        drive_lr(BASE, BASE)

    elif bl:
        # 左が黒 → 左へ戻したい → 左後退 + 右前進(強左旋回)
        drive_lr(-SPIN, +SPIN)

    elif br:
        # 右が黒 → 右へ戻したい → 左前進 + 右後退(強右旋回)
        drive_lr(+SPIN, -SPIN)

    else:
        # 見失い → 停止(探索を入れるならここを改良)
        stop()

    time.sleep(0.01)

調整のコツ(ここだけ触ればOK)

  • SPIN を上げると曲がりが強くなる(例:18000 → 30000)
  • 直進が速すぎてオーバーシュートするなら BASE を下げる(例:25000 → 18000)
  • 「中央が黒でも少し蛇行」なら BASESPIN の差を小さくする

たまに止まってしまう対処

このままですと、たまにすべてのセンサが白を判定し、止まってしまいます。
そこで探索モードを加えると安定して動作します。(最後の方向に探索)

  • 最後に左で黒を見た → 左へその場旋回し続ける
  • 最後に右で黒を見た → 右へその場旋回し続ける
  • 最後が中央(直進中) → 直進を短く続ける(惰性で拾えることが多い)
from machine import ADC, Pin, PWM
import time

# --- ラインセンサ ---
L = ADC(26)  # Line_L
C = ADC(27)  # Line_C
R = ADC(28)  # Line_R

# --- モータ(両方向PWM) ---
L_P = PWM(Pin(0))  # Motor_L+
L_M = PWM(Pin(1))  # Motor_L-
R_P = PWM(Pin(2))  # Motor_R+
R_M = PWM(Pin(3))  # Motor_R-

for p in (L_P, L_M, R_P, R_M):
    p.freq(1000)

def motor_left(speed):
    if speed > 0:
        L_P.duty_u16(speed); L_M.duty_u16(0)
    elif speed < 0:
        L_P.duty_u16(0); L_M.duty_u16(-speed)
    else:
        L_P.duty_u16(0); L_M.duty_u16(0)

def motor_right(speed):
    if speed > 0:
        R_P.duty_u16(speed); R_M.duty_u16(0)
    elif speed < 0:
        R_P.duty_u16(0); R_M.duty_u16(-speed)
    else:
        R_P.duty_u16(0); R_M.duty_u16(0)

def drive_lr(ls, rs):
    motor_left(ls)
    motor_right(rs)

# --- パラメータ ---
TH = 20000
BASE = 22000
SPIN = 24000
SEARCH_FORWARD = 15000   # 全部白のとき、最後が中央なら少し前進で拾う

def is_black(v):
    return v > TH

# last_dir: -1=左で見た, 0=中央で見た, +1=右で見た
last_dir = 0

while True:
    lv = L.read_u16()
    cv = C.read_u16()
    rv = R.read_u16()

    bl = is_black(lv)
    bc = is_black(cv)
    br = is_black(rv)

    if bc:
        # 中央が黒 → 直進
        drive_lr(BASE, BASE)
        last_dir = 0

    elif bl:
        # 左が黒 → 強左旋回
        drive_lr(-SPIN, +SPIN)
        last_dir = -1

    elif br:
        # 右が黒 → 強右旋回
        drive_lr(+SPIN, -SPIN)
        last_dir = +1

    else:
        # 全部白(線が隙間に入った/見失い)
        # 止まらず「最後に見えた方向」に探索する
        if last_dir == -1:
            # 左にいた → 左を探す
            drive_lr(-SPIN, +SPIN)
        elif last_dir == +1:
            # 右にいた → 右を探す
            drive_lr(+SPIN, -SPIN)
        else:
            # 直進中に一瞬見失い → 少し前進して拾う
            drive_lr(SEARCH_FORWARD, SEARCH_FORWARD)

    time.sleep(0.01)

次回はこちら

Follow me!

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

PAGE TOP