RaspberryPi Picoでラジコン制作(Rev#4-3)

2025-12-20 raspi python 電子工作 /posts/2025/2025-12-23-raspi-car-5.jpg

4号機製作も大詰めに。IchigoJam版と同等の機能はあっさり作れたので、次は、PicoWのWifi機能を活かした設計にします。

RaspberryPiをWifiのアクセスポイントとしてセットアップし、そこにTCPのソケットをオープンしておきます。PicoW側から接続して、センサーの値を送信します。その応答としてモーターの回転(今回はとりあえずON/OFFのみ)を返すという要領です。

サーバのプログラムは以下のような感じ:

#!/usr/bin/env ruby

require 'socket'

# 計算ロジックファイルを読み込む
logic_file = ARGV[0] || 'logic_default.rb'
require_relative logic_file.sub(/\.rb$/, '')

unless defined?(calculate_outputs)
  puts "Error: calculate_outputs function not found in #{logic_file}"
  exit 1
end

server = TCPServer.new 2000 # Server bind to port 2000

loop do
  client = server.accept    # Wait for a client to connect
  client.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) # クライアント接続も低遅延化

  puts "Client connected: #{client.peeraddr[3]}"

  begin
    # クライアントが接続している間、高速ループを継続
    loop do
      # センサー値2つを受信 (例: "100,200\n" の形式を想定)
      data = client.gets
      break if data.nil? # 接続が切れたらループを抜ける

      inputs = data.strip.split(',').map(&:to_i)

      # センサー値に基づいてモーター速度を計算
      outputs = calculate_outputs(inputs)

      # モーター速度2つを返信 (例: "150,180\n" の形式)
      client.puts outputs.join(',')
    end
  rescue => e
    puts "Error: #{e.message}"
  ensure
    client.close
    puts "Client disconnected"
  end
end

logic_defaults.rb の実装例:

# デフォルトのセンサー値からモーター速度を計算する関数
def calculate_outputs(inputs)
  sensor1, sensor2 = inputs
  motor1 = sensor1 < 50000 ? 1 : 0
  motor2 = sensor2 < 50000 ? 1 : 0
  [motor1, motor2]
end

以下のようにサーバを実行します。引数としてロジックファイルを指定することが可能です。

ruby tcp-server.rb logic_default.rb

PicoWのコード以下のようになります。センサーの値をサーバに送り、応答を受け取ってモーターの回転方向を決定します。

import socket
import time
import network
from machine import Pin, ADC

# WiFi設定
WIFI_SSID = "360-raspi"
WIFI_PASSWORD = "" # Wifiのパスワード
HOST = 'raspi26.local'
PORT = 2000

led = Pin("LED", Pin.OUT)  # Pico W組み込みLED

print("Connecting to WiFi...")

# WiFiに接続
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)

while wlan.isconnected() == False:
    print("connecting to %s.." % (WIFI_SSID))
    # LED点滅
    for _ in range(2):
        led.value(1)
        time.sleep(0.25)
        led.value(0)
        time.sleep(0.25)

wlan_status = wlan.ifconfig()
print("connected! as %s" % (wlan_status[0]))

# センサーとモーター用ピンの初期化
adc1 = ADC(0)
adc2 = ADC(1)
ma1 = Pin(14, Pin.OUT)
ma2 = Pin(15, Pin.OUT)
mb1 = Pin(16, Pin.OUT)
mb2 = Pin(17, Pin.OUT)

# 初期状態
ma1.value(0)
ma2.value(0)
mb1.value(0)
mb2.value(0)

print("Connecting to %s:%d..." % (HOST, PORT))

try:
    # サーバに接続
    sock = socket.socket()
    sock.connect(socket.getaddrinfo(HOST, PORT)[0][-1])

    # 接続成功: LEDを点灯
    led.value(1)
    print("Connected!")

    # メインループ
    while True:
        # アナログ入力を読み取り (0-65535の16bit値)
        sensor1 = adc1.read_u16()
        sensor2 = adc2.read_u16()

        # サーバに送信
        message = f"{sensor1},{sensor2}\n"
        sock.send(message.encode())

        # 応答を受信
        response = sock.recv(128).decode().strip()
        if response:
            motor1_val, motor2_val = map(int, response.split(','))

            # 0:停止, 正: 前進, 負: 後退
            if motor1_val > 0:
                ma1.value(1)
                ma2.value(0)
            elif motor1_val < 0:
                ma1.value(0)
                ma2.value(1)
            else:
                ma1.value(0)
                ma2.value(0)

            if motor2_val > 0:
                mb1.value(1)
                mb2.value(0)
            elif motor2_val < 0:
                mb1.value(0)
                mb2.value(1)
            else:
                mb1.value(0)
                mb2.value(0)

            # デバッグ出力
            print(f"Sensors: {sensor1}, {sensor2} -> Motors: {motor1_val}, {motor2_val}")
        else:
            # 接続が切れた
            print("Connection lost")
            break

        # 短い待機(必要に応じて調整)
        time.sleep(0.01)

except Exception as e:
    # エラー時: LEDを点滅
    print(f"Error: {e}")
    for _ in range(10):
        led.value(1)
        time.sleep(0.2)
        led.value(0)
        time.sleep(0.2)

finally:
    sock.close()
    led.value(0)
    ma1.value(0)
    ma2.value(0)
    mb1.value(0)
    mb2.value(0)
    print("Disconnected")

これらのコードでひとまずは動くのですが、Wifiの接続が安定しない問題が発生しています。しばらく繋がらない場合と、途中で切断されてしまう場合とがあります。引き続き調査していきます。

Gemini(nano banana)を使ってみた
Coderdojo Miyazaki 新規部門構想