Vuforia Studio 重畳AR備忘録：プリント基板でARを活用する

#はじめに

Vuforia Studioでは、現実にデジタルを重畳表示するARを簡単に作成できる。その例として、プリント基板でARを活用する例を、備忘録としておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/7be23eeb-8f9e-9ab1-dc96-a74b8f4c62a4.png)
Arduino、micro:bit、Raspberry Pi、などのマイコンを搭載したプリント基板を使う電子工作が増えていると思うが、いざジャンパーワイヤーなどで接続しようとすると、どこに接続すればいいんだ？ と基板上の小さな印刷文字をじっと見て探したり、配置図と見比べて端から何番目のピンかを数えたり、といった経験は無いだろうか？ ARを活用すると、何と、探すピンの位置をここだよと教えてくれる！
電子工作用の小さなプリント基板ですら苦労するのだから、業務用の数千部品を搭載しているプリント基板でテストピンを探す、テストクリップをつける位置を探す、電磁波の発信源のICを参照指定子から位置を探す、など苦労はさらに多いと思われる。これらも、ARを活用すると、探す位置をここだよと教えてくれる！
この備忘録では、プリント基板上で探す位置を教えてくれる ARの作成を扱う。

#前提

この備忘録は、Vuforia Studioで、ARコンテンツを独力で作成できる人が対象。

#プリント基板でARを活用する例

###プリント基板上に ARで位置を示す矢印を表示する

まずは、プリント基板の表面を写真撮影する。撮影した写真からプリント基板を切り出してイメージファイルとして保存する。以下は、Arduino UNO R3の表面と裏面のそれぞれで写真を切り出した例。後で座標データをもとに矢印を表示させたいので、基板が斜めにならないよう撮影したり、画像編集ソフトで少し回転させるなどしてから、緑色部分の基板外形にそって切り出すのがポイント。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/73e7402f-9be7-3930-5c70-bddfc43e98b8.png)
Vuforia Studioで、新規ARプロジェクトをモバイルで作成する。そして、3Dキャンパスに、イメージターゲットをドラッグ＆ドロップで配置する。X, Y, Z座標は 0 にしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/d5c3e8bb-b311-1bab-9788-7754af554418.png)
画面右の「イメージ」右の緑＋をクリックして、イメージターゲットに使う写真として上記の切り出した写真のうちの表面写真を選択する。尚、ファイル名に日本語などマルチバイト文字があると、パブリッシュしてもイメージターゲットが作成されずにエラーとなるので、ファイル名に注意。そして、「マーカー幅」に Arduino UNO R3の基板外形幅をメートル単位で入力する。（6.858㎝とのことなので、0.06858を入力）
デフォルトだと、AR実行時にイメージターゲットとして認識すると、このイメージの外枠部分に緑色の枠のインジケータが表示される。緑枠表示は基板と重なり見にくくなるので、「トラッキングインジケータを表示」のチェックを外す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/c606bab8-7955-7283-f059-d132a9fa27d5.png)
これで、イメージターゲットを作成できた。

次に、矢印表示を追加する。3Dイメージウィジェットをドラッグ＆ドロップで配置し、リソースとして、矢印のイメージファイルを指定する。矢印のイメージは下向きで矢印以外の部分は透明にしたものを使っている。矢印の高さを 0.04（= 4cm）、幅を 0.005（= 5mm）にする。矢印を立てるためにX回転を 0に変更する。矢印の先がプリント基板の高さになるように、 Y座標が 0でなければ 0に変更して、「ピボット」を中央下にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/c4d5d9dd-2fc8-f4bf-8015-00f2eb31ab59.png)
矢印の 3Dイメージウィジェットを、ドラッグ＆ドロップで水平移動して、プリント基板上で指し示す位置にする。この例では 8番ピンにの位置に移動してみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/12473925-352b-38ac-967f-ed3b0bc1463b.png)
最後に、どの向きからでも矢印が見やすいように、矢印の 3Dイメージウィジェットの「ビルボード」をチェックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/d2cbddd4-a806-87b9-0c07-e043fd1061f9.png)
プレビューして表示を確認してみる。必要であれば、矢印の 3Dイメージウィジェットを水平移動して、さらに調整する。
![PCB_AR_Post1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/359d409a-f605-0b1c-e338-9988bd82c04f.gif)
良さそうなので、画面左のエクスペリエンスの「ThingMark関連付け」を無しに変更してからパブリッシュして、タブレットの Vuforia View アプリで動作確認してみる。QRコードから作成したARを呼出し、カメラ画像にプリント基板（この例では Arduino UNO R3）の表面を映し出す。おぉ、プリント基板に重ね合わせて、矢印がARで表示された！出来た！
![PCB_AR_Post2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/7c7a3078-15c9-bcec-405d-4998a66e26eb.gif)

よく見ると、ピンの差込口は基板よりも少し高いところにある。そのため、矢印の先の位置がちょっと気になることがある。差込口のところに矢印の先を表示させるなら、矢印の 3Dイメージターゲットの高さを指定する Y座標の値を、ソケットの高さ 0.008（=8mm）に設定するとよさそうだ。高さのあるコンポーネントの場合は高さも考慮した方がよさそうだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/44d3b0c4-ee01-b13b-e4ca-7e4251f96fda.png)

