【This Web Policy】
You should make JavaScript available to read this page.
Please enable the execution of JavaScript!




















































【NyARToolkitCSで拡張現実センサを作ってみよう】

トランジスタ技術【増刊】『今すぐ使える パソコン計測USBマイコン基板』の付録で作る~
    【リンクフリー】 私設研究所ネオテックラボ Neo-Tech-Lab.co.uk
【記載者】 【私設研究所Neo-Tech-Lab】 上田 智章
作成日 2011/01/08
更新 2011/04/03
最終更新 2011/05/06
ここにチェックボックス型外部コンテンツ・メニューが入ります。




【ブラウザはGoogle Chrome】
●最新版ダウンロードはこちら

【お知らせ】

Windows7でChromeがフリーズを起こす場合は、コントロールパネル⇒管理ツール⇒サービス でWindows Media Player Network Sharing Serviceを停止かつ無効にします。Micorosoftのバグです。


【CQ出版トランジスタ技術2012年8月号】『特集 センサとカメラで3次元センシング』

おまたせしました。Kinectを使った拡張現実センシング・システムのソースを公開します。
いままで伏せていましたデプス・カメラの動作原理、骨格追尾の残像表示、ジェスチャー判定アルゴリズムなど
Kinect関連技術を一挙公開いたします。Visual C#ソースもプロジェクト・ファイルごと公開します。
ダウンロードサポートページのエントリはここ[http://www.neo-tech-lab.jp/ARsensing]です。

【CQ出版トランジスタ技術2012年8月号】

『Kinectが変えるセンシングの世界』
【C#】

CQ出版のトランジスタ技術2012年8月号(7月10日発売)のKinect特集の記事サポート・ページです。記事では、Kinectの動作原理、PCプロジェクタとウェブカメラを組み合わせて構成したデプスカメラの実験、骨格追尾で手の奇跡をトラッキングして円運動を検出してジェスチャー判定する方法などを詳しく記載しています。是非、お読みください。
Kinectの一般的なプログラミング方法に関しても記述しています。サンプル・プログラムは上記ページからダウンロードすることができます。
加えて上記ページで幾つかの拡張現実センシングについては動画を公開しています。

【目次】


 ●ようこそ拡張現実 (AR: Augmented Reality) の世界へ
 ●用意するもの
 ●事前準備作業1(無償ツールで開発環境を整える)
 ●NyARToolKitCSを使おう!
 ●まずはNyARToolKitCSをダウンロードしよう!
 ●試作した拡張現実センサの仕様
 ●温度センシングで拡張現実
 ●拡張現実センシングの応用(埋設配管の可視化など)
 ●トランジスタ技術2011年2月号の拡張現実センサ
 ●トランジスタ技術2011年2月号の訂正部分

【ダウンロード】 初めにここをお読みください。
 ●温度センシングのデモプログラム
 ●高周波磁界強度/超音波センシングのデモプログラム
 ■高周波磁界強度/超音波センシング用CPLD設計情報


【関連する技術に関するサイト内リンク】

 ●3次元CG ●ポリゴンフィルとテクスチャーマッピング
 ●透視変換について ●光源計算について
 ●アナログ基礎 ●ディジタル基礎 ●CPLD設計
 ●MikuMikuDanceのPMDで拡張現実

【オリジナル拡張現実(マーカー式AR)のコーディングを開始】

■『オリジナル・アルゴリズムを考案し、マーカー式拡張現実のコーディングを開始』【VBA, C#, JavaScript】
 最終的には、ARToolKitとKinectを独自アルゴリズムで統合したμ-ARsensorを開発の予定です。

【ARを支える他の技術】

【Kinect】【OpenNIの導入方法】【C#】
■『Google翻訳の音声合成(Text-To-Speech: TTS)API』【JavaScript】
■『Webで使う音声合成API μ-iVoice』【JavaScript】


【NyARToolKitCS】『最新版Version4.0.0がリリース』

 2012年3月にNyARToolKitCSのVersion4がリリースされています。

【NyARToolKitCS】『Frameworks4.0でManaged DirectXを使う方法(app.config)について』

 『Frameworks4.0でManaged DirectXを使う方法(app.config)について』をKinectセンサのドキュメントの方にアップしました。
NyARToolKitCSでもManaged DirectXを使いますので、参考まで。

【μ-ARsensor】『マーカー式拡張現実のオリジナル・アルゴリズムを構築開始』

 (第1次試作2012/02/05/~)
Microsoft社のKinectセンサは、骨格抽出、デプスバッファに加え、画像キャプチャーも行うことができます。
骨格抽出とNyARToolKitのようなマーカー方式を両方同時に使いたいと考えたのですが、GPLライセンスには全ソース公開義務の制約があります。
MicrosoftのKinect for Windows SDKに頼る限り、それは不可能だし。
なら、『一からオリジナル・アルゴリズムでコーディングすればいいんじゃないの?』と考えました。
現状、キャプチャー画像からの太線枠抽出(矩形波相関トリガ・フィルタ)、輪郭点座標リストから線分抽出(オリジナル高速最小二乗法)を
Excel VBAでコーディングして動作を確認。将来的にC#、JavaScriptに移植の予定です。
この線分抽出の部分は世の中ではHough変換と呼ばれ、候補ベクトルを投票していく多数決型らしいです。
輪郭線の8方向探索とか、線分認定とか、対象外候補を消すリスト管理とか...
なんか20年前の学会で発表されていたような(あんまり考えていない)逆問題プログラムみたいな感じで
Hough変換は好きになれそうもないのでソース無視して自力で書きます。
2012年7月中旬に原理とソース、動画等を公開しますが、円または円弧を部分画素から推定するロバストな推定アルゴリズムを開発しました。
Kinectのジェスチャー検出に実装してみたところ、このアルゴリズムは矩形黒枠マーカー検出方式に比べ処理負荷が格段に軽いのです。
つまり、ARToolKitの『輪郭線を構成する画素リストの抽出』と『矩形4頂点の抽出』のアルゴリズムに改良の余地がある事を示しています。
(守秘義務を遵守してくださる方には、限定公開YouTube動画を閲覧していただいています。)



■記載日2012年3月14日■09:41頃記載
【Microsoft社からKinect for Windows SDK Version1が正式に発表されています】
2012年2月1日に、Microsoft社は、Kinect for Windowsという定価24,800円のパソコン用汎用版Kinectセンサを発表しました。
深度測定は、従来のDefaultモードでは800mm~4000mmの範囲でしたが、Nearモードが追加されました。範囲は400mm~3000mmです。
これに合わせて、KINECT for Windows SDK Version1もリリースされています。(このページの更新を忘れていました。)
音声認識Speech API(英語)と統合され、仰角制御、音声方向検出機能などが追加されています。
KINECT for Windows SDKに関する情報やサンプルプログラムも用意しましたので、ご参考まで。

■記載日2011年7月2日■19:43頃記載
【Microsoft社からKinect for Windows SDK Betaが正式に発表されています】
以前にMicrosoft社が春にSDKを公開予定とのアナウンスがありましたが、2011年6月16日にKinect for Windows SDK Betaが発表されていました。早速、ダウンロードしてデモプログラム2種を動かしてみました。
現在の所、このSDKはWindows7専用です。32bitOS用と64bitOS用の両方が用意されています。インストールは超簡単です。手順など書く必要は全くないレベルだと思います。5分もかからずインストールを完了させることができます。スケルトン抽出のデモと、上から落ちてくるシェープで遊ぶゲームの2種類のデモプログラムが用意されています。C++だけでなく、C#, Visual Basic, JScript等の.net系に対応しているようです。

【サンプルプログラム】 公開日 /2011/10/15/ 21:34

骨格抽出のWPFアプリケーションの事例はSDKに添付されていますが、Formアプリケーションのサンプルを作成しましたので下記URLにプロジェクトファイルを公開しておきます。
  ●骨格抽出サンプル(Kinect for Windows SDK版)
   Form内のソースも別ページに示します。


【WebGLのページ】

JavaScriptでリアルタイム3次元グラフィックスが楽しめる時代の到来までもうすぐです。
現在、Google Chrome12、Mozilla FireFox5、及び開発版のSafari、Operaで使うことができます。
WebGLは、JavaScriptエンジンv8とDirectXを利用して高速化したOpenGLを融合して構成されたものです。
2011年5月に指摘されたセキュリティー問題はありますが、処理速度の高速性から利便性も高いので、将来的には普及するのではないでしょうか?
 ●WebGLとは? ●事前準備:利用できるウェブブラウザは? ●事前準備:FireFox5でWebGLが動作しない場合には?
 ●WebGLのサンプル ●WebGLのデモ ●クロスドメインテクスチャーを将来も利用できる方法

  ①  ②
  ③  ④
【動画1】AR(拡張現実)センサの動作確認テスト動画
  ①高周波磁界・超音波プローブ【第3次試作】その1
 30万画素ウェブ・カメラ使用
  ②高周波磁界・超音波プローブ【第3次試作】その2
 300万画素ウェブ・カメラ使用
  ③高周波磁界・超音波プローブ【第3次試作】その3
 DirectXプログラミングの練習(ちびミク)
  ④高周波磁界・超音波プローブ【第3次試作】その4
 DirectXプログラミングの練習(3Dゲージ)

【ようこそ拡張現実(AR: Augmented Reality)の世界へ】

 映画『アバター』で有名になった拡張現実(AR: Augmented Reality)は、実写画像上に3次元グラフィックス画像を重畳させて現実にそこに存在するかのように見せる映像技術のひとつです。映画は最新機器を使って莫大な費用を投じて作られたのですが、意外にも私達の周囲に普及しているコンピュータ環境でも手軽に拡張現実(AR)を利用できる段階にまで到達しています。無料のインターネット通話 Skype、Google GMailビデオチャット等の普及で、家庭用パソコンにはウェブ・カメラが標準実装されていることも多く、USB接続のWebカメラを別途購入しても30万画素級で充分だし、130万画素で1000円切るモデルもあったりする。パソコン自体の処理性能向上も著しく、OSが肥大化してきたので知らない人も多いかもしれませんが、現在のWindows7機は10年前のちょっとしたスーパーコンピュータ(当時5000万円級)並みの性能を持っています。
 このページでは、センサで計測した数値データを可視化して実写画像上にリアルタイムに3次元表示させる方法について紹介します。左にテスト動画へのリンクを示しました。実写画像上に単に計測数値を表示する従来方法ではなく、予め登録されたマーカーを実写画像からリアルタイムに検出し、マーカーの3次元座標や向きに基づいて実写画像上のセンサ位置(座標)に測定データに対応した3次元グラフィックスを表示させています。センサは1チャンネルしかなくとも過去に測定したデータも同時に表示することでなんとなく3次元分布状態も把握できてしまう技術なのです。ソフトウェア開発ツールは全て無償のものを利用します。
 加えて無償ソフトウェア開発ツールが拡充し、ソース公開も珍しい事ではなくなったので、安価なハードウェアを買い揃えれば、先端技術を使ったシステムの開発も行えるようになりました。


【写真】CQ出版トランジスタ技術【増刊】
 『今すぐ使える パソコン計測USBマイコン基板』
 の付録基板


【写真】ウェブカメラの例
USBインターフェースを持ったWebカメラなら数年前の30万画素程度のもので十分です。

【用意するもの】

 まず以下のハードウェアを用意する必要があります。

【Windowsパソコン】

 一応、手近なWindowsマシン(XP, Vista, Windows7)で稼働中あるいは引退保管直後の10台を使ってテストしてみましたが問題ありませんでした。もっと古い機種は故障、引退、第二の人生(知り合いにあげた)などで調べていません。取り敢えず、USBインターフェースと後述のソフトウェア開発ツールをインストールするのに十分な容量があるハードディスクを実装していること、DirectXが利用可能であることが重要です。
 ●Dell Vostro1000 (OS: Windows Vista) ●Dell Vostro1400 (OS: Windows Vista)
 ●Dell Vostro220S (OS: Windows XP) ●Dell Vostro220S (OS: Windows Vista)
 ●SONY Vaio VGC LB 2 (OS: Windows Vista) ●SONY Vaio VPCEB28FJ (OS: Windows7)
 ●NEC Lavie M LM550/CS6B (OS: Windows7)
 ●その他{NEC(OS: XP), Fujitsu(OS: Vista), Sotec(OS: XP)}

【パソコン計測USBマイコン基板】

 CQ出版トランジスタ技術【増刊】『今すぐ使える パソコン計測USBマイコン基板』(定価3,990円 付録基板とCD-ROM付)には左側の写真に示す付録基板が付いており、ミニUSBケーブル(別途購入が必要。コンビニか大手100円ショップでも購入可能。)で接続すればパソコンで簡単な計測が行えます。デバイスドライバーは付録のCD-ROMに収録されています。雑誌記事に従ってインストールと動作確認をしてください。インストール方法は第2章に書かれています。特にWindows7はドライバーの認識に不具合があるので記事の通りに実行してください。
 基板にはAnalog Devices社高分解能24ビットΔΣ(デルタ・シグマ)型A-DコンバータIC AD7793とMicrochip社のUSBマイコン PIC18F14K50が実装されていて、アナログ入力3チャンネルの他、AD7793内蔵の温度センサ(誤差±2℃、計測範囲0℃~70℃)を使って温度計測も行うことができます。また、7ビットのディジタル入出力も用意されており、システムを容易に拡張することができる優れものです。CD-ROMに同梱のデモプログラム『USB-ADC-GENERAL.exe』で一度動作を確認しておくとよいでしょう。

【Webカメラ】

 パソコン内蔵、またはUSBインターフェース接続のWebカメラであれば、数年前の30万画素程度のものでも動作しました。オートフォーカスは途中で画面が動いて見づらいので、固定フォーカスに設定できるものが好ましいです。
 デバイスドライバーは各ウェブカメラに標準添付のものを用いてインストールと動作確認を行ってください。

【プリンタ】

 実験で使用するマーカーを印刷するために使います。モノクロで名刺サイズの大きさで印刷できるものであれば十分です。


ARToolkitはマーカー位置に上の様な3次元グラフィックスを動画表示させるために使われることが多い。
下のポリゴン・モデルの場合、頂点数12354個、面(三角形)数22961ポリゴン、材質数17だが、NyARToolkitCSはリアルタイムにカメラ画像からマーカーを検出し、表示することができる。
(このモデルは樋口優氏製作のMikuMikuDanceの標準モデルをkiyo Pさんが改造された『ちびミク』(ver.1.3)を微修正したものです。勿論、NyARToolkitCSのSimpleLiteDirect3dを改編しただけです。内蔵温度センサのデモでは最初はこの娘にドライヤーをかける予定だったんですが、いろいろ間に合いませんでした。<(_ _)>
【只今、カメ足作業中】
【YouTube】事前テスト1事前テスト2【3Dモデル表示】 【YouTube】事前テスト3【PMDモデル表示】
【YouTube】事前テスト4【Sphereマップ】
【YouTube】事前テスト5【表情テスト】
【ソース公開中】【MMDのPMDで拡張現実:Sphere含む】
【ソース公開中】【MMDのPMDで拡張現実:表情テスト】
 


■ボーン処理でいろいろ苦戦中。手足を動かすのは表情と違ってちょっと大変です。モデルにモーションをどうやって与えるかという問題です。結局、MikuMikuDanceからVMDデータをインポートするか、Kinectセンサからモーションデータを入力するかのいずれかだと結論。現在、Kinectで遊んでいる状態です。しかし、Kinect使うのなら、NyARToolkitCSを使う必要はなかったのでは......うーむ。

【事前準備作業1】

 パソコン側ソフトウェア開発環境を整えるためにMicrosoft社から無償ダウンロードできる開発ツールをインストールします。

【Visual Studio 2008 Express - Visual C# 2008 Express Edition】

 C#で拡張現実を簡単に実現することができる公開ツール(NyARToolKitCS)はManaged DirectXを使用しています。ところが、この原稿を書いている時点(2011年1月3日現在)、Visual Studio 2010が登場しているのですが、以前のバージョンC# 2008 Expressで利用できたManaged DirectXをデフォルトでは使うことができなくなってしまっています。奥の手は何通りか存在するようなのですが、一番簡単な方法を選びました。再コンパイルを行わずに、この便利なツールをそのまま利用するためにVisual Studio 2008 ExpressのVisual C# 2008 Express Editionをここからダウンロードします。
【追記】2011年1月11日02:00
 ....と書きましたが、2011年1月11日02:00現在、Visual C# 2010 Express EditionでNyARToolkitCSを再ビルドせずにそのまま使う方法を発見できました。【メモ】を下に書きましたので、ご参考まで。

【DirectX エンドユーザーランタイム】

 Windowsマシンといっても一概に同じ設定ではないようで、C#でプログラムをコンパイル時に『D3D9_xx.dllが見つかりません』のようなエラーメッセージが出る場合があります。このような場合にDirectX エンドユーザーランタイムのウェブインストーラーでインストールします。

【Microsoft .NET Framework 3.5】

 本来はVisual C# 2008 Express Editionをインストールした時点で一緒にインストールされているはずですが、エラーがでてしまった場合にはMicrosoft .NET Framework 3.5をインストールしてください。

【メモ】【Visual C# 2010 Express EditionでNyARToolkitCSを動かすには】

 ■2011年1月11日02:00記載。Visual C# 2010 Express EditionでManaged DirectXを使い、NyARToolkitCSを動かす方法を試行錯誤の末なんとか成功したので、メモ。
  1) Visual C# 2010 Express Editionで旧ソリューションを開くと自動的にプロジェクトを変換してくれる。だが、ビルドやデバッグ実行時にエラーが出る。
  2) 原因はManaged DirectXの『参照』とプロジェクトのプロパティーの『対象のフレームワーク』のバージョンにある。
  3) 自動変換された旧ソリューションを開いたら、ソリューション・エクスプローラーで『参照設定』をダブルクリックして詳細を表示し、Microsoft.DirectX, Microsoft.DirectX.Direct3D, Microsoft.DirectX.Direct3DX, Microsoft.DirectX.DirectDrawを選択してDeleteキーを押して参照設定から一旦削除する。
  4) Visual C# 2010のメニューから、プロジェクト(P)-参照の追加(F)で『参照の追加』ダイアログを表示。
  5) 『参照』タブをクリックして選択。
  6) C:/Windows/Microsoft.NET/DirectX for Managed Code/1.0.2902.0/ の下に存在するMicrosoft.DirectX.dll, Microsoft.DirectX.Direct3D.dll, Microsoft.DirectX.DirectDraw.dllを選択し、『OK』ボタンをクリック。
  7) C:/Windows/Microsoft.NET/DirectX for Managed Code/1.0.2909.0/ の下に存在するMicrosoft.DirectX.Direct3DX.dllを選択して、『OK』ボタンをクリック。
  8) Visual C# 2010 Expressのメニューで『プログラム(P)』-『*******のプロパティー(P)』でプログラムのプロパティー・ダイアログを開く。
  9)『アプリケーション』タブを選択して『対象のフレームワーク(G)』を『.NET Framework 3.5』に設定し、『ファイル(F)』-『全てを保存(L)』で保存したら一度Visual C# 2010を終了する。
  10)Visual C# 2010を再起動後に、『ビルド(B)』-『ソリューションのビルド(B)』を実行したら、OK。

 ■以上の操作により、旧プロジェクト・ファイルでもVisual C# 2010 Expressで、NyARToolkitCSをそのまま再ビルドすることなく、使うことができた。
 ■Visual C# 2010でManaged DirectXを従来のように使うときも、Managed DirectX(Direct3D)の『参照設定』と、プロジェクトのプロパティーの『対象のフレームワーク』を3.5にする点だけだ。

 ■Visual C# 2010 ExpressでNyARToolkitCSをそのまま使うことに成功。スクリーンショットはSimpleLiteDirectD3dのキューブ表示位置を変更しただけの改編版テストの様子。
 Managed DirectXの参照設定を取り直し、対象のフレームワークのバージョンを3.5にするだけ。詳細は右記を参考に。

