Yakatano が 2024年01月30日18時01分31秒 に編集
コメント無し
本文の変更
# はじめに 2023年のMaker Faire Tokyo 2023やNT東京にIsolation Cubeというガジェットを展示しました. @[youtube](https://www.youtube.com/watch?v=w3pRgsE_wJo) Isolation Cubeは,1面8x8=64 LEDの正方形マトリクスを6枚用いて正6面体( 合計384LED)のCubeディスプレイに9軸のIMUセンサを用いてセンサの角度に合わせてディスプレイ表示を更新することで,いつも同じ向きに画像を呈示することができるデバイスです. 今回はより球体に近づけるべく新規に基板を起こして一枚50LEDの正五角形基板から成る正12面体(600LED)に拡張してみました. 今まではマイコンにESP32を使用していましたが,LED数が増えてきたのでマルチコアで画面を分割して高速化を図るアイデアです. ESP32だと2コアなので1コア300LEDとなりますが,SPRESENSEは6コアあるので,メインUIコア+4コア(1コアで150LEDを担当)する実装を目指します. ### モチベーション 球体ディスプレイを作るモチベーションについては以前にオープンエッジデバイス研究会で話しているので、そちらを。。 [slideshare](https://www.slideshare.net/KatanoYasuo/5pptx#2) ### 完成イメージ
@[youtube](https://www.youtube.com/watch?v=4m_QSiS4KtM)
# 部品 ## SPRESENSE 今回はSpresenseさんから以下の製品をお借りして実装しました. ### メインボード  [製品情報](https://elchika.com/promotion/spresense2023/#:~:text=Spresense%20%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9C%E3%83%BC%E3%83%89-,%E8%A3%BD%E5%93%81%E6%83%85%E5%A0%B1,-%E6%8B%A1%E5%BC%B5%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9) ここは説明不要だと思います. コアが6つもあるので,600個のLEDを分散して制御することができれば速度的にも有利です.IMUの回転角度情報から全てのLEDの仮想座標の3次元回転を個別に計算しなければならないのでかなりの計算量となるため,分散処理は必須と言っても良いものです. また,メインボードにLiPo用のDCDCを備えるため,DCDC用の基板を用意する必要がなく,LEDへの電力共有もEXT_VDD経由で大きい電流も出力できそうです. ### LTE拡張デバイス  サンプル提供いただいたものは拡張ボードだったのですが,Isolation Sphereに格納するには大きすぎたので,手持ちのLTE拡張ボードを使用しました. Isolation sphereの正12面体部分の内部にちょうど収まるサイズなので,そのまま収納します. 要求する機能としては表示する画像を格納するためのSDカードが欲しかったのと,UIやデータ転送のためにSIMが使えるならワンチャン....と思ったためです.欲しかった機能はSDカードのみなので,SPI接続で...とも思ったのですが,ボール内に収まりそうなのでそのまま行きます. ### SPRESENSE用Qwiic接続基板  [製品情報](https://www.switch-science.com/products/6318) これと[BMI270](https://nextstep.official.ec/items/66165488)の両方をサンプル提供いただいたのですが,どちらが使えるかわからず,現時点ではIsolation Cubeで使い慣れているBNO055が接続可能なQwiic接続基板を採用しています.Qwiicだとケーブルが邪魔になるので,どこかの機会でBMI270も試してみたいと思います. ### BLE1507(BLE serialization firmware)  [製品情報](https://elchika.com/promotion/spresense2023/#:~:text=BLE%20serialization%20firmware)-,%E8%A3%BD%E5%93%81%E6%83%85%E5%A0%B1,-BLE1507(NUS%20firmware) こちらもサンプル提供いただきました. もともとESP32を採用していた理由がBLEもしくはWiFiが使える(ボールとして完全に閉じた形状&全面ディスプレイになるため,電源ONなどUI操作をボタンとして配置できないため無線機能が必須)こと,だったのでこのデバイスを見つけた時に「おお,これならSPRESENSE使えるやん,しかもマルチコア数多い!」となって採用を決定したのでした.
今回のIsolation Sphereの構成  BLE、Qwiic、電源・LED信号まわりなどでこのような構成になりました。
## 表示用LED ### WS2812C-2020  [秋月電子でも販売している](https://akizukidenshi.com/catalog/g/gI-15068/)Neopixelベースのマイコン内蔵RGB LEDです.2020の名の通り2mm角という非常に小さなLEDです. WS2812-2020のシリーズには二種類(他方はWS2812B-2020)あり,その差は消費電力です.WS2812C-2020は5mAとWS2812B-2020の12mAと比較して小さい電流量なので,大量のLEDをLiPoなどの限られた電源で駆動させるのに向いています(当然暗くなりますが..) このLEDを50個直列で配置するための正五角形基板を起こしました.  中心から螺旋状にシリアル配線してあります.所々にある穴は熱対策&ネジ穴として外骨格を固定することを想定していましたが,現時点ではただの穴です.  裏側には配線およびコンデンサが配置されています. 接続して表示してみるとこんな感じ. @[twitter](https://twitter.com/Yakatano/status/1686709358540046336?s=20) (リンクされないようなのでこちらに[別リンク](https://twitter.com/Yakatano/status/1686709358540046336?s=20)を。。) ## IMU ### BNO055  現状で個人的には一番信頼できるIMUです.BNO085という後継もあるのですが一部使いにくいところがありこちらを愛用しています. 3次元回転にQuaternionを使っておりリソースが有限な中で直接Qaternionが関数から取得できるのは助かります. BNO055はキャリブレーションが不要かつドリフトもかなり抑えられており,Isolation Sphereのような完全封じ込め型のデバイスには助かります. ## 電源  3.7v 2000mahの充電式リチウムポリマー電池, 104040 LiPo電池になります。 40mmx40mmのサイズで、正12面体に内接する最大立方体の1辺が約40mmなのでこのサイズをチョイス。メインボードに接続して使用します。 # 設計 ## 正12面体 ### 基本設計思想 Isolation Sphereを作成するにあたって、重要な正12面体の諸元とそれらを作成するための基本設計を行います。 実はMaker Faire Tokyo 2023においてすでに一度正12面体のバージョンを作成していました。  このときには次のような内骨格構造をしていました(若干5角形のサイズも小さかったのですが。。)構造的には内部のマイコンやバッテリー配置が安定していたのですが、内部の容量が小さくなり(さらに配線の余裕を考えていなかったので)配線がギッチギチになってしまい「組み立てるとどこかが断線する」状態になっておりあえなくお蔵入りとなってしまいました。 そこで今回は最終目標である球体に近づけるため、球体の外殻(内側が正12面体)を作成し、その外殻パネルにLED基板を貼り付ける外骨格構造を採用しました。  外骨格には内側の基板に配置されたLEDから外側の球まで穴を開け、球表面をボロノイ分割で600分割することで全面をディスプレイにすることができています。 これが外骨格の1基板分。 @[twitter](https://twitter.com/Yakatano/status/1739655883725140466?s=20) (リンクされないようなのでこちらに[別リンク](https://twitter.com/Yakatano/status/1739655883725140466?s=20)を。。) (黒レジンで作成したら光の拡散量が少なくなってしまったので、後に白レジンに変更) これをパネルとして組み合わせて球体にします。そうすると内部に骨格を配置する必要がないので内部の容積が十分確保できるようになり、SpresenseもLTE拡張ボードまで収納することができるようになりました。 3Dプリンタで出力するときには3パネルを1セット(150LED)として4セット用意してこれを組み合わせて球体を形成します。SPRESENSEの場合はこの1セットを1コアが担当することを想定しています。 @[twitter](https://twitter.com/Yakatano/status/1740968387570978861?s=20) (リンクされないようなのでこちらに[別リンク](https://twitter.com/Yakatano/status/1740968387570978861?s=20)を。。) 基板は平面・外殻は球体となっているので基板の周辺は球表面までの距離が近く歪が増え、基板重心付近では遠くなるため、若干視野角が異なることがどの程度画像に影響するか、はひとつ大きな課題になります。 ## 600LEDの配置 さて、球表面をn個に均等分割する方法というのはかなり難しく、理論的な方法はなかなか見つかりません。 今回設計した外殻球はφ100mm、内部の正12面体の外接球がφ80mmとなっています。この外殻球表面に600LED(600LED÷12面体=50LED/基板)を均等に配置する、という問題を考えます。 ### 一般化螺旋集合 解析的に解く方法はないだろうか?といろいろと探してみたのですが、私の要求に合うものがなかなか見つかりません。 一つ「[一般化螺旋集合](https://blog.panicblanket.com/archives/3778)」というものがあるのですが、この方法で600LEDを配置してみるとこのようになります。 @[youtube](https://youtu.be/0GsdxohajtE?si=YKieayR3R3wrCN-E) わかるでしょうか?この方法だと一様分布ではありますが螺旋方向の「スジ」ができてしまうのです。 Isolation Sphereのコンセプトは「回転させても絵が変わらない」というものです。しかしこのLEDの配置パターンだと上下に「極」ができてしまい、これを回転させるとどうしてもパターンが回転していることがわかってしまう。 球を回転させてもどこが「極」かわからない、となる必要があるのです。 ### Icosphere IcosphereというのはBlenderなどのCGソフトで球を表現するための方法の一つです。 球の表現の代表的な方法は、上のIcosphereとUV sphereというものです。 UV Sphereというのは、  このように、緯度と経度で構成される球です。そのためテクスチャマッピングの貼り付けなどには非常にやりやすいメリットがありますが、北極・南極に点が集中するため、その部分が歪みやすいデメリットがあります。 対して、Icosphereというのは、  正多面体をsubdivide分割して細かくしていく球の表現方法です。正多面体の性質として極性を持たないため、回転してもどこが極かはわからないのです。 理想的ではあるのですが、図に示すように複雑性の上がり方が極端で自分で個数を制御できないのです(左からsubdivide=2→3→4)。 任意の数(Isolation Sphereの場合は600)の配置を行うことができないため、今回も採用は見送りました。 ### 逐次的な方法 最終的に採用した方法がこちら @[youtube](https://youtu.be/Au8QosJG8wA?si=FrItu_LpA4WdDRp-) UNITYを用いて、内部球(動画中の青い球)に重力を設定し、大気圏のようなもの(動画中の半透明の白い球)を設定し、このこの球の内側に衝突判定を設定することでボールがはみ出すことを防ぎます。 そこに任意数(ここでは600)のボール(動画中の緑球)を配置してお互いが衝突しながら最適配置を探す。。。という方法です。 詳細は以前に書いた[ブログ](https://tajmahal0707.hatenablog.com/entry/2023/06/08/074602)があるのでそちらを・・・ ## LED球体の設計 これまででLED 600個を球体表面に配置することができました。これを基板上では50x12の配置に、球表面では均等部分布にする必要があります。また、外殻球表面の穴を円筒にしてしまうと円筒間に隙間ができてしまい球表面全体をディスプレイにする際に問題になります。 そこで、基板と接する部分は円筒で、球表面上ではボロノイ分割にすることにします。こんな感じ。  ### voronoi分割 うまくいったら絶対エモいので、頑張りましたw [【Blender】有機的なオブジェクトを作る方法 Geometry Nodes編](https://styly.cc/ja/tips/blender-geometrynodes-nyu/) [【Blender】ボロノイ構造のオブジェクトを作る方法](https://styly.cc/ja/tips/blender/nimi-blender-voronoi/) いろいろvoronoiを作る方法は紹介されています。しかし球表面に対して適用しているものがなかなか見つからず、苦労しました。 Isolation Sphereでは使用しませんでしたがこんなサイトもあります。 [voronator](https://www.voronator.com/) [github:py_sphere_Voronoi](https://github.com/tylerjereddy/py_sphere_Voronoi) このようなライブラリも存在するのですが、自分の望んだ動作にならないため、オリジナルで作ることにしました。 #### Delaunay多角形 上で配置した600点の3次元位置はすでに計算できています。しかしこの点群から辺・面を繋いでポリゴンを生成するのはちょっと面倒です。 この点群は定義から全て球表面上にあり、均等に近ければ近いほど各点を繋いだポリゴンは正三角形に近くなることがわかります。正三角形に近くなればなるほどこのDelaunay多角形から作るvoronoi多角形は6角形になります。 また、同時にすべての点が球表面上にあるためこの点の集合は凸多面体であることもわかります。 そこでpythonで凸包を生成するプログラムを作成しました。 ライブラリにはopen3dを使います。 ソースコードの一部。(このままコピペでは動きませんがエッセンスはこんな感じ) ``` python:点群→凸包の生成 import open3d as o3d import pandas as pd import plotly as py import plotly.express as px import numpy as np from stl import mesh points = [] for i, d in df.iterrows(): p = d[['x', 'y', 'z']].values points.append(list(p)) pointcloud = o3d.geometry.PointCloud() # pointcloud.points = o3d.utility.Vector3dVector(df[['x', 'y', 'z']].values) pointcloud.points = o3d.utility.Vector3dVector(points) hull, _ = pointcloud.compute_convex_hull() hull_ls = o3d.geometry.LineSet.create_from_triangle_mesh(hull) hull_ls.paint_uniform_color((0, 1, 0)) hull.compute_vertex_normals() o3d.io.write_triangle_mesh(stl_filename, hull) o3d.visualization.draw_plotly([pointcloud, hull_ls]) ``` 出力結果はこんな感じ。  これをstl形式でファイル出力します。  stlファイルで出力することで、各3角形の3次元位置を個別に取得することができるため、後のvoronoi計算処理が楽になります。 ### voronoi多角形の作成 Delaunay多面体ができたのであれば、そこからvoronoi多角形に拡張します。 @[wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9C%E3%83%AD%E3%83%8E%E3%82%A4%E5%9B%B3) Delaunay多角形をstlで出力したのは、stlの出力フォーマットが3角形を形成する頂点座標を3角形ごとに出力されるためです。 ```python:stl読み込み from stl import mesh stl_data = mesh.Mesh.from_file(filename ) print(stl_data.points) ``` これを実行すると > [[-49.31912 7.78104 2.6608098 ... -49.956623 1.3397751 -1.5939546] [-48.49622 6.584802 10.235096 ... -44.360455 9.363192 21.082712 ] [ 47.784668 -9.589299 -11.165614 ... 45.983433 -17.109419 -9.632838 ] ... [ 47.255344 -12.76661 10.195397 ... 45.92527 -19.05439 5.272538 ] [ 45.92527 -19.05439 5.272538 ... 42.422623 -25.259659 7.891177 ] [ 44.41393 -19.146568 12.681166 ... 42.422623 -25.259659 7.891177 ]] となり、stl.pointsは配列として9x「面の数」となっており、この9はDelaunay三角形の3頂点の3次元座標を表しています。 voronoiの定義から各Delaunay三角形の座標&辺情報が必要で、voronoiはdelaunay三角形の頂点を母点とし、母点でつくる辺の垂直二等分線によって分割される面をvoronoi多角形として、voronoi面を形成する頂点をvoronoi頂点と定義されます。 @[twitter](https://twitter.com/clintfulkerson/status/1748766259682967974?s=20) (リンクされないようなのでこちらに[別リンク](https://twitter.com/clintfulkerson/status/1748766259682967974?s=20)を。。) このように黄線で定義されるDelaunay三角形に対して水色線のvoronoi多角形を定義する。 ここでvoronoi頂点も外殻球表面上の点となるため、頂点で構成される点群も凸包であることがわかります。 そこで先程の凸包プログラムを適用することでvoronoi多角形の形状も取得することができます。 その結果がこちら   ### LEDからの円筒穴の作成 基板上のLEDの位置と、対応する外殻球上の点を結んだパイプ形状(以後ミミズ)を作成する。 こんな感じ。
@[twitter](https://twitter.com/Yakatano/status/1733491717066924194?s=20) (リンクされないようなのでこちらに[別リンク](https://twitter.com/Yakatano/status/1733491717066924194?s=20)を。。)
@[twitter](https://twitter.com/Yakatano/status/1733491717066924194?s=20)(リンクされないようなのでこちらに[別リンク](https://twitter.com/Yakatano/status/1733491717066924194?s=20)を。。)
この大量のミミズを外角球からboolean演算でくり抜くことでLEDからの導光路を確保する。 ```python:blender_booleans import bpy boolean_type = 'DIFFERENCE' root_object = bpy.data.objects['shell-05'] selected_objects = bpy.context.selected_objects for obj in selected_objects: print('boolean', obj.name) name = 'bool-' + obj.name.split('-')[1] + '-' + obj.name.split('-')[2] bool01 = root_object.modifiers.new(type="BOOLEAN", name=name) bool01.object = obj bool01.operation = boolean_type bpy.context.view_layer.objects.active = obj bpy.ops.object.modifier_apply(modifier=name, report=True) ``` これはコード内でroot_object名を指定して、booleanしたいオブジェクトを選択した状態でこのpythonを実行すると、root_objectに対して選択したオブジェクトをすべてboolean演算するマクロです。 これを使い各パネルごとに各色のミミズをboolean演算する。 大量のmodifierが生成されてかなり重くなるが、これを一括で適用する裏技を紹介しておく。 modifierがあるオブジェクトを選択して、  meshを選択することで、このオブジェクトに付いているmodifierが一括適用される。 覚えておくと良い。 ### voronoi 穴の作成 voronoi多角形の頂点(多くは6点、たまに5点や7点)とLEDの中心(正12面体の五角形上の点)をまとめて点群にすると、この点群は凸包となる。 この凸包をポリゴン化するとvoronoi多角形を底面とする多角錐(voronoi錐)ができます。 作成したvoronoi錐がこんな感じ。 |  | | |:---:|:---| これを外殻からbooleanで削除することで外殻にvoronoiの穴があきます。 ただ、その前にvoronoi錐にBevelを適用することでvoronoi錐の角が丸くなりより滑らかな穴が開けられます。  これで外殻球の完成。 @[youtube](https://youtu.be/4m_QSiS4KtM?si=UaREAnfV81zA0ORN) 中にはこのようにspresenseを格納   ### 葬送のフリーレン でこのような球体配置が話題になっていたようです。 @[youtube](https://www.youtube.com/watch?v=odP8k8_zrlc) 私はここで見たのですが @[twitter](https://twitter.com/motcho_tw/status/1741337056067096609?s=20) その時にはフリーレンのことを知らずにマジレスしてしまいました。私の回答も一部を5角形(もしくは7角形)に置換することで球体表面の6角形分割を行うことができるようになる派でしたw ちょうどこのようなガジェットを作っていた最中だったので、タイムリーすぎて思わず反応してしまいました。 # 動作 基本的な動作・アルゴリズムは、Isolation Cubeと同じものなので、それを踏襲します。 Isolation Cubeの動作アルゴリズムについては「[ブログ](https://tajmahal0707.hatenablog.com/entry/2023/06/08/074346)」に記載してあります。[slideshare](https://www.slideshare.net/KatanoYasuo/5pptx#2) ←こちらにも原理的な説明が載っていますが、内容について改めて説明します。 ## 原理 簡単に映像を呈示する原理についてIsolation Cubeを例に説明します。Isolation SphereはCubeのLED配置を変えただけのものなので。 行っていることを概観すると、CGの世界で行われているテクスチャマッピングを現実の世界で実装している感じです。 ### 最初に用意するもの
#### LEDの3次元位置
#### LEDの3次元レイアウト配置
各LEDパネルのLEDの三次元位置を計算し、記録しておきます。 Isolation Cubeの場合には、[aliexpress](https://ja.aliexpress.com/item/1005005994450466.html?spm=a2g0o.order_list.order_list_main.66.21ef585aZRepwj&gatewayAdapt=glo2jpn) このパネル(8x8 WS2812-5050)を使用しています。LEDパネルのサイズは64x64mm。LEDは8x8なので、8mm間隔で並んでいるものと考えると  このようなLEDパネルの配置になるのです。([リンク先](tajmahal.mond.jp/neon-on/images/cube-graph.html)にグリグリ回せるグラフをおいておきます。) このLEDの3次元位置をIMUの値を用いて回転することで自己位置を把握するのです。
#### Isolation SphereのLED3次元配置
#### Isolation SphereのLEDレイアウト3次元配置
Isolation SphereのLEDの配置は、前述のDelaunay多角形のVertex位置がLEDの配置と対応しています。 このLED配置は、基板とは関係なく外側で均等になるように600個のLEDを配置したものです。これらがどのような順番でシリアル接続されているのか、は基板(パネルあたり50LED)の配置によって決まります。そこで600 LEDをそれぞれの基板に割り当てる処理を行いました。
@[twitter](https://twitter.com/Yakatano/status/1733491717066924194?s=20)
@[twitter](https://twitter.com/Yakatano/status/1733491717066924194?s=20)(リンクされないようなのでこちらに[別リンク](https://twitter.com/Yakatano/status/1733491717066924194?s=20)を。。)
これによって、LEDのシリアル接続番号とLEDの3次元座標のデータに連携が取れる状態になりました。 プログラム的にはIsolattion CubeのcsvをIsolation Sphereのcsvに変換するだけで動作する。 #### テクスチャマッピングデータ Isolation Cubeに貼り付けるテクスチャを用意します。汎用的に画像を貼り付けられるように一般的なパノラマ画像を使います。 パノラマ画像は[正距円筒投影図法](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%B7%9D%E5%86%86%E7%AD%92%E5%9B%B3%E6%B3%95#:~:text=%E6%AD%A3%E8%B7%9D%E5%86%86%E7%AD%92%E5%9B%B3%E6%B3%95%EF%BC%88%E3%81%9B,%E3%81%A8%E5%91%BC%E3%81%B0%E3%82%8C%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%82%E3%82%8B%E3%80%82)とも呼ばれ、画像のx軸、y軸がそのまま経度・緯度に対応するような画像で、  そのため、球体にこのテクスチャ画像を貼り付けるには緯度と経度がわかればその場所の色を抽出することが可能になる利点がある反面、北極・南極付近が大きく歪むという欠点があります。 しかしパノラマ画像はVRやCGとも相性がよく、無料で入手可能なサイトも多い([たとえば](https://www.easypano.com/jp/panorama-gallery.html))ので入手も容易と考え、採用しています。
事前にこれらのデータを用意してSDカードに保存しておきます。 レイアウトデータはcsv形式、パノラマデータは画像データをサイズ変更して拡張子datのバイナリファイルに変換して保存します。 変換はこんな感じ。(ホントは直で読みたいんですが、それはいずれ・・) ```python:Image2dat.py import cv2 import os import math from PIL import Image import argparse import pandas as pd import math current_path = os.getcwd() # default値 image_path = 'Image/path/on/your/drive/hogehoge.png' save_path = 'save/path/on/your/drive/fugafuga.dat' image_width = 160 parser = argparse.ArgumentParser(description='clip color and extract') # 2. パーサを作る parser.add_argument('--image', default=image_path) # 動画ファイルのパス parser.add_argument('--save', default=save_path) # 動画ファイルのパス parser.add_argument('--width', type=int, default=image_width) # 保存先 args = parser.parse_args() # 4. 引数を解析 # 変数の定義 image_path = args.image save_path = args.save image_width = int(args.width) image_height = int(image_width/2) # 画像読み込み&リサイズ img = cv2.imread(image_path, 0) img = cv2.resize(img, (image_width, image_height)) # pandas使ってバイナリ化 print(img.shape) df = pd.DataFrame(img) print(df) array = bytes(df.to_numpy()) # 保存 f = open(save_path, 'wb') f.write(array) f.close() ```
## IMUによるLEDの仮想回転 IMU(ここではBNO055)を内蔵することで、Isolation Cube(Sphere)自身が回転するとその回転角度をquaternionで返してきます。 その回転情報に対してLED座標に同じ回転を加えると、現実世界の回転と同じ回転状態を得ることができます。 今回はそうではなく、LED座標に逆の回転を加えると、仮想的なLEDの座標は全く回転していない状態を作ることができるのです。 細かい計算式は[こちらのブログ](https://tajmahal0707.hatenablog.com/)で書いているので説明はしませんが、  ショート動画が貼り付けられないので、[youtube](https://www.youtube.com/shorts/FoM4IvFu58s)←こちらから。 上のリンクのようにセンサ(ここではM5stack)を回転させると画面下の球体(たくさんのLEDに相当するボールで構成されている)に地球のテクスチャが配置されています。
この地球は画面上のパノラマ画像からサンプリングしており、センサの値を相殺するような回転を仮想的なLEDに与えているため、動画のようにセンサが回転しても球体に貼り付けられている地球は回転してない(いつも右下あたりにオーストラリアがある)ことがわかります。 動画の後半、拡大していくとパノラマ画像上にLED位置が表示され、センサに合わせて移動していることがわかると思います。
この地球は画面上のパノラマ画像からサンプリングしており、センサの値を相殺するような回転を仮想的LEDに与えているため、動画のようにセンサが回転しても球体に貼り付けられている地球は回転してない(いつも右下あたりにオーストラリアがある)ことがわかります。 動画の後半、拡大していくとパノラマ画像上に仮想的LED位置が表示されそれを連結して線で結んでいます。これがセンサに合わせて移動していることがわかると思います。
この動きは非線形(特に極付近で顕著)なので、結構計算が大変なのです。 ## テクスチャ値をLEDに反映
各LEDに表示するべき色がわかれば、これらの色をLEDに与えて光らせます。Isolation Cubeでは、テクスチャマップにRGB値を格納するのではなく、マスクデータとしてバイナリデータを格納しています。
各LEDに表示するべき色がわかれば、これらの色をLEDに与えて光らせます。Isolation Cubeでは、テクスチャマップにRGB値を格納するのではなく、マスクパターンとしてバイナリデータを格納しています。
マスクデータにすることで、表示するパターンを切り替えます。 マスクによって定義された表示領域にはいろいろなカラーパターンを表示するモードを作って表示しています。
|  |  |  |
|  |  |  |
|:---:|:---|:---| 上はIsolation Cubeではこの3つのマスクファイルを読み込んでパターンを表示していました。 @[youtube](https://youtu.be/w3pRgsE_wJo?si=4yHgm0cZ4j1_FOTC)
Isolation Sphereでも同様のマスクファイルを読み込むことを目標とします。
まずはIsolation Sphereでも同様のマスクファイルを読み込み&表示することを目標とします。
# 実装 今回の実装は、基本構造はIsolation Cubeと同一です。 異なる点は、 ・BLE実装 ・マルチコア、共有メモリ関連
・IMU
・ファイル読み込み となります。 特にマルチコアはSPERSENSE導入の決め手なので、頑張って実装したいと思います。 ## BLE実装 実装の現状です。 結論から言うと、ちょっと上手く行っていません。 [このあたり](https://sensing-solution-hackathon-materials.sonyged.com/SPRESENSE_BLE%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86.pdf)を参考にBLEを繋いでみたのですが、  ・・・・ エラーが出ます。


一瞬繋がって、すぐに切れてるっぽい。
これがうまく動かないと外殻球が閉じたときにUIがすべて無線に頼るしかないのですが、時間なないのでひとまず放置で次の処理へ
これがうまく動かないと外殻球が閉じたときにUIが使えないのですが、時間なないのでひとまず放置で次の処理へ
進捗があり次第更新します。
## マルチコア実装
ここが実装のメインになります。
ここが今回のメインになります。 コーディングにはマルチコアで構成されていて、 ・メインコア:UI、SDカード読み書き、BLE処理 ・subcore1〜4:IMUによる回転&Neopixel(WS2812C-2020)表示 ・subcore5:IMUをひたすら回す
## LEDコア LEDコア(subcore1〜4)は、1セット(3パネル、150LED)をひたすら表示し続けるコアです。 「①IMUによる姿勢情報」をもとに「②LEDのレイアウト配置」を回転させ「③パノラマ画像」からマスク情報を抽出して、各LEDのRGBカラーを決定・表示します。 #### ②レイアウト配置 上記3点の情報の中で「②レイアウト配置」はsetupで予め設定します。レイアウト配置では600LEDを4分割し4つのcsvファイルを作成、コア番号ごとに異なるcsvを読み込みます。 各LEDコアのプログラムはこの「②レイアウト配置」だけが異なり、表示するLEDのとその3次元座標をそれぞれで格納します。 #### ①パノラマ画像 「③パノラマ画像」はメインコアで管理し、SDカードから読み込んでパノラマデータを共有フォルダに格納します。 メインコアで設定した共有メモリは各LEDコアから読み込むことができるので、各コアはこの共有メモリからパノラマデータを読み込むことができるように共有フォルダを設定します。 #### ③IMUによる姿勢情報 IMUはsubcore5で計測します。BNO055を使用しているのでAdafruit_BNO055を使用しています。 ここで ``` imu::Quaternion quat = bno.getQuat(); ``` を呼び出してquaternion を取得します。これをshare memoryに保存して、各コアが読み出せるようにしました。  メインコアで設定したshare memoryアドレスを各コアに転送しました。その結果が上記中央付近の ``` ---[0*] SharedMemory Address=@0d0c0000 ``` で、[0*]が各コアを表し、すべてのコアが同一のメモリアドレスを保持していることがわかります。 さらにsetup以降で ``` ----03 0.870056 -0.266418 0.414672 -0.005493 ----04 0.870056 -0.266418 0.414672 -0.005493 ``` から各コアがquaternionの4つのfloatを読み込むことができていることが確認できます。 しかし、しばらくすると次のようなエラーが出始めます。  ``` ERROR: Failed to read from i2c (errno = 2) ERROR: Failed to read from i2c (errno = 2) ``` これはsubcore5で発生しており、値は取れているけれどこのようなエラーも発生している状態です。 実はこのエラーは [github](https://github.com/adafruit/Adafruit_BNO055)のサンプル「[read_all_data](https://github.com/adafruit/Adafruit_BNO055/tree/master/examples/read_all_data)」では発生せず、getQuat()でのみ発生していることまでは突き止めています。 [adafruit公式](https://learn.adafruit.com/adafruit-bno055-absolute-orientation-sensor/arduino-code)などを参照してもこのようなエラーが発生する原因はわかっておらず、現在も調査中です。 どなたか原因などご存知の方がいたらアドバイスください。  上は[github](https://github.com/adafruit/Adafruit_BNO055) のサンプル「[rawdata](https://github.com/adafruit/Adafruit_BNO055/blob/master/examples/rawdata/rawdata.ino)」の結果です。 なんでだろ。。。 # おわりに 今回はIsolation Cubeで正6面体であったLEDディスプレイをより球体に近づけるためLED600個で構成する正12面体ディスプレイIsolation Sphereを作成した。 これまではESP32をマイコンに採用してきました。BLEやWifiが使用できることが大きな理由でしたが、600LEDを効率よく表示するためにコア数の多いSPRESENSEで実現してみました。 IMUなどいくつかまだ上手く動いていないところがありますが、マルチコアの効果も見られました。 引き続き残課題をクリアして正しいIsolation Sphereになるように頑張ります。