本文へ移動
花火が下から打ち上がり、
ひまわり型に咲き誇る。応用編。
圧センサーが反応すると、花火の玉が画面下部からロケットのように上昇し、一定の高さに達すると黄金角(約137.5°)を利用した螺旋パターンで「ひまわり」のような爆発を見せる——そんな応用編の演出コードを解説します。基本のトリガー方式はそのままに、Python側のアニメーションロジックだけをグレードアップさせる構成なので、既に基本システムを組んだ方はFireworkクラスを差し替えるだけで発展させられます。
この記事について|基本編との違い
花火が下から打ち上がり、一定の高さに達すると「ひまわり」のような螺旋パターンで爆発する——これが本記事で扱うコード例です。圧センサーに限らず、タッチセンサーや音センサーなど別のセンサーに置き換えても、同じ仕組みでそのまま実現できます。

まだ基本編(1点から花火が広がるだけのシンプルな演出)を作っていない場合は、先に【完全ガイド】ArduinoとPythonで実現するインタラクティブ花火演出システムで全体の流れ(センサー検知→シリアル送信→Pygameで演出)を体感しておくのがおすすめです。そうしておくと、この応用編の内容もスムーズに理解できます。
基本編との違いを比較
| 項目 | 基本編 | 応用編(本記事) |
|---|---|---|
| 花火の発生位置 | その場でいきなり出現 | 画面下部から上昇するロケットフェーズあり |
| 爆発パターン | ランダムな方向へ飛び散る | 黄金角による螺旋状(ひまわり型) |
| Arduino側のコード | 共通(TRIGGERを送るだけ) | 共通(TRIGGERを送るだけ) |
| 難易度 | 入門 | 基礎+(クラス設計への理解が必要) |
Arduino側の役割は、基本編・応用編を通じてまったく同じです。しきい値を超えたら「TRIGGER」を送るだけの単純な処理なので、変更する必要はありません。一方でPython側は、TRIGGERを受け取った後の演出ロジックだけが変わります。そのため、基本編のシステムを土台にして、この記事のFireworkクラスだけを差し替えるという発展のさせ方もおすすめです。
Arduino側のコード(圧センサートリガー)
センサー(例:アナログ入力A0)からの値が一定のしきい値(ここでは500を例示)を超えた場合に「TRIGGER」を送信するコードです。なお、基本編で解説した設計にならい、しきい値を下回ったらフラグをリセットする形にしています。そのため、押し続けても連続で大量送信されることはありません。
// 圧センサー用のピン設定(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文の書き方完全ガイドを参照してください。
Python側のコード:ロケット上昇→ひまわり型爆発
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()
update()メソッドの分岐に注意してください。ロケット上昇フェーズの判定(if self.y <= self.target_y:)と、爆発後のパーティクル更新(else:)は、上記のコードのようにそれぞれ改行してインデントを揃える必要があります。1行に詰めて書いてしまうと構文エラーになります。そのため、コピー時に改行やインデントが崩れていないか、必ず確認してください。
コードのポイント解説
ロケット上昇と爆発のロジック
self.yは画面下部から上昇し、target_yに達するとexplode()を呼び出して爆発を開始します。なおvyがマイナスの値になっているのは、Pygameの座標系では画面の下方向がy軸のプラス、上方向がマイナスになるためです。sqrt(i)に比例させているのも、外側のパーティクルほど速く・遠くまで飛ばすための工夫です。トリガー処理とメインループ
fireworksリストに追加します。以上のコードにより、花火は下部から上昇し、一定の高さでひまわりのような螺旋パターンの爆発を行います。必要に応じて数値(速度、パーティクル数、サイズなど)を調整して、お好みの演出にカスタマイズしてください。
背景画像をPCに配置する方法

背景画像ファイル(例:background.jpg)をPythonプログラムで使用するには、以下の手順でPC上に配置してください。
画像ファイルを準備する:任意の画像編集ソフトやウェブからダウンロードした画像を「background.jpg」などの名前で保存します。
プロジェクトフォルダに配置する:Pythonコード(例:sunflower_fireworks.py)が保存されているフォルダと同じディレクトリに、背景画像ファイルをコピーまたは移動します。
ファイルパスを確認する:コード内では相対パス("background.jpg")で画像を読み込むようにしているため、背景画像がコードファイルと同じフォルダに存在すれば正しく読み込まれます。別の場所に置きたい場合は、絶対パスに書き換えてください。
フォルダ構成の例は次のようになります。
├── sunflower_fireworks.py
└── background.jpg
ちなみに背景画像が見つからない場合でも、コードは自動的に黒背景で動作を続けるようになっています(except節でフォールバックしています)。そのため、まずは画像なしで動作確認し、あとから好きな背景に差し替えるという進め方でも問題ありません。
リハビリ・レクリエーション活用アイデア3選

アイデア次第で、この仕組みは様々な場面に活かすことができます。
プロジェクター演出によるレクリエーション
シンプルにプロジェクターなどを使用し、レクリエーションの一環として活用する方法です。たとえば壁などにセンサーを仕掛けることで、前方リーチの訓練にもなります。壁への到達距離をセンサーの設置位置で調整すれば、難易度の異なる複数の訓練課題としても展開できます。
拍手・音のタイミングで花火を上げる
拍手などの音を拾い、その数値の変化で花火を上げる場合は、タイミングや周囲との協調性を促す練習になります。また、集団でのレクリエーションにも展開しやすく、合図に合わせて花火が上がる一体感を演出できます。
筋出力の可視化によるフィードバック
鍛えたい部分の力の出力などを数値として拾うことで、視覚的フィードバックとして活用できます。同様の仕組みは【コピペok】リアルタイム握力計測ゲームのコードを徹底解説でも紹介しているので、あわせて参考にしてください。
同じ仕組みを他の訓練に転用する視点
センサー入力を即座に視覚的なフィードバックへ変換するという設計思想は、Arduinoを使ったリアルタイムフィードバックシステムの作り方やArduinoとPythonとセンサーを用いたリハビリテーションへの応用で扱っている考え方と共通しています。つまり、目的とする動作に合わせてセンサーの種類・設置位置・しきい値を調整するだけで、同じ骨組みのまま様々な訓練課題に転用できるということです。
カスタマイズのヒント
- パーティクル数・速度の調整:
num_particlesやspeedの計算式を変えると、より密度の高い爆発や、ゆったり広がる爆発など、印象を大きく変えられます。 - 色のバリエーション:現在は
(255, random.randint(100, 255), random.randint(100, 255))でR成分を固定し、G・Bをランダムにしています。そのため、白〜桃〜橙系を中心に毎回異なる色合いになります。一方で青系・緑系の花火にしたい場合は、Rの固定値を下げてB成分やG成分を主体にするなど、RGBの組み合わせを変えてみてください。 - 複数センサーでの打ち分け:複数のセンサーを使い、どのセンサーが反応したかによって花火の色や爆発パターンを変える場合は、Arduinoの配列の使い方|複数センサーの値を一元管理するの考え方が応用できます。
- ロケットの軌道:
vyを固定値ではなく、上昇するにつれて減速するような計算にすると、より実際の打ち上げ花火に近い放物線の軌道になります。
トラブルシューティング・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クラスにロケット上昇フェーズと黄金角による爆発パターンを組み込みました。まずはそのまま動かしてひまわり型の花火を体感してみてください。そこからパーティクル数や色、センサーの種類を少しずつ自分の用途に合わせて調整していくのがおすすめです。
コメント