###座標値データをもとに位置をARの矢印で表示する

上記の例は、矢印を手作業で水平移動したり、高さの Y座標を入力して表示させた。しかし、基板上で確認したい位置は大量にあり、一つ一つを手作業で配置するのは現実的ではない。そこで、座標値データをもとに表示する仕組みを追加する。

まずは、何を表示しているかを追加する。2Dキャンパスを開き、ラベルウィジェットをドラッグ＆ドロップで配置する。デフォルト表示となるテキストは「----」と変更しておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/93f54003-bfbe-3ec8-1f07-0af1871dd594.png)
座標値データのサンプルを一定時間ごとに切り替えて表示することにする。以下の JavaScriptコードを、プロジェクトの「ホームJS」に追加する。

```javaScript:
// 基板上の位置のデータ例（JSON）　単位は mm
var jsondata1 = [];
var data1 = {};
data1['refdes'] = 'TP_A0'; data1['x'] = 50.8;  data1['z'] = 2.54; data1['y'] = 8;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A1'; data1['x'] = 53.34; data1['z'] = 2.54; data1['y'] = 8;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A2'; data1['x'] = 55.88; data1['z'] = 2.54; data1['y'] = 8;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A3'; data1['x'] = 58.42; data1['z'] = 2.54; data1['y'] = 8;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A4'; data1['x'] = 60.96; data1['z'] = 2.54; data1['y'] = 8;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A5'; data1['x'] = 63.5;  data1['z'] = 2.54; data1['y'] = 8;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_AREF'; data1['x'] = 23.876; data1['z'] = 50.8; data1['y'] = 8;
jsondata1.push(data1);
var datalength1 = jsondata1.length;

// プレビューの時に true
var previewMode = twx.app.isPreview();

var brd_W = 68.58 *0.001; // 基板幅（mm -> メートル）
var brd_D = 53.34 *0.001; // 基板奥行（mm -> メートル）

$timeout(function() {
  $scope.setWidgetProp('3DImage-1','x', 0); // 横
  $scope.setWidgetProp('3DImage-1','z', 0); // 縦
  $scope.setWidgetProp('3DImage-1','y', 0); // 高さ
  $scope.startAnimation();
 },1000);

// 一定時間ごとの処理
var i = 0;
$scope.startAnimation = function(){
  var timerId = setInterval(function() {
    $scope.$apply(function(){
      if(i < datalength1){
        // 名前を表示
        $scope.setWidgetProp('label-1', 'text', jsondata1[i]['refdes']); //　高さ 0 （プレビュー時）
        // 位置を設定（位置データの原点が基板表面の左下隅のため、Studio用の位置を下記の通り算出）
        $scope.setWidgetProp('3DImage-1','x',  Number(jsondata1[i]['x']) *0.001 - brd_W /2); // 横位置（mm -> メートル）
        $scope.setWidgetProp('3DImage-1','z', -Number(jsondata1[i]['z']) *0.001 + brd_D /2); // 縦位置（mm -> メートル）
        if(previewMode){
          $scope.setWidgetProp('3DImage-1','y',  0); //　高さ 0 （プレビュー時）
        }
        else {
          $scope.setWidgetProp('3DImage-1','y',  Number(jsondata1[i]['y']) *0.001); // 高さ
        }
        i++;
      }
    })
  }, 3000 ); // 3秒ごと
}
```
プレビューを実行してみる。データを使い、A0 ～ A5、AREFの名前と位置を順に表示している！ 尚、プレビュー時には矢印の先の高さは 0になるようにしている。
![PCB_AR_Post42.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/7ef265bd-ced2-d7c6-5526-7037794d648a.gif)

パブリッシュして、実際の基板に対する ARを試してみる。位置を ARで表示できている！
![PCB_AR_Post5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/f82469a3-c3c0-2326-c00a-1e79eb2804ff.gif)
A5の位置のとき
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/7be23eeb-8f9e-9ab1-dc96-a74b8f4c62a4.png)

位置データは、Arduino UNO R3 のCADデータからエクスポートすると取得できる。デフォルトの単位は mil とのことで、*0.0254 で mm 単位になる。その値を今回のサンプルデータとしている。一般に基板のCADデータからは位置データをテキスト出力できる。業務用基板にもこの ARは活用できそうだ。

###裏面も試してみる

