★「Wiimoteで学ぶ物理」ですが、まじめに書きためています。 反応が多ければ続きも公開しようかと思いますが、反応ないなら私には何の得もないので適当なソースをその辺において、秘密にしようか思案中です。 (初稿は3月8日ごろに起こしてます) 学生プロジェクトの技術的支援のため、Wiimoteとがっぷりぶつかっております。 赤外線センサも自作したのですが、いまいち座標入力には不十分です。 (絶対位置のキャリブレーション用に使われる、という設計でしょうから) というわけで、加速度センサだけで位置検出ができないか、もしくはIRを参照してもいいから、高速で比較的使える精度の加速度センサによる位置入力ができないか、という課題です。 ネットを探してみますが、意外とこのレベルの物理(大学入学以前のニュートン力学だと思いますが)がスカッと解けているリソースが見つかりません。 (そもそもキーワードがよくわかりませんけど) ※私自身、大学は化学受験だったので、どこまでが「大学受験レベル」かいまいち判ってませんが。 さらに気がつくのは、多くの野武士プログラマが作ってくれているアプリやデモ、ソースコードは、センサからの生値を出力したり、加速度センサを単なる傾斜センサとして使っている例が多くて、これでは「ころころカービー」以上のゲームは作れないような気がします。 もちろんタイミング検出や、方向、強さといった使い方はできますけど、やはり位置がほしい! というわけで、勢い込んでWiimoteのハッキングを通して物理を語ってみようと思います。 以下、読者は物理の専門家や研究者ではありません。 物理が苦手なゲーム開発者とか専門学校の学生さんぐらいを想定しています。 グラフィックスプログラマで数学が得意な人も、制御系などは全然、という人もいるでしょうし。 また私の数学や物理の能力を妄信しないでください(大学の3年まで化学系ですから!)。 物理や数学を学ぶ、一番の近道は、こういったハッキングを妄信するのではなく、このような良い例をうまくつかって、自分で実験しながら解いていくことだと思います。 同様に、このネタを使って高校・大学の物理を楽しく学ぼう!という路線もありだと思います。 もしそういうことを考えている先生がいたらご一報ください。 さて、まずコーディングを始める前に、いろんな勘違いを払拭しておく必要があります。 Wiimoteには加速度センサがついていますが、これはユーザの入力をダイレクトに表現したものではないです。 実際にモニタしてみると、静止状態でも値を出力しています。 これは地球とWiimote間に働く引力、いわゆる「万有引力」です。 ※いつまでも値が出続けるからといって、「無限エネルギー!?」「壊れた?」とか考えないでください。センサーに電流を通して測定しているのです。 もちろん、静止状態でこの値が不安定なら、ちょっと問題がありますが、±1ぐらいのばたつきはあると思ってください。 さらに、一般的な高校の物理(力学)でよくでてくる、質点運動だけで考えるとちょっとはまります。 おまけに、Wiiremoteの自重はどうなるのでしょうか? ユーザの入力とWiiremoteの自重を考えた力学系を「慣性系」と呼びます。 余裕のある人はニュートン力学について復習しておくことをお勧めします。 【参考】 http://ja.wikipedia.org/wiki/運動の第2法則 さて、まずはWiiremoteが質点運動だけで処理できるかどうか実験してみましょう。 まず、普通の置き方(Bボタンを床につける)で、各センサの値を見てみます。 適当なフリーウェアでもコーディングでもして観てみてください。Wiiremoteのナマ出力の加速度(Wx,Wy,Wz)は(127,128,153)あたりを指すと思います。 さらに、姿勢を変えるとこの値は一定ではなくなります。 正立(ストラップを床) (127,101,128) 逆立(IRセンサを床) (127,155,128) 左横 (101,127,129) 右横 (154,128,129) 裏(Aボタンを床) (128,127,104) これは、Wiiremote内に実装された加速度センサ(ADXL330)が、Wiimoteの重心にセットされているわけではない、ということを示しているのではないでしょうか。 もしコントローラの中央、ちょうどHOMEボタンあたりに設置されているなら、左右上下で対象になるような値を示してくれてもいいはずですから。 さて、ここで軽いショックを覚えた人もいるでしょう。 「こんな値なんてオフセットすればいいんだよ!」と乱暴なことを考える人もいるでしょう。 ※それですむならこんなアーティクルを書きません! そもそもこのセンサは各軸256レベルしか値を取得できないので、 ちなみに各値のオフセットからの総和はだいたい28±2ぐらいです。この時点で一定ではないのがつらいところですが、マスプロダクトとしての歩留まりは値段の割りに、高いほうだと思います。 ともかく、ゲームやUIとして使うときには、人間の感覚→エレクトロニクスの実装→ソフトウェアで取得できる値、という流れを意識しないと、いつまでたっても「相対的な値でなんとなく遊ぶ」というレベルから抜けられません。 気を取り直して、上の値から、センサの実装位置と静止状態での値域を割り出してみようと思います。 つまりこれは、傾斜センサとして使う場合の値、ということになります。 前後(Y値差分) 101-128-154 => 前-27 後+26 左右(X値差分) 101-127-154 => 左-26 右+27 表裏(Z値差分) 153-129-104 => 裏-25 表+24 軸によって違いはありますが、だいたい50レベルぐらいの違い、ということでしょうか。加速度センサは各軸256レベルの出力ができるので、静止状態で約20%の値域を使っていることになります。 Wiiremoteを「そーっと持って、傾斜だけ入力する」という用途なら、まあこれで十分にも思えますが、少しでも「外力」が入ると、計算が合わなくなってしまうでしょう。 というわけで次は、「外力」とWiimoteの「自重」を考えてみたいと思います。 慣性系力学では、外力が加わらない限り、「慣性の法則」により、静止している物体は静止を続け、動いている物体はその速度で動き続けます(等速直線運動)。 アイススケートや宇宙空間でWiiremoteを使うわけではないですから、まずは「静止状態」について考えましょう。 F=ma 机の上にWiimoteをトンとおいた状態で、下向きの重力がかかっているわけです。 ユーザーの入力である「外力F」はゼロです。(ユーザの)加速度aがないので、質量mがなんであってもゼロということです。 現段階では「Wiimoteの質量はゼロではない」と考えましょう。 Wiimoteに関する全ての物理がハッキリしたところで、自由落下させて測定すれば、m(質量)が求まるでしょうから! で、物理のおさらいですが、質量と重力加速度、重量(重さにかかる重力)の関係を考えてみましょう。 (重量)=(質量)×(重力加速度) [N] = [kg m/s^2] = [kg] × 9.8[m/s^2] 世代によって、重力加速度を「きゅーてんはち・めーとるまいびょうまいびょう」と覚えた人や、「ニュートン?なにそれ」という人がいると思います。 N(ニュートン)は「1キログラムの質量をもつ物体に1メートル毎秒毎秒の加速度を生じさせる力」をあらわす力の単位です。 「重量」は地球と物体の間に働く力、でもありますので、ニュートンは重量の単位でもあります。 余談ですが1キログラムは地球上では約9.8ニュートン、なお、1Nは1/9.8キログラム(=102グラム)になり、リンゴほぼ1個分だとか。 http://ja.wikipedia.org/wiki/ニュートン だいたいWiimoteの重さもそれぐらいです。 さて、話を「加速度センサで位置検出」に戻しましょう。 微分積分について「忘れてしまった、習ってないです」という人のために、以下の関係をまずおさらいしておきます。 ※あえて微分積分を使わずに、説明しています! F=ma 力[N]=質量[kg]×加速度[m/s^2] a=F/m 加速度[m/s^2]=力[N]÷質量[kg] v=at 速度(velocity)[m/s]=加速度[m/s^2]×時間(sec:秒) x=vt 位置[m]=速度[m/s]×時間[s] つまり、加速度センサから横に動いた距離x[m]を取得したい場合… x= vt = at × t = at^2 (加速度×秒の二乗) …ということになります。 コンピュータで時間を扱う場合、便宜上�t(でるたてぃー、dt)と呼びますが時間になります。 プログラムが1回処理をして、同じ場所に戻ってくる「短い時間」です。 ゲームプログラマなら「60FPS死守!」とか言われているわけで、これは60 frame / seconds、つまり「1秒あたり60回描画」ということで、具体的に計算すると、「0.01666…秒」になります。 ちょっとプログラミングとヒューマンインタフェース科学の話になりますが、SDLやOpenGL、DirectXなどでグラフィックスプログラムを書いた場合、ループはせいぜい、上記のように0.0166secで回ります。しかしこれはゲーム用ヒューマンインタフェースにはちょっと足りない速度だと思います。 経験上、「人間が、かるーく腕を振り回す」というアクションをした場合、だいたい1秒以下で1−2回は腕を振り回せるのではないでしょうか?腕を振り回す半径がだいたい1.5mぐらいとしても、秒速2-3m (3 m/s)は出ているという ことです。 これを0.01666秒の間隔(dt)で取得した場合(このときのdtをサンプリング速度とか時間分解能といいます)、1回の取得あたり50mmは取得する必要があるということです(3000/60=50)。 つまり、1回のループで5cmほどの移動が取得できればよいということになるわけです。 ※上記の計算は、「速度から移動量」を計算しているのとほぼ同じことをやったわけです。 なお実際のWiimoteはマルチスレッドをつかってもっと高速に駆動できるようです。 Windows 上で扱う場合、時間を測定するときはgetTimeGetのようなミリ秒精度のタイマーではなく、QueryPerformanceFrequency、 QueryPerformanceCounterといったマイクロ秒のタイ マーを使ったほうが良いでしょう。 (たぶん続きます)