リハビリ×Arduino|圧力センサーで打ち上げ花火を作る方法【Python連携・配線・コード解説】

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






【応用編】Arduino×Pythonで作る打ち上げ花火演出|ロケット上昇→ひまわり型爆発のコード解説



Arduino × Python Fireworks — Advanced Edition

花火が下から打ち上がり、
ひまわり型に咲き誇る。応用編。

圧センサーが反応すると、花火の玉が画面下部からロケットのように上昇し、一定の高さに達すると黄金角(約137.5°)を利用した螺旋パターンで「ひまわり」のような爆発を見せる——そんな応用編の演出コードを解説します。基本のトリガー方式はそのままに、Python側のアニメーションロジックだけをグレードアップさせる構成なので、既に基本システムを組んだ方はFireworkクラスを差し替えるだけで発展させられます。

対象読者: 基本編を組んだ方・Python中級者 対象ボード: Arduino Uno / Nano互換機 環境: Python 3.x(pySerial・Pygame)
黄金角による螺旋パターンロケット上昇フェーズ再現全コードCOPY対応リハビリ活用例3選

執筆・実機検証:YOFUKASI AI運営者

理学療法士/Arduino・Pythonを使った医療機器制作・学会発表経験あり

01

この記事について|基本編との違い

花火が下から打ち上がり、一定の高さに達すると「ひまわり」のような螺旋パターンで爆発する——これが本記事で扱うコード例です。圧センサーに限らず、タッチセンサーや音センサーなど別のセンサーに置き換えても、同じ仕組みでそのまま実現できます。

Arduinoの圧センサーとブレッドボードを使った配線例

まだ基本編(1点から花火が広がるだけのシンプルな演出)を作っていない場合は、先に【完全ガイド】ArduinoとPythonで実現するインタラクティブ花火演出システムで全体の流れ(センサー検知→シリアル送信→Pygameで演出)を体感しておくのがおすすめです。そうしておくと、この応用編の内容もスムーズに理解できます。

基本編との違いを比較

項目 基本編 応用編(本記事)
花火の発生位置 その場でいきなり出現 画面下部から上昇するロケットフェーズあり
爆発パターン ランダムな方向へ飛び散る 黄金角による螺旋状(ひまわり型)
Arduino側のコード 共通(TRIGGERを送るだけ) 共通(TRIGGERを送るだけ)
難易度 入門 基礎+(クラス設計への理解が必要)
POINT

Arduino側の役割は、基本編・応用編を通じてまったく同じです。しきい値を超えたら「TRIGGER」を送るだけの単純な処理なので、変更する必要はありません。一方でPython側は、TRIGGERを受け取った後の演出ロジックだけが変わります。そのため、基本編のシステムを土台にして、この記事のFireworkクラスだけを差し替えるという発展のさせ方もおすすめです。

SPONSORED
02

Arduino側のコード(圧センサートリガー)

センサー(例:アナログ入力A0)からの値が一定のしきい値(ここでは500を例示)を超えた場合に「TRIGGER」を送信するコードです。なお、基本編で解説した設計にならい、しきい値を下回ったらフラグをリセットする形にしています。そのため、押し続けても連続で大量送信されることはありません。

pressure_trigger.ino
// 圧センサー用のピン設定(A0を使用)
int sensorPin = A0;
int threshold = 500;      // 圧力のしきい値(適宜調整してください)
bool triggered = false;   // 連続送信を防ぐフラグ(基本編と同じ設計)
void setup() {
  Serial.begin(9600);  // シリアル通信を開始
}
void loop() {
  int sensorValue = analogRead(sensorPin);  // センサーの値を読み取る
  Serial.println(sensorValue);              // センサー値をシリアルモニタに出力(デバッグ用)
  if (sensorValue > threshold && !triggered) {
    // しきい値を超え、かつ直前にトリガーされていない場合だけ送信する
    triggered = true;
    Serial.println("TRIGGER");
    delay(1000);  // トリガー後、1秒待機して多重送信を防止
  }
  if (sensorValue <= threshold) {
    // 圧力が下がったらフラグをリセットする
    triggered = false;
  }
  delay(100);  // 少し待機してから再度読み取り
}

