Arduino×Pythonで握力計測ゲーム|コピペOKのコードと応用

プログラミング・電子工作






【リアルタイム握力計測ゲーム!】Python×Arduino×Pygameで作る握力計測ゲームを徹底解説 | YOFUKASI LAB


目次


ARDUINO × PYTHON × PYGAME / DIY計測ゲーム

【リアルタイム握力計測ゲーム!】

〜Python×Arduino×Pygameで作る握力計測ゲームを徹底解説〜

握力計測ゲームの実機検証の様子
実機で組み上げた握力計測ゲームのテスト風景

01はじめに

みなさん、ゲームの世界ではボタンを押す強さはほとんど関係ありませんよね。しかしもし現実の「握力」を使ってゲームができたらどうでしょう? Arduino を使ってセンサーから取得した「握力」データを Python で受け取り、Pygame を使って可視化・ゲーム化する。想像しただけでワクワクする実験的なプロジェクトです。

本記事では、初心者の方でも理解できるように、シリアル通信(Arduino → Python)や Pygameを使ったPythonゲーム開発の基礎 を中心に、この握力ゲームのコードを徹底的に分解・解説します。理学療法士として臨床現場で握力測定や機能評価に携わってきた筆者が、実機検証をしながらまとめました。

内部リンク・関連記事【究極ガイド】Arduinoメインボード徹底解説 ― 初心者からプロまで、これだけ読めば全てがわかる!


02全体の流れ

  1. Arduinoからのデータ受信(シリアル通信)
    Arduino側でアナログセンサーから得た握力データを数値化し、シリアルポート経由でPythonへ送信します。
  2. Pygameによる画面描画
    Python側で受信した握力データをもとに、リアルタイムでグラフ(棒グラフ)の高さと色を変更します。ゲーム開始前(READY → GO)→ ゲーム中(RUNNING)→ 終了(FINISH)といった状態遷移を管理し、画面上に大きく文字を表示します。
  3. 制限時間管理と終了処理
    制限時間30秒間、グラフを動かし続けます。時間が経過したら「FINISH」を表示し、最終的にはゲームを停止します。

「制限時間内にどれだけ強く握力を出せるか」を測るゲームにするも良し、一定の値を狙うゲームにするも良し。いろいろとアレンジ可能です。本記事のサンプルコードをベースに、自由に発想を広げてみてください。仕組みの土台になっている Arduino-PC間のシリアル通信入門 も合わせて読むと理解が深まります。

03事前準備

ハードウェア

  • Arduino(UNOやNanoなどお好みでOK)
  • 握力センサー
    ここでは最大値を 0〜1023 で計測する単純なセンサーを想定しています。Arduinoのアナログ入力(A0など)に接続し、analogRead() で値を取得できるイメージです。※実際には圧力センサーやグリップセンサーなどを使うと良いでしょう。

内部リンク・関連記事【完全攻略】圧センサーを用いた電子工作の基礎編|圧センサーの種類、配線、読み取り値など徹底解説

握力・圧力のようなアナログ量をリハビリや動作分析に応用したい方は、以下の記事も参考になります。

ソフトウェア

  • Python 3.x
    Python 2.x ではなく3系を使いましょう。
  • Pygame(バージョンは2.0以上推奨)
    pip install pygame でインストールできます。詳細はPygame公式ドキュメントを参照してください。
  • PySerial
    pip install pyserial でインストールできます。API仕様はPySerial公式リファレンスで確認できます。
  • Arduino IDE または PlatformIO など
    Arduino側のスケッチを書き込むために使用。インストールがまだの方はArduino IDEのインストール手順まとめをどうぞ。

04Arduino側のコード(例)

本記事ではArduino側の詳しいコードは割愛しますが、シリアルモニタに対して握力センサーの値をそのまま出力するだけのシンプルなものを想定しています。例えば以下のようなイメージです。

内部リンク・関連記事【2025年版】Arduinoのコードの書き方|基礎から応用まで全てがわかる初心者の手引き

void setup() {
  Serial.begin(9600);
}

void loop() {
  int sensorValue = analogRead(A0); // センサーをA0に繋いでいる場合
  Serial.println(sensorValue);
  delay(50); // 50msごとに送信
}