【Kinectセンサについて】

2011/04/03 追記

■Microsoft社XBOX用モーションキャプチャーセンサKinect

■Visual C#でKinectアプリケーション開発が行えます。
 【記事編集中】Kinect Sensorを利用して拡張現実センサを作る試み

【注】Kinect(キネクト)センサは、Microsoft社のXBOX360用のモーションキャプチャーセンサ(1万5千円弱)で単独で購入することができます。640×480画素のRGBカメラのほか、赤外線カメラも搭載しており、毎秒30フレームの速度で深さ情報(depth buffer)を得る事ができます。USBインターフェースなのでWindowsパソコンに接続可能です。2011年1月6日にC#で使える暫定版OpenNI.net.dllがオープンソースで公開されています。OpenNI.net.dllを使えば上の写真のように基本的な骨格情報(関節の3次元座標)をリアルタイムに得られます。下の動画は磁界・超音波センサに応用してみた事例です。トランジスタ技術2011年2月号の拡張現実センサの進化形に該当します。(詳細は別ページで記載の予定。現在、導入メモ等と一部ソースを記載。[2011/02/05 - 2011/03/31])


【図1.a】試作した拡張現実センサの表示例
     (検出レベル200未満の時)

【図1.b】試作した拡張現実センサの表示例
     (検出レベル200以上の時)

【NyARToolkitCSを使おう!】

 ARToolkitは、Augmented Reality(拡張現実)アプリケーションを開発するためのC言語ライブラリの名称で、奈良先端科学技術大学院大学の加藤博一教授が米国ワシントン大学のHuman Interface Technology(HIT) Labに留学中に開発されました。開発言語はC言語で3次元グラフィックス・ライブラリはOpen GLが採用されています。これは当時はSGI(米国シリコングラフィックス社)やSun等の3次元グラフィック・ワークステーション全盛の時代であったためであろうと想像します。このライブラリはオープン・ソースであり、C++のソースが公開されています。非商用、商用のいずれでも利用することができるのですが、バイナリを公開する場合にはソース公開を行う必要があります。
 Microsoft社の.net系Visual C#にも移植されており、NyARToolkitCSでは3次元グラフィックスをManaged DirectX (Direct3D)で利用することが可能になっています。このためパソコンでアプリケーションを開発し易い環境が整っているのです。C#から利用できるmanaged DirectXにはDirectShowが未だ実装されていないので、NyARToolkitCSはカメラからの画像キャプチャーをDirectShowを使って行うためのDirectShowLib-2005.dllを提供しています。ARToolkitのライブラリとしてはNyARToolkitCS.dllとNyARToolkitCSUtils.dllが存在しています。プログラミング初心者にもとても使い易いライブラリで、サンプルソースをわずかに修正するだけで、アプリケーション・ソフトウェアを簡単に試作することができます。

 私もDirectX及びC#を全く理解しない(1冊も本を読まない)状態でNyARToolkitCSで遊んでみたのですが、構造がとてもシンプルで理解が容易でした。使わないで時間が経つと忘れてしまうので、メモ代りにここに記載することにしました。
 私がNyARToolkitCSに興味を持ったのは、『マーカー(測定)位置をカメラ画像から簡単に得られる』という点です。精確にはカメラのキャプチャー映像からマーカーというグラフィックスオブジェクトに対する相関性が高いポジションへの座標変換マトリックスが得られるので、マーカーの3次元座標だけでなく、法線方向もわかるとても便利なツールになっています。大学の研究ミッションでは、磁気ハイパーサーミア治療装置(癌治療に用いる)やセンチネルリンパ節検出磁気プローブ等の医療用電子機器を研究しているので、趣味と仕事を絡めてNyARToolkitCSを楽しんでみることにしたのです。新しいものをマスターするには遊んでみるのが早道だと思いますしね。

 追試したいと考える方のことを考え、付録基板に内蔵されている温度センサを使うデモ・プログラムを製作しました。全く電子回路を製作する必要なく、トランジスタ技術【増刊】『今すぐ使える パソコン計測USBマイコン基板』の付録基板をそのまま使ってソースを公開いたします。
 本格的なセンサも3種類試作しました。センサは温度でも照度計でもなんでもよかったのですが、少しでも研究に近い高周波磁界(10kHz~100kHz)と超音波(20kHz~60kHz)の両方が測定できる特別なガン・プローブ(ピストル型)を最初に製作しました。測定値はADC(A/Dコンバータ)で読込み、付録基板のUSBインターフェース経由でパソコンに取り込んでいます。と言っても仮想シリアル通信インターフェースなので通信プログラムは簡単です。ソース内でADCの初期化やサンプリング処理を関数化しておきましたので、他の計測システムに移植するのは容易だろうと思います。
 上述の試作で十分理解できたので、研究用センチネルリンパ節検出センサ・プローブも拡張現実対応のものを製作しました。今後、心臓内の電気的活動によって発生する地磁気の100万分の1という微弱な磁界(心磁:MCG Magnetocardiogram)を常温センサで捕捉する虚血性心疾患診断装置に応用する予定です。これらは別の機会に公開したいと思います。

【NyARToolkitCS】  ホームページ
         NyARToolkitCSトップページ
【ARToolkit】(本家) ホームページ『英語』
         ARToolkitダウンロード『英語』


【図2】マーカーの例  【図3】SimpleLiteD3Dの表示例


【図4】キャプチャ画像と3Dオブジェクト表示用Form1


【図5】キャプチャデバイスを選択用Form2

【まずはNyARToolkitCSをダウンロードしよう】

 まずはsourceforgeからNyARToolkitCSの最新版ソースをダウンロードしましょう。2010年11月21日現在、NyARToolkitCS-2.5.2.zip (1.3MB, 2010-04-26 11:59)が最新版でした。
 解凍すると、data, extlib, forFW2.0, forWM5の4つのフォルダがあります。forFW2.0がWindowsマシンの.net Framework2.0をベースに開発された部分で、NyARToolkitCS, NyARToolkitCS.sandbox, NyARToolkitCSUtils, sample, toolsの5つのフォルダがあります。sampleフォルダには5つのサンプルプログラムのプロジェクト・ファイルが格納されています。
 サンプルプログラムはCaptureTest, RawTest, SimpleLiteDirect3d, SingleARMarker, SingleNyIdMarkerDirect3dが用意されているのですが、私はSimpleLiteDirect3dというプログラムを修正利用しました。この『SimpleLiteDirect3d』はHiroと書かれたマーカーの位置にキューブを出現させるプログラムです。プリンタで白い紙に印刷してマーカーを作ります。マーカー作成上の注意点としては、黒い枠部分だけでなく、周囲に白い部分を残しておかないと、マーカー検出成功率が低下してしまいます。ご注意ください。
 デモ・プログラムSimpleLiteDirect3dは、Form1.csとForm2.cs、Program.cs、SimpleLiteD3d.csにより構成されています。