アナログ値としきい値判定の基礎をもう一度確認したい場合は圧センサーを用いた電子工作の基礎編を、条件分岐の書き方全般はArduinoのif文の書き方完全ガイドを参照してください。

03

Python側のコード:ロケット上昇→ひまわり型爆発

sunflower_fireworks.py
import serial
import pygame
import sys
import random
import math
# --- シリアル通信の設定 ---
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("Sunflower Fireworks")
clock = pygame.time.Clock()
# 背景画像の読み込み(なければ黒背景)
try:
    background = pygame.image.load("background.jpg").convert()
    background = pygame.transform.scale(background, (WIDTH, HEIGHT))
except Exception:
    print("背景画像が見つかりません。背景は黒になります。")
    background = None
# --- Fireworkクラス(ロケットとひまわり爆発) ---
class Firework:
    def __init__(self, x, target_y):
        self.x = x
        self.y = HEIGHT       # ロケットは画面下部から発射
        self.target_y = target_y  # 爆発する高さ
        self.vy = -8           # 上向きの初速度
        self.exploded = False
        self.particles = []    # 爆発後のパーティクルリスト
    def update(self):
        if not self.exploded:
            # ロケット上昇フェーズ
            self.y += self.vy
            if self.y <= self.target_y:
                self.explode()
        else:
            # 爆発パーティクルの更新
            for p in self.particles:
                p[0] += p[2]   # x座標更新
                p[1] += p[3]   # y座標更新
                p[3] += 0.05   # ゆるやかな重力効果(y方向)
                p[4] -= 0.05   # パーティクルのサイズが徐々に減少
            self.particles = [p for p in self.particles if p[4] > 0]
    def explode(self):
        self.exploded = True
        num_particles = 150  # パーティクル数(調整可能)
        golden_angle = math.radians(137.5)
        # 黄金角を利用したひまわりパターンの生成
        for i in range(num_particles):
            angle = i * golden_angle
            # パーティクルの速度は sqrt(i) に比例(調整可能)
            speed = 3 + (math.sqrt(i) * 0.2)
            vx = speed * math.cos(angle)
            vy = speed * math.sin(angle)
            size = random.randint(4, 8)  # 初期サイズ(調整可能)
            self.particles.append([self.x, self.y, vx, vy, size])
    def draw(self, surface):
        if not self.exploded:
            # 上昇中のロケットを小さな白い円で描画
            pygame.draw.circle(surface, (255, 255, 255), (int(self.x), int(self.y)), 3)
        else:
            # 爆発パーティクルの描画(ランダムな色)
            for p in self.particles:
                color = (255, random.randint(100, 255), random.randint(100, 255))
                pygame.draw.circle(surface, color, (int(p[0]), int(p[1])), int(p[4]))