このように常に握力値を送信しています。今回のPython側コードは、基本的にこの形式で送られてくる値を受信すると仮定します。analogRead()delay() の挙動をもっと詳しく知りたい方は、analogRead / analogWriteの使い方まとめdelay()とmillis()の違い・使い分けもあわせてどうぞ。

05Python側コード(完成版)

以下が今回解説するPythonコードの全体です。なお、後の章で細かく分解解説していきますので、いったんざっと眺めておきましょう。

import serial
import pygame
import sys
import time

# --- シリアル通信の設定 ---
SERIAL_PORT = 'COM3'  # ご利用の環境に合わせて変更してください
BAUD_RATE = 9600

try:
    ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
except Exception as e:
    print(f"シリアルポート {SERIAL_PORT} に接続できませんでした: {e}")
    sys.exit()

# --- Pygame初期設定 ---
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Grip Force Meter Game")
clock = pygame.time.Clock()

# フォント設定
large_font = pygame.font.SysFont("Comic Sans MS", 72)
medium_font = pygame.font.SysFont("Comic Sans MS", 48)
small_font = pygame.font.SysFont("Comic Sans MS", 32)

# ゲーム状態とタイミング
READY_DURATION = 2000   # 2秒
GO_DURATION = 1000      # 1秒
GAME_DURATION = 30000   # 30秒

game_state = "READY"  # "READY", "GO", "RUNNING", "FINISH"
state_start_time = pygame.time.get_ticks()

# センサー(握力)関連
sensor_value = 0        # 現在の握力値(例:0~1023)
MAX_FORCE = 1023        # 握力計の最大値
MAX_THRESHOLD = 900     # 閾値。これ以上なら棒グラフの色を変える

# 棒グラフの設定
BAR_WIDTH = 100
BAR_MAX_HEIGHT = 300
BAR_X = WIDTH // 2 - BAR_WIDTH // 2
BAR_Y_BOTTOM = HEIGHT - 100  # 棒グラフの底位置

# カラー設定
BG_COLOR = (245, 245, 255)   # 明るいパステルブルー
BAR_COLOR_NORMAL = (100, 200, 100)  # グリーン
BAR_COLOR_MAX = (255, 100, 100)     # レッド(閾値超え時)
BAR_BORDER_COLOR = (50, 50, 50)

# 枠の描画関数
def draw_frame(surface):
    pygame.draw.rect(surface, BAR_BORDER_COLOR, (BAR_X-5, BAR_Y_BOTTOM - BAR_MAX_HEIGHT - 5, BAR_WIDTH+10, BAR_MAX_HEIGHT+10), 5)

