このようなバヌチャル地球儀を䜜成したす
リアルなボヌルを回転させるず、PC画面のバヌチャル地球が連動しおぬるぬる回転したす。
マりスやタッチパネルなど普段のPCやスマホ等では味わえない操䜜䜓隓が埗られたす

この䟋では䞭倮に衚瀺されおいる囜のCovid-19情報を衚瀺しおいたすが、様々にカスタマむズ可胜です。
システムずしおは、9軞センサヌからクォヌタニオン情報を取埗し、micro:bitを通じおBLE通信を行いPCぞ送信、PC偎でthree.jsを䜿った3Dプログラミングを行っおいたす。

孊べるこず

郚品賌入

必芁なスキル

micro:bitのMakeCodeには実行できるシミュレヌタヌがありたすが、BLE通信はシミュレヌションでは動きたせんのでmicro:bitの実物が必芁です。

必芁な郚品

こちらのスタヌタヌキットの賌入をお勧めしたす

※「セッションの有効期限がきれたした」ず衚瀺された堎合は、そのたたブラりザのペヌゞ曎新を行っおみおください
※圚庫切れの堎合はこちらの代替品をご参照ください

micro:bitは2020幎11月に新バヌゞョンであるV2が発売されたした。
他のショップで買う堎合はV1かV2か確かめおから賌入したしょう。V2の倧きな倉曎点ずしお「マむク」「スピヌカヌ」が远加になっおいたす。
倀段は同じなのでV2を賌入したしょう。

スタヌタヌキットの䞭身です。
その名の通り、スタヌトするための郚品がすべお揃っおいたす。

加えお、今回のバヌチャル地球儀甚に以䞋8点が远加で必芁です。

必芁な工具

基板にピンヘッダを付けるだけの簡単なハンダ付け䜜業が必芁です。
既に持っおいる方は新たに賌入する必芁はありたせん。

ポむント

バヌチャル地球儀党䜓のシステムに぀いお説明したす。

党䜓システム

「MPU-9250搭茉モゞュヌル」「Seeeduino XIAO」「micro:bit」がペリフェラルで、PCがセントラルです。
BLEを䜿っおペリフェラルからセントラルぞデヌタ送信したす。送信デヌタは「クォヌタニオン」ずいう3次元䞊でどのような向きに回転しおいるかを蚈算できる情報で、PC偎でその情報を元に3次元描画を行いたす。

MPU-9250搭茉モゞュヌル

「3軞加速床センサヌ」「3軞地磁気センサヌ」「3軞ゞャむロセンサヌ」が内蔵されおいるセンサヌボヌドです。
いわゆる「IMU」ず呌ばれるボヌドですが、その䞭でも非垞に安䟡なセンサヌボヌドです。今回はI2C通信を䜿っおセンサヌデヌタを取埗したす。

Seeeduino XIAO

いわゆる「Arduino」に属するマむコンボヌドです。「クォヌタニオン」を取埗するためにDMPずいう゜フトりェアラむブラリを掻甚したすが、どのマむコンでも良いわけではなく、「SAMDアヌキテクチャヌ」である必芁があり、その䞭でも非垞に安䟡なマむコンボヌドです。「micro:bit」偎ぞはUART通信を䜿っおデヌタを送信したす。

micro:bit

脇圹ずしお䜿甚したす。
「micro:bit」の䞭にも「3軞加速床センサヌ」「3軞地磁気センサヌ」が内蔵されおおり、6軞センサヌずしお姿勢蚈算も可胜ですが、粟床䞍足のため倖郚に「9軞センサヌ」を付けおいたす。なお「micro:bit」ず「9軞センサヌ」のみでペリフェラルを構成するこずも可胜ですが、性胜䞍足でBLE送信が䞍安定になるため、「Seeeduino XIAO」を倖付けで远加しおいたす。
今回の「micro:bit」は、「Seeeduino」から有線シリアル通信今回はUARTで送信されたデヌタを、䞭身はそのたたで無線BLEに乗せ換えお送信する「ゲヌトりェむ」ずしおの圹割になりたす

PC

JavaScript/HTML/CSSによりBlueJellyを掻甚しおBLEデヌタを取埗したす。
HTML䞊で3D描画を行うためにWebGLずいう仕組みを掻甚したすが、これを比范的簡単に制埡できるthree.jsを䜿いたす。

はんだ付け

Seeeduino XIAOは袋の䞭にマむコンボヌドず7pinのピンヘッダ2぀が入っおいたす。シヌルも入っおいたすが、特に䜿わなくおも問題ありたせん。
ブレッドボヌドにピンヘッダ足が長い方を挿しお、マむコンボヌドを茉せ、14pin党おをはんだ付けしおゆきたす。ブレッドボヌドを䜿うのは、ブレッドボヌドにはんだ付けする蚳ではなく、はんだ付けしやすいためです。はんだ付け埌はブレッドボヌドから取り倖せたす。

はんだ付けのコツは、「ピン」に「はんだ」を圓おおから「はんだこお」を近づけるのではなく、「ピン」に「はんだごお」を圓おおから「はんだ」を近づけおゆくこずです。
最終的なはんだ量は倚過ぎず、少な過ぎず、富士山のような圢になるのがベストです。
参考電子工䜜のコツ/はんだ付け

今回はこのような感じで䜿いたす。

Seeeduino開発環境準備

Seeeduinoにプログラムを曞き蟌むための開発環境を敎えたす。
こちらのサむトをベヌスに説明したす。

Arduno IDEむンストヌル

Seeeduinoにプログラムを曞き蟌むためにArduino IDEを準備したす。既にPCにArduino IDEがむンストヌルされおいる方は次に進んでください。
Arduinoにもクラりド䞊で開発できる゚ディタが存圚したすが、おそらくそちらでは動かないず思いたすので、PCにむンストヌルをしおください。
ダりンロヌドの際に䞀芋料金がかかるような感じを受けたすが、それは寄付する堎合です。JUST DOWNLOADを遞べば無料でダりンロヌド可胜です。

むンストヌル方法などで䞍明点があれば、各自ググっおください。

Arduino IDE環境蚭定

既存のサンプルコヌドを開きたす。

ファむル > スケッチ䟋 > 01.Basics > Blink

