解説「照らす技術」をRubyで照らす #kanrk09

2026年7月20日

ヘッダー画像: 大津市伝統芸能会館の能舞台に設置したムービングライトから照らされる光

718()に大津市伝統芸能会館開催された関西Ruby会議09にて、「『らす技術』をRubyらす」という登壇させていただきました。今回発表はデモがく、スライドもアニメーションを多用しておりそのまま公開するのがしいです。また、発表中にふれられなかった要素がたくさんあるので、当日用意していたスクリプトをもとに、そのへんもあわせてテキストで紹介したいといます。Markdown形式のスライドはGitHub公開しています。

今回記事もまあまあいので、ルビがらない右上のボタンからすとみやすいかもしれません。

Harucom

さて、今回発表でもノートパソコンは使わず、わたしがつくっているHarucom使って発表しました。HarucomPicoRubyでつくっているさなRubyコンピュータで、モニターとキーボードをつなぐだけでIRBがいて、Rubyでグラフィカルなアプリケーションをかすことができます。

RubyKaigi 2026で発表したときからさらに開発めていて、エディターのきがスムーズになったり音声出力にのっていたノイズがえてクリーンになったり発表中画面やすいように2倍解像度表示できるようになったりCtrl-zでサスペンドできたりCtrl-oでIRBにスクリプトファイルをロードできたり今回あまりふれていないところでもいろいろな改善めています。

また、今回発表のためにPicoRabbit日本語描画もつくってみました。発表使ったブランチpushしているので、このへんをみればどうやるかわかるといます。HarucomBOOTH販売しているので、もしご興味あればってもらえるとうれしいです。

わたしは音楽ライブにいくと、ステージがまれていて、ラインアレイがつながれていて、PAがまれているんですけど、そういう機材をながめながら、開演にむけてテンションをあげていくようなことをやっています。今回のテーマは「」ということで、舞台機材のなかでも普段直接あたることのない照明にスポットライトをあててみたいとおもいました。

こういった舞台照明装置にもソフトウェアが組込まれていて、それぞれが通信することで、遠隔複雑きを実現しています。にすることがあってもその仕組みまで理解したことがないものでも、づいてみればおもしろい発見があるかもしれません。

舞台照明のしくみ

さて、舞台照明では、をだすライトそのもののことを「灯体」とびます。この会場でも後方にいくつかPARライトが設置されていますが、こういった灯体はただの電球なので、くしたりるくしたり、それ自体るさを調整することはできません。

そこで、るさを調整するために、調光器ばれる機械使います。実際照明操作はステージだったり、ライブだと客席後方にブースをんでそこからうので、調光器操作するためにいわゆる「」とばれる調光卓使います。

トライアック調光

さきほどれたように、PARライトに使われているのは普通電球なので、交流電源、つまり一般的な100V電源らせます。そのため、るくしたりくしたりするためには、電球供給する電力調整してあげる必要があります。

このような調光にトライアックとばれる素子使っていたのでトライアック調光ばれます。交流波形一部とすことで、灯体れる実効電力制御して、それによってるくしたりくしたりする仕組みです。

LED照明

最近電球使PARライトではなく、LEDを使った照明使うことがくなりました。LEDだと電球より省電力発熱もおさえられます。また、えられたり、さまざまな形状があったりと、いろいろな機能をもっています。

今日会場にもってきたムービングライトもLED照明です。こういったLED照明くは調光器内蔵しているので、電源をつなぐだけで単体使うことができ、外部から操作可能になっています。

Rubyらす

さて、今回のタイトルは「Rubyらす」ということで、このLED照明をどのようにらせるのか、Ruby使ってみていこうといます。ふだんれることのない、しくみえる照明技術Rubyであれば、なんとなくわかったになれるかもしれません。使っているコードはHarucomだけではなく、ふつうのPicoRubyでもうごくはずなので、Raspberry Pi PicoM5Stackでもせるはずです。

Lチカ

当日発表では時間関係でスキップしてしまったのですが、PicoRubyでLEDをらせるといえばまずはLチカです。PicoRubyでLチカするには picoruby-gpio使います。HarucomのIRBでのスクリプトを実行してみてください。いLEDがチカチカ点滅するはずです(Raspberry Pi Pico場合は1を25にえる必要があります)。

