質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

1995閲覧

【AndroidStudio】コンパスの作成について

takk_014

総合スコア53

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2021/10/19 04:41

AndroidStudioでkotlinを用いてコンパスを作成しようとしています。
サイトにを参考にしてアプリを作って実行してみました。
しかし最初から針が方位を示していない上に端末を動かすと針の向きが変わってしまいます。
スマホを右に回した場合、回した角度以上に針が右に回ります。その逆も然りです。
エラーなどは出ていないです。
使用している端末はAndroid7.1.1です。
使用しているAndroidStudioのバージョンは4.1.1です。

こちらのサイトを参考にしています。
Androidで動作する簡易方位磁針の作成
Androidに表示する画像を動的に回転させる

こちらがコードです。

kotlin

1import android.content.Context 2import android.graphics.Matrix 3import android.hardware.Sensor 4import android.hardware.SensorEvent 5import android.hardware.SensorEventListener 6import android.hardware.SensorManager 7import android.os.Bundle 8import android.os.Handler 9import android.util.Log 10import android.widget.ImageView 11import android.widget.TextView 12import androidx.appcompat.app.AppCompatActivity 13import java.text.SimpleDateFormat 14import java.util.* 15 16 17class MainAdventure : AppCompatActivity() { 18 19 /* 現在時刻と経過時間の表示に用いる変数 */ 20 lateinit var currentTime_TextView: TextView 21 lateinit var elapsedtime_time: TextView 22 private var cnt = 0 23 private val hnd0 = Handler() 24 /* コンパスの描画に用いる変数 */ 25 lateinit var mSensorManager: SensorManager 26 lateinit var mAccelerationSensor: Sensor 27 lateinit var mMagneticSensor: Sensor 28 lateinit var mSensorEventListener: SensorEventListener 29 private var mMagneticFieldflg = false 30 private var mAccelerationValue: FloatArray = FloatArray(3) 31 private var mMagneticFieldValue: FloatArray = FloatArray(3) 32 lateinit var compassNeedle_ImageView: ImageView 33 lateinit var tmpMatrix: Matrix 34 35 36 // 1000msごとに発生するハンドラー 37 val rnb0 = object : Runnable{ 38 override fun run() { 39 cnt++ 40 //現在時刻更新 41 if(cnt % 10 == 0){ 42 val d = Date() 43 val sdf = SimpleDateFormat("a hh:mm") 44 currentTime_TextView.text = sdf.format(d) 45 } 46 // 経過時間更新 47 elapsedtime_time.text = "経過時間:${cnt/3600}時間 ${(cnt%3600)/60}${(cnt%60)}秒" 48 hnd0.postDelayed(this, 1000) 49 } 50 } 51 52 override fun onCreate(savedInstanceState: Bundle?) { 53 super.onCreate(savedInstanceState) 54 setContentView(R.layout.adventure_main) 55 56 val d = Date() 57 val sdf = SimpleDateFormat("a h時 m分") 58 59 /* 現在時刻と経過時間表示処理 */ 60 currentTime_TextView = findViewById(R.id.currentTime_TextView) 61 currentTime_TextView.text = sdf.format(d) 62 elapsedtime_time = findViewById(R.id.elapsedtime_time) 63 hnd0.post(rnb0) 64 65 /* コンパス針表示処理 */ 66 // 子Viewを追加したいLinearLayoutオブジェクトを取得する。 67 currentTime_TextView = findViewById(R.id.currentTime_TextView) 68 compassNeedle_ImageView = findViewById(R.id.compassneedle_ImageView) 69 tmpMatrix = compassNeedle_ImageView.imageMatrix 70 initializeSensor() 71 compassNeedle_ImageView.post(object : Runnable { 72 override fun run() { 73 compassNeedle_ImageView.scaleType = ImageView.ScaleType.MATRIX 74 } 75 }) 76 } 77 78 private fun initializeSensor() 79 { 80 mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager 81 mAccelerationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) 82 mMagneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) 83 mSensorEventListener = MySensorEventListener() 84 } 85 86 inner class MySensorEventListener: SensorEventListener 87 { 88 override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) 89 { 90 // センサーの精度が変更されても何もしない 91 } 92 93 override fun onSensorChanged(event: SensorEvent?) 94 { 95 if(event != null) 96 { 97 when(event.sensor.type) 98 { 99 Sensor.TYPE_ACCELEROMETER -> { 100 if (event.values != null) { 101 this@MainAdventure.setAccelerationValue(event.values) 102 } 103 } 104 Sensor.TYPE_MAGNETIC_FIELD -> { 105 if (event.values != null) { 106 this@MainAdventure.setMagneticFieldValue(event.values) 107 this@MainAdventure.setMagneticFieldflg(true) 108 } 109 } 110 } 111 // 地磁気センサーの値を取得することができたら 112 if(this@MainAdventure.mMagneticFieldflg){ 113 // 方位を出すための変換行列 114 var rotate = FloatArray(16) 115 var inclination = FloatArray(16) 116 // 回転角 117 var orientation = FloatArray(3) 118 // 行列化 119 SensorManager.getRotationMatrix(rotate, inclination, 120 this@MainAdventure.mAccelerationValue, 121 this@MainAdventure.mMagneticFieldValue) 122 //回転角取得 123 SensorManager.getOrientation(rotate, orientation) 124 val doubleOrientation = (orientation[0]).toDouble() 125 val degreeDir = Math.toDegrees(doubleOrientation).toFloat() 126 127 Log.d("Sensor",degreeDir.toString()) 128 // ここでコンパスを描画 129 drawCompass(degreeDir) 130 } 131 } 132 } 133 } 134 135 /* MySensorEventListenerクラスで使う関数たち */ 136 private fun setAccelerationValue(accelerationValue: FloatArray) 137 { 138 mAccelerationValue = accelerationValue 139 } 140 private fun setMagneticFieldValue(magneticFieldValue: FloatArray) 141 { 142 mMagneticFieldValue = magneticFieldValue 143 } 144 private fun setMagneticFieldflg(magneticFieldflg: Boolean) 145 { 146 mMagneticFieldflg = magneticFieldflg 147 } 148 private fun drawCompass(degreeDir: Float) 149 { 150 tmpMatrix.reset() 151 // 角度分回転させる(ImageViewの中心で回転) 152 tmpMatrix.postRotate(degreeDir, compassNeedle_ImageView.width * 0.5f, compassNeedle_ImageView.height * 0.5f) 153 // 行列を適用 154 compassNeedle_ImageView.imageMatrix = tmpMatrix 155 compassNeedle_ImageView.invalidate() 156 } 157 158 159 /* 各センサーにイベントリスナーを登録する */ 160 override fun onResume() { 161 super.onResume() 162 setSensorEventListener() 163 } 164 private fun setSensorEventListener() 165 { 166 mSensorManager.registerListener( 167 mSensorEventListener, 168 mAccelerationSensor, 169 SensorManager.SENSOR_DELAY_NORMAL 170 ) 171 mSensorManager.registerListener( 172 mSensorEventListener, 173 mMagneticSensor, 174 SensorManager.SENSOR_DELAY_NORMAL 175 ) 176 } 177 178}

サイトにあるコードを入力して間違いがないか何度も確認はしています。
mMagneticFieldflgがtrueとなっているため地磁気センサーから値を取得することはできています。
MySensorEventListenerクラスで取得した角度の単位をそのままでdrawCompass関数で使用しているのが悪いのかと思ってpostRotateを調べましたがどちらもdegrees単位の角度なので問題はないはずです。
そのためやはりMySensorEventListenerクラスで取得している角度自体に問題があるのかと思いました。
しかし下記のコードを見てもセンサーについて疎い私には何が悪いのか分かりません。

kotlin

1// 地磁気センサーの値を取得することができたら 2 if(this@MainAdventure.mMagneticFieldflg){ 3 // 方位を出すための変換行列 4 var rotate = FloatArray(16) 5 var inclination = FloatArray(16) 6 // 回転角 7 var orientation = FloatArray(3) 8 // 行列化 9 SensorManager.getRotationMatrix(rotate, inclination, 10 this@MainAdventure.mAccelerationValue, 11 this@MainAdventure.mMagneticFieldValue) 12 //回転角取得 13 SensorManager.getOrientation(rotate, orientation) 14 val doubleOrientation = (orientation[0]).toDouble() 15 val degreeDir = Math.toDegrees(doubleOrientation).toFloat() 16 17 Log.d("Sensor",degreeDir.toString()) 18 // ここでコンパスを描画 19 drawCompass(degreeDir) 20 }

こちらがdegreeDirのログの一部になります。

Log

12021-10-19 13:34:53.399 5673-5673/com.example.adventurecompass D/Sensor: -163.80122 22021-10-19 13:34:53.513 5673-5673/com.example.adventurecompass D/Sensor: -163.66557 32021-10-19 13:34:53.600 5673-5673/com.example.adventurecompass D/Sensor: -166.42857 42021-10-19 13:34:53.670 5673-5673/com.example.adventurecompass D/Sensor: -166.5633 52021-10-19 13:34:53.924 5673-5673/com.example.adventurecompass D/Sensor: -161.92937 62021-10-19 13:34:54.081 5673-5673/com.example.adventurecompass D/Sensor: -161.9288 72021-10-19 13:34:54.104 5673-5673/com.example.adventurecompass D/Sensor: -164.62039 82021-10-19 13:34:54.238 5673-5673/com.example.adventurecompass D/Sensor: -164.62105 92021-10-19 13:34:54.305 5673-5673/com.example.adventurecompass D/Sensor: -163.4262 102021-10-19 13:34:54.396 5673-5673/com.example.adventurecompass D/Sensor: -163.42574

どうかご教授いただきたいです。
よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2021/10/19 05:38

とりあえず、onPause は追加が必要そうです。
takk_014

2021/10/19 06:50

コメントありがとうございます。 そうですね、バックグラウンドに入った時の処理は必要ですね。 今後追加していきます。
jimbe

2021/10/19 07:19

実行してみてはいるのですが、回転の向きを逆にした("drawCompass(-degreeDir)")ぐらいで、角度はそれほど変になってはいないように感じます。 (Android10/XperiaXZ3)
takk_014

2021/10/19 08:29

私も-degreeDirで試してみました。 ゆっくり慎重に水平に回せば方位を指し示せます。 しかし激しい動きをすると簡単にずれてしまいます。 iPhoneのコンパスではこのようなことはないため精度が低いと感じます。 端末の問題かと思いましたがGoogleMapのコンパスと比べても精度が低いです。
jimbe

2021/10/19 10:15 編集

データの取得が遅い所為で表示が追い付いていないようであれば、センサーのリスナを登録する際の SensorManager.SENSOR_DELAY_NORMAL を SensorManager.SENSOR_DELAY_UI にすればそれなりに早くなるとは思います。(というかこの件では UI のほうが良いようにも思います。) ですが(「激しい動き」の程度が合うか分かりませんが)数秒ぐりぐり向きを変えても「北を指していたのが北東になった」ようなずれは感じず、再実行して最初に表示された状態と比べても変わったようには見えませんでした。 何が累積しているのか・・・
takk_014

2021/10/20 02:11

端末の違いによって差があるのでしょうか... 私の端末では10秒で一周する程度の速度で回したところ 下記のログのように角度が飛んでしまいます。 D/Sensor: -94.597435 D/Sensor: -99.673096 D/Sensor: -99.673096 D/Sensor: -25.52211 D/Sensor: -25.52211 D/Sensor: -32.830616 D/Sensor: -32.830616 D/Sensor: 12.273596 D/Sensor: 12.273596 D/Sensor: 10.763565 D/Sensor: 10.763565 D/Sensor: -39.18111 D/Sensor: -39.18111 D/Sensor: -38.16778 D/Sensor: -38.16778 D/Sensor: 6.477657 D/Sensor: 6.477657 D/Sensor: 77.402534 D/Sensor: 77.402534 D/Sensor: 82.352646 普通ならば連続した値が出るはずなのですが。。。
jimbe

2021/10/20 04:32 編集

確かにすごい数値の飛びです。これでは動きが変になるのも頷けますね。 生データ(?)が分かる方なら mAccelerationValue や mMagneticFieldValue のデータから何か読めるのかもしれませんが、私もその辺は分かりません。 センサーの API の説明にはやはりハードに繋がると書いてありますので、値の動きにはハードの違いは出てくるとは思いますが、同端末上での GoogleMap 等ではこのような飛びが感じられないのであれば、コードの何か違うと思われますし…。 ディレイを NORMAL にして机上にスマホを置き、左回りに同じく10秒ぐらいで一周させた時のログの一部は 2021-10-20 13:20:47.542 21833-21833/com.teratail.q365132 D/Sensor: -24.138458 2021-10-20 13:20:47.604 21833-21833/com.teratail.q365132 D/Sensor: -27.473104 2021-10-20 13:20:47.619 21833-21833/com.teratail.q365132 D/Sensor: -27.418327 2021-10-20 13:20:47.658 21833-21833/com.teratail.q365132 D/Sensor: -27.179459 2021-10-20 13:20:47.736 21833-21833/com.teratail.q365132 D/Sensor: -27.431726 2021-10-20 13:20:47.797 21833-21833/com.teratail.q365132 D/Sensor: -31.47621 2021-10-20 13:20:47.813 21833-21833/com.teratail.q365132 D/Sensor: -30.498672 2021-10-20 13:20:47.853 21833-21833/com.teratail.q365132 D/Sensor: -29.209267 2021-10-20 13:20:47.930 21833-21833/com.teratail.q365132 D/Sensor: -28.505411 2021-10-20 13:20:47.992 21833-21833/com.teratail.q365132 D/Sensor: -33.369507 2021-10-20 13:20:48.010 21833-21833/com.teratail.q365132 D/Sensor: -35.16359 2021-10-20 13:20:48.086 21833-21833/com.teratail.q365132 D/Sensor: -36.38696 2021-10-20 13:20:48.124 21833-21833/com.teratail.q365132 D/Sensor: -37.932613 2021-10-20 13:20:48.186 21833-21833/com.teratail.q365132 D/Sensor: -42.660385 2021-10-20 13:20:48.201 21833-21833/com.teratail.q365132 D/Sensor: -44.030773 2021-10-20 13:20:48.280 21833-21833/com.teratail.q365132 D/Sensor: -44.926147 2021-10-20 13:20:48.318 21833-21833/com.teratail.q365132 D/Sensor: -47.696815 2021-10-20 13:20:48.379 21833-21833/com.teratail.q365132 D/Sensor: -52.31947 2021-10-20 13:20:48.396 21833-21833/com.teratail.q365132 D/Sensor: -54.647438 2021-10-20 13:20:48.474 21833-21833/com.teratail.q365132 D/Sensor: -49.896282 2021-10-20 13:20:48.551 21833-21833/com.teratail.q365132 D/Sensor: -48.113018 2021-10-20 13:20:48.573 21833-21833/com.teratail.q365132 D/Sensor: -53.85168 2021-10-20 13:20:48.590 21833-21833/com.teratail.q365132 D/Sensor: -57.098675 2021-10-20 13:20:48.667 21833-21833/com.teratail.q365132 D/Sensor: -61.820385 2021-10-20 13:20:48.745 21833-21833/com.teratail.q365132 D/Sensor: -61.480328 となりました。 USBケーブルで邪魔されて値が少し戻ったりしてますが^^; 一応ビルドの Version を 7.1.1 に該当する API25 に設定して実行しましたが、まぁこれは意味が無いだろうとは思います。
takk_014

2021/10/21 04:37

そうですね、私はUSBケーブルから発生している磁気に後から気付いて検証をし直したりしました。 複数のAndroid端末にストアで一番DLされているコンパスアプリをインストールして実行してみました。 どの端末でも回して一定の地点で針が動き辛くなって急に針が回る現象が確認できました。 私の作ったアプリと同じ挙動です。 そのためこの問題はアプリの問題ではなくてAndroid(OS)自体の問題だと思われます。 もしくは、全てASUSの端末で実行したためASUS製の端末の地磁気センサーの問題だと思われます。 ここまでお付き合いいただきありがとうございます。
guest

回答1

0

自己解決

解決はしていませんが、この問題はAndroid(OS)が地磁気センサーから取得する値に問題がある可能性が高いと判明しました。
そのためこれ以上はアプリ開発者側でなんとかするのは難しいと判断して諦めます。

投稿2021/10/21 04:41

takk_014

総合スコア53

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問