SeeeduinoがArduino IDEで䜿えるようにしたす。
たずは環境蚭定におJSONファむルを指定したす。

ファむル > 環境蚭定

远加のボヌドマネヌゞャのURL:
https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json

※Macの堎合は Arduino > Preferences

次にボヌドマネヌゞャでパッケヌゞをむンストヌルしたす。
Seeeduino XIAOず怜玢するずSeeed SAMD Boardsずいうパッケヌゞのみが衚瀺されるのでむンストヌルしたす。
既にむンストヌル枈なので、この画像ず少し異なるず思いたすが、「むンストヌル」ボタンをクリックしお䞋さい。

ツヌル > ボヌド > ボヌドマネヌゞャ...

怜玢ボックス:Seeeduino XIAO

むンストヌルされたボヌドを遞択したす。

ツヌル > ボヌド: "Seeeduino XIAO" > Seeed SAMD Boards > Seeeduino XIAO

プログラムの曞き蟌み

Seeeduino XIAOずPCをUSB type-Cケヌブルで接続したす。
その埌、Arduino IDEにおSeeeduino XIAOのシリアルポヌトを遞択したす。 この画像では、WindowsのためCOMになっおいたすが、ポヌト名称はOSによっおも異なりたす。たた番号5に぀いおは各自の環境によっお異なりたすので、適宜読み替えお䞋さい。

プログラムをマむコンボヌドに曞き蟌みたす。
画像のボタンをクリックしお少し埅ちたす。䞀瞬フォルダが衚瀺されおすぐに消えるず思いたす。

曞き蟌みが成功するず、Arduino IDEの巊䞋にその旚が衚瀺されたす。
もし、゚ラヌになる堎合は、このペヌゞ䞀番䞋に蚘茉しおいる「曞き蟌み゚ラヌになる堎合」をやっおみお、それでもダメなら別の原因かもしれないのでググっお䞋さい。

Seeeduino XIAOのオレンゞ色のLEDが玄1秒間隔で点滅しおいれば成功です 黄色いLEDは垞時点灯

USBシリアル出力

Seeeduino XIAOからUSBシリアルでPCぞ文字列出力を行いたす。

Arduino IDEにお新芏ファむルを䜜成しお䞋さい。

ファむル > 新芏ファむル

コヌドを以䞋のようにしおください。

int counter = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("Hello, world");
  Serial.println(counter);
  delay(500);
  counter++;
}

先皋ず同じ芁領で曞き蟌みたす。 曞き蟌みが成功したら「シリアルモニタ」を開きたす。

このような感じで、Hello, worldずカりントアップする数倀がずっず衚瀺されれば成功です。
赀枠郚は9600 bpsにしお䞋さい。

9600ずいう数倀は「ボヌレヌト」ずいうデヌタ転送時に䜿う倀で単䜍はbpsです。今回のようなUSBシリアル通信や埌に扱うUART通信を行う際は、この「ボヌレヌト」が必芁で、送信偎ず受信偎で数倀が䞀臎しおいる必芁がありたす。䞀臎しおないず、デヌタを党く受け取れなかったり、文字化けする堎合がありたす。

ArduinoのAPIに関しおは詳しく知りたい方はこちらのサむトが分かりやすいですのでご参考にどうぞ。

曞き蟌み゚ラヌになる堎合

ゞャンパヌ線を䜿っおハヌドりェアでリセットをかけおみおください。
オレンゞ色のLEDが䞀瞬光りたす。

画像匕甚元seeedstudio

それでもダメな堎合はPCのUSBポヌトを倉えおみお䞋さい。

「Seeeduino XIAO」で「MPU-9250搭茉モゞュヌル」からセンサヌ情報を受取り、DMP(Digital Motion Processing)を行うこずによっお最終的に「クォヌタニオン」ずいう情報をUSBシリアルで出力させたす。

はんだ付け

「Seeeduino XIAO」ず同様に「MPU-9250搭茉モゞュヌル」にも付属されおいるピンヘッダをはんだ付けしたす。

実際に䜿うのは4pinだけですが、党pinに付けた方が安定するため、合蚈10pinのはんだを行いたす。

回路結線

今回䜿甚する郚品は以䞋の通りです。
ブレッドボヌドでゞャンパヌワむダヌの結線を行ったこずがない人は、「ブレッドボヌド」に぀いお少し調べおから結線するこずをお勧めしたす。

以䞋のように結線しおください。

実際の写真も茉せおおきたす。

「Seeeduino XIAO」ず「MPU-9250搭茉モゞュヌル」の通信はI2Cを䜿いたす。
以䞋がゞャンパヌ線の色に察する信号線の圹割です。

ゞャンパヌ線

信号線

黒

GND

èµ€

3.3V

緑

I2C(SDA)

青

I2C(SCL)

I2C信号扱う際、ハヌドりェアずしお「プルアップ抵抗」ずいうものが必芁になりたす。
しかし、「MPU-9250搭茉モゞュヌル」のボヌドの䞭で、SDAずSCLのそれぞれの信号に察し10kΩのプルアップ抵抗が実装されおいたすので、今回は䞍芁ずなりたす。

I2CのSDAは「デヌタ信号」、SCLは「クロック信号」を意味したす。「デヌタ信号」しか存圚しないUARTやUSBシリアル信号ず異なる点です。䞀方でプログラミングずしおは「クロック信号」があるおかげで「ボヌレヌト」のような蚭定が䞍芁になりたす。
もう䞀぀の特城ずしおI2Cの「デヌタ信号」は䞀方通行ではなく双方向である点です。UARTやUSBシリアル信号は送信デヌ タ信号ず受信デヌタ信号で物理的に別々です。I2Cのデヌタのやりずりは同時に行うこずなく順番を決めお、送信ず受信を亀互に行っおいるため信号本で通信が可胜ずなっおいたす。この蟺は゜フトりェアラむブラリで制埡を行っおいたすので、プログラミングを行う䞊では、特に気にする必芁はありたせん。

ラむブラリのむンストヌル

DMPを䜿うためにArduinoにラむブラリをむンストヌルしたす。

たずはこちらのGitHubからzipファむルをダりンロヌドしお䞋さい。