Form2はプログラム起動後、キャプチャーデバイスの検出を行い、検出したデバイスから利用するものを選択するダイアログとして使います。キャプチャーデバイスの選択後にForm1が起動し、カメラ画像がウィンドウ内に表示されます。
 図6に示すフロー図にあるようにカメラ映像のキャプチャー完了後に発生するイベントOnBufferMainLoopと非同期に実行されるプログラム構造となっています。
 OnBufferイベントではカメラ映像をARバッファにコピーし、マーカーの検出処理を行います。マーカー検出に成功した場合には座標変換マトリックスやフラグを保存し、カメラ画像も保存します。
 MainLoop内ではOnBufferイベント内で保存されたキャプチャ画像を直接描画後、マーカー検出確度が0.4以上の場合に、ARから得られた座標変換マトリックスを使って予め用意されている3Dオブジェクトを表示する処理が行われます。
 sampleプログラムSimpleLiteD3Dでは、3DグラフィックオブジェクトはCustomVertex.PositionColored形式の8頂点、12個の三角形で構成されたキューブです。
 sampleプログラムSimpleLiteD3Dを実行し、カメラで図2のマーカーを撮影すると、図3のように実写画像上にグローシェーディングのかかったキューブがリアルタイムに表示されます。


【図6】SimpleLiteD3dの概略フロー

【備考】
 MetasequoiaLE R2.4はOsamu Mizuno氏が開発された3次元ポリゴンモデリング・ソフトウェアMetasequoiaのフリー版の名称です。DirectXで使える.x(テキストファイル)形式のexport機能を利用することができます。これを使えば法線ベクトルの出力も行うことができるので便利です。
【参照図1】MetasequoiaLE R.24の表示画面の例

【試作した拡張現実センサの仕様】

 1チャンネルのセンサ・プローブに印刷したマーカーを貼り付けます。実験ならセロテープや両面テープで仮どめすれば良いでしょう。マーカー貼付け位置からセンサー実装位置までの相対的な位置関係は変化しないので、マーカー検出位置からオフセット移動させた位置に適当な大きさの球体を表示させ、計測値を256レベルに変換し、表示色を変えて表示させることにしました。
 ソフトウェアでFIFO(First-In First-Out 最初に入力されたデータが最初に出てくるレジスタ)を実現し、所定個数の表示色と座標変換マトリックスのデータを保存しておくことで、過去の測定結果も残像のように残す表示モードを実現しました。残像表示させると、計測結果が重なってしまい、現在値がわかりにくいので、現在値を示す3次元カラーゲージも加えました。256色のカラーバーの上側に現在値に対応する表示色の球体が表示されるようにしました。後はお遊びで、マーカー自身をグラフィックスで覆い隠し、検出レベルによって表示を変更する簡易なエージェント(Agent)表示を追加してみた。ただ今回はオリジナルな3次元キャラクターをデザインする暇がなく、2次元の簡単な画像をテクスチャーマッピングするのに留めました。
 球体は、図7に示すように92頂点、180ポリゴン(三角形)からなるオブジェクトであり、CustomVertex.PositionNormal形式(3次元座標と法線ベクトル)を持った形式とした。球体の3次元ポリゴンデータはMetaseuoiaLE R2.4で作成した。export機能で一旦.x形式で出力し、頂点毎の3次元座標と法線ベクトル、3角形の頂点インデックス・リストをExcel VBAで抜き出し、頂点情報とインデックス情報を抽出した。これは.x形式での読込後の物体色変更方法がわからなかったためだ。(前述したようにDirectXの仕様を理解していないので)
 Agent表示はCustomVertex.PositionTextured形式を使いました。これは将来的に物体(Material)色を光源計算させる描画方式とテクスチャーマッピングを混在表示させるための練習を行うことが初期の目的でした。
 SimpleLiteD3dを改変して、図8に示すようなフロー図に変更しています。
 ADC(A/Dコンバータ)に対するコマンド送信/データ受信のシリアル通信プロトコルに関しては、トランジスタ技術【増刊】『今すぐ使える パソコン計測USBマイコン基板』の第5章に解説されています。
 球体表示色は256通りなので先頭の2バイト(Hex形式2桁分)だけを使えばよい。



【図7】MetasequoiaLite R2.4で作成した球体

測定位置に測定値に対応した物体色で表示。

【図8】拡張現実センサ実装後のSimpleLiteD3d概略フロー



【図9】付録基板の裏面はフラットなのでセロテープでマーカーを張付けます。

【図10】全ての参照が正常の場合

【図11】デバイスマネージャー(Windows7)

【温度センシングで拡張現実】

          【ダウンロード注意事項】 初めにここをお読みください。
 付録プリント基板に実装されたADコンバータの温度センサを使って温度計測を行い、拡張現実を実験してみたデモプログラムを紹介します。左の写真のように付録基板にマーカーを張付けるだけで電子回路を製作する必要はありません。
 まずは、Visual C# 2008 Express Editionのzip形式に圧縮したプロジェクト・ファイルをダウンロードしてください。解凍すると、プロジェクトファイル内には次のようなフォルダーがあります。
●Marker
 hiroパターン2.docファイルを印刷して、右下の一番小さなマーカーを付録基板の裏側に貼り付けます。必ず周囲に白い余白部分を残しておくようにしてください。マーカーの認識率に影響します。
●AR_Data
 NyARToolkitCSのデータフォルダです。『Hiro』というパターンのデータとカメラ情報が格納されています。
●Image
 カラーバーと計測温度に応じて表示するテクスチャーの画像データを格納しているフォルダーです。
●Library
 NyARToolkitCSの『DirectShowLib-2005.dll』、『NyARToolkitCS.dll』、『NyARToolkitCSUtils.dll』を格納するフォルダーです。
 最初に、SimpleLiteDirect3d.slnをダブルクリックして、Visual C# 2008 Express Editionを起動します。
 起動後、メニューの『表示(V)』-『ソリューション エクスプローラ(P)』を実行すると、右側にソリューション エクスプローラが表示されます。『参照設定』と書かれた部分をダブルクリックします。するとコンパイルに必要な『参照』が表示されるはずですが、一部に警告マークがついているケースがあるかもしれません。そのような場合は『プロジェクト(P)』-『参照の追加(R)』を実行して『参照の追加』ダイアログを表示し、警告表示のついているモジュールに参照を与えます。DirectXのエンドユーザーランタイムをインストール済みなら、『.NET』タブを選択し、同じ名前のモジュールをワンクリックして選択し、OKボタンをクリックして1つ1つ参照を与えていきます。Microsoft.DirectX.Direct3DXは複数見つかるでしょうから一番最近のバージョンを参照します。
 『DirectShowLib-2005.dll』、『NyARToolkitCS.dll』、『NyARToolkitCSUtils.dll』の参照に警告が出ている場合には、『参照の追加』ダイアログの『参照』タブを選択し、Libraryフォルダ内のモジュールを選択して参照を与えます。
 以上の操作が完了したら、全ての参照が取れる状態になったはずです。

 次に、Visual C# 2008をそのままにして、Windowsのデバイスマネージャーを確認します。Windows7では、『コントロールパネル』-『ハードウェアとサウンド』-『デバイスマネージャー』でたどり着けるはずです。
 『ポート(COMとLPT)』をクリックして、付録基板に割り当てられているポート番号を調べます。例の場合にはCOM3でした。
 Visual C# 2008に戻り、ソリューションエクスプローラーのSimpleLiteD3d.csをダブルクリックします。画面中央にソースが表示されたはずです。大変申し訳ないのですが、デモプログラムはポート番号を自動検出していませんので、下に示した部分のCOMPORTの定数を正しく設定入力してください。

namespace SimpleLiteDirect3d
{
    public partial class SimpleLiteD3d : IDisposable, CaptureListener
    {
        private const String COMPORT = "COM3"; //【A/Dコンバータ通信用ComPort番号】
        private const int SCREEN_WIDTH=640;    //【表示領域の横方向画素数】 
        private const int SCREEN_HEIGHT=480;   //【表示領域の縦方向画素数】
        private const String AR_CODE_FILE = "../../AR_Data/patt.hiro"; //【パターンデータへのパス】
    

 ファイルを上書きしてから、次の手順を実行します。
 メニューの『ビルド(B)』-『ソリューションのビルド(B)』を実行してコンパイルを行い、左下に『ビルドを開始しました』⇒⇒⇒『ビルド正常終了』と表示されればOKです。
 メニューの『デバッグ(D)』-『デバッグ開始(S)』か『デバッグなしで開始(H)』でプログラムの実行が開始されます。
 画面内にマーカーが入れば表示が開始するはずです。


【YouTube動画】この動画を見る場合は上の動画を止めてくださいね。(すみません。手動です。)
【使用したパターン画像】

YouTubeの動画では、室温~50℃(T5.jpg~T10.jpg)くらいで実験したものをアップしました。
保冷剤を使えば、ずっと時間がかかりますが、-5℃~室温(T0.jpg~T5.jpg)の実験もソース変更なく、そのままで行えます。但し霜焼けに注意の事。
Imageフォルダ内のjpg形式画像を別の正方形画像に差し替えれば簡単に表示を変更することができます。


【図12】拡張現実センサの応用例

金属探知機に応用した例。壁の裏側に埋設された配管の可視化を行うことを目指した試みのひとつ。

【拡張現実センシングの応用】

 上述のように拡張現実(AR: Augmented Reality)がセンシング技術にとって、とても役に立つ技術だと感じました。それまではセンサの世界は比較的地味な技術と思っていましたが、いきなり未来技術っぽく感じたのです。そこで、いくつか拡張現実をセンシングに取り入れたものを試作してみました。金属探知機、高周波磁界強度/超音波センサプローブ、さらに医療応用のセンチネルリンパ節検出センサにも応用してみました。図12は、金属探知機を拡張現実化したものです。壁の中に埋め込まれた配管やセンサボックスの位置もこうすれば一目瞭然でわかります。
 NyARToolkitCSは比較的簡単なコーディングだけで計測分野の常識を変えてくれる素晴らしいツールであるように思えます。温度だけでなく、照度、ガス濃度、加速度、音圧、風速などの計測で、たとえセンサが1チャンネルしかなくても、実写画像上に過去の履歴を重畳させることで2次元/3次元的な分布状態の把握が容易になります。それはまるで、鳥山明さんの『DRAGON BALL(ドラゴンボール)』に出てくる『スカウター』のようなイメージです。ウェブカメラを実装したヘッドマウントディスプレイが実験用にほしくなってしまう。網膜ディスプレイとウェブカメラを組み合わせれば自作も可能なレベルだ。

 ところで実写画像上に過去の測定値も重畳表示していますが、この個数は容易に変更することができます。SimpleLiteD3d.csのソース中に
private const Int16 nSample = 50;                         // FIFOの段数
    
という部分があります。この指定値を多くすれば過去の測定値をウェブカメラで撮影した実写画像上に多く残すことができます。
ソースでは配列FIFO_matとFIFO_colorに検出マーカーの座標変換マトリックスと表示色をnSample個残しておくようにしています。書き込みポインタwpと読み出しポインタrpの利用でFIFO(First-In First-Out)を実現しています。
 

■現在、このセンチネルリンパ節検出磁気プローブに局所磁気ハイパーサーミア機能を追加中です。癌細胞に対して43度以上の加温(ハイパーサーミア)を行うと、その部位のがん細胞を殺すだけでなく、ヒートショックプロテインを人体の免疫機構が検出してがんに対する免疫が発生し、その他の転移癌にも有効であるという研究結果が存在するためです。
 従来、磁気ハイパーサーミアには大電力電源装置が必要でしたが、電源小型化の目処がつき試作を行っています。
■局所ハイパーサーミアは、癌治療だけでなく、一部の生物毒(ムカデ、ハチ、蚊等)に対しても効果があることが判明しています。これはこれらの生物毒が比較的低温で失活する酵素毒で構成されているためです。

【センチネルリンパ節検出用磁気プローブ】記載日 2011/02/28

 100μgから150μg程度の鉄量の医療用磁性流体リゾビスト(あるいはフェリデックス)の滞留するリンパ節を同定するためのセンサです。乳がん、肺がん、食道がんの摘出手術の際に、転移の有無を判定するために腫瘍組織近傍に磁性流体を局注し、5分後にこのセンサを使って周囲のリンパ節を非侵襲的に計測を行い、最も多くの磁性流体が滞留しているリンパ節(=センチネルリンパ節)を探します。このセンチネルリンパ節を摘出し、手術中に並行して生検を行ってがん転移の有無を調べます。転移したがん細胞が発見されなければ癌は転移しておらず、この場合には周囲のリンパ節の郭清(かくせい)を行わずに済みます。患者の負担を最小限度に抑える目的で使用されます。
 常温で動作する磁気センサを使いながら、従来高温超電導SQUIDでなければ検出することができなかった微量の磁性流体滞留部位を10mm以上離れても検出することができます。(10年以上前に秋田大学医学部殿の要請で製作した磁気プローブでは感度はたったの3mmでした。)しかも地磁気や環境磁気ノイズの影響も受けません。磁気シールドも必要ありません。非常にロバストなセンサです。
 Augmented Realityを使うと検出部位の可視化を簡単に行うことができます。


【各種拡張現実センサの開発】
≪NTLの真面目な研究内容のひとつ≫

どちらかと言うと、専用センサを開発するのがメインで、拡張現実センサの機能を付加しているという程度です。
用途としては、センサを放射線センサに変えれば、生活環境の汚染状況を可視化することができるので、除染の必要な場所を調べるのに役立つでしょう。
あるいは、センサを金属探知器に変えれば、壁や床に埋設された配管の位置を可視化することができます。
または、医療用センサで特定の体内組織部位の可視化など数多くの応用が考えられます。



【図13】拡張現実センサの応用例

