1️⃣

Androidでセンシングをします ~加速度を表示する~

2023/07/18に公開

はじめに

Androidの加速度センサの値を取得し、それを画面に表示するアプリを作ります。

加速度って何?

加速度とは "速度の変化" です。

これを理解する前に距離と速度の関係について離します。
例えば 3km の距離を 1時間 かけて移動したとします。
この時の速度は 3km / 1h = 3km/h
時速3kmですね。

速度は距離/時間で求められましたね。
つまり "速度は距離を時間で微分したもの" です。
そして "加速度は速度を時間で微分したもの" です。

速度は距離を時間で微分したもの、加速度は速度を時間で微分したもの。
なので、逆に加速度を時間で積分すると速度に、速度を時間で積分すると距離になります。

Androidのセンサについて

Androidでは位置センサ、環境センサなど色々なセンサが扱えます。
ではここに書いてあるセンサは全て使えるのでしょうか。

いいえ違います。
このセンサを使うなら"Android端末に搭載されている"必要があります。

例えば温度センサ、湿度センサなどはGoogle Pixel 7 には搭載されていません。
逆にCat S61には搭載されています。

一部センサの解説

加速度センサ

  • TYPE_ACCELEROMETER
    • 重力加速度を含む加速度センサ
  • TYPE_LINEAR_ACCELERATION
    • 重力加速度を含まない加速度センサ
    • こっちを使うのをオススメする

ジャイロセンサ

  • TYPE_GYROSCOPE
    • 角速度が取れる
    • 単位がrad/sなのに注意

気圧センサ

  • TYPE_PRESSURE
    • 気圧が取れる
    • 数cmの差も取れる

加速度センサの値を見るだけのアプリを作ろう

まずはソースコード全てです。

MainActivity

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private var AccSensor: Sensor? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        AccSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
    }

    //センサーに何かしらのイベントが発生したときに呼ばれる
    override fun onSensorChanged(event: SensorEvent) {
        var sensorX: Float
        var sensorY: Float
        var sensorZ: Float
        // Remove the gravity contribution with the high-pass filter.
        if (event.sensor.type === Sensor.TYPE_LINEAR_ACCELERATION) {
            sensorX = event.values[0]
            sensorY = event.values[1]
            sensorZ = event.values[2]
            val strTmp = """加速度センサー
                         X: $sensorX
                         Y: $sensorY
                         Z: $sensorZ"""
            val sensorText: TextView = findViewById(R.id.textView)
            sensorText.setText(strTmp)
        }
    }
    //センサの精度が変更されたときに呼ばれる
    override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
    }

    override fun onResume() {
        super.onResume()
        //リスナーとセンサーオブジェクトを渡す
        //第一引数はインターフェースを継承したクラス、今回はthis
        //第二引数は取得したセンサーオブジェクト
        //第三引数は更新頻度 UIはUI表示向き、FASTはできるだけ早く、GAMEはゲーム向き
        sensorManager.registerListener(this, AccSensor, SensorManager.SENSOR_DELAY_UI)
    }
    
    //アクティビティが閉じられたときにリスナーを解除する
    override fun onPause() {
        super.onPause()
        //リスナーを解除しないとバックグラウンドにいるとき常にコールバックされ続ける
        sensorManager.unregisterListener(this)
    }
}

テスト

実際にAndroid(とセンサ)が入っている端末に今作ったアプリを入れて動かしましょう。

注意!)Android端末のUSBデバックモードからUSBのデフォルト設定をファイル転送にする必要があります!
https://developer.android.com/studio/debug/dev-options?hl=ja

端末を有線でPCに接続します。
すると赤の矢印の所に接続した端末の名前が出ます。
接続した端末の名前が出たら、そのすぐ右にある緑の三角ボタンを押します。

プログラムの解説

class MainActivity : AppCompatActivity(), SensorEventListener

class MainActivity : AppCompatActivity(), SensorEventListener

  • interfaceとabstractの違い
  • AppCompatActivity()はabstract
  • SensorEventListenerはinterface
    • SensorEventListenerはinterfaceなので「このメソッドあるよね」がある。
    • クラスがSensorEventListenerを実装すると言っているのにonSensorChangedとonAccuracyChangedを持ってないとこんな感じに怒られるので書きましょう。