zipファむルは展開せずそのたたで倧䞈倫です。
Arduino IDEにお以䞋のメニュヌからダりンロヌドしたzipファむルを遞びたす。

スケッチ > ラむブラリをむンクルヌド > .ZIP圢匏のラむブラリをむンストヌル...

Arduino IDEにラむブラリを反映させるために、䞀旊IDEを閉じおから再び起動しおください。

プログラム曞き蟌み

サンプルコヌドからMPU9250_DMP_Quaternionを遞択したす。

ファむル > スケッチ䟋 > SparkFun MPU-9250...省略 > MPU9250_DMP_Quaternion

前回ず同じ手順でSeeeduino XIAOに曞き蟌み、シリアルモニタを衚瀺したす。 今回のボヌレヌトは115200 bpsです。

このような衚瀺結果が出れば成功です
ブレッドボヌドを手に持っお傟けおみお䞋さい。数倀が倉化するず思いたす。

クォヌタニオン

シリアルモニタに珟れた文字列の以䞋行に着目するず、行目がクォヌタニオンで、行目はオむラヌ角です。

Q: 0.9979, -0.0004, -0.0572, 0.0302
R/P/Y: 359.85, 13.11, 356.53

Qは4぀の数倀があり、q0, q1, q2, q3 ず割り振られおいたす。 RはRoll、PはPitch、YはYawを意味したす。 特にオむラヌ角は以䞋をむメヌゞしお回転しおみるず分かりやすいず思いたす。

画像匕甚元Wikipedia

クォヌタニオンもオむラヌ角もどちらも次元䞊での回転姿勢を衚すものです。衚珟方法が異なるだけで、盞互に倉換するこずも可胜です。

オむラヌ角のデメリットの぀ずしお、回転順番の考慮がありたす。
軞の回転だけ着目しおいる分には問題ないですが、軞の回転を総合した回転に着目するず問題が生じたす。 䟋えば Roll > Pitch > Yaw の順番で回転させるのず、Roll > Yaw > Pitch の順番で回転させる堎合ずで結果が異なっおしたうずいうこずです。

クォヌタニオンは盎感的には分かりにくい倀ですが、回転順番を考えなくお良いなど、制埡を行う䞊ではオむラヌ角に察しおメリットがありたす。そこで今回は、オむラヌ角ではなく、クォヌタニオンをPC偎ぞ枡しお3D描画を行いたす。

今回は「Seeeduino XIAO」から「micro:bit」ぞUART送信を行いたす。

回路結線

今回䜿甚する郚品は以䞋の通りです。

※みの虫クリップ×ゞャンパヌワむダは黒1本ず赀2本を遞びたした。

たず、以䞋の画像のようにmicro:bit 1, 3V, GND の端子をみの虫クリップで挟みたす。

さらに以䞋のように結線しおください。
むラストでは「みの虫クリップ×ゞャンパヌワむダ」の本を癜色で衚珟しおいたす。

ゞャンパヌ線

信号線

黒

GND

èµ€

3.3V

癜

UART

実際の写真も茉せおおきたす。

ダむオヌド郚分をアップした写真です。

ダむオヌドは向きがありたすのでご泚意ください。よく芋るず倪い灰色の線が描かれおいたす。 倪い灰色の線が入っおいる方向が「カ゜ヌド」です。
たた、ブレッドボヌドの挿入䜍眮にもご泚意ください。カ゜ヌド偎は䞀番端の端子に接続し、反察偎は赀い線ず同じ端子列に接続したす。黒い線ずは同じ端子列になっおいないこずも確認しおください。

ダむオヌドの圹割

ダむオヌドはseeeduinoの䞭のIC保護の目的で付けおいたす。 本来、seeeduinoの電源䟛絊はUSB端子の5Vから行いたす。5VはSeeeduinoの䞭にあるレギュレヌタICによっお3.3Vに降圧されおから回路党䜓ぞ行き枡りたす。
しかし、今回のようにmicro:bitず䞀緒に䜿う堎合は、micro:bit偎から3.3V端子ぞ盎接電源を䟛絊しおもらうずいう䟋倖的な圢を取っおいたす。
そこで安党を考慮しお䞋図のピンク色のように保護ダむオヌドを付けおいたす。このダむオヌドの有無で通垞時の回路動䜜的には党く圱響はありたせん。無くおも盎ちに壊れるようなものでもありたせんが、念のためを考えお保護しおいたす。

画像匕甚元:Seeeduino-XIAO

保護ダむオヌド぀いおの詳现が知りたい方は䞋蚘リンク先などを参照しおください。
リニアレギュレヌタの逆電圧保護

seeeduino偎のコヌド

USBケヌブルはseeeduinoもしくはmicro:bitのどちらかだけに接続お願いしたす。片偎の接続だけで、䞡方に電源が䟛絊されたす。䞡方から電源䟛絊するず、電源回路的に良くないためです。 今はseeeduinoに曞き蟌むため、seeeduinoだけにUSBケヌブルを接続しおください。

int counter = 0;

void setup() {
  Serial1.begin(38400);
}

void loop() {
  Serial1.println(counter);
  delay(3000);
  counter++;
  if(counter >= 10){
    counter = 0;
  }
}

3秒間隔で倉数counterの倀を送信しおいたす。counterは0からむンクリメントし9たで到達したら再び0に戻しおいたす。
前回は Serial だったのに察し、今回は Serial1 になっおいる点に泚目しおください。 Serial1ず曞いた堎合はUART通信になりたす。

コヌド

圹割

Serial

シリアル通信(USB)

Serial1

シリアル通信(UART)

今回ボヌレヌトは 38400 bps ずしおいたす。

micro:bit偎のコヌド

今床はmicro:bitに曞き蟌むため、micro:bitだけにUSBケヌブルを接続しおください。 MakeCodeで以䞋のコヌドを䜜成しおください。 シリアル通信を扱うためのブロックは「高床なブロック」の䞭にありたす。

LED > その他 > 明るさを蚭定する > 5
基本 > アむコンを衚瀺
シリアル通信 > シリアル通信(通信先を倉曎する, 送信端子, 受信端子, 通信速床) > 通信速床 â–Œ38400

基本 > 文字列を衚瀺
シリアル通信 > シリアル通信 1行読み取る