rootfs/kanrk09/led1.rb

require "gpio"

led = GPIO.new(1, GPIO::OUT)
loop do
  led.write 1
  sleep 0.5
  led.write 0
  sleep 0.5
end

コンストラクタの引数には物理ピンとGPIOのモードを設定します。のスクリプトだとGPIOの1ピンを出力モードで初期化します。Harucomには GPIO01 ()と GPIO23 ()のLEDがついています。Raspberry Pi Pico場合GPIO25 です。

出力するには GPIO#write メソッドを使います。 GPIOでは 1むとそのピンをHIGH (電源電圧Raspberry Pi Picoではふつう3.3V) に、 0むとそのピンをLOW (GND = 0V)にします。 以下回路図のようにLEDのアノードがチップの出力ピン、カソードがGNDにつながっているので、出力ピンをHIGHにしてあげると電流れてるようになります。

ちなみに truefalse使わず、10使理由は、出力するか/しないかという論理値しているからではなく、ピンの出力ビットを 1 にするか 0 にするかという処理しているからです。 ほかにも 10使っていればビット演算使って複数ビットをまとめて操作できる利点もあります。

PWM (Pulse Width Modulation)

これでLEDをチカチカさせることができるようになりましたが、これだけだとLEDをるくしたりくしたりすることができません。LEDの場合はさきほどやったチカチカを高速すことで調光実現します。 この方法使われているのがPWMです。PWMでは一定周波数でON/OFFを高速し、ONの時間割合えることでるさを調整します。このONの割合のことをduty比びます。

Rubyスクリプトのようなソフトウェアで高速にチカチカさせるのはしいですが、マイコンチップにはハードウェアPWM機能がついていてPWM制御実現できるようになっています。 PicoRubyでは picoruby-pwm使うことで簡単にハードウェアPWMを利用できます。ループduty比えていくだけで、ふわっとるLEDをつくることができます。

rootfs/kanrk09/led2.rb

require "pwm"

pwm = PWM.new(1, frequency: 1000, duty: 0.0)

loop do
  0.upto(100) do |i| # 0 -> 100
    pwm.duty((i ** 2) / 100.0)
    sleep 0.015
  end
  100.downto(0) do |i| # 100 -> 0
    pwm.duty((i ** 2) / 100.0)
    sleep 0.015
  end
end

調光制御

PWMを使うことでLEDをるくしたりくしたりすることができることはわかりました。実際調光制御では調光卓からたくさんのLED照明操作する必要があります。調光卓照明には距離があるのでなるべくない配線通信実現したいです。また、たくさんの種類灯体わせることで理想照明実現します。メーカーごとに互換性がないとそれぞれの必要になってしまいます。これらの問題解決するため、米国劇場技術協会によって40年前制定されたのがDMX512という通信規格です。

DMX512: Digital Multiplex 512

DMX512では512chの制御信号多重化(Multiplex)してります。成瀬さんの発表でもてきましたが、512chのデジタル信号多重化してるのでDigital Multiplex 512 = DMX512です。この512chのまとまりを1 Universe (宇宙)とびます。

灯体はデイジーチェーンで数珠つなぎに接続し、それぞれの灯体開始アドレスを設定します。それぞれの灯体自分のアドレスから必要なチャンネルぶんのみます。たとえば13chの制御信号使灯体をアドレス1に設定すると1-13chを、灯体はアドレス14に設定し14-26chを使うようにてます。

RS485

DMX512の電気的通信規格にはRS485というバランス接続のシリアル通信規格採用されています。FAなどの工場でよく使われている規格で、おなじような規格だと、RS232Cとかはいたことがあるかもしれません。

RS485ではD+とD-の2つの信号線反転させたデータを送信する差動信号使います。外部ノイズがのったとしても2信号間相殺されるのでノイズに安定した通信ができるため、最大で1200m、32機器接続できます。

UART

