【サンプルあり】Core Motionでモーションセンサーの値を取得する方法【Swift】

本記事は、Core Motionライブラリを使って、モーションセンサーの値を取得する方法をわかりやすく解説します。

本記事を読むことで、下記を身に付けることができます。

  • Core Motionを使って取得できるモーションデータの種類を知る
  • 大きく2つに分けられるモーションデータのうち、補正済みのモーションデータの取得方法を理解する
  • 大きく2つに分けられるデータの取得方法のうち、データ更新のたびに通知をうける方法を習得する

Core Motionを使って加速度を取得してみる

Core Motionでは、様々なモーションデータを2つの方法で取得することができます。しかし、あれもこれもやろうとすると心が折れてしまうので、まずは1種類のデータを1種類の方法で取得してみましょう。今回は下記のデータ・方法で取得してみます。

  • 加速度
  • データが変化する度に受け取る

データ取得の開始

加速度のデータを取得するためのコードを先にお見せします。

class MotionSensor: NSObject {
    
    let motionManager = CMMotionManager()
    
    func start() {
        if motionManager.isDeviceMotionAvailable {
            motionManager.deviceMotionUpdateInterval = 0.1
            motionManager.startDeviceMotionUpdates(to: OperationQueue.current!, withHandler: {(motion:CMDeviceMotion?, error:Error?) in
                self.updateMotionData(deviceMotion: motion!)
            })
        }
    }
    
    func updateMotionData(deviceMotion: CMDeviceMotion) {
        let x = deviceMotion.userAcceleration.x
        let y = deviceMotion.userAcceleration.y
        let z = deviceMotion.userAcceleration.z
    }
}

順番に解説していきますね。

まずは補正したモーションデータを管理する「CMMotionManager」を初期化します。

    let motionManager = CMMotionManager()

CMMotionManagerはアプリケーションの中で1つだけ保持するようにしないといけません。モーションセンサーはデバイスにつき1つしかないので、当然といえば当然ですよね。

次にモーションセンサーのデータ更新間隔を指定して、データ更新の度に呼び出されるメソッドを指定します。

        if motionManager.isDeviceMotionAvailable {
            motionManager.deviceMotionUpdateInterval = 0.1
            motionManager.startDeviceMotionUpdates(to: OperationQueue.current!, withHandler: {(motion:CMDeviceMotion?, error:Error?) in
                self.updateMotionData(deviceMotion: motion!)
            })
        }

はじめのif分の「motionManager.isDeviceMotionAvailable」はその名前の通り、そのデバイスがモーションデータに対応しているかどうかをチェックしています。

次の「motionManagerDeviceMotionUpdateInterval」もわかりやすいとは思いますが、データ更新間隔を指定しているだけです。

最後に「motionManager.startDeviceMotionUpdates」でデータ取得の開始と、データ更新時に呼び出される関数を指定しています。ここでは「updateMotionData()」を呼び出すように指定しています。

これで定期的に加速度を取得し、更新がある度に関数が呼び出されるようになりました。

データ更新時の処理

先ほど、データ更新の度に「updateMotionData()」が呼び出されるように指定したので、この関数を実装してみましょう。

    private func updateMotionData(deviceMotion:CMDeviceMotion) {
        let x = deviceMotion.userAcceleration.x
        let y = deviceMotion.userAcceleration.y
        let z = deviceMotion.userAcceleration.z
    }

deviceMotionのuserAccelerationに加速度の値が格納されているので、そのまま加速度が格納しているだけです。アプリケーションに応じてこの関数を改造してあげればよいでしょう。単位は[G]です。

SwiftUIで加速度の値を表示してみる。

以上の使い方を踏まえて、実際に加速度の値を表示するアプリケーションをSwiftUIで作ってみましょう。

まずは先ほどのMotionSensorクラスを次のように改造します。

import UIKit
import CoreMotion

class MotionSensor: NSObject, ObservableObject {
    
    @Published var isStarted = false
    
    @Published var xStr = "0.0"
    @Published var yStr = "0.0"
    @Published var zStr = "0.0"
    
    let motionManager = CMMotionManager()
    
    func start() {
        if motionManager.isDeviceMotionAvailable {
            motionManager.deviceMotionUpdateInterval = 0.1
            motionManager.startDeviceMotionUpdates(to: OperationQueue.current!, withHandler: {(motion:CMDeviceMotion?, error:Error?) in
                self.updateMotionData(deviceMotion: motion!)
            })
        }
        
        isStarted = true
    }
    
    func stop() {
        isStarted = false
        motionManager.stopDeviceMotionUpdates()
    }
    
    private func updateMotionData(deviceMotion:CMDeviceMotion) {
        xStr = String(deviceMotion.userAcceleration.x)
        yStr = String(deviceMotion.userAcceleration.y)
        zStr = String(deviceMotion.userAcceleration.z)
    }
    
}