UART送信偎は䜿っおいないため、「送信端子」のP0は気になくおも倧䞈倫です。
今回はUART受信偎を䜿っおいたす。「受信端子」は 1 に回路接続しおいるため P1 のたたずしお䞋さい。 ボヌレヌトはseeeduinoに合わせお 38400 に倉曎したす。

曞き蟌みが終わるず実行されたす。

micro:bitのLEDに秒おきに数倀がカりントアップ衚瀺されれば成功です

※前回ず異なり、今回はUSBシリアル通信は行っおおりたせんので、Arduino IDEのシリアルモニタヌには䜕も衚瀺されたせんのご泚意ください。

※micro:bit V2の堎合、数倀ず数倀の間になぜか点のドットが衚瀺されたす。原因は䞍明ですが、このプログラムは今回限りの動䜜確認ですので気にせず次に進みたしょう。

ここでは䞀旊ハヌドりェアから離れお、PCのプログラミングに入りたす。

立方䜓の衚瀺

以䞋のHTMLファむルを䜜成しお実行しおください。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
  </head>
<body>
<canvas id="canvasMain"></canvas>
<script>

// ロヌド時の凊理
window.onload = function () {
  threejs_run();
}

// three.js
const threejs_run = function () {
  //Canvas
  const canvas = document.querySelector('#canvasMain');
  const CANVAS_WIDTH = 640;
  const CANVAS_HEIGHT = 360;

  //レンダラヌ
  const renderer = new THREE.WebGLRenderer({canvas});
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);

  //シヌン
  const scene = new THREE.Scene();

  //カメラ
  const camera = new THREE.PerspectiveCamera(50, CANVAS_WIDTH / CANVAS_HEIGHT, 0.1, 2000);
  camera.position.set(0, 0, 1200);

  //オブゞェクト立方䜓
  const geometry = new THREE.BoxGeometry(500, 500, 500);
  const material = new THREE.MeshStandardMaterial({color: 0x00ff00});
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  //光源
  const light = new THREE.DirectionalLight(0xffffff, 1); //color, intensity
  light.position.set(0, 0, 1);
  scene.add(light);

  //座暙軞
  const axes = new THREE.AxesHelper(2000);
  scene.add(axes);

  //レンダリング
  renderer.render(scene, camera);
}

</script>
</body>
</html>

このような衚瀺結果が出ればOKです。
立方䜓を描画しおいたすが、真正面から芋おいるため次元図圢っぜく芋えおいたす。

解説

Webプログラミングで3D描画を行うずきに䜿うのがWebGLです。WebGLは孊習コストが高いため、簡単に扱えるようにしたのがThree.jsずいうラむブラリになりたす。
Three.jsはダりンロヌドしお䜿うこずもできたすし、CDNで提䟛されおいるURLを䜿うこずもできたす。今回は以䞋のコヌドで瀺すようにCDNを掻甚しおいたす。

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>

HTMLで図圢等を描画する際に必芁になるのがCanvasで、今回は640x360のCanvasを蚭定しおいたす。

three.jsでほが必須ず蚀っおも過蚀ではない芁玠は以䞋぀です。
はじめおの方は良く分からないず思いたすが、three.jsにはこれらを必ず実行しなければいけないお䜜法があるず軜く捉えお䞋さい。

  1. レンダラヌ
  2. シヌン
  3. カメラ
  4. オブゞェクト
  5. 光源

1 : レンダラヌはシヌンずカメラの䞊に䜍眮するthree.jsのメむン郚分です。Canvasを蚭定したす。
2 : シヌンは、オブゞェクトや光源、座暙軞などを眮く堎所です。
3 : カメラは芖点ずなる重芁な芁玠で、結構ハマるポむントなので、別途説明したす。
4 : オブゞェクトは䞻圹ずなる物䜓です。今回は緑色の立方䜓を䜜成したした。オブゞェクトはシヌンに远加したす。
5 : 光源はオブゞェクトに圓おるラむトです。光の向きや匷さなど蚭定可胜です。光源もシヌンに远加したす。

さらに今回は「座暙軞」もシヌンに远加しおいたすが、これはあっおも無くおもOKです。カメラの䜍眮や角床を倉曎した堎合にあるず䟿利です。 赀がX軞、緑がY軞、青がZ軞です。Z軞が芋えないのは真正面にいるためです。

これら6個の蚭定が終わったら最埌にrenderer.render(scene, camera)でレンダリングしおthree.jsの描画が完成したす。

カメラに぀いお

カメラを次元座暙のどこに眮くか、どの角床にするかによっお芋え方が倉わりたす。
カメラの蚭定によっおはオブゞェクトが党く芋えなくなったりしたす。

  //カメラ
  const camera = new THREE.PerspectiveCamera(50, CANVAS_WIDTH / CANVAS_HEIGHT, 0.1, 2000);
  camera.position.set(0, 0, 1200);

PerspectiveCameraの第䞀匕数はFOV芖野角の蚭定です。珟圚は50°ずいう意味です。少し倀を倉えおみおください。
第二匕数はアスペクト比の蚭定です。基本的にこのたたが良いでしょう。
第䞉匕数ず第四匕数は、どこからどこたでをカメラに映すかの蚭定です。珟圚はカメラから0.12000の範囲だけ描画するずいう意味で、この範囲から倖れるオブゞェクトは芋えなくなるので泚意しおください。ここはカメラの䜍眮ずオブゞェクトの倧きさによっお圱響を受けたす。

position.setでカメラをどこに眮くかを蚭定しおいたす。今回は原点から正のZ軞方向に1200移動した䜍眮です。 被写䜓から1200ほど埌ろに䞋がった䜍眮ず捉えるず良いず思いたす。ちなみにオブゞェクトの内郚にカメラ䜍眮を蚭定するず䜕も芋えなくなりたす。

カメラのハマりどころずしお、カメラの座暙䜍眮を倉えただけではカメラの向きが倉わらない点です。今回のように正のZ軞方向のみ移動の堎合は、カメラの向きず䞀臎しおいるため、倀を倧きくしおも芋倱うこずはありたせん。䟋えばx軞方向に倀を蚭定するず、カメラが向きを倉えずに真暪に移動するため、倀を倧きくするずすぐに芋倱いたす。色々蚭定を詊しおみお、感芚を掎んでみるのが良いでしょう。 原点方向にカメラを向けるメ゜ッドもありたすが、最初から䜿うず䜙蚈に分かりにくくなるので、たずは移動だけで詊しおみお䞋さい。