 CQ出版トランジスタ技術2011年2月号(1/8発売)の投稿記事です。ハードウェア(回路図等)は記事に記載しています。こちらを参考にしてください。

【トランジスタ技術2011年2月号の拡張現実センサ】

          【ダウンロード注意事項】 初めにここをお読みください。
 トランジスタ技術2011年2月号に投稿した拡張現実センサのソースリストを公開します。Visual C# 2008 Express Editionのソースはこちら実装しているAltera社CPLD MaxⅡシリーズ EPM570T100C5の設計ファイルはこちらです。
 注意事項は【温度センシングで拡張現実】に書いてある通りです。ソースにも殆ど差がありません。バイナリは付属していませんのでコンパイル(ビルド)してご使用ください。

 記事には試作した中から『高周波磁界強度/超音波センサ』を取り上げました。これは金属探知機とセンチネルリンパ節検出用プローブには一般に入手困難な部品が含まれていたためです。
 私の研究テーマの中には癌治療用磁気ハイパーサーミア装置があります。磁気ハイパーサーミア装置とは、癌組織に打ち込んだ磁性ナノ微粒子に高周波磁界を印加し、磁性ナノ微粒子を直接非侵襲的に加熱する装置です。実験で取り扱う高周波磁界の周波数は20kHz~1MHzです。ハイパーサーミア治療とは、癌細胞は健常組織と違って周囲の血管の放熱機構が作用せず、加熱に弱いことを利用した治療方法です。この装置は磁性微粒子だけを非侵襲的に加熱できるため、局所加熱が容易で、人体に対する負担か小さいことが特徴です。『高周波磁界強度/超音波センサ』はその研究の中で装置の漏えい磁界や磁性微粒子やコイルが発生する超音波を測定するために用いています。

 ここでちょっと超音波について説明します。癌組織に打ち込んだ磁性ナノ微粒子や磁性体に高周波磁界が印加されると、磁界強度Bと磁気勾配∂B/∂xの積に比例した力を受けるため、印加磁界周波数の倍周波で振動し、超音波が発生するのです。この超音波なのですが、ちゃんとした対策を施さないととてつもなく強烈な音圧なのです。ラーメン屋さんなどで使われている業務用IH機器でも85dBに達する音圧を店内に鳴り響かせている所がありました。業務用IH機器の印加周波数は15kHz~20kHzなので超音波の周波数はその倍の30KHz~40kHzになります。超音波なので当然耳には聞こえませんが、可聴域で85dBと言えば近くで昔の電話のベルが鳴り響いている状態です。長時間その場所にいて問題ないのかちょっと気になります。犬やネズミなら聞こえる周波数です。問題ないのでしょうか?
 人間も普段の生活の中では30Hz~20kHzまでの音圧しか『音』として認識していませんが、録音技術で80kHzまでの成分を含まないと楽器の音が違うという人もいるので、全く聞こえていないわけではないのかもしれません。ヨーロッパなどでは超音波の法的規制が存在するようです。よく電磁波の生体影響を気にする人がいますが、それよりも超音波の方が影響ありそうに思えるのです。個人的に興味があるのでさらに改良を重ね携帯型拡張現実センサを製作し、街に調査に繰り出したいと考えています。(ピストル型は怪しいので形状は変更の予定です。)

【図14】試作した『高周波磁界強度/超音波センサ』のシステム構成図

【YouTube動画】付録基板を使って20kHzの高周波磁界強度を拡張現実センシング


【Altera CPLD MaxⅡ EPM570T100C5のロジック設計】

 トランジスタ技術2011年2月号の投稿記事『磁界&超音波リアルタイム・ビューワの製作』ではページ数の関係でアナログ回路の動作を中心に説明しました。ここでは書ききれなかったCPLD部分の動作について簡単に説明します。
 しかし、詳細な動作原理などについては、関連ドキュメント『CPLD設計』を参考にしてください。
 また、トランジスタ技術の記事のアナログ回路の動作に関しては、『アナログ基礎』を、ディジタル回路の考え方については、『ディジタル基礎』を参考にしてください。
 全く電子回路の事をご存じない場合には、『アナログ基礎』⇒『ディジタル基礎』⇒『CPLD設計』⇒『本ページ』の順序で読まれることをお勧めいたします。

【クロック分周回路部】

 図C-1は80MHzのクロックを分周してCPLD内で必要なクロック周波数を作るカウンタ回路です。80MHzを1/2に分周して40MHzを作り、さらに1/4に分周して10MHzを作っています。1MHzについてはデューティーサイクル50%の矩形波を得るために最初に1/5に分周してから1/2に分周しています。
 QuartusⅡ Web Editionは階層構造の回路設計を行うことができます。図中の回路ブロックClockDivider3はさらにゲートとレジスタを組み合わせてカウンタを構成しています。

【図C-1】クロック分周回路部

【DDS (Direct Digital Synthesizer)回路部】

 図C-2は磁気センサの地磁気センサ回路および超音波センサ回路が同期検波回路で構成されているので、そのための信号を生成するDDS (Direct Digital Synthesizer)回路部です。
 磁気センサの電流駆動用信号も同期検波回路の乗算器AD633に供給する信号も矩形波を用いているので、本来はサイン波出力が可能なDDSを使う必要はないのですが、実は試作を何回か繰り返しており、最初は磁気センサをサイン波を用いた交流駆動で構成していました。その名残でDDSが残っています。
 DDS_AC_withVol1という回路ブロック内には、24ビットのDFL (Direct Frequency Loop)と、その上位9ビットに対して一定のオフセット値を加算して位相遅延した矩形波を得る回路が組み込まれています。アナログで作る位相遅延回路の場合と違って、0°~360°まで9ビット精度で位相調整を行うことができます。
 DDSはDFLの24ビットの累積加算値の上位9ビットをEPM570T100C5に内蔵のフラッシュメモリ(512アドレス、データ16ビット幅)に供給してサイン波形を得ています。しかし、そのままでは外部にD/Aコンバータが必要になるので、パルス密度変調 (PDM: Pulse Density Modulation)型D/Aコンバータ回路により40Mbpsのパルス密度信号に変換しています。そのまま出力すると0V~3.3Vの1.65Vの振幅のサイン波となってしまうので、パルスを一定の割合で間引く方式の8ビット分解能のPDMボリュームを通しています。
 このDDS自体は100kHzまでのサイン波を出力する能力があります。いわば簡易ファンクション・ジェネレータです。そのままだと40Mbosのパルス信号ですが、CPLDの72番ピンに簡単なLPF(Low Pass Filter)を接続すればきれいなサイン波を得ることができます。ガンの引き金にあたるスイッチを押したときだけサイン波が出るようにOR回路を入れています。
 DDSの最大周波数が100kHzどまりになる原因は内蔵のフラッシュメモリが最大10MHzのクロックでしか動作せず、シリアル入出力であるため、工夫しても16クロックで1サンプルしか読みだすことができないためです。外付けのパラレル入出力のフラッシュメモリを使えば10MHz以上で動作するDDSを構成することも可能です。

【図C-2】DDS (Direct Digital Synthesizer)回路部

【図C-2-a】DDS (Direct Digital Synthesizer)回路部内の24ビットDFLと9ビット位相遅延回路

【ΔΣ A/D変換回路部】

 トランジスタ技術の記事中(159ページ、図5)で説明しているΔΣ型A-Dコンバータのロジック部分に該当します。CPLDの75番ピンから40MHzのクロックを出力していますが、外部で抵抗とコンデンサで簡易なLPFを構成して、平均電圧(1.65V)を作るために使用しています。
 D-FF(inst55)の出力CntDatはアナログ信号(0~3.3V)がパルス密度に変換されたディジタル信号です。一定時間内の"1"パルスの数をカウントすることでA/D変換を行うことができます。

【図C-3】ΔΣ A/D変換回路部

【A/D変換用カウンタ回路部】

 lpm_counter40は、DDSの1周期分の時間内に"1"パルスの数をカウントするためのカウンタです。lpm_dff58はカウンタがカウント中に前回のカウント値を保持しておくためのものです。これはDFLで構成されたトーン・ジェネレータに供給されます。
 磁気センサの場合にはうなり周波数が入力するのでCntDatをそのまま52番ピンから出力しますが、超音波センサのときはちょっと工夫します。
 カウント値(iDA[15..0])は16ビットPDM D/Aコンバータにも供給されています。16ビットPDM D/Aコンバータの出力は52番ピンから出力され、外部のLPF(Low Pass Filter)でアナログ信号に戻されます。同期検波回路の乗算器出力はサイン波の絶対値|Asinωt|が入力しているので大きなリップルを持っています。アナログLPF回路を通してもこのリップルは十分に落とすことができません。そこでCPLD内で同期検波の1周期に同期したA/D変換を行ってPDM D/Aから出力することでリップルを取り去る工夫を行っています。

【図C-4】A/D変換用カウンタ回路部

【図C-4a】リップル除去の説明図

【16ビットパルス密度変調 (PDM: Pulse Density Modulation)型D/Aコンバータ回路部】

 上記説明の通り、超音波センサのときに使います。
 このPDM D/Aコンバータは必要なロジック・エレメント数が少なく、34LEしか使いません。16ビット精度でありながら非常にローコストな特徴を持っています。

【図C-5】16ビットパルス密度変調 (PDM: Pulse Density Modulation)型D/Aコンバータ回路部

【DFL (Direct Frequency Loop)トーン・ジェネレータ回路部】

 このCPLDの99番ピンと100番ピンは圧電素子に接続されています。互いに相反なロジックの矩形波で駆動することで音を発生させることができます。引き金(スイッチ)を押したときだけ、測定値の大きさを音の周波数で知ることができるように設計しています。測定物理量が大きいとlpm_dff58の出力値も大きくなるのでDFLの出力周波数も高くなります。
 磁界も超音波も人間には知覚できない物理量ですが、このセンサは拡張現実(AR: Augmented Reality)で可視化(視覚)し、トーン・ジェネレータで可聴化(聴覚)する変換を行う特徴を有しています。
 センシング技術における拡張現実(AR: Augmented Reality)の意義は、物理量の『可視化』にあるといっても過言ではないでしょう。

【図C-6】DFL (Direct Frequency Loop)トーン・ジェネレータ回路部

【参考ソース】

 実際のコードはダウンロードできるプロジェクトファイルのソースを参考にしてください。

【Program.csのソース】


using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using NyARToolkitCSUtils.Capture;
/**
 * このサンプルプログラムは、NyARToolkitCSのSimpleLiteD3dを改変したサンプルプログラムです。
 * Hiroマーカーを識別し、マーカーの検出位置を基準に、センサで計測した計測値に対応する物体色で球体を表示します。
 * Direct3Dの単位系は、1.0を1mmとしています。視点は0,0,0から、Z+方向を向いて、上方向がY+です。
 **/
namespace SimpleLiteDirect3d
{   static class Program
    {   static void Main()
        {   Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            CaptureDeviceList capture_device_list = new CaptureDeviceList(); //キャプチャデバイスリストを取得
            if (capture_device_list.count < 1) { MessageBox.Show("キャプチャデバイスが見つかりませんでした。"); return; }
            int cdevice_number = 0; //キャプチャデバイスを選択してもらう。
            using (Form2 frm2 = new Form2())
            {   frm2.ShowDialog(capture_device_list, out cdevice_number); }
            using (CaptureDevice capture_device = capture_device_list[cdevice_number])
            {   using (Form1 frm = new Form1())  // フォームとメインサンプルクラスを作成
                {   using (SimpleLiteD3d sample = new SimpleLiteD3d())
                    {   if (sample.InitializeApplication(frm, capture_device)) // アプリケーションの初期化
                        {   frm.Show();                 // メインフォームを表示
                            sample.ADCInitialize();     //【A/Dコンバータ初期化処理】 
                            sample.StartCap();          //キャプチャ開始
                            while (frm.Created)         // フォームが作成されている間はループし続ける
                            {   sample.MainLoop();      // メインループ処理を行う
                                Thread.Sleep(1);        //スレッドスイッチ
                                Application.DoEvents(); // イベントがある場合はその処理する
                            }
                            sample.ADCTerminate();      //【A/Dコンバータ終了処理】 
                            sample.StopCap();           //キャプチャの停止
                        }
                        else
                        {
                            // 初期化に失敗
                        }
                    }
                }
            }
        }
    }
}

【SimpleLiteD3d.csのソース】


using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Microsoft.DirectX;                 // DirectXを使う為
using Microsoft.DirectX.Direct3D;        // DirectXを使う為
using NyARToolkitCSUtils.Capture;        // NyARToolkitのキャプチャー部
using NyARToolkitCSUtils.Direct3d;       // NyARToolkitの3次元描画部
using NyARToolkitCSUtils.NyAR;           // NyARToolkitのマーカー検出部
using jp.nyatla.nyartoolkit.cs;          //
using jp.nyatla.nyartoolkit.cs.core;     //
using jp.nyatla.nyartoolkit.cs.detector; //
using System.Runtime.InteropServices;    // DllImport機能によりWin32APIを使う為

namespace SimpleLiteDirect3d
{
    public partial class SimpleLiteD3d : IDisposable, CaptureListener
    {
        private const String COMPORT = "COM1"; //【A/Dコンバータ通信用ComPort番号】
        private const int SCREEN_WIDTH=640;    //【表示領域の横方向画素数】 
        private const int SCREEN_HEIGHT=480;   //【表示領域の縦方向画素数】
        private const String AR_CODE_FILE = "../../../Data/patt.hiro";         //【マーカー(Hiro)のパターンデータへのパス】
        private const String AR_CAMERA_FILE = "../../../Data/camera_para.dat"; //【カメラ情報へのパス】 
        private const String TEXTURE_FILE = "../../../img/ColorBar.jpg";  //上下、左右逆
        private const String MTEXTURE_FILE1 = "../../../img/Miku1.jpg";   //上下、左右逆
        private const String MTEXTURE_FILE2 = "../../../img/Miku2.jpg";   //上下、左右逆
  //      private const String MTEXTURE_FILE1 = "../../../img/Robo1.jpg"; //上下、左右逆
  //      private const String MTEXTURE_FILE2 = "../../../img/Robo2.jpg"; //上下、左右逆
        private CaptureDevice _cap; // DirectShowからのキャプチャー
        private NyARSingleDetectMarker _ar;  // NyARToolkit
        private DsBGRX32Raster _raster;      // NyARToolkit
        private NyARD3dUtil _utils;          // NyARToolkit
        private NyARSurface_XRGB32 _surface; // 背景テクスチャー(カメラで取得したフレーム画像、NyARToolkitマーカー検出用画像データ)