fireworks = []
def trigger_firework():
    # ランダムなx座標と、爆発する高さ(上半分)を設定
    x = random.randint(100, WIDTH - 100)
    target_y = random.randint(100, HEIGHT // 2)
    fireworks.append(Firework(x, target_y))
def main():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                ser.close()
                pygame.quit()
                sys.exit()
        # 背景描画
        if background:
            screen.blit(background, (0, 0))
        else:
            screen.fill((0, 0, 0))
        # Arduinoからのシリアルデータを監視
        if ser.in_waiting:
            try:
                line = ser.readline().decode('utf-8').strip()
            except UnicodeDecodeError:
                line = ""
            if line == "TRIGGER":
                trigger_firework()
        # 花火の更新と描画
        for fw in fireworks:
            fw.update()
            fw.draw(screen)
        # 完全に終了した花火はリストから除去
        fireworks[:] = [fw for fw in fireworks if (not fw.exploded) or fw.particles]
        pygame.display.flip()
        clock.tick(60)  # 60FPS
if __name__ == "__main__":
    main()
POINT

update()メソッドの分岐に注意してください。ロケット上昇フェーズの判定(if self.y <= self.target_y:)と、爆発後のパーティクル更新(else:)は、上記のコードのようにそれぞれ改行してインデントを揃える必要があります。1行に詰めて書いてしまうと構文エラーになります。そのため、コピー時に改行やインデントが崩れていないか、必ず確認してください。

SPONSORED
04

コードのポイント解説

ロケット上昇と爆発のロジック

self.y += self.vy / if self.y <= self.target_y: self.explode()
ロケットフェーズ:self.yは画面下部から上昇し、target_yに達するとexplode()を呼び出して爆発を開始します。なおvyがマイナスの値になっているのは、Pygameの座標系では画面の下方向がy軸のプラス、上方向がマイナスになるためです。
golden_angle = math.radians(137.5) / angle = i * golden_angle
爆発フェーズ:黄金角を用いて150個のパーティクルを生成します。パーティクルを1つ生成するごとに角度を137.5°ずつずらしていくと、粒同士が重なりにくくなります。その結果、中心から外側へ均等に広がる螺旋状の配置が自然に生まれます。ちなみに速度をsqrt(i)に比例させているのも、外側のパーティクルほど速く・遠くまで飛ばすための工夫です。

トリガー処理とメインループ

def trigger_firework(): ...
trigger_firework()関数:シリアルから「TRIGGER」が受信されると、ランダムな位置と爆発高さで新たな花火(Fireworkインスタンス)を生成し、fireworksリストに追加します。
def main(): ...
main()関数:シリアルデータの監視、背景描画、花火の更新&描画を毎フレーム行い、60FPSでループします。この一連の流れはArduinoとPCのシリアル通信入門|Serial.printで値を可視化するで解説しているシリアル通信の基本パターンそのものです。

以上のコードにより、花火は下部から上昇し、一定の高さでひまわりのような螺旋パターンの爆発を行います。必要に応じて数値(速度、パーティクル数、サイズなど)を調整して、お好みの演出にカスタマイズしてください。

05

背景画像をPCに配置する方法

Pythonスクリプトと背景画像を同じフォルダに配置したフォルダ構成のイメージ

背景画像ファイル(例:background.jpg)をPythonプログラムで使用するには、以下の手順でPC上に配置してください。

1

画像ファイルを準備する:任意の画像編集ソフトやウェブからダウンロードした画像を「background.jpg」などの名前で保存します。

2

プロジェクトフォルダに配置する:Pythonコード(例:sunflower_fireworks.py)が保存されているフォルダと同じディレクトリに、背景画像ファイルをコピーまたは移動します。

3

ファイルパスを確認する:コード内では相対パス("background.jpg")で画像を読み込むようにしているため、背景画像がコードファイルと同じフォルダに存在すれば正しく読み込まれます。別の場所に置きたい場合は、絶対パスに書き換えてください。

フォルダ構成の例は次のようになります。

MyFireworksProject/
├── sunflower_fireworks.py
└── background.jpg
POINT

ちなみに背景画像が見つからない場合でも、コードは自動的に黒背景で動作を続けるようになっています(except節でフォールバックしています)。そのため、まずは画像なしで動作確認し、あとから好きな背景に差し替えるという進め方でも問題ありません。

06

リハビリ・レクリエーション活用アイデア3選

圧センサーを使ったリハビリ・レクリエーション活用のイメージ

アイデア次第で、この仕組みは様々な場面に活かすことができます。

IDEA 01

プロジェクター演出によるレクリエーション

シンプルにプロジェクターなどを使用し、レクリエーションの一環として活用する方法です。たとえば壁などにセンサーを仕掛けることで、前方リーチの訓練にもなります。壁への到達距離をセンサーの設置位置で調整すれば、難易度の異なる複数の訓練課題としても展開できます。

IDEA 02

拍手・音のタイミングで花火を上げる

拍手などの音を拾い、その数値の変化で花火を上げる場合は、タイミングや周囲との協調性を促す練習になります。また、集団でのレクリエーションにも展開しやすく、合図に合わせて花火が上がる一体感を演出できます。

IDEA 03

筋出力の可視化によるフィードバック

鍛えたい部分の力の出力などを数値として拾うことで、視覚的フィードバックとして活用できます。同様の仕組みは【コピペok】リアルタイム握力計測ゲームのコードを徹底解説でも紹介しているので、あわせて参考にしてください。

同じ仕組みを他の訓練に転用する視点

センサー入力を即座に視覚的なフィードバックへ変換するという設計思想は、Arduinoを使ったリアルタイムフィードバックシステムの作り方ArduinoとPythonとセンサーを用いたリハビリテーションへの応用で扱っている考え方と共通しています。つまり、目的とする動作に合わせてセンサーの種類・設置位置・しきい値を調整するだけで、同じ骨組みのまま様々な訓練課題に転用できるということです。

07

カスタマイズのヒント

  • パーティクル数・速度の調整:num_particlesspeedの計算式を変えると、より密度の高い爆発や、ゆったり広がる爆発など、印象を大きく変えられます。
  • 色のバリエーション:現在は(255, random.randint(100, 255), random.randint(100, 255))でR成分を固定し、G・Bをランダムにしています。そのため、白〜桃〜橙系を中心に毎回異なる色合いになります。一方で青系・緑系の花火にしたい場合は、Rの固定値を下げてB成分やG成分を主体にするなど、RGBの組み合わせを変えてみてください。
  • 複数センサーでの打ち分け:複数のセンサーを使い、どのセンサーが反応したかによって花火の色や爆発パターンを変える場合は、Arduinoの配列の使い方|複数センサーの値を一元管理するの考え方が応用できます。
  • ロケットの軌道:vyを固定値ではなく、上昇するにつれて減速するような計算にすると、より実際の打ち上げ花火に近い放物線の軌道になります。
08

トラブルシューティング・FAQ・まとめ

よくあるつまずき

  • 「SyntaxError」が出て実行できない:手元のコードをコピーする際に改行やインデントが崩れていないか確認してください。特に3章で解説したupdate()メソッドの分岐部分は崩れやすい箇所です。
  • 花火が上昇せず、その場で爆発する:target_yが花火の初期位置(画面下部)に近すぎる可能性があります。まずはrandom.randint(100, HEIGHT // 2)の範囲を見直してください。
  • Arduinoからの信号を受信できない:基本編と同様の原因が考えられます。Arduinoが「ポートが見つからない」時の原因と解決策まとめArduinoのシリアルモニタ活用術を参照してください。

コードと画像設定についてのQ&A

背景画像はどのようにPCに入れればいいですか?

background.jpgのような名前で画像を保存し、sunflower_fireworks.pyなどPythonスクリプトと同じフォルダに置くだけです。コード内では相対パスで画像を読み込む設計になっているため、スクリプトと同じディレクトリに置けば自動的に読み込まれます。別の場所に置きたい場合は、読み込み部分のパスを絶対パスに書き換えてください。

黄金角(137.5°)を使うとなぜひまわりのようなパターンになるのですか?

黄金角はひまわりの種やパイナップルの模様など、自然界の螺旋配置によく現れる角度です。パーティクルを生成するたびに角度をこの値だけずらしていくと、粒同士が重なりにくくなります。その結果、中心から外側へ均等に広がる螺旋状のパターンが自然にできあがります。

学習の進め方とセンサー選びのQ&A

前回の基本システムとこの応用編はどちらから作るべきですか?

シリアル通信やPygameの扱いに慣れていない場合は、まず基本のインタラクティブ花火演出システムで全体の流れを体感してから、この応用編でロケット上昇と黄金角爆発のロジックに挑戦することをおすすめします。「TRIGGER」を受け取って花火を表示するという骨組みは共通しているため、基本編のコードを土台にこの記事のFireworkクラスだけ差し替えることもできます。

リハビリで使う場合、どのセンサーを選べばいいですか?

リーチ動作の訓練にはタッチセンサーや圧センサーが、拍手など音を使ったタイミング訓練にはサウンドセンサーが向いています。また、握力や特定の筋出力を可視化したい場合は、圧センサーや筋電センサーが向いています。目的とする動作に合わせて選べば、Arduinoのanalog Read()で読み取れるセンサーであれば基本的にどれでも本記事の仕組みに組み込めます。

まとめ

この応用編では、Arduino側の役割を基本編と変えず「しきい値を超えたらTRIGGERを送る」だけの構成にしました。そのうえで、Python側のFireworkクラスにロケット上昇フェーズと黄金角による爆発パターンを組み込みました。まずはそのまま動かしてひまわり型の花火を体感してみてください。そこからパーティクル数や色、センサーの種類を少しずつ自分の用途に合わせて調整していくのがおすすめです。

ARDUINO × PYTHON SUNFLOWER FIREWORKS — 黄金角がつくる、自然界と同じ美しい螺旋。



関連記事

特集記事

コメント

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

TOP
CLOSE