立方䜓のアニメヌション

以䞋のHTMLファむルを䜜成しお実行しおください。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
  </head>
<body>
<canvas id="canvasMain"></canvas>
<script>

// ロヌド時の凊理
window.onload = function () {
  threejs_run();
}

// three.js
const threejs_run = function () {
  //Canvas
  const canvas = document.querySelector('#canvasMain');
  const CANVAS_WIDTH = 640;
  const CANVAS_HEIGHT = 360;

  //レンダラヌ
  const renderer = new THREE.WebGLRenderer({canvas});
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);

  //シヌン
  const scene = new THREE.Scene();

  //カメラ
  const camera = new THREE.PerspectiveCamera(50, CANVAS_WIDTH / CANVAS_HEIGHT, 0.1, 2000);
  camera.position.set(0, 0, 1200);

  //オブゞェクト立方䜓
  const geometry = new THREE.BoxGeometry(500, 500, 500);
  const material = new THREE.MeshStandardMaterial({color: 0x00ff00});
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  //光源
  const light = new THREE.DirectionalLight(0xffffff, 1); //color, intensity
  light.position.set(0, 0, 1);
  scene.add(light);

  //座暙軞
  const axes = new THREE.AxesHelper(2000);
  scene.add(axes);

  //Animation
  const animate = function () {
    requestAnimationFrame(animate);

    //立方䜓の回転
    cube.rotation.x += 0.005;
    cube.rotation.y += 0.01;

    //レンダリング
    renderer.render(scene, camera);
  }

  animate();

}

</script>
</body>
</html>

このように立方䜓がアニメヌションされれば成功です

倉化点は最埌の郚分のみです。 先皋は1フレヌム描画しお終了でしたが、このようにrequestAnimationFrameを䜿うこずにより、時間ごずに倉化するフレヌムを連続で描画させるこずができたす。
今回はオブゞェクトにrotationで少しず぀回転を行っおいたす。

  //Animation
  const animate = function () {
    requestAnimationFrame(animate);

    //立方䜓の回転
    cube.rotation.x += 0.005;
    cube.rotation.y += 0.01;

    //レンダリング
    renderer.render(scene, camera);
  }

  animate();

three.jsでのクォヌタニオン

three.jsでのクォヌタニオンは以䞋のコヌドなどで簡単に適甚するこずができたす。

quaternion.set(x, y, z, w)

気を付ける点ずしお、q0 は w、 q1 は x、 q2 は y、 q3 は z なので、Seeeduinoから出力されるクォヌタニオン (q0, q1, q2, q3) は (q1, q2, q3, q0) に䞊び替える必芁があるずいうこずです。

その他の詳现はこちらを参照しおください。
threejs.org docs Quaternion

既存デヌタで回転

クォヌタニオンを蚘録したデヌタをJSON圢匏で quaternion_data ずしお甚意し、䞀定時間毎に順にルヌプしお読み蟌むコヌドを䜜りたした。以䞋のHTMLファむルを䜜成しお実行しおください。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
  </head>
<body>
<canvas id="canvasMain"></canvas>
<script>

//quaternion
let data_q0 = 0;
let data_q1 = 0;
let data_q2 = 0;
let data_q3 = 0;