        private Device _device = null;              // Direct3Dデバイス
        private VertexBuffer _vertexBuffer = null;  //【頂点バッファ】球体の頂点情報(座標と色)を格納するバッファ
        private IndexBuffer _indexBuffer = null;    //【インデックスバッファ】球体を構成する三角形の頂点番号を格納するバッファ
        private VertexBuffer _vertexBufferT = null; //【頂点バッファ】球体の頂点情報(座標と色)を格納するバッファ
        private IndexBuffer _indexBufferT = null;   //【インデックスバッファ】球体を構成する三角形の頂点番号を格納するバッファ
        private Texture _texture = null;            //【テクスチャーバッファ】カラーゲージ格納用
        private VertexBuffer _vertexBufferM = null; //【頂点バッファ】球体の頂点情報(座標と色)を格納するバッファ
        private Texture _Mtexture1 = null;          //【テクスチャーバッファ】ちびミク格納用1
        private Texture _Mtexture2 = null;          //【テクスチャーバッファ】ちびミク格納用2
        public const int nVertex = 92;              //【球体モデル】頂点数
        public const int nTriangle = 180;           //【球体モデル】ポリゴン数(三角形)
        public const float Bairitsu = 20.0f;        //【球体モデル】表示倍率  
        private static CustomVertex.PositionNormal[] vertices = new CustomVertex.PositionNormal[nVertex]; // 球体モデルの頂点情報を格納メモリを確保
        private static CustomVertex.PositionTextured[] verticesT = new CustomVertex.PositionTextured[4];  // カラーバーの頂点情報を格納メモリを確保
        private static CustomVertex.PositionTextured[] verticesM = new CustomVertex.PositionTextured[4];  // ちびミク表示枠の頂点情報を格納メモリを確保

        //【球体モデル】頂点情報:92個の頂点からなる球体モデルのデータ【CustomVertex.PositionNormal形式用】
        private static float[] _vertex = new float[] {
        // ■MetasequoiaLE R2.4で球体モデルを作成⇒.x形式で保存⇒Excelで読込・編集
        //       x座標,       y座標,       z座標,      法線Nx,      法線Ny,      法線Nz
           0.00000000f, 0.20000000f, 0.00000000f, 0.00000000f, 1.00000000f, 0.00000000f,
           0.03633000f, 0.19021000f, -0.05000000f, 0.00000000f, 0.95128700f, -0.30830600f,
           0.00000000f, 0.19021000f, -0.06180000f, 0.17470500f, 0.94062300f, -0.29104300f,
           0.05878000f, 0.19021000f, -0.01910000f, 0.31241300f, 0.94062200f, -0.13276900f,
           0.05878000f, 0.19021000f, 0.01910000f, 0.33078800f, 0.94062200f, 0.07622100f,
           0.03633000f, 0.19021000f, 0.05000000f, 0.22281000f, 0.94062300f, 0.25609500f,
           0.00000000f, 0.19021000f, 0.06180000f, 0.02972900f, 0.94062300f, 0.33814800f,
          -0.03633000f, 0.19021000f, 0.05000000f, -0.17470500f, 0.94062300f, 0.29104300f,
          -0.05878000f, 0.19021000f, 0.01910000f, -0.31241300f, 0.94062200f, 0.13276900f,
          -0.05878000f, 0.19021000f, -0.01910000f, -0.33078800f, 0.94062200f, -0.07622100f,
          -0.03633000f, 0.19021000f, -0.05000000f, -0.21049000f, 0.93368000f, -0.28971700f,
           0.00000000f, -0.19021000f, -0.06180000f, -0.03291900f, 0.82232000f, -0.56807300f,
           0.03633000f, -0.19021000f, -0.05000000f, 0.33405400f, 0.80964100f, -0.48258600f,
           0.00000000f, -0.20000000f, 0.00000000f, 0.55391500f, 0.80964000f, -0.19406500f,
           0.05878000f, -0.19021000f, -0.01910000f, 0.56219600f, 0.80964000f, 0.16857800f,
           0.05878000f, -0.19021000f, 0.01910000f, 0.35573600f, 0.80964300f, 0.46683000f,
           0.03633000f, -0.19021000f, 0.05000000f, 0.01340000f, 0.80964100f, 0.58677300f,
           0.00000000f, -0.19021000f, 0.06180000f, -0.33405400f, 0.80964100f, 0.48258600f,
          -0.03633000f, -0.19021000f, 0.05000000f, -0.55391500f, 0.80964000f, 0.19406500f,
          -0.05878000f, -0.19021000f, 0.01910000f, -0.56219600f, 0.80964000f, -0.16857800f,
          -0.05878000f, -0.19021000f, -0.01910000f, -0.39681500f, 0.78985800f, -0.46761300f,
          -0.03633000f, -0.19021000f, -0.05000000f, -0.00944700f, 0.58868700f, -0.80830600f,
           0.06910000f, 0.16180000f, -0.09511000f, 0.46746700f, 0.58868900f, -0.65948500f,
           0.00000000f, 0.16180000f, -0.11756000f, 0.76582300f, 0.58869100f, -0.25876300f,
           0.11180000f, 0.16180000f, -0.03633000f, 0.77165900f, 0.58869400f, 0.24079200f,
           0.11180000f, 0.16180000f, 0.03633000f, 0.48275000f, 0.58868900f, 0.64838100f,
           0.06910000f, 0.16180000f, 0.09511000f, 0.00944700f, 0.58868700f, 0.80830600f,
           0.00000000f, 0.16180000f, 0.11756000f, -0.46746700f, 0.58868900f, 0.65948500f,
          -0.06910000f, 0.16180000f, 0.09511000f, -0.76582300f, 0.58869100f, 0.25876300f,
          -0.11180000f, 0.16180000f, 0.03633000f, -0.77165900f, 0.58869400f, -0.24079200f,
          -0.11180000f, 0.16180000f, -0.03633000f, -0.48275000f, 0.58868900f, -0.64838100f,
          -0.06910000f, 0.16180000f, -0.09511000f, -0.00485100f, 0.30967300f, -0.95083100f,
           0.09511000f, 0.11756000f, -0.13090000f, 0.55496000f, 0.30967100f, -0.77209000f,
           0.00000000f, 0.11756000f, -0.16180000f, 0.90279400f, 0.30967300f, -0.29843700f,
           0.15388000f, 0.11756000f, -0.05000000f, 0.90579300f, 0.30966800f, 0.28921300f,
           0.15388000f, 0.11756000f, 0.05000000f, 0.56280600f, 0.30966700f, 0.76639200f,
           0.09511000f, 0.11756000f, 0.13090000f, 0.00485100f, 0.30967300f, 0.95083100f,
           0.00000000f, 0.11756000f, 0.16180000f, -0.55496000f, 0.30967100f, 0.77209000f,
          -0.09511000f, 0.11756000f, 0.13090000f, -0.90279400f, 0.30967300f, 0.29843700f,
          -0.15388000f, 0.11756000f, 0.05000000f, -0.89694500f, 0.28781700f, -0.33563600f,
          -0.15388000f, 0.11756000f, -0.05000000f, -0.50441100f, 0.33900900f, -0.79413000f,
          -0.09511000f, 0.11756000f, -0.13090000f, 0.00000000f, 0.00000000f, -1.00000000f,
           0.11180000f, 0.06180000f, -0.15388000f, 0.58778600f, -0.00000300f, -0.80901700f,
           0.00000000f, 0.06180000f, -0.19021000f, 0.95105600f, -0.00000200f, -0.30901800f,
           0.18090000f, 0.06180000f, -0.05878000f, 0.95105600f, 0.00000200f, 0.30901800f,
           0.18090000f, 0.06180000f, 0.05878000f, 0.58778600f, 0.00000300f, 0.80901700f,
           0.11180000f, 0.06180000f, 0.15388000f, 0.00000000f, 0.00000000f, 1.00000000f,
           0.00000000f, 0.06180000f, 0.19021000f, -0.58778600f, -0.00000300f, 0.80901700f,
          -0.11180000f, 0.06180000f, 0.15388000f, -0.95105600f, -0.00000200f, 0.30901800f,
          -0.18090000f, 0.06180000f, 0.05878000f, -0.96861000f, -0.03159300f, -0.24657000f,
          -0.18090000f, 0.06180000f, -0.05878000f, -0.62450500f, 0.02259700f, -0.78069400f,
          -0.11180000f, 0.06180000f, -0.15388000f, 0.00485100f, -0.30967300f, -0.95083100f,
           0.11756000f, 0.00000000f, -0.16180000f, 0.56280600f, -0.30966700f, -0.76639200f,
           0.00000000f, 0.00000000f, -0.20000000f, 0.90579300f, -0.30966800f, -0.28921300f,
           0.19021000f, 0.00000000f, -0.06180000f, 0.90279400f, -0.30967300f, 0.29843700f,
           0.19021000f, 0.00000000f, 0.06180000f, 0.55496100f, -0.30967100f, 0.77209000f,
           0.11756000f, 0.00000000f, 0.16180000f, -0.00485100f, -0.30967300f, 0.95083100f,
           0.00000000f, 0.00000000f, 0.20000000f, -0.56280600f, -0.30966700f, 0.76639200f,
          -0.11756000f, 0.00000000f, 0.16180000f, -0.90579300f, -0.30966800f, 0.28921300f,
          -0.19021000f, 0.00000000f, 0.06180000f, -0.90279400f, -0.30967300f, -0.29843700f,
          -0.19021000f, 0.00000000f, -0.06180000f, -0.55496100f, -0.30967100f, -0.77209000f,
          -0.11756000f, 0.00000000f, -0.16180000f, 0.00944700f, -0.58868700f, -0.80830600f,
           0.11180000f, -0.06180000f, -0.15388000f, 0.48275000f, -0.58868900f, -0.64838100f,
           0.00000000f, -0.06180000f, -0.19021000f, 0.77165900f, -0.58869400f, -0.24079200f,
           0.18090000f, -0.06180000f, -0.05878000f, 0.74246000f, -0.60631000f, 0.28485300f,
           0.18090000f, -0.06180000f, 0.05878000f, 0.43799200f, -0.56183500f, 0.70178600f,
           0.11180000f, -0.06180000f, 0.15388000f, -0.00944700f, -0.58868700f, 0.80830600f,
           0.00000000f, -0.06180000f, 0.19021000f, -0.48275000f, -0.58868900f, 0.64838100f,
          -0.11180000f, -0.06180000f, 0.15388000f, -0.77165900f, -0.58869400f, 0.24079200f,
          -0.18090000f, -0.06180000f, 0.05878000f, -0.76582300f, -0.58869100f, -0.25876300f,
          -0.18090000f, -0.06180000f, -0.05878000f, -0.46746700f, -0.58868900f, -0.65948500f,
          -0.11180000f, -0.06180000f, -0.15388000f, 0.01340000f, -0.80964100f, -0.58677300f,
           0.09511000f, -0.11756000f, -0.13090000f, 0.35573600f, -0.80964300f, -0.46683000f,
           0.00000000f, -0.11756000f, -0.16180000f, 0.56219400f, -0.80964200f, -0.16857600f,
           0.15388000f, -0.11756000f, -0.05000000f, 0.54251800f, -0.82748600f, 0.14471000f,
           0.15388000f, -0.11756000f, 0.05000000f, 0.37293400f, -0.79603500f, 0.47670600f,
           0.09511000f, -0.11756000f, 0.13090000f, -0.01340000f, -0.80964100f, 0.58677300f,
           0.00000000f, -0.11756000f, 0.16180000f, -0.35573600f, -0.80964300f, 0.46683000f,
          -0.09511000f, -0.11756000f, 0.13090000f, -0.56219400f, -0.80964200f, 0.16857600f,
          -0.15388000f, -0.11756000f, 0.05000000f, -0.55391200f, -0.80964200f, -0.19406500f,
          -0.15388000f, -0.11756000f, -0.05000000f, -0.33405400f, -0.80964200f, -0.48258500f,
          -0.09511000f, -0.11756000f, -0.13090000f, 0.02972900f, -0.94062300f, -0.33814800f,
           0.06910000f, -0.16180000f, -0.09511000f, 0.22281000f, -0.94062300f, -0.25609200f,
           0.00000000f, -0.16180000f, -0.11756000f, 0.33078600f, -0.94062300f, -0.07621800f,
           0.11180000f, -0.16180000f, -0.03633000f, 0.31241300f, -0.94062300f, 0.13276600f,
           0.11180000f, -0.16180000f, 0.03633000f, 0.17470700f, -0.94062300f, 0.29104100f,
           0.06910000f, -0.16180000f, 0.09511000f, -0.02972900f, -0.94062300f, 0.33814800f,
           0.00000000f, -0.16180000f, 0.11756000f, -0.22281000f, -0.94062300f, 0.25609200f,
          -0.06910000f, -0.16180000f, 0.09511000f, -0.33078600f, -0.94062300f, 0.07621800f,
          -0.11180000f, -0.16180000f, 0.03633000f, -0.31241300f, -0.94062300f, -0.13276600f,
          -0.11180000f, -0.16180000f, -0.03633000f, -0.17470700f, -0.94062300f, -0.29104100f,
          -0.06910000f, -0.16180000f, -0.09511000f, 0.00000000f, -1.00000000f, 0.00000000f
        };

