iPhoneはx,y,z軸それぞれを計測する加速度センサーを搭載しています。端末を傾けることで、スマホならではのゲームを作ることができます。
Xcode 11.3.1
CMAccelerometerData
まずは生のデータを取り出してみたいと思います。
- CoreMotionをimport
- CMMotionManagerのインスタンスを生成
- accelerometerUpdateIntervalでインターバル時間を設定
その後、加速度センサー値取得の開始で queue と handler を設定します。
1 2 |
startAccelerometerUpdates(to queue: OperationQueue, withHandler handler: @escaping CMAccelerometerHandler) |
ViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import UIKit import CoreMotion class ViewController: UIViewController { // MotionManager let motionManager = CMMotionManager() // 3 axes @IBOutlet var accelerometerX: UILabel! @IBOutlet var accelerometerY: UILabel! @IBOutlet var accelerometerZ: UILabel! override func viewDidLoad() { super.viewDidLoad() if motionManager.isAccelerometerAvailable { // intervalの設定 [sec] motionManager.accelerometerUpdateInterval = 0.2 // センサー値の取得開始 motionManager.startAccelerometerUpdates( to: OperationQueue.current!, withHandler: {(accelData: CMAccelerometerData?, errorOC: Error?) in self.outputAccelData(acceleration: accelData!.acceleration) }) } } func outputAccelData(acceleration: CMAcceleration){ // 加速度センサー [G] accelerometerX.text = String(format: "%06f", acceleration.x) accelerometerY.text = String(format: "%06f", acceleration.y) accelerometerZ.text = String(format: "%06f", acceleration.z) } // センサー取得を止める場合 func stopAccelerometer(){ if (motionManager.isAccelerometerActive) { motionManager.stopAccelerometerUpdates() } } } |
後はstoryboardにUILabelを3つおいて紐付けして実行です。
stopAccelerometerUpdates()はセンサー取得でバッテリー消費量が増大するため使わない場合は止めておく必要があります。
机の上に置いた状態ではX,Y軸はほぼ「0」でZ軸が「-1」となります。単位はG,地球の重力[m/s2]ですからY軸方向に-1Gが加わっていることを表します。
このケースでは生データなのでローパスフィルターを入れてみたいと思います。
ローパスを入れるか、ハイパスフィルターを入れるかは使用目的異なるでしょう。
フィルター
EMA(指数移動平均)フィルターを作成します。
St = α * Yt-1 + (1 -α) * St-1 |
St:t時点でのEMA
α:平滑化係数
Yt-1:1つ前のデータ
St-1:1つ前のEMA
ViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
import UIKit import CoreMotion class ViewController: UIViewController { // MotionManager let motionManager = CMMotionManager() // 3 axes @IBOutlet var accelerometerX: UILabel! @IBOutlet var accelerometerY: UILabel! @IBOutlet var accelerometerZ: UILabel! var acceleX: Double = 0 var acceleY: Double = 0 var acceleZ: Double = 0 let Alpha = 0.4 var flg: Bool = false override func viewDidLoad() { super.viewDidLoad() if motionManager.isAccelerometerAvailable { // intervalの設定 [sec] motionManager.accelerometerUpdateInterval = 0.2 // センサー値の取得開始 motionManager.startAccelerometerUpdates( to: OperationQueue.current!, withHandler: {(accelData: CMAccelerometerData?, errorOC: Error?) in self.lowpassFilter(acceleration: accelData!.acceleration) }) } } func lowpassFilter(acceleration: CMAcceleration){ acceleX = Alpha * acceleration.x + acceleX * (1.0 - Alpha); acceleY = Alpha * acceleration.y + acceleY * (1.0 - Alpha); acceleZ = Alpha * acceleration.z + acceleZ * (1.0 - Alpha); accelerometerX.text = String(format: "%06f", acceleX) accelerometerY.text = String(format: "%06f", acceleY) accelerometerZ.text = String(format: "%06f", acceleZ) } // どこかに実装 func stopAccelerometer(){ if (motionManager.isAccelerometerActive) { motionManager.stopAccelerometerUpdates() } } } |
公式通りの実装ではありませんがほぼそれないりの動作となっていると思います。
ここでαの値とintervalはそれぞれの特性に応じて最適化する必要があります。
ハイパスフィルターにするには、ざっくり元のデータからローパスを引いた成分と考えると簡単です
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func highpassFilter(acceleration: CMAcceleration){ acceleX = Alpha * acceleration.x + acceleX * (1.0 - Alpha); acceleY = Alpha * acceleration.y + acceleY * (1.0 - Alpha); acceleZ = Alpha * acceleration.z + acceleZ * (1.0 - Alpha); let xh = acceleration.x - acceleX let yh = acceleration.y - acceleY let zh = acceleration.z - acceleZ accelerometerX.text = String(format: "%06f", xh) accelerometerY.text = String(format: "%06f", yh) accelerometerZ.text = String(format: "%06f", zh) } |
ハイパスフィルターですから、瞬間的な変動成分のみになります。これを使うとZ軸の値も0になります。3軸のGの影響を引いた値が必要な場合は有効でしょう。
References:
UIAcceleration – UIKit | Apple Developer Documentation
Core Motion | Apple Developer Documentation