これまでは基板の表面の場合だった。裏面でも同様に ARを作成して表示できるだろうから、実際に試してみる。一つの ARの中で、ビューを切り替えて裏面対応にしてみる。
まずは、デフォルトの「ホーム」ビューの名前を変更する。画面左の「ホーム」をクリックして、画面右上の「名前変更」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/77c590d6-6dbe-b22e-2f34-9e8e82bab5e7.png)
名前を「表面」に変更する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/d4638852-c521-535e-4acb-d94a9cd14ff7.png)
次に、ビューの右の＋をクリックして、新規ビューとしてタイプを ARで「裏面」を作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/96242d07-fb89-6476-f4a8-90f1b74457fa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/6f97dc94-62e5-0b46-5c15-7d2ad15b8425.png)
「裏面」ビューが作成された。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/2a00f55b-74dd-f9b2-bbc4-7f595d9db1f5.png)
プレビューしてみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/cd2c9d0a-ca61-161d-a7f1-a0b75a6c50ec.png)
Menuを表示すると、ビューのリストが表示されるので、「裏面」を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/fec65793-57a4-27b5-ba77-9139421339f2.png)
「裏面」に切り替わった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/c6b978e4-4f49-d54f-dc99-d01861050115.png)
尚、この Menuによるビュー切り替えは、画面左の「情報」にて、ナビゲーションメニューを表示を有効にしていると可能だが、非表示にすると利用できない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/f4b3cdd0-0898-0921-a0c9-a1bd0a5fa42c.png)

あとは、表面と同様に作成していくことで、裏面用の AR表示を追加できる。
ただし、表面と異なる点がある。
表面のサンプルのピン位置は、表面ではソケットの高さを考慮したが、裏面にはソケットは無いので高さ（Y座標）は 0 でよい。

```javaScript:
// 基板上の位置のデータ例（JSON）　単位は mm
var jsondata1 = [];
var data1 = {};
data1['refdes'] = 'TP_A0'; data1['x'] = 50.8;  data1['z'] = 2.54; data1['y'] = 0;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A1'; data1['x'] = 53.34; data1['z'] = 2.54; data1['y'] = 0;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A2'; data1['x'] = 55.88; data1['z'] = 2.54; data1['y'] = 0;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A3'; data1['x'] = 58.42; data1['z'] = 2.54; data1['y'] = 0;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A4'; data1['x'] = 60.96; data1['z'] = 2.54; data1['y'] = 0;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_A5'; data1['x'] = 63.5;  data1['z'] = 2.54; data1['y'] = 0;
jsondata1.push(data1);
data1 = {};
data1['refdes'] = 'TP_AREF'; data1['x'] = 23.876; data1['z'] = 50.8; data1['y'] = 0;
jsondata1.push(data1);
var datalength1 = jsondata1.length;

```
また、裏面では左右反転するので、横位置の計算がその分異なる。

```javaScript:
        // 位置を設定（位置データの原点が基板表面の左下隅のため、Studio用の位置を下記の通り算出）
        //$scope.setWidgetProp('3DImage-1','x',  Number(jsondata1[i]['x']) *0.001 - brd_W /2); // 横位置（mm -> メートル） 表面用
        $scope.setWidgetProp('3DImage-1','x', -Number(jsondata1[i]['x']) *0.001 + brd_W /2); // 横位置（mm -> メートル） 裏面用
        $scope.setWidgetProp('3DImage-1','z', -Number(jsondata1[i]['z']) *0.001 + brd_D /2); // 縦位置（mm -> メートル）
        if(previewMode){
          $scope.setWidgetProp('3DImage-1','y',  0); //　高さ 0 （プレビュー時）
        }
        else {
          $scope.setWidgetProp('3DImage-1','y',  Number(jsondata1[i]['y']) *0.001); // 高さ
        }

```
プレビューで確認してみる。おぉ！ ちゃんと正しい位置を示している！
![PCB_AR_Post6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/e0dcfb39-5c43-f9ff-f357-b51285c4f205.gif)
パブリッシュして、実基板で試してみる。ちゃんと正しい位置を示している！　出来た！
![PCB_AR_Post7.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297270/45c77ed4-a0ed-b6a2-bb4d-25033d7033fb.gif)


#おわりに

Vuforia Studioで、イメージターゲットを使い、プリント基板実物に対してピン位置などを 矢印の ARで重ね合わせ表示できる例を記載した。位置のデータは、基板のCADデータからエクスポートした値を加工して使用できそうだ。
尚、電子工作の基板にしても、業務用基板にしても、誤った位置へ接続して通電することで壊してしまうことがある点は、くれぐれも注意が必要。位置が密集する箇所などで、位置表示が誤っていないか、あいまいな位置になっていないかなど、ARの作成結果をテスト評価する工程は必要だろう。
本投稿を参考にして実基板に ARを活用するにあたっては、本投稿は一切の責任を負わないのでご注意を。

今回の続きとして、位置データをテキストファイルから読み取る変更を実施した。下記の参考欄にその備忘録へのリンクを掲載した。

#注意

ファイル名に日本語などマルチバイト文字があると、パブリッシュしてもイメージターゲットが作成されずにエラーとなるので、イメージターゲットに使うファイル名に注意。

#参考

・[Vuforia Studio 重畳AR備忘録：プリント基板でARを活用する その２　位置データをテキストファイルから読み取る](https://qiita.com/tm181001/items/560ef489fb451bfa06d7)

・[Vuforia Studio 重畳AR備忘録：プリント基板でARを活用する その３　位置をリストから選択する](https://qiita.com/tm181001/items/6cfa10c6fe248dd72ddf)