RS485のれるデータにはUART使われます。UARTはマイコンどうしのシリアル通信規格としてよく使われるので、電子工作をやっていたらいたことがあるかもしれません。UARTではその通信速度(baud rate)とデータ転送フォーマットを設定できます。DMX512の場合は250kbps、パリティがない8N2の設定使われます。 つまり、1bitのスタートビット、8bitのデータ、2bitのストップビットで通信します。

 1 bit           8 bit             2 bit
|start| b0 b1 b2 b3 b4 b5 b6 b7 |stop|stop|

DMXパケット

DMX512では512chのデータをひとまとめに送信しますが、UARTにはパケットの概念がありません。そこで、DMX512ではBREAKとMABという信号をつくりだすことでパケットの区切りをつくります。

|BREAK|MAB|SC| 1 | 2 | 3 | ... |512|
  • BREAK: 最低88マイクロLOWにする
  • MAB (Mark After Break): 最低8マイクロHIGHにする
  • SC (Start Code): 1byteのスタートコード。調光データは0x00

BREAKで「フレームのおわり」、MABで「フレームのはじまり」という区切りをつくり、スタートコードにけて512のチャンネルデータをります。これをすことで照明操作します。

ムービングライト

今回デモにはSHEHDS LED Spot 80Wというムービングライトを使いました。このムービングライトは10chと13chモードが使えますが、13chの場合パラメーターのてはのようになっています。

ch 1  Pan
ch 2  Pan fine
ch 3  Tilt
ch 4  Tilt fine
ch 5  Speed
ch 6  Dimmer
ch 7  Strobe
ch 8  Color wheel
ch 9  Gobo wheel
ch 10 Focus
ch 11 Prism
ch 12 Motor auto
ch 13 Function

DMXパケットを送信する

今回構成以下のようになっています。

HarucomM5Stack DMX512ユニットはふつうのロジックレベルのUART通信します。M5Stack DMX512ユニットには絶縁型のRS485トランシーバーICがのっていて、XLRケーブルでムービングライトと直接接続できます。

UARTってみる

さきほど説明したとおりDMXのデータはUART通信されるので、picoruby-uart使えばデータを送信できます。スクリプトはのようになります。

rootfs/kanrk09/dmx1.rb

uart = UART.new(unit: :RP2040_UART1,
                txd_pin: 20, rxd_pin: 21,
                baudrate: 250_000, stop_bits: 2)

universe = "\x00" * (512 + 1) # SC + 512ch
universe[3] = 38.chr
universe[14+3] = 38.chr
universe[6] = 100.chr #
universe[14+6] = 100.chr

loop do
  uart.break 1
  uart.write universe
  uart.flush
end

1 universeぶん、つまり512chとスタートコードで513byteの文字列用意し、これを送信するだけです。picoruby-uart では UART#break メソッドを使うことでBREAK信号送信できます。

ただし、この方法ではUART送信がブロッキングになってしまうのでデータをけているほかのことがもできなくなってしまいます。そこでDMAを使ってDMXパケットを送信する picoruby-dmx というmrbgemをつくりました。

picoruby-dmx

picoruby-dmx では、C言語側universeのバッファを確保しておき、タイマーとDMAが40fpsでDMXパケットを送信けます。これによって、RubyはバッファにむだけでDMXを送信することができるようになります。

rootfs/kanrk09/dmx2.rb

require "board/dmx"

dmx = Board::DMX.new
dmx.start
loop do
  t = Machine.board_millis / 1000.0
  [1, 14].each_with_index do |b, i|
    dir = i.zero? ? 1 : -1
    dmx[b]     = 171 + (40 * Math.sin(t * 0.7) * dir).to_i  # Pan (171=正面)
    dmx[b + 2] = 41 + (29 * Math.sin(t * 1.1)).to_i        # Tilt (12〜70)
    dmx[b + 5] = 255
  end
  dmx.keepalive
  sleep_ms 20
end

デッドマンスイッチ

DMXで制御する照明信号停止してもその状態維持する実装になっていることが一般的です。これはにケーブルがけてしまったときに状態維持してくれるという意味では実装ですが、プログラムがハングしてもムービングライトがかりをつけたまままってしまいます。そこで、ループをまわしているDMX#keepalive メソッドをけ、しがまったら自動でブラックアウトするデッドマンスイッチを実装しました。

