本文へ移動
触れると、花火が上がる。
Arduino×Pythonのインタラクティブ演出システム。

壁に取り付けたタッチセンサーや圧センサーに人が触れると、Arduinoがその瞬間をPCへ知らせ、Python(Pygame)が花火のアニメーションをプロジェクターへ映し出す——そんな体験型の演出システムの作り方を、配線からコード、実行手順、リハビリ現場での応用アイデアまで一気に解説します。
システム全体の概要

このシステムは、大きく3つのパートで構成されています。それぞれの役割を最初に押さえておくと、後の配線やコードの意味が理解しやすくなります。
ハードウェア部分(Arduino+タッチ/圧センサー):人が壁に触れる・押すと、センサーが反応します。Arduinoがその出力を読み取り、シリアル通信で「TRIGGER」という合図をPCへ送信します。
PC側のプログラム(Python+Pygame+pySerial):PythonプログラムがArduinoからのシリアルデータを監視し、「TRIGGER」を検出すると花火のアニメーションを表示します。初期状態では花火打ち上げ前の背景画像を表示し、トリガーで演出に切り替わります。
表示部分(プロジェクター):PCで生成された映像をプロジェクターへ出力し、来場者に視覚的なインタラクションを提供します。
「センサーで検知 → シリアル通信で合図を送る → PC側で映像を切り替える」という構成は、この記事のテーマである花火演出に限らず、Arduinoを使ったインタラクティブ作品全般に応用できる基本パターンです。同じ構成でPygameのジャンプゲームを作る例を【コピペok】Arduino×Pygameのジャンプゲームのコード徹底解説で、シリアル通信そのものの基礎をArduinoとPCのシリアル通信入門|Serial.printで値を可視化するで、それぞれ詳しく解説しています。
必要なハードウェアと配線
必要な部品
| 部品 | 詳細 |
|---|---|
| Arduino Uno(または互換機) | Arduinoボード本体 |
| TTP223タッチセンサーモジュール | 壁に貼るなどして直接タッチを感知するタイプのセンサー |
| ブレッドボード・ジャンパーワイヤー | 回路の配線用 |
| USBケーブル | ArduinoとPCを接続(データ通信対応のものを使用) |
| (任意)圧センサー・抵抗など | 4章で紹介する圧センサー版を使う場合に必要 |
部品がまだ手元にない場合はArduinoに必要な工具・部品リスト|初心者が最初に揃えるべきものを、ブレッドボードでの配線に不安がある場合はブレッドボード完全攻略ガイドをあわせてご覧ください。
配線例(TTP223タッチセンサー)
Arduinoの5V
ArduinoのGND
Arduinoのデジタルピン(例:ピン2)
壁面など、来場者が自然に触れる位置
配線が確実に行われているか、ジャンパーワイヤーの接続が緩んでいないかを組み立て後に必ず確認してください。緩みは「反応が不安定になる」「まったく反応しない」といった不具合の最も多い原因です。
Arduino側のプログラム(タッチセンサー版)
Arduinoはセンサーの状態を監視し、タッチが検出された際にシリアル通信でPCへ「TRIGGER」メッセージを送信します。
// Arduino用タッチセンサー検出プログラム
const int touchPin = 2; // タッチセンサーのOUTが接続されるピン
bool triggered = false; // 連続検出を防ぐためのフラグ
void setup() {
Serial.begin(9600); // シリアル通信開始(ボーレート:9600bps)
pinMode(touchPin, INPUT); // タッチセンサー用ピンを入力モードに設定
}
void loop() {
int sensorValue = digitalRead(touchPin); // センサーの状態を読み取り
if (sensorValue == HIGH && !triggered) {
// センサーが反応した場合(HIGH)かつ直前にトリガーされていない場合
triggered = true;
Serial.println("TRIGGER"); // シリアル経由で「TRIGGER」メッセージを送信
delay(1000); // 1秒間待機し、連続検出を防止
}
if (sensorValue == LOW) {
// センサーがオフになったらフラグをリセットする
triggered = false;
}
}
triggeredフラグがあることで、指を触れたままにしても連続で何十回も送信されることを防いでいます。指を離してLOWに戻るとフラグがリセットされ、次に触れた時にまた1回だけ送信される、という設計です。この考え方はArduinoのif文の書き方完全ガイドで扱っている条件分岐の基本がそのまま活きています。動作確認の際は、Python側を起動する前にArduino IDEのシリアルモニタで「TRIGGER」が正しいタイミングで表示されるかを確認しておくと、後のトラブルシューティングが楽になります。シリアルモニタの使い方はArduinoのシリアルモニタ活用術|デバッグを爆速にするコツで詳しく解説しています。
圧センサー版との違いと使い分け