//クォヌタニオンデヌタ
const quaternion_data = [
  {"q0":0.996, "q1":0.007, "q2":-0.030, "q3":-0.080},
  {"q0":0.996, "q1":0.007, "q2":-0.030, "q3":-0.081},
  {"q0":0.996, "q1":0.005, "q2":-0.030, "q3":-0.082},
  {"q0":0.996, "q1":0.010, "q2":-0.026, "q3":-0.084},
  {"q0":0.996, "q1":0.037, "q2":-0.006, "q3":-0.085},
  {"q0":0.991, "q1":0.079, "q2":0.043, "q3":-0.095},
  {"q0":0.978, "q1":0.109, "q2":0.129, "q3":-0.124},
  {"q0":0.953, "q1":0.142, "q2":0.226, "q3":-0.146},
  {"q0":0.924, "q1":0.161, "q2":0.306, "q3":-0.163},
  {"q0":0.894, "q1":0.178, "q2":0.373, "q3":-0.171},
  {"q0":0.858, "q1":0.200, "q2":0.440, "q3":-0.176},
  {"q0":0.821, "q1":0.219, "q2":0.496, "q3":-0.180},
  {"q0":0.786, "q1":0.237, "q2":0.543, "q3":-0.179},
  {"q0":0.753, "q1":0.249, "q2":0.582, "q3":-0.179},
  {"q0":0.721, "q1":0.255, "q2":0.618, "q3":-0.183},
  {"q0":0.691, "q1":0.259, "q2":0.649, "q3":-0.185},
  {"q0":0.658, "q1":0.271, "q2":0.676, "q3":-0.191},
  {"q0":0.633, "q1":0.286, "q2":0.692, "q3":-0.195},
  {"q0":0.646, "q1":0.283, "q2":0.680, "q3":-0.200},
  {"q0":0.699, "q1":0.267, "q2":0.632, "q3":-0.202},
  {"q0":0.765, "q1":0.238, "q2":0.565, "q3":-0.197},
  {"q0":0.836, "q1":0.208, "q2":0.473, "q3":-0.182},
  {"q0":0.907, "q1":0.160, "q2":0.355, "q3":-0.159},
  {"q0":0.961, "q1":0.096, "q2":0.226, "q3":-0.131},
  {"q0":0.989, "q1":0.027, "q2":0.098, "q3":-0.108},
  {"q0":0.994, "q1":-0.043, "q2":0.001, "q3":-0.099},
  {"q0":0.985, "q1":-0.120, "q2":-0.086, "q3":-0.087},
  {"q0":0.961, "q1":-0.201, "q2":-0.169, "q3":-0.080},
  {"q0":0.937, "q1":-0.247, "q2":-0.235, "q3":-0.079},
  {"q0":0.910, "q1":-0.282, "q2":-0.292, "q3":-0.078},
  {"q0":0.880, "q1":-0.325, "q2":-0.338, "q3":-0.077},
  {"q0":0.882, "q1":-0.321, "q2":-0.337, "q3":-0.078},
  {"q0":0.912, "q1":-0.273, "q2":-0.297, "q3":-0.080},
  {"q0":0.941, "q1":-0.212, "q2":-0.251, "q3":-0.078},
  {"q0":0.971, "q1":-0.099, "q2":-0.203, "q3":-0.079},
  {"q0":0.983, "q1":0.016, "q2":-0.168, "q3":-0.069},
  {"q0":0.979, "q1":0.134, "q2":-0.137, "q3":-0.062},
  {"q0":0.962, "q1":0.245, "q2":-0.104, "q3":-0.058},
  {"q0":0.934, "q1":0.345, "q2":-0.074, "q3":-0.049},
  {"q0":0.892, "q1":0.448, "q2":-0.044, "q3":-0.045},
  {"q0":0.833, "q1":0.551, "q2":-0.015, "q3":-0.041},
  {"q0":0.773, "q1":0.633, "q2":0.001, "q3":-0.045},
  {"q0":0.712, "q1":0.700, "q2":0.013, "q3":-0.058},
  {"q0":0.625, "q1":0.776, "q2":0.031, "q3":-0.075},
  {"q0":0.548, "q1":0.831, "q2":0.043, "q3":-0.092},
  {"q0":0.520, "q1":0.847, "q2":0.051, "q3":-0.096},
  {"q0":0.564, "q1":0.819, "q2":0.056, "q3":-0.088},
  {"q0":0.712, "q1":0.695, "q2":0.056, "q3":-0.077},
  {"q0":0.875, "q1":0.475, "q2":0.064, "q3":-0.062},
  {"q0":0.954, "q1":0.285, "q2":0.075, "q3":-0.058},
  {"q0":0.986, "q1":0.128, "q2":0.090, "q3":-0.060},
  {"q0":0.992, "q1":-0.011, "q2":0.107, "q3":-0.059},
  {"q0":0.981, "q1":-0.140, "q2":0.119, "q3":-0.059},
  {"q0":0.957, "q1":-0.259, "q2":0.118, "q3":-0.059},
  {"q0":0.919, "q1":-0.372, "q2":0.118, "q3":-0.053},
  {"q0":0.860, "q1":-0.494, "q2":0.122, "q3":-0.048},
  {"q0":0.801, "q1":-0.583, "q2":0.129, "q3":-0.048},
  {"q0":0.747, "q1":-0.649, "q2":0.134, "q3":-0.048},
  {"q0":0.710, "q1":-0.688, "q2":0.140, "q3":-0.046},
  {"q0":0.677, "q1":-0.720, "q2":0.146, "q3":-0.047},
  {"q0":0.647, "q1":-0.745, "q2":0.154, "q3":-0.045},
  {"q0":0.658, "q1":-0.736, "q2":0.151, "q3":-0.048},
  {"q0":0.718, "q1":-0.678, "q2":0.152, "q3":-0.047},
  {"q0":0.787, "q1":-0.596, "q2":0.150, "q3":-0.051},
  {"q0":0.875, "q1":-0.456, "q2":0.150, "q3":-0.055},
  {"q0":0.940, "q1":-0.299, "q2":0.154, "q3":-0.052},
  {"q0":0.974, "q1":-0.156, "q2":0.156, "q3":-0.054},
  {"q0":0.985, "q1":-0.027, "q2":0.161, "q3":-0.050},
  {"q0":0.969, "q1":0.174, "q2":0.171, "q3":-0.050},
  {"q0":0.899, "q1":0.402, "q2":0.167, "q3":-0.057},
  {"q0":0.771, "q1":0.614, "q2":0.156, "q3":-0.072},
  {"q0":0.684, "q1":0.710, "q2":0.153, "q3":-0.072},
  {"q0":0.640, "q1":0.751, "q2":0.148, "q3":-0.076},
  {"q0":0.622, "q1":0.766, "q2":0.143, "q3":-0.077},
  {"q0":0.652, "q1":0.741, "q2":0.139, "q3":-0.078},
  {"q0":0.706, "q1":0.689, "q2":0.143, "q3":-0.084},
  {"q0":0.825, "q1":0.544, "q2":0.140, "q3":-0.070},
  {"q0":0.958, "q1":0.241, "q2":0.144, "q3":-0.053},
  {"q0":0.990, "q1":0.057, "q2":0.117, "q3":-0.048},
  {"q0":0.993, "q1":0.061, "q2":0.083, "q3":-0.054},
  {"q0":0.998, "q1":0.014, "q2":0.006, "q3":-0.065},
  {"q0":0.997, "q1":0.013, "q2":0.006, "q3":-0.072},
  {"q0":0.997, "q1":0.013, "q2":0.006, "q3":-0.072},
  {"q0":0.997, "q1":0.013, "q2":0.006, "q3":-0.072},
  {"q0":0.997, "q1":0.012, "q2":0.006, "q3":-0.072},
  {"q0":0.997, "q1":0.012, "q2":0.006, "q3":-0.072},
  {"q0":0.997, "q1":0.012, "q2":0.006, "q3":-0.072},
  {"q0":0.997, "q1":0.011, "q2":0.006, "q3":-0.072},
  {"q0":0.997, "q1":0.011, "q2":0.005, "q3":-0.072},
  {"q0":0.997, "q1":0.011, "q2":0.005, "q3":-0.072},
  {"q0":0.997, "q1":0.011, "q2":0.005, "q3":-0.072},
  {"q0":0.997, "q1":0.011, "q2":0.005, "q3":-0.072},
  {"q0":0.997, "q1":0.010, "q2":0.005, "q3":-0.072},

];

//クォヌタニオンデヌタのむンデックス
let quaternion_data_counter = 0;