        //【球体モデル】インデックスバッファ:球体を構成する180個の三角形を構成する頂点番号リスト
        private static Int16[] _vertexIndices = new Int16[] {
        // ■MetasequoiaLE R2.4で球体モデルを作成⇒.x形式で保存⇒Excelで読込・編集
          0, 1, 2,   0, 3, 1,   0, 4, 3,   0, 5, 4,   0, 6, 5,   0, 7, 6,   0, 8, 7,   0, 9, 8,   0, 10, 9,  0, 2, 10,
          11,12,13,  12,14,13,  14,15,13,  15,16,13,  16,17,13,  17,18,13,  18,19,13,  19,20,13,  20,21,13,  21,11,13,
          1,22,23,   1,23,2,    3,24,22,   3,22,1,    4,25,24,   4,24,3,    5,26,25,   5,25,4,    6,27,26,   6,26,5,
          7,28,27,   7,27,6,    8,29,28,   8,28,7,    9,30,29,   9,29,8,    10,31,30,  10,30,9,   10,2,23,   10,23,31,
          22,32,33,  22,33,23,  24,34,32,  24,32,22,  25,35,34,  25,34,24,  26,36,35,  26,35,25,  27,37,36,  27,36,26,
          28,38,37,  28,37,27,  29,39,38,  29,38,28,  30,40,39,  30,39,29,  31,41,40,  31,40,30,  23,33,41,  23,41,31,
          32,42,43,  32,43,33,  34,44,42,  34,42,32,  35,45,44,  35,44,34,  36,46,45,  36,45,35,  37,47,46,  37,46,36,
          38,48,47,  38,47,37,  39,49,48,  39,48,38,  40,50,49,  40,49,39,  41,51,50,  41,50,40,  33,43,51,  33,51,41,
          42,52,53,  42,53,43,  44,54,52,  44,52,42,  45,55,54,  45,54,44,  46,56,55,  46,55,45,  47,57,56,  47,56,46,
          48,58,57,  48,57,47,  49,59,58,  49,58,48,  50,60,59,  50,59,49,  50,51,61,  50,61,60,  43,53,61,  43,61,51,
          52,62,63,  52,63,53,  54,64,62,  54,62,52,  55,65,64,  55,64,54,  56,66,65,  56,65,55,  57,67,66,  57,66,56,
          58,68,67,  58,67,57,  59,69,68,  59,68,58,  60,70,69,  60,69,59,  61,71,70,  61,70,60,  53,63,71,  53,71,61,
          62,72,73,  62,73,63,  64,74,72,  64,72,62,  65,75,74,  65,74,64,  66,76,75,  66,75,65,  67,77,76,  67,76,66,
          68,78,77,  68,77,67,  69,79,78,  69,78,68,  70,80,79,  70,79,69,  71,81,80,  71,80,70,  63,73,81,  63,81,71,
          72,82,83,  72,83,73,  74,84,82,  74,82,72,  75,85,84,  75,84,74,  75,76,86,  75,86,85,  77,87,86,  77,86,76,
          78,88,87,  78,87,77,  79,89,88,  79,88,78,  80,90,89,  80,89,79,  81,91,90,  81,90,80,  73,83,91,  73,91,81,
          82,12,11,  82,11,83,  84,14,12,  84,12,82,  85,15,14,  85,14,84,  86,16,15,  86,15,85,  87,17,16,  87,16,86,
          88,18,17,  88,17,87,  89,19,18,  89,18,88,  90,20,19,  90,19,89,  91,21,20,  91,20,90,  83,11,21,  83,21,91
        };

        //【カラーインデックスデータ】A/Dコンバータの計測値に対応する球体表示色を与える配列
        private static int[] ColorLookupTable = new int[] {
         0x00FFFFFF, 0x00FBFBFB, 0x00F7F7F7, 0x00F3F3F3, 0x00EFEFEF, 0x00EBEBEB, 0x00E7E7E7, 0x00E4E4E4, 0x00E0E0E0, 0x00DCDCDC,
         0x00D8D8D8, 0x00D4D4D4, 0x00D0D0D0, 0x00CCCCCC, 0x00C8C8C8, 0x00C4C4C4, 0x00C0C0C0, 0x00BCBCBC, 0x00B8B8B8, 0x00B4B4B4,
         0x00B1B1B1, 0x00ADADAD, 0x00A9A9A9, 0x00A5A5A5, 0x00A1A1A1, 0x009D9D9D, 0x00999999, 0x00959595, 0x00919191, 0x008D8D8D,
         0x00898989, 0x00858585, 0x00828282, 0x007E7E7E, 0x007A7A7A, 0x00767676, 0x00727272, 0x006E6E6E, 0x006A6A6A, 0x00666666,
         0x00626262, 0x005E5E5E, 0x005A5A5A, 0x00565656, 0x00525252, 0x004F4F4F, 0x004B4B4B, 0x00474747, 0x00434343, 0x003F3F3F,
         0x003B3B3B, 0x000000FF, 0x000005FF, 0x00000AFF, 0x00000FFF, 0x000014FF, 0x000019FF, 0x00001EFF, 0x000023FF, 0x000028FF,
         0x00002DFF, 0x000032FF, 0x000037FF, 0x00003CFF, 0x000041FF, 0x000046FF, 0x00004BFF, 0x000050FF, 0x000055FF, 0x00005AFF,
         0x00005FFF, 0x000064FF, 0x000069FF, 0x00006EFF, 0x000073FF, 0x000078FF, 0x00007DFF, 0x000082FF, 0x000087FF, 0x00008CFF,
         0x000091FF, 0x000096FF, 0x00009BFF, 0x0000A0FF, 0x0000A5FF, 0x0000AAFF, 0x0000AFFF, 0x0000B4FF, 0x0000B9FF, 0x0000BEFF,
         0x0000C3FF, 0x0000C8FF, 0x0000CDFF, 0x0000D2FF, 0x0000D7FF, 0x0000DCFF, 0x0000E1FF, 0x0000E6FF, 0x0000EBFF, 0x0000F0FF,
         0x0000F5FF, 0x0000FAFF, 0x0000FFFF, 0x0000FFFA, 0x0000FFF5, 0x0000FFF0, 0x0000FFEB, 0x0000FFE6, 0x0000FFE1, 0x0000FFDC,
         0x0000FFD7, 0x0000FFD2, 0x0000FFCD, 0x0000FFC8, 0x0000FFC3, 0x0000FFBE, 0x0000FFB9, 0x0000FFB4, 0x0000FFAF, 0x0000FFAA,
         0x0000FFA5, 0x0000FFA0, 0x0000FF9B, 0x0000FF96, 0x0000FF91, 0x0000FF8C, 0x0000FF87, 0x0000FF82, 0x0000FF7D, 0x0000FF78,
         0x0000FF73, 0x0000FF6E, 0x0000FF69, 0x0000FF64, 0x0000FF5F, 0x0000FF5A, 0x0000FF55, 0x0000FF50, 0x0000FF4B, 0x0000FF46,
         0x0000FF41, 0x0000FF3C, 0x0000FF37, 0x0000FF32, 0x0000FF2D, 0x0000FF28, 0x0000FF23, 0x0000FF1E, 0x0000FF19, 0x0000FF14,
         0x0000FF0F, 0x0000FF0A, 0x0000FF05, 0x0000FF00, 0x0005FF00, 0x000AFF00, 0x000FFF00, 0x0014FF00, 0x0019FF00, 0x001EFF00,
         0x0023FF00, 0x0028FF00, 0x002DFF00, 0x0032FF00, 0x0037FF00, 0x003CFF00, 0x0041FF00, 0x0046FF00, 0x004BFF00, 0x0050FF00,
         0x0055FF00, 0x005AFF00, 0x005FFF00, 0x0064FF00, 0x0069FF00, 0x006EFF00, 0x0073FF00, 0x0078FF00, 0x007DFF00, 0x0082FF00,
         0x0087FF00, 0x008CFF00, 0x0091FF00, 0x0096FF00, 0x009BFF00, 0x00A0FF00, 0x00A5FF00, 0x00AAFF00, 0x00AFFF00, 0x00B4FF00,
         0x00B9FF00, 0x00BEFF00, 0x00C3FF00, 0x00C8FF00, 0x00CDFF00, 0x00D2FF00, 0x00D7FF00, 0x00DCFF00, 0x00E1FF00, 0x00E6FF00,
         0x00EBFF00, 0x00F0FF00, 0x00F5FF00, 0x00FAFF00, 0x00FFFF00, 0x00FFFA00, 0x00FFF500, 0x00FFF000, 0x00FFEB00, 0x00FFE600,
         0x00FFE100, 0x00FFDC00, 0x00FFD700, 0x00FFD200, 0x00FFCD00, 0x00FFC800, 0x00FFC300, 0x00FFBE00, 0x00FFB900, 0x00FFB400,
         0x00FFAF00, 0x00FFAA00, 0x00FFA500, 0x00FFA000, 0x00FF9B00, 0x00FF9600, 0x00FF9100, 0x00FF8C00, 0x00FF8700, 0x00FF8200,
         0x00FF7D00, 0x00FF7800, 0x00FF7300, 0x00FF6E00, 0x00FF6900, 0x00FF6400, 0x00FF5F00, 0x00FF5A00, 0x00FF5500, 0x00FF5000,
         0x00FF4B00, 0x00FF4600, 0x00FF4100, 0x00FF3C00, 0x00FF3700, 0x00FF3200, 0x00FF2D00, 0x00FF2800, 0x00FF2300, 0x00FF1E00,
         0x00FF1900, 0x00FF1400, 0x00FF0F00, 0x00FF0A00, 0x00FF0500, 0x00FF0000
         };

        //【カラーゲージの頂点データ】【CustomVertex.PositionTextured形式】
        private static float[] GaugeVertex = new float[] {
           0.0f,  0.0f, 2.0f, 0.0f, 0.0f,
         100.0f,  0.0f, 2.0f, 1.0f, 0.0f,
         100.0f, 10.0f, 2.0f, 1.0f, 1.0f,
           0.0f, 10.0f, 2.0f, 0.0f, 1.0f
        };
        //【ちびミク表示枠の頂点データ】【CustomVertex.PositionTextured形式】
        private static float[] MikuGaugeVertex = new float[] {
          25.0f,  25.0f, 0.0f, 0.0f, 0.0f,
         -25.0f,  25.0f, 0.0f, 1.0f, 0.0f,
         -25.0f, -25.0f, 0.0f, 1.0f, 1.0f,
          25.0f, -25.0f, 0.0f, 0.0f, 1.0f
        };
        //【カラーゲージのインデックスデータ】ちびミクと兼用
        private static Int16[] GaugeIndex = new Int16[] {
           0, 1, 2, 2, 3, 0
        };

        private NyARTransMatResult __OnBuffer_nyar_transmat = new NyARTransMatResult(); // NyARToolkitのマーカー検出結果のバッファ
        private bool _is_marker_enable; // NyARToolkitのマーカー検出結果フラグ
        private Matrix _trans_mat;      // NyARToolkitの座標変換マトリックス

        private int ADcount = 1;                                  // ADC計測値の上位8ビット
        private int LoopNum = 1;                                  //
        private const Int16 nSample = 50;                         // FIFOの段数
        private static Matrix[] FIFO_mat = new Matrix[nSample];   // 過去nSample個分のセンサの座標変換マトリックス
        private static Int32[]   FIFO_color = new Int32[nSample]; // 過去nSample個分のA/D変換値に基づく色情報
        private Int16 wp = 0;                                     // FIFO書込み側(A/D変換側)ポインタ
        private Int16 rp = 0;                                     // FIFO読出し側(DirectX側)ポインタ

        // System.Windows.Forms.Timer timer2 = new System.Windows.Forms.Timer();
        System.IO.Ports.SerialPort SerialPort_for_ADC = new System.IO.Ports.SerialPort(); //【ADC用シリアルポート】
        private static Byte[] TxBuf = new Byte[256]; //【シリアル通信送信用バッファ】
        private static Byte[] RxBuf = new Byte[256]; //【シリアル通信受信用バッファ】未使用
        private string RxMsg;                        //【シリアル通信受信用行バッファ】このプログラムではこちらを使用