「start()」メソッドを呼び出されたら、加速度のデータ取得を開始し、更新のたびにx, y, z方向への加速度を、xStr, yStr, zStrに文字列として格納します。これらの文字列はあとでSwiftUIのViewにバインディングします。

また、isStartedをフラグとして使い、「start()」時にtrue、「stop()」時にfalseとし、こちらもあとでSwiftUIで表示を切り替えるために使用します。

それでは加速度を表示するUIを作ってみましょう。

import SwiftUI

struct ContentView: View {
    @ObservedObject var sensor = MotionSensor()
    
    var body: some View {
        VStack {
            Text(sensor.xStr)
            Text(sensor.yStr)
            Text(sensor.zStr)
            Button(action: {
                self.sensor.isStarted ? self.sensor.stop() : self.sensor.start()
            }) {
                self.sensor.isStarted ? Text("STOP") : Text("START")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

SwiftUIの基本的なコードなので特に説明は不要と思います。

このプログラムを実機で実行してみましょう。シミュレーションではモーションセンサーが使えないので動きません。必ず実機で試してみましょう。

STARTボタンを押せば、加速度の値が表示されます。とても簡単にモーションデータが取得できますね!

このシンプルなコードはモーションセンサーを使ったプログラムをはじめて作る上では非常に役に立つでしょう。GitHubにも公開していますので、ご自由にお使いください。

CoreMotionSensorSample

Core Motionで取得できるモーションセンサーの値

ひとまずモーションデータを取得することに成功したので、他のデータも取得できるようになりましょう。Core Motionでは下記のモーションデータを取得することができます。

  • センサーの生データ
    • 加速度センサーの生データ
    • ジャイロセンサーの生データ
    • 磁力計の生データ
  • 補正したモーションデータ
    • 加速度
    • 回転速度
    • 姿勢
    • コンパス(磁力)
    • 重力

先ほど取得したのは、補正したモーションデータの加速度です。

センサーの生データより、補正したモーションデータの方が感覚的にもわかりやすく、扱いやすいデータになっており、こちらを使うことの方が多いかと思います。その理由については、下記のサイト様にてわかりやすく解説してくれていますので、気になる方は読んでみてください。

9軸センサ制御- Watako-Lab.

他の補正したモーションデータを取得してみる

他の補正したモーションデータを取得する方法を学んでみましょう。

回転速度を取得する場合は、先ほどの「updateMotionData()」を下記のように書き換えるだけです。単位は[rad/ms]です。

    private func updateMotionData(deviceMotion:CMDeviceMotion) {
        xStr = String(deviceMotion.rotationRate.x)
        yStr = String(deviceMotion.rotationRate.y)
        zStr = String(deviceMotion.rotationRate.z)
    }

次々にいきましょう。こちらは姿勢です。単位は[rad]です。

    private func updateMotionData(deviceMotion:CMDeviceMotion) {
        xStr = String(deviceMotion.attitude.pitch)
        yStr = String(deviceMotion.attitude.roll)
        zStr = String(deviceMotion.attitude.yaw)
    }

こちらは回転速度です。単位は[rad/s]です。

    private func updateMotionData(deviceMotion:CMDeviceMotion) {
        xStr = String(deviceMotion.rotationRate.x)
        yStr = String(deviceMotion.rotationRate.y)
        zStr = String(deviceMotion.rotationRate.z)
    }

こちらは重力です。単位は[G]です。

    private func updateMotionData(deviceMotion:CMDeviceMotion) {
        xStr = String(deviceMotion.gravity.x)
        yStr = String(deviceMotion.gravity.y)
        zStr = String(deviceMotion.gravity.z)
    }

こちらはコンパス(磁力)です。単位は[μT]です。

    private func updateMotionData(deviceMotion:CMDeviceMotion) {
        xStr = String(deviceMotion.magneticField.field.x)
        yStr = String(deviceMotion.magneticField.field.y)
        zStr = String(deviceMotion.magneticField.field.z)
    }

ただし、コンパスを使う場合は注意が必要で、方向を決めるための基準を指定してあげる必要があります。基準はデータ取得開始時に指定します。例えば、下記のように書きます。

        motionManager.startDeviceMotionUpdates(using: [.xMagneticNorthZVertical], to: OperationQueue.current!, withHandler: {(motion:CMDeviceMotion?, error:Error?) in
            self.updateMotionData(deviceMotion: motion!)
        })

これまでと違うのは、引数で「using」で基準を指定している点です。この基準については公式のドキュメントを参照してください。とりあえず動かしたい場合は、上記のコードをコピペしてください。

CMAttitudeReferenceFrame

おわりに

本記事では、Core Motionを使って、補正済みのモーションデータの取得方法を解説しました。

Core Motionに関して、本記事だけではお伝えできない内容は別の記事を用意しています。ぜひご覧になってください。

また、Core Motionで取得したデータをグラフ表示・ログ取得するアプリをApp Storeに公開しています。モーションセンサーの値をみてみたい方はぜひダウンロードしてみてください。

もせろぐ

App Storeはこちら

使い方はこちら