2017年3月9日木曜日

モーションセンサー、うまくいくようないかないような



モーションセンサーには以下のようなものがある。
値の範囲は決まっているはずだが、センサーの精度の問題なのか、範囲の上限/下限値にならないことも多く、上限/下限値をまたいだ処理をさせると値が飛んでしまうこともあり困る。
プロはどうやってスムーズな処理をしてるんだろう? 誰かおせーて!!
  • 加速度センサー(Accelerometer)
    • X軸、Y軸、Z軸方向に動かした場合、どちらの方向にどれだけ加速度がかかったか
  • ジャイロセンサー(Gyro)
    • X軸、Y軸、Z軸を軸として傾けた場合の、1秒あたりに傾いたラジアン角を得られる(角速度)
  • 磁気センサー(Magnetometer)
    • X軸、Y軸、Z軸の磁力センサーの値
  • デバイスモーション(DeviceMotion)
    • 加速度、ジャイロ、磁気の3センサーを統合して加工。ということはこれだけ使えばいい?
    • 公式には「装置の姿勢、回転速度、および加速度のカプセル化された測定値」とある。
    • 加速度計は、重力加速度とユーザー加速度の2つの加速度ベクトルの合計を測定します。 ユーザー加速度は、ユーザーがデバイスに与える加速度です。 Core Motionは、ジャイロスコープと加速度計の両方を使用してデバイスの姿勢を追跡することができるため、重力とユーザーの加速度を区別できます。 CMDeviceMotionオブジェクトは、重力加速度およびユーザー加速度プロパティの両方の測定値を提供します。
    • 姿勢センサー(Attitude)
      • X軸、Y軸、Z軸を軸として傾けた場合の角度(Pitch、Roll、Yawのオイラー角)、行列(マトリックス)、クォータニオン(よくわからんが)を得られる。
      • X軸中心の前後の傾きがpitch
        • 画面上で寝かせて0度。起こしていくと増えていき、垂直で90度。さらに画面下向きに寝かせていくと減っていき0度に。
        • 増えていった値が90度を境に減るはずだが、時には87度前後で減り出したり、よくわからない。
      • Y軸中心の左右の回転がroll
        • pitchが90度を超えると急に-150度とかになったり、わからない
      • Z軸中心の左右の傾きがyaw
        • これがわずかに傾いてるだけでもpitchの値が90までいかないうちに減り出すことがあるようだ。
  • 重力加速度(Gravity)
    • 地球の重力に対しての向き
    • X軸はLandscapeでホームボタンが右なら-1.0、左なら1.0、Portrateなら0
    • Y軸はPortrateでホームボタンが下なら-1.0、上なら1.0、Landscapeなら0
    • Z軸は画面が真上なら-1.0、垂直に立てると0、画面が真下なら1.0

ライブラリはあらかじめCoreMotionをimportしとく。
import CoreMotion //モーションセンサー

モーションマネージャーはインスタンス変数として作っておかないと、ARCですぐに消されちゃうので注意。
var motionMgr:CMMotionManager?

センサーの設定とスタートまで

//モーションマネージャーは各種センサー共通
motionMgr = CMMotionManager()
//更新頻度(秒)
motionMgr?.deviceMotionUpdateInterval = 0.1 //姿勢センサー
//姿勢センサースタート(クロージャでセンサーが更新された時の処理を書く)
motionMgr?.startDeviceMotionUpdates(to: OperationQueue.current!, withHandler: { (data, error) in
        let pitchDeg = round(180 * data!.attitude.pitch / M_PI)
        let rollDeg = round(180 * data!.attitude.roll / M_PI)
        let yawDeg = round(180 * data!.attitude.yaw / M_PI)

})

//姿勢センサーの停止
motionMgr?.stopDeviceMotionUpdates()

ローパスフィルター

センサーの値がノイズによってばらけることが多いらしく、前回検出した値との加算平均を取る処理(これをローパスフィルターとかハイパスフィルターとか言うらしい)をした方がいいそうである。
最新の値の90%と、前回の値の10%を足すわけですな。


//ローパスフィルター処理(ここでは0.9がレート)
var outputValue:Double
if (self.prevAngle != nil) {
        outputValue = (pitchDeg * 0.9) + (self.prevAngle! * (1.0 - 0.9))
    } else {
        outputValue = pitchDeg
    }
self.prevAngle = outputValue

他のセンサー使用時

センサーの値を取り出す各種プロパティは省略。


//各センサーの更新頻度(秒)設定
motionMgr?.accelerometerUpdateInterval = 0.1 //加速度センサー
motionMgr?.gyroUpdateInterval = 0.1 //ジャイロセンサー

//各センサーのスタート(引数は省略したが姿勢センサーと同様に)

motionMgr?.startAccelerometerUpdates() //加速度センサー

motionMgr?.startGyroUpdates() //ジャイロセンサー



//各センサー停止

motionMgr?.stopAccelerometerUpdates() //加速度センサー

motionMgr?.stopGyroUpdates() //ジャイロセンサー

参考サイト

0 件のコメント:

コメントを投稿