花火ではありませんが、上の写真は圧センサーを押すと光が様々な方向へ飛び交うデモの一場面です。→動画で見る:IMG_0766.movこれはリハビリ場面であれば、リーチ訓練などで壁に触れると花火が上がるといったフィードバックとして応用できます。実際の応用例は9章で紹介します。
掲載している動画は圧センサーを利用したものです。以下がそのコードで、タッチセンサー版と同じ「TRIGGER」を送る設計に統一しつつ、圧センサーならではのしきい値判定と連続送信防止のロジックを加えています。
// 圧センサー用のピン設定(A0を使用)
int sensorPin = A0;
int threshold = 500; // 圧力のしきい値(環境に合わせて要調整)
bool triggered = false; // タッチセンサー版と同じ、連続送信を防ぐフラグ
void setup() {
Serial.begin(9600); // シリアル通信を開始
}
void loop() {
int sensorValue = analogRead(sensorPin); // センサーの値を読み取る(0〜1023)
Serial.println(sensorValue); // センサー値をシリアルモニタに出力(デバッグ用)
if (sensorValue > threshold && !triggered) {
// しきい値を超え、かつ直前にトリガーされていない場合だけ送信する
triggered = true;
Serial.println("TRIGGER");
delay(1000); // トリガー後、1秒待機して多重送信を防止
}
if (sensorValue <= threshold) {
// 圧力が下がったらフラグをリセットする
triggered = false;
}
delay(100); // 少し待機してから再度読み取り
}
3章のタッチセンサー版は「押している間はフラグがtrueのまま」という設計でしたが、圧センサーは押す強さが変動しやすいため、しきい値の上下で確実にフラグを更新する形に揃えています。こうしてタッチ版・圧センサー版のどちらでも「1回押すごとに1回だけTRIGGERを送る」という挙動を統一しておくと、Python側のコードを共通のまま使い回せます。
どちらを選べばいいか
| 比較項目 | TTP223タッチセンサー | 圧センサー |
|---|---|---|
| 反応方式 | デジタル(ON/OFFの2値) | アナログ(0〜1023の連続値) |
| 必要な力 | 軽く触れるだけで反応 | ある程度押し込む力が必要 |
| 配線の手軽さ | 非常にシンプル | やや配線・調整が必要 |
| 向いている演出 | 「触れたら」という単純な合図 | 「強く押すほど派手に」など強度に応じた演出 |
アナログ値としきい値判定の基本的な考え方をもう少し掘り下げたい場合は圧センサーを用いた電子工作の基礎編で詳しく解説しています。
Python側のプログラム(Pygame×pySerial)
PC側のPythonプログラムは、次の2つの役割を持ちます。
- シリアル通信の監視:
pySerialライブラリを使用してArduinoからのデータを受信します。 - 花火アニメーションの表示:
Pygameを利用して花火のアニメーションを生成・表示します。初期状態では背景画像(花火打ち上げ前の画像)を表示し、トリガー後に花火演出へ切り替えます。
必要なライブラリのインストール
pip install pyserial pygame
Pythonコード
import serial
import pygame
import sys
import random
import math
# --- シリアル通信の設定 ---
# ※ Windowsの場合は 'COM3' など、Mac/Linuxの場合は '/dev/tty.usbserial-XXXX' や
# '/dev/ttyACM0' などに書き換えてください(調べ方は10章のFAQを参照)。
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("Fireworks Animation")
clock = pygame.time.Clock()
# 背景画像の読み込み(花火打ち上げ前の画像)
# ※ スクリプトと同じフォルダにbackground.jpgを置いてください。
try:
background = pygame.image.load("background.jpg").convert()
background = pygame.transform.scale(background, (WIDTH, HEIGHT))
except Exception:
print("背景画像が見つかりません。背景を黒で表示します。")
background = None
# --- 花火のパーティクルアニメーションのクラス ---
class Firework:
def __init__(self, x, y):
self.particles = []
# 複数のパーティクルを初期化(50個程度)
for _ in range(50):
angle = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 5)
vx = speed * math.cos(angle)
vy = speed * math.sin(angle)
# パーティクルは[x座標, y座標, x速度, y速度, サイズ]
self.particles.append([x, y, vx, vy, random.randint(2, 4)])
def update(self):
for p in self.particles:
p[0] += p[2] # x座標の更新
p[1] += p[3] # y座標の更新
p[3] += 0.1 # 重力効果:y方向の加速度
p[4] -= 0.1 # サイズを徐々に縮小
# サイズが0以下になったパーティクルは除去
self.particles = [p for p in self.particles if p[4] > 0]
def draw(self, surface):
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 = random.randint(100, WIDTH - 100)
y = random.randint(50, HEIGHT // 2)
fireworks.append(Firework(x, 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 fw.particles]
pygame.display.flip()
clock.tick(60) # 60FPSでループ
if __name__ == "__main__":
main()
コードのポイントを分解して読む
SERIAL_PORTの値を変更してください。BAUD_RATEはArduino側のSerial.begin(9600)と必ず一致させます。update()とdraw()で表現しています。パーティクルのサイズが0以下になったら自動的にリストから除去され、メモリや描画負荷が際限なく増え続けないようになっています。Pygameでの本格的な当たり判定やゲーム的な要素をもっと追加したい場合は、Arduino×Pygameの構成をゲーム寄りに発展させた【コピペok】Arduino×Pygameのジャンプゲームのコード徹底解説も参考になります。
組み立てと実行手順
ハードウェアの接続:ArduinoにTTP223タッチセンサー(または圧センサー)を接続し、正しく動作するか確認します。ArduinoとPCをUSBケーブルで接続してください。
Arduinoへの書き込み:Arduino IDEを起動し、3章または4章のスケッチをコピー&ペーストして、正しいシリアルポートに書き込みます。
PC側のPython環境のセットアップ:Python 3.xがインストールされていることを確認し、pip install pyserial pygameを実行します。5章のコードをfireworks.pyという名前で保存し、同じフォルダに背景画像(例:background.jpg)を用意します。
Pythonプログラムの実行:コマンドラインまたはIDEからfireworks.pyを実行し、プロジェクターに映し出されることを確認します。
動作確認:壁に設置したセンサーに実際に触れ、Arduinoが「TRIGGER」メッセージを送信し、Python側で花火アニメーションが発生するかを確認します。必要に応じてセンサー感度や各パラメータを調整してください。
カスタマイズのヒント
- 背景画像の変更:プロジェクションの雰囲気に合わせて
background.jpgをお好みの画像に変更できます。 - 花火アニメーションのパラメータ調整:
Fireworkクラス内のパーティクル数、速度、サイズ、重力加速度などを変更すると、よりリアルまたは派手な演出にカスタマイズできます。 - センサーの種類:TTP223が合わない場合、静電容量式タッチセンサーや抵抗膜式タッチセンサーに置き換え可能です。回路配線とArduinoコードのピン設定を変更するだけで対応できます。
- 複数センサーへの拡張:触れた場所によって花火の色や位置を変えたい場合は、複数のセンサー値をまとめて管理するArduinoの配列の使い方|複数センサーの値を一元管理するの考え方が応用できます。
トラブルシューティング
Arduino側がシリアル通信できない場合:Arduino IDEのシリアルモニタで出力を確認し、接続ポートやボーレートの設定を再確認してください。ポート自体が認識されない場合はArduinoが「ポートが見つからない」時の原因と解決策まとめを参照してください。
Python側でシリアル通信エラーが発生する場合:SERIAL_PORTの値が正しいか、また他のプログラム(Arduino IDEのシリアルモニタなど)が同じポートを同時に使用していないか確認してください。1つのポートは同時に1つのプログラムしか使用できません。
Pygameウィンドウが表示されない場合:Pygameの初期化やディスプレイ設定を見直し、コンソールに表示されているエラーメッセージの有無を確認してください。
センサーの反応がおかしい・シリアルモニタで文字化けする場合:ボーレートの不一致や配線不良が典型的な原因です。Arduinoのシリアルモニタが文字化けする原因と直し方も確認してみてください。
リハビリ・作品応用アイデア
「触れる・押す」という単純な動作をきっかけに、視覚的に華やかなフィードバックが返ってくるこの仕組みは、エンタメ演出だけでなくリハビリテーションの現場でも応用しやすい構成です。
- リーチ動作訓練:壁の少し離れた位置にセンサーを設置し、手を伸ばして触れると花火が上がるようにすることで、単調になりがちなリーチ訓練に達成感のあるフィードバックを加えられます。
- 握力・押す力のトレーニング:4章の圧センサー版を使えば、押す強さに応じて演出の派手さを変えるといった応用もできます。握力を使った類似の取り組みは【コピペok】リアルタイム握力計測ゲームのコードを徹底解説で紹介しています。
- フィードバックシステムとしての発展:センサー入力を即座に視覚・聴覚フィードバックへ変換するという設計思想そのものは、Arduinoを使ったリアルタイムフィードバックシステムの作り方やArduinoとPythonとセンサーを用いたリハビリテーションへの応用で扱っている考え方と共通しています。あわせて読むと、この花火システムをリハビリ用途へ発展させるヒントが得られます。
センサーの種類や設置方法を変えるだけで、同じ「TRIGGER→演出」という骨組みのまま、様々な訓練課題に転用できます。まずは本記事の構成をそのまま組み立てて動作を体感し、そこから目的に合わせて配置やしきい値を調整していくのがおすすめです。
FAQ・まとめ
タッチセンサーと圧センサー、どちらを使えばいいですか?
軽く触れるだけで反応させたい・配線をシンプルにしたい場合はTTP223タッチセンサー、押す強さによって演出を変えたい・厚手のパネル越しに設置したい場合は圧センサーが向いています。どちらも同じ「TRIGGER」という合図をPCへ送る設計なので、Python側のコードは共通のまま使えます。
Pythonのシリアルポート名はどう調べればいいですか?
Windowsはデバイスマネージャーの「ポート(COMとLPT)」欄でCOM番号を確認できます。macOSやLinuxはターミナルでArduinoの接続前後にls /dev/tty.*(Mac)やls /dev/ttyACM*(Linux)を実行し、増えたデバイス名を確認する方法が簡単です。
花火のアニメーションが重い・カクつく場合はどうすればいいですか?
同時に発生する花火の数やパーティクル数を減らす、ウィンドウサイズを小さくする、clock.tick()のFPS値を下げるといった調整が有効です。パーティクルの寿命が尽きたものをリストから確実に除去できているかも確認してください。
このシステムはリハビリの現場でも使えますか?
はい。壁に触れる・押すという単純な動作を視覚的なフィードバックに変換できるため、リーチ動作訓練や上肢のモチベーション向上を目的とした運動課題との相性が良い構成です。実際の応用例は9章で紹介した関連記事も参考にしてください。
このシステムは、①タッチ・圧センサーが検知→②Arduinoが「TRIGGER」をシリアル送信→③Pythonがそれを受信して花火アニメーションを表示、というシンプルな3ステップで動作します。まずは3章・5章のコードをそのまま動かして一連の流れを体感し、その後は背景画像やパーティクルの挙動、センサーの種類を少しずつ自分の用途に合わせて調整していくのがおすすめです。各パラメータの変更や機能追加により、エンタメ演出からリハビリ用途まで、さらに幅広い応用が可能になります。
コメント