# ゲーム状態のテキスト描画関数
def draw_state_text(surface, text):
    rendered = large_font.render(text, True, (255, 50, 50))
    surface.blit(rendered, (WIDTH // 2 - rendered.get_width() // 2, HEIGHT // 4 - rendered.get_height() // 2))

# メインループ
def main():
    global game_state, state_start_time, sensor_value

    running = True
    while running:
        dt = clock.tick(60)  # 60FPS
        current_time = pygame.time.get_ticks()

        # イベント処理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        # ゲーム状態の遷移
        if game_state == "READY" and current_time - state_start_time >= READY_DURATION:
            game_state = "GO"
            state_start_time = current_time
        elif game_state == "GO" and current_time - state_start_time >= GO_DURATION:
            game_state = "RUNNING"
            state_start_time = current_time
        elif game_state == "RUNNING" and current_time - state_start_time >= GAME_DURATION:
            game_state = "FINISH"
            state_start_time = current_time

        # センサー値の更新
        if game_state == "RUNNING":
            if ser.in_waiting:
                try:
                    line = ser.readline().decode('utf-8').strip()
                    sensor_value = int(line)
                except Exception:
                    pass  # 読み取りエラーの場合は無視

        # 画面背景の描画
        screen.fill(BG_COLOR)

        # ゲーム状態ごとの描画
        if game_state in ["READY", "GO", "FINISH"]:
            if game_state == "READY":
                draw_state_text(screen, "READY")
            elif game_state == "GO":
                draw_state_text(screen, "GO")
            elif game_state == "FINISH":
                draw_state_text(screen, "FINISH")
        elif game_state == "RUNNING":
            # ゲーム中は、棒グラフをリアルタイムに更新
            pass

        # 棒グラフ描画
        draw_frame(screen)
        bar_height = int((sensor_value / MAX_FORCE) * BAR_MAX_HEIGHT)
        if sensor_value >= MAX_THRESHOLD:
            bar_color = BAR_COLOR_MAX
        else:
            bar_color = BAR_COLOR_NORMAL
        bar_y = BAR_Y_BOTTOM - bar_height
        pygame.draw.rect(screen, bar_color, (BAR_X, bar_y, BAR_WIDTH, bar_height))

        # 棒グラフの数値表示
        value_text = small_font.render(f"{sensor_value}", True, (0, 0, 0))
        screen.blit(value_text, (BAR_X + BAR_WIDTH // 2 - value_text.get_width() // 2, bar_y - 40))

        # ゲームタイマーの表示
        if game_state == "RUNNING":
            remaining_time = max(0, GAME_DURATION - (current_time - state_start_time))
            seconds = remaining_time // 1000
            timer_text = medium_font.render(f"{seconds} sec", True, (50, 50, 255))
            screen.blit(timer_text, (WIDTH - timer_text.get_width() - 20, 20))

        pygame.display.flip()

    pygame.quit()
    ser.close()
    sys.exit()

if __name__ == "__main__":
    main()

06コード分解:シリアル通信の設定

import serial
import pygame
import sys
import time

# --- シリアル通信の設定 ---
SERIAL_PORT = 'COM3'  # ご利用の環境に合わせて変更してください
BAUD_RATE = 9600

try:
    ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
except Exception as e:
    print(f"シリアルポート {SERIAL_PORT} に接続できませんでした: {e}")
    sys.exit()

解説

  1. import serial
    PySerialライブラリをインポート。pip install pyserial でインストールしておきましょう。
  2. SERIAL_PORT
    Arduinoが接続されているポートを指定します。Windowsなら COM3COM4、MacやLinuxなら /dev/tty.usbmodemXXXX/dev/ttyACM0 等に変更が必要です。ポートが見つからない場合はArduinoのポートが認識されない時の対処法を参照してください。
  3. BAUD_RATE
    Arduino側の Serial.begin(9600); に合わせる必要があります。ここでは9600bps。
  4. ser = serial.Serial(...)
    実際にシリアルポートをオープンします。エラーが出る場合(ポートが開けない等)は except で例外処理し、プログラムを終了 (sys.exit()) しています。
  5. timeout=1
    1秒待っても読み取れない場合は処理を先に進める設定。無限に待ち続けないようにするための保険です。

シリアル通信の全パラメータは PySerial公式APIリファレンス に一覧があります。あわせてArduinoシリアルモニタでのデバッグ方法も読んでおくと、通信トラブル時の切り分けがスムーズです。

07コード分解:Pygameの初期設定

# --- Pygame初期設定 ---
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Grip Force Meter Game")
clock = pygame.time.Clock()

解説

  1. pygame.init()
    Pygameを使う際に必ず最初に呼び出す初期化関数。
  2. WIDTH, HEIGHT = 800, 600
    ウィンドウの幅と高さ。ここでは横800px、縦600pxに設定しています。
  3. screen = pygame.display.set_mode((WIDTH, HEIGHT))
    Pygameのメイン画面(Surface)を作成します。詳細はpygame.display公式ドキュメントを参照してください。
  4. pygame.display.set_caption("Grip Force Meter Game")
    ウィンドウのタイトルバーに表示される文字列を設定。
  5. clock = pygame.time.Clock()
    フレームレート管理に使うClockオブジェクトを生成します。後ほど clock.tick(60) を使って「60FPS」を目安に処理を進める形になります(pygame.time公式ドキュメント)。

Pygame以外の選択肢として、レトロゲーム風の表現がしやすいPythonゲームエンジン「Pyxel」の紹介記事Pyxelの入門ガイドも参考になります。

08コード分解:フォントやゲーム状態の管理

# フォント設定(好みのフォント名に変更可能)
large_font = pygame.font.SysFont("Comic Sans MS", 72)
medium_font = pygame.font.SysFont("Comic Sans MS", 48)
small_font = pygame.font.SysFont("Comic Sans MS", 32)

# ゲーム状態とタイミング
READY_DURATION = 2000   # 2秒
GO_DURATION = 1000      # 1秒
GAME_DURATION = 30000   # 30秒

game_state = "READY"  # "READY", "GO", "RUNNING", "FINISH"
state_start_time = pygame.time.get_ticks()

解説

  1. pygame.font.SysFont("Comic Sans MS", サイズ)
    Pygameのフォントを取得します。第一引数はフォント名、第二引数はサイズ。日本語フォントを使いたい場合は MSGothic 等を指定することもできますが、環境によっては文字化けするかもしれません(詳しい仕様はpygame.font公式ドキュメントを参照)。
  2. 状態遷移用の定数
    • READY_DURATION = 2000 → 「READY」状態を2秒間
    • GO_DURATION = 1000 → 「GO」状態を1秒間
    • GAME_DURATION = 30000 → 「RUNNING」状態を30秒間
  3. game_state = "READY"
    初期状態は「READY」に設定。
  4. state_start_time = pygame.time.get_ticks()
    状態が始まった時刻(ミリ秒単位)を保管。経過時間を計算するために使用します。後ほど if current_time - state_start_time >= READY_DURATION のように比較して状態遷移を管理します。

09コード分解:センサー関連の変数

# センサー(握力)関連
sensor_value = 0        # 現在の握力値(例:0~1023)
MAX_FORCE = 1023        # 握力計の最大値(Arduinoの値に合わせる)
MAX_THRESHOLD = 900     # 閾値。これ以上なら棒グラフの色を変える

解説

sensor_value
Arduinoから受け取った値を格納します。0〜1023の範囲を想定。

MAX_FORCE
センサーの最大値を設定。Arduinoの analogRead() は通常は 0〜1023 なので1023に設定しています。

MAX_THRESHOLD
この値以上の握力なら「最大レベル(赤色の棒グラフ)を表示させる」などの演出を入れたいときに使用します。

同じくアナログ入力を扱う例として、ポテンショメータとanalogReadの使い方ジョイスティックのアナログ値取得も同じ考え方で読み替えられます。


10コード分解:棒グラフの設定

# 棒グラフの設定
BAR_WIDTH = 100
BAR_MAX_HEIGHT = 300
BAR_X = WIDTH // 2 - BAR_WIDTH // 2
BAR_Y_BOTTOM = HEIGHT - 100  # 棒グラフの底位置
変数名 意味
BAR_WIDTH 棒グラフの幅(px)
BAR_MAX_HEIGHT 棒グラフの最大高さ(px)
BAR_X 棒グラフ描画の左上X座標
BAR_Y_BOTTOM 棒グラフの底辺のY座標(画面下から100px上)

たとえば握力が MAX_FORCE の時にこの BAR_MAX_HEIGHT(300px)を表示し、0の時は高さ0で描画するイメージです。

11コード分解:カラー設定

# カラー設定(背景や棒グラフ)
BG_COLOR = (245, 245, 255)   # 明るいパステルブルー
BAR_COLOR_NORMAL = (100, 200, 100)  # グリーン
BAR_COLOR_MAX = (255, 100, 100)     # レッド(閾値超え時)
BAR_BORDER_COLOR = (50, 50, 50)
  • RGB のタプル形式で色を指定。
  • PythonやPygameでは (R, G, B) が 0〜255 の範囲で色を表します。

12コード分解:フレーム描画関数

def draw_frame(surface):
    # やわらかいパステルカラーの枠
    pygame.draw.rect(surface, BAR_BORDER_COLOR, (BAR_X-5, BAR_Y_BOTTOM - BAR_MAX_HEIGHT - 5, BAR_WIDTH+10, BAR_MAX_HEIGHT+10), 5)

解説

  • 棒グラフの外枠を描画する関数。
  • 棒グラフより少し大きめの矩形を描画し、枠線の太さを引数の最後(4番目)に指定 (5) しています。図形描画全般の詳細はpygame.draw公式ドキュメントを参照してください。

13コード分解:ゲーム状態テキスト描画関数

def draw_state_text(surface, text):
    rendered = large_font.render(text, True, (255, 50, 50))
    surface.blit(rendered, (WIDTH // 2 - rendered.get_width() // 2, HEIGHT // 4 - rendered.get_height() // 2))

解説

  1. large_font.render(text, True, (255, 50, 50))
    指定した文字列をフォントでレンダリングし、Surface オブジェクトにします。第2引数の True はアンチエイリアスの有効化、第3引数 (255, 50, 50) は文字色を指定。
  2. テキストの中央揃え
    rendered.get_width()rendered.get_height() を使い、画面の中央付近に文字を表示するように計算。ここでは画面横方向は中央揃え、縦方向は画面全体の1/4の位置に表示しています。

14コード分解:メインループの構造

def main():
    global game_state, state_start_time, sensor_value

    running = True
    while running:
        dt = clock.tick(60)  # 60FPS
        current_time = pygame.time.get_ticks()

        # イベント処理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

イベントループ

  • while running: でゲームループを回します。
  • clock.tick(60) は「1秒間に最大60回ループを回す」目安です。Pygameで定番の制御方法。
  • for event in pygame.event.get(): でウィンドウの×ボタンなどのイベントを処理。pygame.QUIT イベントがあれば running = False にしてループを抜ける→ゲーム終了。

15コード分解:ゲーム状態の遷移

if game_state == "READY" and current_time - state_start_time >= READY_DURATION:
    game_state = "GO"
    state_start_time = current_time
elif game_state == "GO" and current_time - state_start_time >= GO_DURATION:
    game_state = "RUNNING"
    state_start_time = current_time
elif game_state == "RUNNING" and current_time - state_start_time >= GAME_DURATION:
    game_state = "FINISH"
    state_start_time = current_time

ここがゲームの流れを管理する重要な部分です。

  • READY → GO → RUNNING → FINISH のように順番に状態が移っていきます。
  • それぞれの状態に応じて表示されるテキストを変えたり、センサー読み取りのON/OFFをコントロールします。
状態 持続時間(ミリ秒) 画面表示 センサー読み取り
READY 2000 「READY」 しない
GO 1000 「GO」 しない(ここではOFF)
RUNNING 30000 握力グラフをリアルタイム更新 する
FINISH 「FINISH」 しない

state_start_time を更新しないと、状態が切り替わった後も同じ経過時間が使われてしまいます。忘れずに切り替えのたびに current_time を代入。


16コード分解:センサー値の更新

if game_state == "RUNNING":
    if ser.in_waiting:
        try:
            line = ser.readline().decode('utf-8').strip()
            sensor_value = int(line)
        except Exception:
            pass  # 読み取りエラーの場合は無視

if game_state == “RUNNING”:
ゲーム中(RUNNING状態)のみセンサー値を更新します。ゲーム前や終了後は値を固定したまま。

ser.in_waiting
受信バッファに読み込めるデータがあるかチェック。1行分のデータが来ていなければ読み取りをスキップ。

line = ser.readline().decode('utf-8').strip()
シリアルから1行読み取って文字列に変換し、両端の空白や改行を削除。

sensor_value = int(line)
数値化して sensor_value に代入。ここでエラーが起きた場合(不正な文字列など)は except Exception で無視するだけにしてあります。int() の詳しい仕様はPython公式ドキュメントを、変数の型変換全般はint・float・stringの違いと変換方法を参照してください。

17コード分解:画面背景と状態別描画

# 画面背景の描画
screen.fill(BG_COLOR)

# ゲーム状態ごとの描画
if game_state in ["READY", "GO", "FINISH"]:
    if game_state == "READY":
        draw_state_text(screen, "READY")
    elif game_state == "GO":
        draw_state_text(screen, "GO")
    elif game_state == "FINISH":
        draw_state_text(screen, "FINISH")
elif game_state == "RUNNING":
    # ゲーム中は、棒グラフをリアルタイムに更新
    pass

screen.fill(BG_COLOR) で画面全体をリセットしてから、各状態に応じた描画をします。

  • READY, GO, FINISH であれば大きな文字をど真ん中に表示。
  • RUNNING は一時的に pass になっていますが、後で棒グラフやタイマーを描画する流れになっています。

18コード分解:棒グラフ描画と数値表示

# 棒グラフの描画
draw_frame(screen)
bar_height = int((sensor_value / MAX_FORCE) * BAR_MAX_HEIGHT)
if sensor_value >= MAX_THRESHOLD:
    bar_color = BAR_COLOR_MAX
else:
    bar_color = BAR_COLOR_NORMAL
bar_y = BAR_Y_BOTTOM - bar_height
pygame.draw.rect(screen, bar_color, (BAR_X, bar_y, BAR_WIDTH, bar_height))

# 棒グラフの数値表示
value_text = small_font.render(f"{sensor_value}", True, (0, 0, 0))
screen.blit(value_text, (BAR_X + BAR_WIDTH // 2 - value_text.get_width() // 2, bar_y - 40))

解説

  1. bar_height = int((sensor_value / MAX_FORCE) * BAR_MAX_HEIGHT)
    握力(0〜1023)を、棒グラフ高さ(0〜300)にスケーリング。仮に sensor_value が 512 のとき、高さは約150px になります。
  2. 色分け(MAX_THRESHOLD)
    sensor_value >= 900 の場合は赤色、それ以外は緑色を指定。これにより握力が900を超えたらバーが赤くなる。
  3. bar_y = BAR_Y_BOTTOM - bar_height
    Pygameの座標系では「上が0、下に行くほど数値が大きい」ため、バーの「下端」は BAR_Y_BOTTOM を固定し、上方向に伸ばすには底辺から高さ分を差し引く形になります。
  4. テキスト表示
    棒グラフの上付近( bar_y - 40 )にセンサーの数値を表示。中央揃えしたいので、 (BAR_X + BAR_WIDTH // 2 - value_text.get_width() // 2, bar_y - 40) のように計算します。

19コード分解:ゲームタイマー

if game_state == "RUNNING":
    remaining_time = max(0, GAME_DURATION - (current_time - state_start_time))
    seconds = remaining_time // 1000
    timer_text = medium_font.render(f"{seconds} sec", True, (50, 50, 255))
    screen.blit(timer_text, (WIDTH - timer_text.get_width() - 20, 20))

解説

remaining_time
残り時間(ミリ秒)。GAME_DURATION(30000)から現在の経過時間を引き、最低でも0に保つ (max(0, ...))。

seconds = remaining_time // 1000
ミリ秒を秒に変換(整数)。

画面の右上付近に表示する形で blit しています。

20コード分解:最後の処理

pygame.display.flip()

フレームバッファを画面に反映。これを呼ばないと画面が更新されません。

pygame.quit()
ser.close()
sys.exit()

ループを抜けた後は、Pygameを終了し、シリアルポートを閉じてからプログラムを終了。

21コード全体の流れまとめ

ここで改めて、ゲームのメインループをフローチャート的にまとめます。

┌───────── Start ─────────┐
│ │
│ game_state = READY │
│ state_start_time = 0 │
│ │
├──────── [Main Loop] ────┤
│ 1) イベント確認 │
│ – もしQUITなら終了 │
│ 2) 状態遷移確認 │
│ – READY → GO → │
│ RUNNING → FINISH │
│ │
│ 3) センサー値受信 │
│ – RUNNING時のみ │
│ │
│ 4) 画面描画 │
│ – 背景クリア │
│ – 状態表示 │
│ – 棒グラフ描画 │
│ – タイマー描画 │
│ │
│ 5) 画面更新 │
│ – pygame.display.flip() │
│ │
├───── ループ継続 or 終了 ─────┤
└──────────────────────────────┘

22実行方法

  1. Arduinoを接続
    シリアルモニタ等で、センサー値が出力されていることを確認(0〜1023)。文字化けが起きる場合はシリアルモニタの文字化け対処法を確認しましょう。
  2. Pythonスクリプトを実行
    コマンドラインで python filename.py (Windowsなら python、Mac/Linuxなら python3)。
  3. Pygameのウィンドウが開く
    最初は「READY」→「GO」→「RUNNING」→「FINISH」と順番に切り替わるアニメーションを確認。
  4. 握力センサーを握る
    「RUNNING」状態の間、センサー値によって棒グラフの高さと色が変化します。閾値以上で赤くなる演出を楽しみましょう。
  5. 終了後
    30秒後(デフォルト設定)に「FINISH」が表示されます。ウィンドウの×ボタンでゲーム終了。

23応用アイデア

  1. 得点表示
    最高値や平均値をゲーム終了時に表示してみる。数値をリストに格納し、最大値や平均を計算できます。
  2. 複数人対戦
    2つのセンサーを用意して左右で競争するなど、同時対戦ゲームも面白いでしょう。無線化したい場合はM5StackでのWi-Fi通信も参考になります。
  3. アニメーション追加
    握力に応じてキャラクターがジャンプしたり、画面が震えたりする演出を追加すると、よりゲーム感がアップします。手を使わないジェスチャー入力に発展させたい場合はMediaPipeによる姿勢・手指認識も面白い方向性です。
  4. センサーデータの記録
    CSVファイルなどにログをとって後でグラフ化するのも良いかもしれません。ArduinoのSDカードへのCSV保存RaspberryPiでのCSV・Excel連携を組み合わせると、握力の推移をリハビリの記録として残すこともできます。
  5. 可変のゲーム時間
    GAME_DURATION をユーザーが設定できるようにすれば、15秒版や1分版など柔軟に試せます。
理学療法士の視点から:握力データをリアルタイムで可視化できる仕組みは、そのままリハビリの評価・フィードバックツールにも応用できます。関連して力(フォース)計測に関する電子工作の基礎や、スマートフォンを使ったTUGテスト計測リハビリ×テクノロジーの取り組みもあわせてご覧ください。

24トラブルシューティング

  1. シリアルポートが開けない
    COM3 などポート番号があっているかを確認。OSによって異なるので要注意。Arduino IDEのシリアルモニタは閉じましたか? 1つのポートを同時に2つのプログラムで使うことはできません。詳しくはポートが見つからない時の対処法を参照。
  2. センサー値が常に0または1023
    Arduinoの配線やセンサーが正しく接続されているか確認。analogRead() のピン番号を間違えている可能性があります。
  3. Pygameウィンドウがフリーズする
    無限ループに陥っていないか。毎フレームで pygame.display.flip() が呼ばれるようになっているか確認。
  4. エラーが出る (ValueErrorなど)
    Arduino側のシリアル出力が想定と違っている可能性あり。strip() したあとに int() 変換が失敗するパターンかもしれません。無駄な文字を送信していないかチェックしましょう。書き込み時のエラーが別途出る場合はavrdudeの書き込みエラー対処法も確認してください。

25まとめ

以上で「PythonとArduinoで作るリアル握力計測ゲーム」の詳細な解説をしました。ポイントは以下の通りです。

  • ArduinoでセンサーデータをSerial.println() し、Pythonで受け取る
    シリアル通信の基本設定はしっかり押さえましょう。
  • Pygameでの状態管理と描画
    READY → GO → RUNNING → FINISH の流れで分かりやすい画面切り替えを実装しています。RUNNING中のみセンサー値を反映し、棒グラフを動的に描画します。
  • 棒グラフの高さや色の制御
    sensor_valueMAX_FORCE で割ってスケーリング。閾値を超えれば色を変化。
  • 初心者に優しい構造
    状態遷移を明確にし、見通し良くゲームを制御。

本記事のサンプルはあくまでも基本形です。「得点の可視化」「ランキング表示」「センサーの校正」など様々なカスタマイズが可能です。アイデア次第で、握力だけでなく、他のアナログセンサーを使ったゲーム開発にも応用できます。

さあ、あなたならではの発想を加えて、オリジナルの握力バトルを盛り上げてみましょう!

  • 「どこまで握力を上げられるか」に挑戦するストイックなゲーム
  • 一定値をキープする必要があるゲーム
  • 連打ならぬ“連続握り”でキャラクターが跳ねたり進んだりするアクションゲーム

などなど、可能性は無限大です。ぜひ、今回の解説をもとに、あなたのアイデアを実現してください!さらに体系的にArduinoを学びたい方はArduino学習ロードマップもぜひ参考にしてください。

以上、Python × Arduino × Pygame で作る握力計測ゲームの超ロング解説でした。ここまで読んでいただきありがとうございます。この記事が、あなたの創造力を刺激して、新しいデバイスゲームの可能性を切り開く一助となれば幸いです。ぜひ楽しみながら、ハードウェア×ソフトウェアの世界にチャレンジしてみてください!


関連記事

特集記事

コメント

この記事へのコメントはありません。

TOP
CLOSE