SATCHのcompassを使って距離と高さを測ってみる
SATCH AR SDKのcompassを使ってみました。
これで何をしようかと、つらつら考え思いついたのが、
これで、対象物の「距離」と「高さ」を測ってみましょう...ということ。
対象は室内レベルですが、精度はそこそこ出て.....います(多分)。
あとこれを拡張して、AR表現(どこでもドア)をする予定です(^^)。
できれば、スマホではなくヘッドマウントディスプレイやグーグル・グラス
みたいなもんでやりたいです(^^)。
適用範囲
●屋内限定
●高さは、ご自分の目線より高いもののみ
●Android 2.3.3以上
X,Y,Zの値は、それぞれ以下のような意味。
X:方位角
Y:傾き角
Z:端末の回転角(ポートレートかランドスケープかの判断に使用)
使い方
このような、収納スペースのドアの高さを計測してみます。
アプリ起動後、以下のボタンをタップしてご自分の身長を登録します。この値が基準になります。
収納スペースのドアの下端に赤い線を合わせます。
スマホを少し見下ろす感じで持ち、Yの値のブレが安定したら、 (メニュ)をクリック
収納スペースのドアの上端に赤い線を合わせてます。
スマホを少し見上げる感じで持ち、Yの値のブレが安定したら、 (メニュ)をクリック
計測値は184cm
因みに、実測値は190cm
リセット
をタップするか、(メニュ)をクリック
終了
(バック)をクリック
インストール
TOP
TOP
【継承クラス】
TOP
トップページ| サイトマップ|
これで何をしようかと、つらつら考え思いついたのが、
これで、対象物の「距離」と「高さ」を測ってみましょう...ということ。
対象は室内レベルですが、精度はそこそこ出て.....います(多分)。
あとこれを拡張して、AR表現(どこでもドア)をする予定です(^^)。
できれば、スマホではなくヘッドマウントディスプレイやグーグル・グラス
みたいなもんでやりたいです(^^)。
こんなアプリです
画面はこんな感じ。
適用範囲
●屋内限定
●高さは、ご自分の目線より高いもののみ
●Android 2.3.3以上
X,Y,Zの値は、それぞれ以下のような意味。
X:方位角
Y:傾き角
Z:端末の回転角(ポートレートかランドスケープかの判断に使用)
使い方
このような、収納スペースのドアの高さを計測してみます。
アプリ起動後、以下のボタンをタップしてご自分の身長を登録します。この値が基準になります。
収納スペースのドアの下端に赤い線を合わせます。
スマホを少し見下ろす感じで持ち、Yの値のブレが安定したら、 (メニュ)をクリック
収納スペースのドアの上端に赤い線を合わせてます。
スマホを少し見上げる感じで持ち、Yの値のブレが安定したら、 (メニュ)をクリック
計測値は184cm
因みに、実測値は190cm
リセット
をタップするか、(メニュ)をクリック
終了
(バック)をクリック
インストール
TOP
SATCH側のコード
目安として画面中央の赤いライン
SATCH_TESTのバーコード(testBarCodePlugin)の赤いライン(オーバーレイ)を流用
オーバーレイの実装(以下の3つのファイルを使います)
barcode.overlay、barcode.material、barcode_red.png
SATCH Studioを起動
Add -> 2D Elements -> Overlay
Nameは任意ですが、Overlay nameは、barcode.overlayに記述されている「BarCode/Visor」を使用。
【Luaコード】
local scene = getCurrentScene() local componentInterface = getComponentInterface() local inputManager = getInputManager() local lTextCompass_1 = Text2D(scene:getObjectByName("text1")) local lTextCompass_2 = Text2D(scene:getObjectByName("text2")) local lTextCompass_3 = Text2D(scene:getObjectByName("text3")) if isCompassDeviceAvailable() then local lcompass = Compass(inputManager:getDevice(TIINPUT_COMPASSDEVICE)) lcompass:acquire() --local entity = Entity(scene:getObjectByName("frame")) repeat compass_x = lcompass:getOrientationX() compass_y = lcompass:getOrientationY() compass_z = lcompass:getOrientationZ() --str = "Compass: " .. compass_x .. ", " .. compass_y .. ", " .. compass_z str_1 = "X: " .. compass_x lTextCompass_1:setText(str_1) str_2 = "Y: " .. compass_y lTextCompass_2:setText(str_2) str_3 = "Z: " .. compass_z lTextCompass_3:setText(str_3) --entity:setOrientationEuler(0.0,0.0, compass_x) -- not the best use... componentInterface:executeAppFunc("setCompassStatus",compass_x .. "," .. compass_y .. "," .. compass_z) isCommand, command = componentInterface:pullCommand() if isCommand then if command["CommandName"] == "object" then -- --componentInterface:executeAppFunc("sendLog","receive command") local s_type = command["arg0"] if s_type == "show" then -- elseif s_type == "hide" then -- end end end until coroutine.yield() else lTextCompass_1:setText("Compass device is not available!") lTextCompass_2:setText("") lTextCompass_3:setText("") end
TOP
Java側のコード
vvvvv,xxxxx,yyyyy,zzzzzは適当に読み替えてください。
因みに、継承クラスのパッケージ名(com.kddi.satch.vvvvv)は、SATCHのアプリケーションIDと一致させてください。
【抽象クラス】
package com.kddi.satch.xxxxx; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager.LayoutParams; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import com.kddi.satch.LoadScenarioStatus; import com.kddi.satch.ARViewer; import android.app.AlertDialog; import android.content.DialogInterface; import android.widget.EditText; import android.widget.Toast; // import android.widget.ImageButton; import android.widget.LinearLayout; import android.view.View.OnClickListener; import android.view.Gravity; public abstract class yyyyy extends Activity { protected abstract String getSampleScenarioName(); protected abstract String getSampleLogTag(); protected boolean _isInitializedCorrectly; protected ARViewer _kddiComponent; protected FrameLayout _frameLayout; public TextView Text_View; public LinearLayout textLayout; public int click_count = -1; public double MyTall = 163; private Handler mHandler; public double distance_cm = 0; public double height = 0; public double upper_Y = 0; //public double standard_Y = 0; public double standard_Y = 90; //public double delta_Y = 0; public double lower_Y = 0; public double max_X = 0; public double min_X = 0; public double[] lower_list = new double[3]; public int lower_index = 0; public double[] upper_list = new double[3]; public int upper_index = 0; //private double max_Z = 0; //private double min_Z = 0; public int img_id1 = 0; public int img_id2 = 0; public void resetMembers(){ _isInitializedCorrectly = false; _frameLayout = null; _kddiComponent = null; } private static final int DIALOG_EXIT = 0; public void initComponent(){ _isInitializedCorrectly = false; _kddiComponent = new ARViewer(this); // This FrameLayout must be empty (but initialized) when you pass it to the kddiComponent.initialize() method. _frameLayout = new FrameLayout(this); _kddiComponent.initialize(_frameLayout); _isInitializedCorrectly = true; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Add null to AR Viewer Library compornent's reference. resetMembers(); } @Override public void onRestart() { super.onRestart(); } @Override public void onStart() { super.onStart(); // Create AR Viewer Library compornent. initComponent(); postInitComponent(); initContentView(); if (_isInitializedCorrectly) { // Do authorize and madia is loaded. // You must call loadScenario() method. loadScenario(); } } @Override public void onResume(){ super.onResume(); if (_isInitializedCorrectly) { // GL context is recreated and media is reloaded. _kddiComponent.onResume(); reservePlayScenario(); } } @Override public void onPause() { // When the activity is paused the GL context is destroyed, so all media is unloaded. if (_isInitializedCorrectly) { cancelReservePlayScenario(); if (_kddiComponent.checkLoadScenarioStatus() == LoadScenarioStatus.COMPLETE) { _kddiComponent.pauseScenario(); } _kddiComponent.onPause(); } super.onPause(); } @Override public void onStop() { releaseContentView(); // Destroy AR Viewer Library Objects. if (_isInitializedCorrectly){ _kddiComponent.terminate(); _kddiComponent = null; _frameLayout = null; } super.onStop(); } @Override public void onDestroy() { // Destroy AR Viewer Library Objects. if (_isInitializedCorrectly){ _frameLayout = null; _kddiComponent = null; } super.onDestroy(); resetMembers(); // forced clean } @Override public boolean onKeyDown(int keyCode, KeyEvent msg){ switch(keyCode){ case android.view.KeyEvent.KEYCODE_MENU : //showDialog( DIALOG_EXIT ); click_count++; if (click_count == 2) { click_count = 0; reset_data(); } calc_it(); return true; case android.view.KeyEvent.KEYCODE_BACK : showDialog( DIALOG_EXIT ); return true; } return false; } public void postInitComponent(){ // override this if you need to do some special handling on the component after standard initialization if (_isInitializedCorrectly) { _kddiComponent.activateAutoFocusOnDownEvent(true); } } public void initContentView(){ // override this if you need to do some special handling on the component after standard initialization if (_isInitializedCorrectly) { // you'll probably use some other UI object as the content view that itself will embed the component's frame layout -- here you can change all this // by default, the frame layout containing DFusion will be the activity content view setContentView(_frameLayout); } LinearLayout linear_LayoutL = new LinearLayout(this); linear_LayoutL.setOrientation(LinearLayout.HORIZONTAL); linear_LayoutL.setGravity(Gravity.LEFT|Gravity.BOTTOM); addContentView(linear_LayoutL, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); LinearLayout linear_LayoutC = new LinearLayout(this); linear_LayoutC.setOrientation(LinearLayout.HORIZONTAL); linear_LayoutC.setGravity(Gravity.CENTER|Gravity.BOTTOM); addContentView(linear_LayoutC, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); LinearLayout linear_LayoutR = new LinearLayout(this); linear_LayoutR.setOrientation(LinearLayout.HORIZONTAL); linear_LayoutR.setGravity(Gravity.RIGHT|Gravity.BOTTOM); addContentView(linear_LayoutR, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); //reset ImageButton imgbtn_reset = new ImageButton(this); imgbtn_reset.setImageResource(img_id2); //imgbtn_B.setAlpha(50); imgbtn_reset.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { ButtonReset_OnClick(); } }); linear_LayoutR.addView(imgbtn_reset, new LinearLayout.LayoutParams(72,72)); textLayout = new LinearLayout(this); Text_View = new TextView(this); Text_View.setTextColor(Color.YELLOW); Text_View.setText("data"); linear_LayoutC.addView(Text_View, new LinearLayout.LayoutParams(250,72)); //height ImageButton imgbtn_height = new ImageButton(this); imgbtn_height.setImageResource(img_id1); //imgbtn_C.setAlpha(50); imgbtn_height.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { ButtonHeight_OnClick(); } }); linear_LayoutR.addView(imgbtn_height, new LinearLayout.LayoutParams(72,72)); } private void ButtonReset_OnClick(){ reset_data(); } private void ButtonHeight_OnClick(){ input_yourtall(); } public void releaseContentView(){ // override this if you need to do some special handling on the component after standard initialization if (_isInitializedCorrectly) { // do here the release of the your UI instances (if customized) } } public void loadScenario(){ ApplicationInfo appInfo = null; PackageManager packMgmr = getApplicationContext().getPackageManager(); try { appInfo = packMgmr.getApplicationInfo(getPackageName(), 0); } catch (NameNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Unable to locate assets, aborting..."); } String dpdfile = appInfo.sourceDir + getSampleScenarioName(); _kddiComponent.loadScenario(dpdfile); } // Set polling rate for loading the media. private final int REPEAT_INTERVAL = 100; private Handler handler = new Handler(); private Runnable runnable = null; // private void reservePlayScenario(){ if (runnable == null){ runnable = new Runnable(){ @Override public void run(){ LoadScenarioStatus status = _kddiComponent.checkLoadScenarioStatus(); if (status == LoadScenarioStatus.CANCEL){ // cancel(appli suspend) }else if (status == LoadScenarioStatus.COMPLETE){ // Ready to play scenario _frameLayout.setVisibility(View.VISIBLE); _kddiComponent.playScenario(); }else if ( status == LoadScenarioStatus.ERROR_NETWORK_UNUSABLE || // faild to load a media becase of no network connection. status == LoadScenarioStatus.ERROR_NETWORK || // faild to load a media becase of network error. status == LoadScenarioStatus.ERROR_SOFTWAREKEY || // faild to load a media becase software key has not be found on server. status == LoadScenarioStatus.ERROR_CONTENT_STOPPED || // faild to load a media becase content has stopped. status == LoadScenarioStatus.ERROR_SERVER || // faild to load a media becase of server error. status == LoadScenarioStatus.ERROR_ETC // faild to load a media becase of another error. ){ // error }else{ handler.postDelayed(this, REPEAT_INTERVAL); } } }; handler.postDelayed(runnable, REPEAT_INTERVAL); } } // private void cancelReservePlayScenario(){ if (handler != null && runnable != null){ handler.removeCallbacks(runnable); runnable = null; } } protected Dialog onCreateDialog(int id) { Dialog dialog; switch(id) { case DIALOG_EXIT: { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage( "Really want to quit the application?" ) .setCancelable(true) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); }}) .setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); }}); dialog = builder.create(); break; } default: dialog = null; } return dialog; } public void input_yourtall(){ // final EditText editView = new EditText(yyyyy.this); editView.setText("163"); new AlertDialog.Builder(yyyyy.this) .setIcon(android.R.drawable.ic_dialog_info) .setTitle("身長をcmで入力") // .setView(editView) .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // if (editView.getText().toString().equals("")) { } else{ MyTall = Double.parseDouble(editView.getText().toString()); } } }) .setNegativeButton("キャンセル", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }) .show(); } public void calc_it(){ Log.i("CAL","OK"); double rad = 0; String mes = ""; if (click_count == 0) { height = MyTall - 33; //Text_View.setText(String.valueOf(max_data)); lower_Y = (lower_list[0] + lower_list[1] + lower_list[2]) / 3; double sieta = 90 - Math.abs(standard_Y - lower_Y); Log.i("ANGLE",sieta + ""); rad = Math.toRadians(sieta); distance_cm = Math.round(height / Math.tan(rad)); //int distance_cm = (int)Math.round(height / Math.tan(rad)); //double distance_m = distance_cm / 100; mes = "距離:" + String.valueOf(distance_cm) + " cm:" + sieta; Text_View.setText(mes); } if (click_count == 1) { height = MyTall - 5; upper_Y = (upper_list[0] + upper_list[1] + upper_list[2]) / 3; double gamma = Math.abs(upper_Y) - 90; rad = Math.toRadians(gamma); double M = distance_cm * Math.tan(rad); double H = Math.round(M + height); mes = "距離:" + String.valueOf(distance_cm) + " cm" + "\n" + "高さ:" + String.valueOf(H) + " cm"; Text_View.setText(mes); } } private void reset_data(){ // click_count = -1; standard_Y = 0; lower_Y = 0; upper_Y = 0; distance_cm = 0; Text_View.setText(""); } }
【継承クラス】
package com.kddi.satch.vvvvv; import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.util.Log; import com.kddi.satch.xxxxx.yyyyy; import android.widget.Toast; import android.content.res.Resources; import android.graphics.drawable.Drawable; public class xxxxx extends yyyyy { private static final String THIS_CLASS_SHORT_NAME = xxxxx.class.getName().replace("com.kddi.satch.vvvvv.", ""); private static final String THIS_LOGTAG = THIS_CLASS_SHORT_NAME; // Set AR scenario file path. private static final String SCENARIO_NAME = "/assets/Scenario/Scenario_satchcompass/satchCompass.dpd"; protected String getSampleScenarioName() { return SCENARIO_NAME; } protected String getSampleLogTag() { return THIS_LOGTAG; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Resources res = getResources(); img_id1 = res.getIdentifier("height" , "drawable", getPackageName()); img_id2 = res.getIdentifier("reset" , "drawable", getPackageName()); } @Override public void postInitComponent(){ super.postInitComponent(); if (_isInitializedCorrectly) { _kddiComponent.activateAutoFocusOnDownEvent(true); _kddiComponent.registerCommunicationCallback("setCompassStatus", this, "setCompassStatus"); } } public void setCompassStatus(String[] arrayOfString) { String[] data_list = new String[3]; data_list = arrayOfString[0].split(","); //X //Y double temp_Y = Double.parseDouble(data_list[1]); if (Double.parseDouble(data_list[1]) < -90) { upper_list[upper_index] = temp_Y; upper_index++; if (upper_index == 3) { upper_index = 0; } upper_Y = temp_Y; }else{ lower_list[lower_index] = temp_Y; lower_index++; if (lower_index == 3) { lower_index = 0; } //lower_Y = temp_Y; } //Z if (Math.abs(Double.parseDouble(data_list[2])) < 45) { //portrate }else{ //landscape } //------------------------------------------ } }
TOP
トップページ| サイトマップ|