跳ねるLEDプログラムを打ち上げる改造してみた
跳ねるLED
— PikaPikaらいと (@KPmilk3) 2021年6月19日
最初に下から上に打ち上げる改造をしてみました。 @KenKenMkIISR さんのコードから初期位置と初期速度のわずか2行を変えただけです。 https://t.co/m6kqpmHzk8 pic.twitter.com/cIbkudjBLo
初めに
@KenKenMkIISRさんが開発された「跳ねるLED」が最高にいかしてます。
github.com GitHubでArduinoとRaspberry Pi Pico用のソースコードを公開されていて、 「本プログラムは公共の場を含め、誰でも自由にご利用いただけます。改変も可能です。ぜひご活用ください。」という太っ腹ぶり。NeoPixelを使った「跳ねるLED」のラズパイPico版を公開しました。Arduino UNO版同様、店頭デモなどで自由にご利用ください。LEDの数や落下速度などは簡単に変更可能です。https://t.co/g2t11VWXQu pic.twitter.com/WtRg7q5Yuj
— KenKenMkIISR (@KenKenMkIISR) 2021年5月1日
お言葉に甘えて自由に使わせていただきました。
このプログラムのすごさ
「跳ねるLED」の魅力、それは何といっても跳ねる動きがとても自然なところです。LEDテープとは思えない自然な動きが感動を呼ぶのだと思います。
いったいどんなプログラムでLEDテープでこの動きを実現しているのか。Arduinoのソースを見てみて驚きました。たった110行。コメントとか抜かせば100行以下です。
肝心の跳ねる動作と思われるところはわずか20行ほどで書かれています。一読しただけでは何がどうなってこうなっているのかわかりませんでした。
分からないなりに、小手先でいじれる部分を改造してこんなものを作ってみたりしました。
この時点ではプログラムの肝を理解してませんでした。そんな時、@KenKenMkIISRさんがツイートで「跳ねるLED」@KenKenMkIISR さんの大傑作です。メビウスの輪にしてやってみました。
— PikaPikaらいと (@KPmilk3) 2021年5月26日
真ん中で二つ折りにしてひねってつないでる構造なのでLEDナンバーを変換するのに苦労しました😅#WS2812B pic.twitter.com/Rdp7uCltIO
この言葉に思わず「跳ねる動きのプログラムはちょっと見ただけでは理解できないものだったので、時間かけて解読したいと思っていました。」と返信したら、最初のブロック落下スタートからてっぺんまで積みあがるところまで、全部でたったこれだけなんです。LED表示更新はset_block関数で別にしていますが。
— KenKenMkIISR (@KenKenMkIISR) 2021年6月13日
興味がある方はぜひソースを読んでみてください。物理の教科書に載せたいようなプログラムになっていますから。
この解説でようやく理解できました。これはまさにエレガントなプログラムです。しびれました。肝心なのはこの2行です。重力加速度を積分したものが落下速度vになり、落下速度を積分したものが高さyとなります。
— KenKenMkIISR (@KenKenMkIISR) 2021年6月13日
v+=GRAVITY
y+=v
v=-v*R1//R2は反射をさせています。
8ビットシフトさせているのは下位8ビットが小数以下となる固定小数点を使用しているためです。
応用
プログラムを理解してみて、同時に奥深さに気付きました。これは加速度を考慮した速度を位置に変換するプログラムなので、パラメータの設定次第でLEDテープで色々な動きを表現できるものであると。
思いついたのが、初期位置を最下点にして初期速度をマイナス方向の値に設定すれば打ち上げの動きになるはずだと。
オリジナルは初期位置はてっぺん、速度はゼロで自由落下の動きをさせています。
//LEDブロック初期位置、初期速度設定 y=0; v=0;
これを初期位置yを最下点に、初期速度をマイナス方向にしてちょうどてっぺんでゼロになるような値にすれば、下から打ち上がり、てっぺんで止まって落ちていく動きになるはず。そう思って色々やってみた結果、このようにすることで出来ました。(LED_NUM 144, BLOCK 8 ,GRAVITY 20 の条件下)
void loop() { int32_t y; //LEDブロック先頭位置(0が一番上) int32_t v; //落下速度 int32_t h = (int32_t)LED_NUM << 8; //最下点位置 int i = 0; do { y = h - 256; v = - (1220 - (40*i++));
y = h - 256; は最下点の一つ上を示しています。こうしないとうまく消す動きができなかったのでこうしました。
v = - (1220 - (40*i++)); この一見謎の計算式は、実測から求めました。ちょうどてっぺんで速度ゼロにするにはLEDが積みあがるたびに初期速度も変えなければいけないのですが、どういう計算をしたらいいか思いつかず、実測で確認することにしました。
通常の跳ねるLEDプログラムを実行してみて最下点の速度をprintすることで実測しました。それで判明したのが最初の速度は1220で一段積みあがるごとに40少なくなるという結果でした。これをマイナスにすれば打ち上げの動作が実現できます。
理論を理解していなくても現実を観測することでなんとかできるというエンジニア的問題解決の実例です。
ただしこの式は LED_NUM 144, BLOCK 8 ,GRAVITY 20 の条件下でしか正しく動きません。その場しのぎのプログラムです。
この一連をツイートしたところ@KenKenMkIISRさんから本質の式をアドバイスいただきました。
「まともに計算するのは…」とはこの式を導けなかった私に対しての優しさでしょう。実際にはv0=sqrt(2gy)という計算をさせても動作速度上全く問題なかったです。重力加速度g、高さyの位置まで届く初速度v0はいくらか求めよ、という問題ですね。
— KenKenMkIISR (@KenKenMkIISR) 2021年6月19日
答えはv0=sqrt(2*g*y)なので、まともに計算するのは馬鹿げてますね。
結局はオリジナルのプログラムからこの2行を変えるだけで打ち上げの動作に改変することができました。 (LEDピン、LED数等の基本設定は除く)
//LEDブロック初期位置、初期速度設定 y = h - 256; v = - (sqrt(2 * GRAVITY * y));
結び
加速度、速度、位置をプログラムに落とし込む基本を学ぶことができました。 本当に教科書に載せるべきプログラムと思います。LEDテープを使って工作している人はぜひ扱ってもらいたいプログラムです。