        //***********************************************************************
        // 時間待ち(ミリ秒単位) 【Win32APIをimportする方法】
        //***********************************************************************
        [DllImport("kernel32.dll")]
        public static extern void Sleep(Int32 dwMilliseconds);

        // ■■■■■■■■■■ 【非同期カメラ・キャプチャー・イベントハンドラ】 ■■■■■■■■■■
        //  CaptureDeviceからの1フレーム毎のキャプチャーイベントをハンドリングして、バッファと背景テクスチャを更新する。
        //   MainLoop処理の進行とは無関係に非同期に行われている。キャプチャー画像とマーカーの位置検出はここで行っている。
        public void OnBuffer(CaptureDevice i_sender, double i_sample_time, IntPtr i_buffer, int i_buffer_len)
        {
            int w = i_sender.video_width;               // CaptureDeviceの横方向画素数を取得
            int h = i_sender.video_height;              // CaptureDeviceの縦方向画素数を取得
            int s = w * (i_sender.video_bit_count / 8); // CaptureDeviceの1スキャンライン分のバイト数を取得
            NyARTransMatResult nyar_transmat = this.__OnBuffer_nyar_transmat; // 座標変換バッファを用意
            
            //テクスチャにRGBを取り込み()
            lock (this)
            {
                this._raster.setBuffer(i_buffer, i_sender.video_vertical_flip);        //カメラ映像をARのバッファにコピー
                bool is_marker_enable = this._ar.detectMarkerLite(this._raster, 160); //マーカー検出処理をしきい値110で実施
                if (is_marker_enable) // マーカーを発見できたか? Yesなら以下を実施
                {
                    this._ar.getTransmationMatrix(nyar_transmat);                // 座標変換マトリックスを計算する
                    this._utils.toD3dMatrix(nyar_transmat, ref this._trans_mat); // マトリックスをコピーする
                }
                this._is_marker_enable=is_marker_enable;    // 検出可否フラグをコピー
                this._surface.CopyFromXRGB32(this._raster); // カメラキャプチャー画像を背景テクスチャーにコピーして更新する
            }
            return;
        }

        // ■■■■■■■■■■ 【キャプチャーを開始する関数】 ■■■■■■■■■■
        public void StartCap()
        {
            this._cap.StartCapture();
            return;
        }

        // ■■■■■■■■■■ 【キャプチャーを停止する関数】 ■■■■■■■■■■
        public void StopCap()
        {
            this._cap.StopCapture();
            return;
        }

        // ■■■■■■■■■■ 【Direct3Dデバイスを準備する関数】 ■■■■■■■■■■
        private Device PrepareD3dDevice(Control i_window)
        {
            PresentParameters pp = new PresentParameters();
            pp.Windowed = true;
            pp.SwapEffect = SwapEffect.Flip;
            pp.BackBufferFormat = Format.X8R8G8B8;
            pp.BackBufferCount = 1;
            pp.EnableAutoDepthStencil = true;
            pp.AutoDepthStencilFormat = DepthFormat.D16;
            CreateFlags fl_base = CreateFlags.FpuPreserve;

            try{
                return new Device(0, DeviceType.Hardware, i_window.Handle, fl_base|CreateFlags.HardwareVertexProcessing, pp);
            }catch (Exception ex1){
                Debug.WriteLine(ex1.ToString());
                try{
                    return new Device(0, DeviceType.Hardware, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
                }catch (Exception ex2){
                    // 作成に失敗
                    Debug.WriteLine(ex2.ToString());
                    try{
                        return new Device(0, DeviceType.Reference, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
                    }catch (Exception ex3){
                        throw ex3;
                    }
                }
            }
        }

        // ■■■■■■■■■■ 【Direct3Dデバイスを準備する関数】 ■■■■■■■■■■
        public bool InitializeApplication(Form1 topLevelForm, CaptureDevice i_cap_device)
        {
            topLevelForm.ClientSize=new Size(SCREEN_WIDTH,SCREEN_HEIGHT);
            //キャプチャを作る(QVGAでフレームレートは30)
            i_cap_device.SetCaptureListener(this);
            i_cap_device.PrepareCapture(SCREEN_WIDTH, SCREEN_HEIGHT, 30);
            this._cap = i_cap_device;

            //【Augmented Realityの設定】
            //ARラスタを作る(DirectShowキャプチャ仕様)。
            this._raster = new DsBGRX32Raster(i_cap_device.video_width, i_cap_device.video_height, i_cap_device.video_width * i_cap_device.video_bit_count / 8);
            //AR用カメラパラメータファイルをロードして設定する
            NyARParam ap = new NyARParam();
            ap.loadARParamFromFile(AR_CAMERA_FILE);
            ap.changeScreenSize(SCREEN_WIDTH, SCREEN_HEIGHT);
            //AR用のパターンコードを読み出し	
            NyARCode code = new NyARCode(16, 16);
            code.loadARPattFromFile(AR_CODE_FILE);
            //1パターンのみを追跡するクラスを作成
            this._ar = new NyARSingleDetectMarker(ap, code, 40.0, this._raster.getBufferType(), NyARSingleDetectMarker.PF_NYARTOOLKIT);
            //Direct3D用のユーティリティ準備
            this._utils = new NyARD3dUtil();
            //計算モードの設定
            this._ar.setContinueMode(true);

            //3dデバイスを準備する
            this._device = PrepareD3dDevice(topLevelForm);
            this._device.RenderState.ZBufferEnable = true; // 陰面処理をzバッファ方式で行う
            this._device.RenderState.ZBufferFunction = Compare.LessEqual;  //.LessEqual;    
            this._device.RenderState.Lighting = true;     // 光源計算処理を行う true;

            // 平行光源(無限遠に設定される方向を持った光源。太陽光の様な光源)
            this._device.Lights[0].Type = LightType.Directional;
            this._device.Lights[0].Diffuse = Color.White;
            this._device.Lights[0].Direction = new Vector3(-0.57735f, -0.57735f, -0.57735f);
            this._device.Lights[0].Enabled = true;
            // 環境光(全ての物体を均一に照らす光源)
            this._device.RenderState.Ambient = Color.FromArgb(0x202020);

            //カメラProjectionの設定
            Matrix tmp = new Matrix();
            this._utils.toCameraFrustumRH(ap, ref tmp);
            this._device.Transform.Projection = tmp;

            // ビュー変換の設定(左手座標系ビュー行列で設定する)
            // 0,0,0から、Z+方向を向いて、上方向がY軸
            this._device.Transform.View = Matrix.LookAtLH(
                new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 1.0f, 0.0f));
            Viewport vp = new Viewport();
            vp.X = 0;
            vp.Y = 0;
            vp.Height = ap.getScreenSize().h;
            vp.Width = ap.getScreenSize().w;
            vp.MaxZ = 1.0f;
            //ビューポート設定
            this._device.Viewport = vp;

            // 球体(頂点数92)の準備
            this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormal),
                nVertex, this._device, Usage.None, CustomVertex.PositionNormal.Format, Pool.Managed);
            //92点から成る球体の頂点座標情報を格納する
            // ■MetasequoiaLE R2.4で適当に球体を作成⇒.x形式で保存⇒Excelで読込・表示倍率、書式を編集。色は取り敢えず『黒』
            for (var i = 0; i < nVertex; i++)
            {   vertices[i] = new CustomVertex.PositionNormal(
                   _vertex[6 * i] * Bairitsu, _vertex[6 * i + 1] * Bairitsu, _vertex[6 * i + 2] * Bairitsu,
                   _vertex[6 * i + 3], _vertex[6 * i + 4], _vertex[6 * i + 5]     );
            }
            // 頂点バッファをロックする
            using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
            {   data.Write(vertices);        // 頂点データを頂点バッファにコピーします
                this._vertexBuffer.Unlock(); // 頂点バッファのロックを解除します
            }
            
            // インデックスバッファの作成
            // 第2引数の数値は(三角ポリゴンの数)*(ひとつの三角ポリゴンの頂点数)*
            // (16 ビットのインデックスサイズ(2byte))
            this._indexBuffer = new IndexBuffer(this._device, nTriangle * 3 * 2, Usage.WriteOnly, Pool.Managed, true);
            // インデックスバッファをロックする
            using (GraphicsStream data = this._indexBuffer.Lock(0, 0, LockFlags.None))
            {   data.Write(_vertexIndices); // インデックスデータをインデックスバッファにコピーします
                this._indexBuffer.Unlock(); // インデックスバッファのロックを解除します
            }

            //【カラーバー】の準備
            this._vertexBufferT = new VertexBuffer(typeof(CustomVertex.PositionTextured),
                4, this._device, Usage.None, CustomVertex.PositionTextured.Format, Pool.Managed);
            for (var i = 0; i < 4; i++)
            {   verticesT[i] = new CustomVertex.PositionTextured(
                   GaugeVertex[5 * i], GaugeVertex[5 * i + 1], GaugeVertex[5 * i + 2],
                   GaugeVertex[5 * i + 3], GaugeVertex[5 * i + 4]   );
            }
            // 頂点バッファをロックする
            using (GraphicsStream data = this._vertexBufferT.Lock(0, 0, LockFlags.None))
            {
                data.Write(verticesT);        // 頂点データを頂点バッファにコピーします
                this._vertexBufferT.Unlock(); // 頂点バッファのロックを解除します
            }
            // インデックスバッファの作成
            // 第2引数の数値は(三角ポリゴンの数)*(ひとつの三角ポリゴンの頂点数)*
            // (16 ビットのインデックスサイズ(2byte))
            this._indexBufferT = new IndexBuffer(this._device, 2 * 3 * 2, Usage.WriteOnly, Pool.Managed, true);
            // インデックスバッファをロックする
            using (GraphicsStream data = this._indexBufferT.Lock(0, 0, LockFlags.None))
            {
                data.Write(GaugeIndex); // インデックスデータをインデックスバッファにコピーします
                this._indexBufferT.Unlock(); // インデックスバッファのロックを解除します
            }

            //【ミクゲージ】の準備
            this._vertexBufferM = new VertexBuffer(typeof(CustomVertex.PositionTextured),
                4, this._device, Usage.None, CustomVertex.PositionTextured.Format, Pool.Managed);
            for (var i = 0; i < 4; i++)
            {   verticesM[i] = new CustomVertex.PositionTextured(
                   MikuGaugeVertex[5 * i], MikuGaugeVertex[5 * i + 1], MikuGaugeVertex[5 * i + 2],
                   MikuGaugeVertex[5 * i + 3], MikuGaugeVertex[5 * i + 4]   );
            }
            // 頂点バッファをロックする
            using (GraphicsStream data = this._vertexBufferM.Lock(0, 0, LockFlags.None))
            {
                data.Write(verticesM);        // 頂点データを頂点バッファにコピーします
                this._vertexBufferM.Unlock(); // 頂点バッファのロックを解除します
            }

            
            // テクスチャーファイルをDirectXにロードする
            this._texture = TextureLoader.FromFile(this._device, TEXTURE_FILE);
            this._Mtexture1 = TextureLoader.FromFile(this._device, MTEXTURE_FILE1);
            this._Mtexture2 = TextureLoader.FromFile(this._device, MTEXTURE_FILE2);


            //背景サーフェイスを作成
            this._surface = new NyARSurface_XRGB32(this._device, SCREEN_WIDTH, SCREEN_HEIGHT);

            this._is_marker_enable = false;

            return true;
        }
        