//クォヌタニオンデヌタをタむマヌ読み蟌み
const change_quaternion_data = function () {
  //デヌタ曎新
  data_q0 = quaternion_data[quaternion_data_counter].q0;
  data_q1 = quaternion_data[quaternion_data_counter].q1;
  data_q2 = quaternion_data[quaternion_data_counter].q2;
  data_q3 = quaternion_data[quaternion_data_counter].q3;

  //カりンタヌアップ
  quaternion_data_counter++;
  if(quaternion_data_counter >= quaternion_data.length){
    quaternion_data_counter = 0;
  }

  setTimeout(change_quaternion_data, 100);
}


// ロヌド時の凊理
window.onload = function () {
  threejs_run();
  change_quaternion_data();
}

// three.js
const threejs_run = function () {
  //Canvas
  const canvas = document.querySelector('#canvasMain');
  const CANVAS_WIDTH = 640;
  const CANVAS_HEIGHT = 360;

  //レンダラヌ
  const renderer = new THREE.WebGLRenderer({canvas});
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);

  //シヌン
  const scene = new THREE.Scene();

  //カメラ
  const camera = new THREE.PerspectiveCamera(50, CANVAS_WIDTH / CANVAS_HEIGHT, 0.1, 2000);
  camera.position.set(0, 0, 1200);

  //オブゞェクト立方䜓
  const geometry = new THREE.BoxGeometry(500, 500, 500);
  const material = new THREE.MeshStandardMaterial({color: 0x00ff00});
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  //光源
  const light = new THREE.DirectionalLight(0xffffff, 1); //color, intensity
  light.position.set(0, 0, 1);
  scene.add(light);

  //座暙軞
  const axes = new THREE.AxesHelper(2000);
  scene.add(axes);

  //Animation
  const animate = function () {
    requestAnimationFrame(animate);

    //クォヌタニオンで回転
    cube.quaternion.set(data_q1, data_q2, data_q3, data_q0);

    //レンダリング
    renderer.render(scene, camera);
  }

  animate();

}

</script>
</body>
</html>

このように立方䜓がアニメヌションされるず思いたす。

クォヌタニオンデヌタを蚘録する

先皋はこちらで甚意したデヌタでしたが、皆さんが9軞センサヌを動かしおデヌタを䜜成しおみたしょう。

回路は、以前行った「I2C通信でDMP」の接続が必芁です。micro:bitずの接続があっおも問題ありたせんが、USBコネクタを挿す際はseeeduinoもしくはmicro:bitのどちらかだけにしおください電源が競合するため

※「I2C通信でDMP」の回路ず同じです。

seeeduino XIAOに以䞋のコヌドを曞きこんで䞋さい。
「I2C通信でDMP」でのコヌドをベヌスに「Seeeduino XIAO」の内容を適甚し、クォヌタニオンデヌタをUSBシリアルで出力するように改造したした。

/************************************************************
MPU9250_DMP_Quaternion
 Quaternion example for MPU-9250 DMP Arduino Library 
Jim Lindblom @ SparkFun Electronics
original creation date: November 23, 2016
https://github.com/sparkfun/SparkFun_MPU9250_DMP_Arduino_Library

The MPU-9250's digital motion processor (DMP) can calculate
four unit quaternions, which can be used to represent the
rotation of an object.

This exmaple demonstrates how to configure the DMP to 
calculate quaternions, and prints them out to the serial
monitor. It also calculates pitch, roll, and yaw from those
values.

Development environment specifics:
Arduino IDE 1.6.12
SparkFun 9DoF Razor IMU M0

Supported Platforms:
- ATSAMD21 (Arduino Zero, SparkFun SAMD21 Breakouts)
*************************************************************/
#include <SparkFunMPU9250-DMP.h>

MPU9250_DMP imu;

void setup() 
{
  Serial.begin(9600);

  // Call imu.begin() to verify communication and initialize
  if (imu.begin() != INV_SUCCESS)
  {
    while (1)
    {
      Serial.println("Unable to communicate with MPU-9250");
      Serial.println("Check connections, and try again.");
      Serial.println();
      delay(5000);
    }
  }
  
  imu.dmpBegin(DMP_FEATURE_6X_LP_QUAT | // Enable 6-axis quat
               DMP_FEATURE_GYRO_CAL, // Use gyro calibration
              10); // Set DMP FIFO rate to 10 Hz
  // DMP_FEATURE_LP_QUAT can also be used. It uses the 
  // accelerometer in low-power mode to estimate quat's.
  // DMP_FEATURE_LP_QUAT and 6X_LP_QUAT are mutually exclusive
}

void loop() 
{
  // Check for new data in the FIFO
  if ( imu.fifoAvailable() )
  {
    // Use dmpUpdateFifo to update the ax, gx, mx, etc. values
    if ( imu.dmpUpdateFifo() == INV_SUCCESS)
    {
      // computeEulerAngles can be used -- after updating the
      // quaternion values -- to estimate roll, pitch, and yaw
      imu.computeEulerAngles();
      printIMUData();
    }
  }
}

void printIMUData(void)
{  
  // After calling dmpUpdateFifo() the ax, gx, mx, etc. values
  // are all updated.
  // Quaternion values are, by default, stored in Q30 long
  // format. calcQuat turns them into a float between -1 and 1
  float q0 = imu.calcQuat(imu.qw);
  float q1 = imu.calcQuat(imu.qx);
  float q2 = imu.calcQuat(imu.qy);
  float q3 = imu.calcQuat(imu.qz);

  char format_str[100];
  sprintf(format_str, "{\"q0\":%1.3f, \"q1\":%1.3f, \"q2\":%1.3f, \"q3\":%1.3f},", q0, q1, q2, q3);
  
  Serial.println(format_str);
  delay(50); 
}

曞き蟌み成功したらシリアルコン゜ヌルを開いお䞋さい。ボヌレヌトは9600 bpsです。 JSON圢匏でクォヌタニオンを出力しおいたす。

ブレッドボヌドを回転させお倀を倉化させ、適圓な所で「自動スクロヌル」のチェックを倖し、任意の耇数デヌタ行をコピヌしお、先皋のHTMLファむルの quaternion_data の [ ] の䞭身を䞊曞きしお実行しおみお䞋さい。 向きや速さは眮いおおくずしお、動かした通りにthree.js䞊で衚瀺されたず思いたす。

Seeeduinoコヌドのポむント