SensorManagerとContext

    private lateinit var sensorManager: SensorManager
    private var AccSensor: Sensor? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        AccSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
    }
  • 今回は線形加速度なのでSensor.TYPE_LINEAR_ACCELERATIONと書いてある
  • 他のセンサが使いたいならAndroid Developersに書いてある。
  • getSystemService(Context.SENSOR_SERVICE)
    • さまざまな端末にAndroidアプリが入ってる。アプリがいちいち端末に搭載されているセンサの癖を考慮して何パターンもコードを書くのは無理がある。
    • なのでContextという端末の設定や情報が乗ってる便利なやつを使う。
    • ちなみにContextにはいくつか種類がある。

onSensorChanged

//センサーに何かしらのイベントが発生したときに呼ばれる
    override fun onSensorChanged(event: SensorEvent) {
        var sensorX: Float
        var sensorY: Float
        var sensorZ: Float
        // 全て
        if (event.sensor.type === Sensor.TYPE_LINEAR_ACCELERATION) {
            sensorX = event.values[0]
            sensorY = event.values[1]
            sensorZ = event.values[2]
            val strTmp = """加速度センサー
                         X: $sensorX
                         Y: $sensorY
                         Z: $sensorZ"""
            val sensorText: TextView = findViewById(R.id.textView)
            sensorText.setText(strTmp)
        }
    }
  • センサの値が変わったら呼ばれる。
  • 複数のセンサを使ってても全部onSensorChangedが呼ばれる。
  • なのでif (event.sensor.type === Sensor.TYPE_LINEAR_ACCELERATION)のように、なんのセンサイベントなのかを判別してから処理をしよう。
  • 今回はセンサの値が変わったらTextViewのTextに加速度のテキストをセットするプログラムになっている。

onAccuracyChanged

//センサの精度が変更されたときに呼ばれる
    override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
    }
  • コメントの通り、センサの精度が変更されると呼ばれるメソッド。
  • SensorEventListenerを継承したから必要なメソッド。
  • 僕にはセンサの精度を変更した経験がないから使ったことない。

onResume()

    override fun onResume() {
        super.onResume()
        //リスナーとセンサーオブジェクトを渡す
        //第一引数はインターフェースを継承したクラス、今回はthis
        //第二引数は取得したセンサーオブジェクト
        //第三引数は更新頻度 UIはUI表示向き、FASTはできるだけ早く、GAMEはゲーム向き
        sensorManager.registerListener(this, AccSensor, SensorManager.SENSOR_DELAY_UI)
    }
  • onResume()がわからないならAndroidアプリ開発の一歩目 ~プロジェクト編~を見てくれ。
  • 端末内のセンサは実はセンサの値をどこにも返していないだけ常に動いてる(と思って欲しい)。
  • センサの値をどこに返せばいいかを示すのが、sensorManager.registerListener。
  • thisはこのクラス、MainActivityを意味する。(thisは書き方、書いた場所で意味が変わるので注意)
  • この第一引数にはSensorEventListenerを実装したクラスしか入れられない。(onSensorChangedがある)
  • するとセンサの値がここに返ってくる。センサの値が変わるとonSensorChangedが呼ばれる。

onPause()

    //アクティビティが閉じられたときにリスナーを解除する
    override fun onPause() {
        super.onPause()
        //リスナーを解除しないとバックグラウンドにいるとき常にコールバックされ続ける
        sensorManager.unregisterListener(this)
    }
  • onPause()がわからないならAndroidアプリ開発の一歩目 ~プロジェクト編~を見てくれ。
  • onResumeで、センサの値をこのクラスに返してもらうコードを書いた。
  • これはセンサの値をもう返さなくてもいいよ。ってコード。
  • これをしないとアプリにずっとセンサの値が返ってきてずっとonSensorChangedが呼ばれ続ける。

おわりに

今回は加速度センサの値を画面に出すアプリを作りました。
実際に横に振ったりすると値が大きくなったりマイナスになったりすると思います。

加速度センサを含めセンサには取得頻度というものがあります。
単位はHz(ヘルツ)です。
1秒間に10回取るなら10Hz、400回取るなら400Hzです。

同じセンサでも端末によって取得周波数が変わります。
センシングするときはこの周波数に気をつけましょう。

それでは.また.
Happy Hacking

Discussion