0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

sphero boltで遊ぶAdvent Calendar 2024

Day 22

入力: ゲームパッド

Last updated at Posted at 2024-12-21

背景

pygameを使うとゲームパッドの入力を使用できるようです。

ここではdualsenseを使ってSphero boltを操作してみます。

Scratch

Sphero IDEにはゲームパッドを使用する機能は見つかりませんでした。
(もしかするとbluetoothなどでゲームパッドつなげたらドライブモードで使えるのかも)

spheroV2

chatGPT先生と相談しながら作成しました。
12/21と同様に、bluetoothの通信とpygameの並列化が衝突するようだったので、入力内容をqueingして逐次spheroを操作する方式にしました。

import pygame
import sys
import time
import threading
from queue import Queue
from spherov2 import scanner
from spherov2.sphero_edu import SpheroEduAPI
from spherov2.types import Color

# コントローラの入力処理スレッド
def controller_input_process(queue):
    pygame.init()
    pygame.joystick.init()

    if pygame.joystick.get_count() == 0:
        print("No controller connected.")
        sys.exit()

    controller = pygame.joystick.Joystick(0)
    controller.init()
    print(f"Controller connected: {controller.get_name()}")

    # 直前の値を保存する変数
    last_speed = None
    last_heading_change = None
    speed_threshold = 0.1  # スピードの閾値
    heading_threshold = 0.2  # 方向変更の閾値

    try:
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    queue.put(("exit", None))
                    return

            # R2 トリガー値を取得
            r2_trigger = controller.get_axis(5)  # R2 トリガー
            if r2_trigger > speed_threshold:
                speed = r2_trigger * 30  # 最大速度を30に制限
            else:
                speed = 0  # トリガーが離されると停止

            # 左スティックの X 軸値を取得
            left_stick_x = controller.get_axis(0)
            if abs(left_stick_x) > heading_threshold:
                heading_change = left_stick_x * 10  # 感度を調整
            else:
                heading_change = 0

            # 速度と方向のどちらかが閾値を超えていて、直前の値と異なる場合のみキューに追加
            if (abs(speed - (last_speed or 0)) > speed_threshold or
                    abs(heading_change - (last_heading_change or 0)) > heading_threshold):
                queue.put(("move", (speed, heading_change)))
                last_speed = speed
                last_heading_change = heading_change

                # デバッグ用出力
                print(f"R2 (raw): {r2_trigger:.2f}, Speed: {speed:.1f}, Left Stick X: {left_stick_x:.2f}, Heading Change: {heading_change:.2f}")

            # フレームレート調整
            time.sleep(0.03)

    except KeyboardInterrupt:
        print("\nController process interrupted by user.")
        queue.put(("exit", None))
    except Exception as e:
        print(f"An error occurred in controller process: {e}")

# Sphero Bolt の制御スレッド
def sphero_control_process(queue):
    print("Connecting to Sphero Bolt...")
    toy = scanner.find_BOLT()
    if toy is None:
        print("Failed to connect to Sphero Bolt.")
        return

    with SpheroEduAPI(toy) as droid:
        print("Connected to Bolt.")
        droid.reset_aim()
        droid.set_stabilization(True)
        droid.set_front_led(Color(r=5, g=5, b=5))  # LED を白に設定

        speed = 0
        heading = 0

        try:
            while True:
                # キューから操作内容を取得
                if not queue.empty():
                    action, data = queue.get()
                    if action == "exit":
                        break
                    elif action == "move":
                        speed, heading_change = data
                        heading += heading_change
                        heading %= 360  # 方向は 0〜360 度に制限

                        # Sphero Bolt を制御
                        droid.set_speed(int(speed))
                        droid.set_heading(int(heading))

                        # デバッグ用出力
                        print(f"Speed: {speed:.1f}, Heading Change: {heading_change:.2f}, Heading: {heading:.1f}")

                # フレームレート調整
                time.sleep(0.03)

        except KeyboardInterrupt:
            print("\nSphero control process interrupted by user.")
        except Exception as e:
            print(f"An error occurred in sphero control process: {e}")

# メインスクリプト
if __name__ == "__main__":
    queue = Queue()

    # コントローラの入力処理スレッドを起動
    controller_thread = threading.Thread(target=controller_input_process, args=(queue,))
    controller_thread.daemon = True
    controller_thread.start()

    # Sphero Bolt の制御スレッドを起動
    sphero_thread = threading.Thread(target=sphero_control_process, args=(queue,))
    sphero_thread.daemon = True
    sphero_thread.start()

    try:
        # メインスレッドでスレッドが終了するのを待つ
        while controller_thread.is_alive() and sphero_thread.is_alive():
            time.sleep(0.03)
    except KeyboardInterrupt:
        print("\nMain program interrupted by user.")
    finally:
        print("Exiting...")
        pygame.quit()

実行結果はこちらのようになりました。

残念ながらyawの変更がカクカクになったので、要修正です。

終了後に接続したままになったので、終了の工夫が必要です。

git\spherov2.py\spherov2\test>python BoltTest_ds.py
pygame 2.6.1 (SDL 2.28.4, Python 3.12.7)
Hello from the pygame community. https://www.pygame.org/contribute.html
Connecting to Sphero Bolt...
Controller connected: DualSense Wireless Controller
Connected to Bolt.
R2 (raw): 0.51, Speed: 15.4, Left Stick X: 0.04, Heading Change: 0.00
R2 (raw): 1.00, Speed: 30.0, Left Stick X: 0.04, Heading Change: 0.00
R2 (raw): 0.87, Speed: 26.2, Left Stick X: 0.04, Heading Change: 0.00
Speed: 15.4, Heading Change: 0.00, Heading: 0.0
R2 (raw): -0.24, Speed: 0.0, Left Stick X: 0.04, Heading Change: 0.00
Speed: 30.0, Heading Change: 0.00, Heading: 0.0
Speed: 26.2, Heading Change: 0.00, Heading: 0.0
Speed: 0.0, Heading Change: 0.00, Heading: 0.0
R2 (raw): 0.34, Speed: 10.2, Left Stick X: 0.07, Heading Change: 0.00
R2 (raw): 1.00, Speed: 30.0, Left Stick X: 0.06, Heading Change: 0.00
Speed: 10.2, Heading Change: 0.00, Heading: 0.0
...
R2 (raw): -1.00, Speed: 0.0, Left Stick X: -0.89, Heading Change: -8.90
R2 (raw): -1.00, Speed: 0.0, Left Stick X: 0.15, Heading Change: 0.00
Speed: 0.0, Heading Change: -8.90, Heading: 117.4
Speed: 0.0, Heading Change: 0.00, Heading: 117.4

Main program interrupted by user.
Exiting...

まとめ

pygameを使い、dualsenseを使ってspheroを操作をするプログラミングを実験しました。
左右のパッドで左右のタイヤを動かすようにするとか、一つのパッドで前後左右に動かすとか、工夫すると面白そうです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?