シーケンサー「序破急

さて、ここまででPicoRubyからDMX512で照明操作することができるようになりました。実際にライブで使うにはより複雑きをプログラミングし、同期させてかす必要があります。そこで、同時えるシーケンサーを実装しました。これを序破急びます。

序破急RubyKaigi 2026でasonasさんが発表していたstrudel-rbをもとにしており、スクリプトで照明のパターンを記述でき、REPL演奏することができます。

たとえば、4つちにあわせてらせるならのようなかんじです。

fixture :s1, "shehds_80w_led_spot_light", mode: "13ch", address: 1
fixture :s2, "shehds_80w_led_spot_light", mode: "13ch", address: 14

tempo 120

track(:strobe) { dmx(:s1).dimmer("[1 0]*4").color("<red blue green>") }
track(:wash)   { dmx(:s2).dimmer(sine.slow(2)).pan(sine.range(0.3, 0.7).slow(8)) }
track(:drums)  { sound("bd*4, ~ sd ~ sd, hh*8") }

灯体定義にはOpen Fixture LibraryJSON使っていて、dimmerpanといったパラメーターをチャンネルにマッピングできるようになっています。

QOA: Quite OK Audio

さて、本来のデモはシーケンサーでおわりだったのですが、出囃子桃井はるこさんの「ルミカ」という素敵使わせていただいたので、せっかくならもうすこしくみなさんにいてもらいたいといました。

内部生成するドラム音源以外にサンプリングえるようにQOAとWAVの機能Harucomにつくっていました。そこで、このQOAデコーダーを使い、をまるっと再生できるような機能序破急組込みました。

QOAは整数演算だけでデコードできる超軽量のオーディオフォーマットです。リファレンス実装はC言語で400くらいしかありません。HarucomFlashは2MBほどの領域があったので、ここをフルに使って1のみに編集した「ルミカ」をぎゅうぎゅうにみました。

照明プログラムは序破急機能使い、ビートにあわせてらせたり、イントロ、Aメロ、Bメロ、サビでそれぞれ再生パターンが変化するようになっています。サビではムービングライトのプリズムも使い、能楽堂全体らせるようにしたのですが、うまくサビのタイミングできまってかったです。

スクリプトをみると小節ごとにパターンをえている様子がよくわかるといます。

たとえば、以下配列照明きを定義していて、のはじまりから11小節がイントロ、そのの23小節がAメロ/Bメロ、そのあとの39がサビからラストにかけてのきになっています。

rootfs/data/johakyu/lumica.rb#L62-L91

pan_left = [
  [hold(0.667), 11],
  [sine.range(0.60, 0.73).slow(8).segment(16), 23],
  [sine.range(0.60, 0.73).slow(2).segment(16), 39],
  [hold(0.667), 1],
  [rest, 286],
]

pan_right = [
  [hold(0.667), 11],
  [sine.range(0.73, 0.60).slow(8).segment(16), 23],
  [sine.range(0.73, 0.60).slow(2).segment(16), 39],
  [hold(0.667), 1],
  [rest, 286],
]

tilt_path = [
  [hold(0.141), 11],
  [hold(0.141), 23],
  [cosine.range(0.0, 0.3).slow(2).segment(16), 39],
  [hold(0.141), 1],
  [rest, 286],
]

prism_path = [
  [hold(0), 34],
  [hold(180), 39],
  [hold(0), 1],
  [rest, 286],
]

さいごにスポットライトがスクリーンをくところは、trackのミュートをえてライブでかしていました。ちょっとれてしまったけど、こういうのをリアルタイムにできるのが序破急のよさです。

まとめ

舞台照明はふだんの生活れることがない世界ですが、こういったよくわからないものについていろいろとしているときって一番しいですよね。今回発表では能舞台らせるっていう、これまでにない体験ができました。なにかよくわからないけど、なんかしそうにしているのがわったらいいな、とっています。今週末土曜日、725には大阪桃井はるこさんのワンマンライブがあるのでまた関西予定です!