        // ■■■■■■■■■■ 【メインループ処理】 ■■■■■■■■■■
        public void MainLoop()
        {
            //ARの計算
            lock (this)
            {
                //【背景サーフェイスを直接描画】
                Surface dest_surface = this._device.GetBackBuffer(0, 0, BackBufferType.Mono);
                Rectangle src_dest_rect = new Rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
                this._device.StretchRectangle(this._surface.d3d_surface, src_dest_rect, dest_surface, src_dest_rect, TextureFilter.None);

                //【A/D変換処理を実行、FIFOに格納する】マーカー検出確度が0.4より大きい場合のみ
                if (this._is_marker_enable && this._ar.getConfidence() > 0.4)
                {
                    ADconvert();
                    if (ADcount != -1)
                    {
                        Matrix transform_mat2 = Matrix.Translation(-85.0f, -10.0f, 3.0f); // 球体オブジェクト表示位置のオフセット[単位mm]
                        transform_mat2 *= this._trans_mat;                                // 座標変換マトリックスを計算(オフセット考慮)
                        FIFO_mat[wp] = transform_mat2;                                    // FIFOに座標変換マトリックスを格納 
                        FIFO_color[wp] = ColorLookupTable[ADcount];                       // FIFOにA/D変換値を格納
                        rp = wp;                                                          // 読出し開始ポインタ(rp)を設定
                        wp++; if (wp == nSample) { wp = 0; }                              // 書込みポインタ(wp)を設定
                    }
                }
                
                //【FIFOにデータが蓄積するまで表示を留保する】
                if (LoopNum != nSample) { LoopNum++; return; }

                //【3Dオブジェクトの描画初期化】
                this._device.BeginScene();                                       // シーン作成開始
                this._device.Clear(ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0); // フレームバッファ・クリア
                this._device.SetStreamSource(0, this._vertexBuffer, 0);          // 頂点バッファをデバイスのデータストリームにバインド
                this._device.VertexFormat = CustomVertex.PositionNormal.Format;  // 頂点バッファのフォーマットを設定
                this._device.Indices = this._indexBuffer;                        // インデックスバッファを設定
                this._device.RenderState.Lighting = true;     // 光源計算処理を行う true;
                this._device.Lights[0].Enabled = true;

                float position = 100.0f * (float)ADcount / 256.0f;
                Matrix transform_matG = Matrix.Translation(position - 20.0f, 40.0f, -5.0f); // 球体オブジェクト表示位置のオフセット[単位mm]
                transform_matG *= this._trans_mat;                                // 座標変換マトリックスを計算(オフセット考慮)
                //【ゲージの位置にカレント値を球体で表示】
                Material material = new Material();
                material.Diffuse = Color.FromArgb(FIFO_color[rp]);
                material.Ambient = Color.FromArgb(FIFO_color[rp]);
                material.Specular = Color.White; // FromArgb(FIFO_color[rp]);
                material.SpecularSharpness = 10.0f;
                material.Emissive = Color.FromArgb(FIFO_color[rp]);
                this._device.Material = material;
                // 頂点バッファをロックする
                using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
                {
                    data.Write(vertices);        // 頂点データを頂点バッファにコピーします
                    this._vertexBuffer.Unlock(); // 頂点バッファのロックを解除します
                }
                this._device.SetTransform(TransformType.World, transform_matG); // 計算したマトリックスで座標変換
                this._device.RenderState.CullMode = Cull.Clockwise;             // 頂点登録順時計回りで座標から法線ベクトルを演算し、裏ポリゴン非表示を決定
                this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, nVertex, 0, nTriangle); // 3Dレンダリング(描画処理)
                
                //【FIFOに蓄積した測定結果(球体)を表示する】
                for (int j = 0; j < nSample; j++)
                {
                    // 頂点データを設定する
                    //Material material = new Material();
                    material.Diffuse = Color.FromArgb(FIFO_color[rp]);
                    material.Ambient = Color.FromArgb(FIFO_color[rp]);
                    material.Specular = Color.White; // FromArgb(FIFO_color[rp]);
                    material.SpecularSharpness = 10.0f;
                    material.Emissive = Color.FromArgb(FIFO_color[rp]);
                    this._device.Material = material;

                    // 頂点バッファをロックする
                    using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
                    {
                        data.Write(vertices);        // 頂点データを頂点バッファにコピーします
                        this._vertexBuffer.Unlock(); // 頂点バッファのロックを解除します
                    }
                    this._device.SetTransform(TransformType.World, FIFO_mat[rp]);  // 計算したマトリックスで座標変換
                    this._device.RenderState.CullMode = Cull.Clockwise;            // 頂点登録順時計回りで座標から法線ベクトルを演算し、裏ポリゴン非表示を決定
                    this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, nVertex, 0, nTriangle); // 3Dレンダリング(描画処理)
                    rp--; if (rp <0 ) { rp = nSample-1; }
                }

                this._device.Lights[0].Enabled = false;
                this._device.RenderState.Lighting = false;     // 光源計算処理を行う true;
                this._device.SetStreamSource(0, this._vertexBufferT, 0);           // 頂点バッファをデバイスのデータストリームにバインド
                this._device.VertexFormat = CustomVertex.PositionTextured.Format;  // 頂点バッファのフォーマットを設定
                this._device.Indices = this._indexBufferT;                         // インデックスバッファを設定
                using (GraphicsStream data = this._vertexBufferT.Lock(0, 0, LockFlags.None))
                {
                    data.Write(verticesT);        // 頂点データを頂点バッファにコピーします
                    this._vertexBufferT.Unlock(); // 頂点バッファのロックを解除します
                }
                this._device.SetTexture(0, this._texture);
                this._device.TextureState[0].ConstantColorValue = 0xaa00bb;
                this._device.TextureState[0].ColorOperation = TextureOperation.MultiplyAdd; //.Modulate;
                this._device.TextureState[0].ColorArgument0 = TextureArgument.Temp;
                this._device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;
                this._device.TextureState[0].ColorArgument2 = TextureArgument.Current;
                this._device.TextureState[0].AlphaOperation = TextureOperation.Disable;
                Matrix transform_mat3 = Matrix.Translation(-20.0f, 25.0f, 0.0f); // 球体オブジェクト表示位置のオフセット[単位mm]
                transform_mat3 *= this._trans_mat;                               // 座標変換マトリックスを計算(オフセット考慮)
                this._device.SetTransform(TransformType.World, transform_mat3);  // 計算したマトリックスで座標変換
                this._device.RenderState.CullMode = Cull.Clockwise;              // 頂点登録順時計回りで座標から法線ベクトルを演算し、裏ポリゴン非表示を決定
                this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2); // 3Dレンダリング(描画処理)

                this._device.SetStreamSource(0, this._vertexBufferM, 0);           // 頂点バッファをデバイスのデータストリームにバインド
                this._device.VertexFormat = CustomVertex.PositionTextured.Format;  // 頂点バッファのフォーマットを設定
                this._device.Indices = this._indexBufferT;                         // インデックスバッファを設定
                using (GraphicsStream data = this._vertexBufferM.Lock(0, 0, LockFlags.None))
                {   data.Write(verticesM);        // 頂点データを頂点バッファにコピーします
                    this._vertexBufferM.Unlock(); // 頂点バッファのロックを解除します
                }
                if (ADcount > 200) {   this._device.SetTexture(0, this._Mtexture2); } // 計測値に応じてちびミク画像を変更する
                else               {   this._device.SetTexture(0, this._Mtexture1); } //
                this._device.SetTransform(TransformType.World, this._trans_mat); // 計算したマトリックスで座標変換
                this._device.RenderState.CullMode = Cull.Clockwise;              // 頂点登録順時計回りで座標から法線ベクトルを演算し、裏ポリゴン非表示を決定
                this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2); // 3Dレンダリング(描画処理)
                //【描画はここまで】
                this._device.SetTexture(0, null); //【テクスチャー設定をクリア】
                this._device.EndScene(); //【シーン作成終了】
                this._device.Present();  //【ディスプレイに反映】(ダブルバッファ切替等)
            }
            return;
        }

        // ■■■■■■■■■■ 【リソースの破棄をする処理】 ■■■■■■■■■■
        public void Dispose()
        {
            lock (this)
            {
                if (this._vertexBuffer != null) { this._vertexBuffer.Dispose(); }  // 頂点バッファを解放
                if (this._indexBuffer != null)  { this._indexBuffer.Dispose();  }  // インデックスバッファを解放
                if (this._texture != null)      { this._texture.Dispose();      }  // テクスチャー領域を開放  
                if (this._Mtexture1 != null)    { this._Mtexture1.Dispose();    }  // テクスチャー領域を開放  
                if (this._Mtexture2 != null)    { this._Mtexture2.Dispose();    }  // テクスチャー領域を開放  
                if (this._surface != null)      { this._surface.Dispose();      }  // 背景テクスチャー領域を解放
                if (this._device != null)       { this._device.Dispose();       }  // Direct3D デバイスのリソース解放
            }
        }

        // ■■■■■■■■■■ 【A/Dコンバータの初期化処理】 ■■■■■■■■■■
        public Boolean ADCInitialize()
        {
            //【ADC用シリアルポートの設定】
            this.SerialPort_for_ADC.PortName = COMPORT;
            //     this.SerialPort_for_ADC.BaudRate = 115200;                       //【通常のシリアル通信の場合の設定】
            //     this.SerialPort_for_ADC.DataBits = 8;                            //
            //     this.SerialPort_for_ADC.StopBits = System.IO.Ports.StopBits.One; //
            //     this.SerialPort_for_ADC.Parity = System.IO.Ports.Parity.None;    //
            //     this.SerialPort_for_ADC.ReadTimeout = 50;                        //
            //     this.SerialPort_for_ADC.ReadBufferSize = 256;                    //
            //     this.SerialPort_for_ADC.WriteBufferSize = 256;                   //
            try { this.SerialPort_for_ADC.Open(); }
            catch (Exception) { MessageBox.Show("シリアルポートが開けません"); return false; }

            TxBuf[0] = 0x45; // "E"   A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[1] = 0x0D; // CR
            TxBuf[2] = 0x0A; // LF 
            this.SerialPort_for_ADC.Write(TxBuf, 0, 3);
            Sleep(50);
            RxMsg = this.SerialPort_for_ADC.ReadLine();

            //【モードレジスタ】000A 【コマンド】C08000A + CR + LF
            TxBuf[0] = 0x43; // "C" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[1] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[2] = 0x38; // "8" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[3] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[4] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[5] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[6] = 0x41; // "A" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[7] = 0x0D; // CR
            TxBuf[8] = 0x0A; // LF 
            this.SerialPort_for_ADC.Write(TxBuf, 0, 9);
            Sleep(50);
            RxMsg = this.SerialPort_for_ADC.ReadLine();

            //【コンフィギュレーションレジスタ】5080 【コマンド】C105080 + CR + LF
            TxBuf[0] = 0x43; // "C" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[1] = 0x31; // "1" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[2] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[3] = 0x35; // "5" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[4] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[5] = 0x38; // "8" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[6] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[7] = 0x0D; // CR
            TxBuf[8] = 0x0A; // LF 
            this.SerialPort_for_ADC.Write(TxBuf, 0, 9);
            Sleep(50);
            RxMsg = this.SerialPort_for_ADC.ReadLine();

            //【IOレジスタ】0A 【コマンド】C280A + CR + LF
            TxBuf[0] = 0x43; // "C" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[1] = 0x32; // "2" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[2] = 0x38; // "8" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[3] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[4] = 0x41; // "A" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[5] = 0x0D; // CR
            TxBuf[6] = 0x0A; // LF 
            this.SerialPort_for_ADC.Write(TxBuf, 0, 7);
            Sleep(50);
            RxMsg = this.SerialPort_for_ADC.ReadLine();

            //【オフセットレジスタ】800000 【コマンド】C30800000 + CR + LF
            TxBuf[0] = 0x43; // "C" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[1] = 0x33; // "3" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[2] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[3] = 0x38; // "8" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[4] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[5] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[6] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[7] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[8] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[9] = 0x0D; // CR
            TxBuf[10] = 0x0A; // LF 
            this.SerialPort_for_ADC.Write(TxBuf, 0, 11);
            Sleep(50);
            RxMsg = this.SerialPort_for_ADC.ReadLine();

            return true;
        }

        // ■■■■■■■■■■ 【A/Dコンバータの終了処理】 ■■■■■■■■■■
        public void ADCTerminate()
        {
            TxBuf[0] = 0x43; // "C" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[1] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[2] = 0x38; // "8" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[3] = 0x36; // "6" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[4] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[5] = 0x30; // "0" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[6] = 0x41; // "A" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[7] = 0x0D; // CR
            TxBuf[8] = 0x0A; // LF 
            this.SerialPort_for_ADC.Write(TxBuf, 0, 9);
            Sleep(5);
            RxMsg = this.SerialPort_for_ADC.ReadLine();

            this.SerialPort_for_ADC.Close();
        }

        // ■■■■■■■■■■ 【A/D変換処理】 ■■■■■■■■■■
        // ここにA/D変換読み取り処理を挿入する。設定範囲を0≦ADcount≦255とする。
        public void ADconvert()
        {
            string c;

            this.SerialPort_for_ADC.DiscardInBuffer();
            Sleep(10);

            TxBuf[0] = 0x52; // "R" A:41 B:42 C:43 D:44 E:45 F:46
            TxBuf[1] = 0x0D; // CR
            TxBuf[2] = 0x0A; // LF 
            this.SerialPort_for_ADC.Write(TxBuf, 0, 3);
            Sleep(10);
            RxMsg = this.SerialPort_for_ADC.ReadLine(); //【1サンプル受信】
            c = RxMsg.Substring(1, 1);
            if (String.IsNullOrEmpty(c))
            {
                c = RxMsg.Substring(2, 1);
                ADcount = ConvertAscii(c);
                c = RxMsg.Substring(3, 1);
                ADcount = ADcount*16 + ConvertAscii(c);
             }
            else
            {
                ADcount = ConvertAscii(c);                //【先頭2文字を8ビットの数値に変換】
                c = RxMsg.Substring(2, 1);
                ADcount = ADcount * 16 + ConvertAscii(c);
            }
        }

        public int ConvertAscii(string c)
        {
            int a=-1;

            switch (c)
            { 
              case "0":
                a = 0; break;
              case "1":
                a = 1; break;
              case "2":
                a = 2; break;
              case "3":
                a = 3; break;
              case "4":
                a = 4; break;
              case "5":
                a = 5; break;
              case "6":
                a = 6; break;
              case "7":
                a = 7; break;
              case "8":
                a = 8; break;
              case "9":
                a = 9; break;
              case "A":
                a = 10; break;
              case "B":
                a = 11; break;
              case "C":
                a = 12; break;
              case "D":
                a = 13; break;
              case "E":
                a = 14; break;
              case "F":
                a = 15; break;
            }
            return (a);
        }
    }
}

【トランジスタ技術2011年2月号記事の訂正について】

 チェック不十分だったようで、誤植や回路図にミスがありました。
 1) P.154 l.2 Ament ⇒ Augmented
 2) 記事【図2】の②はAに接続します。下図(E-1)に示すように地磁気センサのAD620出力のノイズ抑制点から信号を取り出し、2段のLPFに供給します。
 3) さらに記事【図2】に2か所ある全波整流回路は単電源で動作させますので、下図(E-2)に示すようにAD823の4番ピン(負側電源ピン)はグランドに接続します。


【図E-1】地磁気センサとLPFとの接続

【図E-2】全波整流回路の電源は単電源