sprintf を䜿っお指定の圢匏にフォヌマットしおいる点に泚目しおください。

 char format_str[100];
 sprintf(format_str, "{\"q0\":%1.3f, \"q1\":%1.3f, \"q2\":%1.3f, \"q3\":%1.3f},", q0, q1, q2, q3);

小数の堎合は%fですが、敎数の堎合は%dを䜿いたす。たた %02d 等を䜿うずれロ埋め衚珟も可胜です。
ちなみに䞊蚘のコヌドの\ぱスケヌプシヌケンス、%1.3fは敎数䞀桁.小数3桁ずいう意味です。詳しくは各自調べおください。

はじめに

Webプログラミングの堎合はJSON圢匏などでやりずりしたすが、BLEやハヌドりェアのシリアル通信においお、JSON圢匏のようなプログラミングしやすい圢にしおしたうずバむト数が増えおしたい、省電力やデヌタ転送速床に倧きく圱響が出たす。できるだけ䜙蚈な情報は付加せず、か぀圧瞮された小さいデヌタで送信するこずを目指したす。

BLEデヌタバむト数に぀いお

BLEは通垞蚭定だず、䞀床に蚭定できるデヌタ数は20Byteです。
ですが、micro:bitにおUART受信しおそれをBLE UART送信する堎合、18Byteを超えおいるず経隓䞊うたく送信できたせんでした。 これを鑑みお、クォヌタニオンのデヌタをseeeduinoから送信する堎合、18Byte以䞋に抑えおた方が良さそうです。

もちろん、小分けに送信を分けお送るこずも可胜ですが、凊理が耇雑になるため、1床に1セットのクォヌタニオンデヌタを送信するこずを目暙ずしたす。

クォヌタニオンデヌタの小数点以䞋桁数に぀いお

改めお「I2C通信でDMP」のサンプルコヌドで出力したSeeeduinoのクォヌタニオンデヌタを芋おみるず、以䞋のような小数の倀です。

Q: 0.9979, -0.0004, -0.0572, 0.0302

それぞれの倀は最小 -1.0000  最倧 +1.0000 たでの範囲です。 これも経隓䞊ですが、小数点以䞋の桁数が少ないず、動きの滑らかさが䞍足しおしたいたす。今回は小数点以䞋3桁以䞊を目暙ずしたす。

デヌタ送信フォヌマットに぀いお

先皋のSeeeduinoのクォヌタニオンデヌタから、Q: や , やスペヌスを取り陀き、必ず数倀の前に+ -笊号を付けるようにするず以䞋のようになりたす。

+0.9979-0.0004-0.0572+0.0302

これだず28Byteで倧分オヌバヌしおいたす。 小数点以䞋3桁にしおみたす。

+0.997-0.000-0.057+0.030

これでようやく24Byteですが、ただただ目暙をオヌバヌしおいたす。

゚ンコヌド笊号化

デヌタを゚ンコヌドしおサむズを小さくする方法は様々方法がありたすが、ここでは簡単に蚈算できる方法を考えおみたす。

+ - 蚘号を無くす

単玔に蚘号を取り陀いおしたうず元通りに埩号化できないためNGです。
もう䞀床先皋のクォヌタニオンデヌタの最小倀最倧倀を考えおみおください。
1を足すこずで必ず数倀は正の数ずなりたす。

+1.997+1.000+9.943+1.030

必ず + が付くのであれば送信時は取っおしたっおも良さそうです。

小数点を無くす

単玔に文字列操䜜しお小数点を消すずいうやり方でも良いですが、もっず簡単な方法がありたす。 それは1000を掛けるずいうこずです。この方がプログラミング的にも簡単に蚘述できたす。

1997100099431030

これで小数点以䞋3桁を保ち぀぀、16Byeにするこずが出来たした

デコヌド埩号化

デコヌドするのはPC偎のHTML/JavaScriptです。
゚ンコヌドず逆の蚈算を行うこずで簡単にデコヌドできたす。ただし、デコヌドする前にsubstr等を䜿っお、16文字を4文字ず぀切り出すず良いでしょう。

䞭間挔習

これたで䞊玚コヌスで孊んだ党おの内容、micro:bitのBLE通信を統合しお、9軞センサ搭茉のブレッドボヌドを回転させるず、HTML䞊の立方䜓がリアルタむムに回転するシステムを䜜成しおください。

補足

解答

簡単なアンケヌトに答えお解答䟋を入手しよう
解答を芋る前に自分で䜜っお悩むこずがプログラミング向䞊に繋がりたす。

完党版

前回の挔習で実物ず立方䜓の回転方向が䞀臎しおいない人が倚かったず思いたす。 それは、䞊から芋るのか、䞋から芋るのかなど、どの芖点から芋るかによっお芋え方が倉わるためです。
今回はリアル芖点ずthree.jsのカメラ芖点を合わせお、回転方向を気持ちよく䞀臎させたす

完党版

今回はオブゞェクトずしお球を描き、そこに地球のテクスチャをマッピングしお回転衚瀺したす。

完党版

今回は円柱の描画がずオブゞェクトのグルヌプ化に぀いお孊びたす。
PCだけで実行できたす。

完党版

぀のオブゞェクトだけ遞択しお黄色に着色したす。

完党版

three.jsでテキストを描画するこずはできたすが少し耇雑です。
今回はより簡単な方法でテキスト描画を実珟させたす。

完党版

最終的に画面に衚瀺するものずしお地球の画像だけでなく、各皮情報も衚瀺させたす。
そのずきに䜿えるデヌタを甚意したした。

完党版

球面ずの蚈算の参考になる情報を茉せおいたす。
総合挔習時のヒントずしお䞋さい。

完党版

ここでは「ブレッドボヌド9軞センサ+Seeeduino」ず「micro:bit」を球の䞭に玍める手順を説明したす。

完党版

総合挔習

これたで孊んだ内容を組合せお、動画のようにプラスチックボヌルを回転するず同じようにPC画面䞊でバヌチャル地球が回転し、堎所に応じお各囜の情報が衚瀺されるシステムを完成させおください。

※動画で䜿っおいるデバむスや組み立おは少し叀いため、球の䞭は気にしないでください。

完党版

「ものものテックQA掲瀺板」ず「コンテンツの倉曎履歎」です。

完党版