Detect acceleration in absolute vertical axis

2019-08-17 00:20发布

问题:

I'm writing an app in which I want to access the acceleration data in an absolute vertical axis (i.e., from the ground to the floor), but with CoreMotion I'm only able to retrieve acceleration from the device's relative axis. For example if the device is laying flat on the table, it's vertical axis is actually at 90º from the absolute vertical axis.

I've been playing with the gyro data to try to fix this, but data don't make sense.

// Make sure the accelerometer hardware is available.
    if self.motion.isAccelerometerAvailable && self.motion.isDeviceMotionAvailable {
        self.motion.accelerometerUpdateInterval = 1.0 / Double(slider.value)  // Hz managed by slider
        self.motion.startAccelerometerUpdates()
        self.motion.startDeviceMotionUpdates(using: self.motion.attitudeReferenceFrame)
        self.motion.deviceMotionUpdateInterval = 1.0 / Double(slider.value)



        // Configure a timer to fetch the data.
        self.timer = Timer(fire: Date(), interval: (1.0/Double(slider.value)),
                           repeats: true, block: { (timer) in
                            // Get the gyroscope data.

                            if let data = self.motion.deviceMotion {
                                //Angles: pitch
                                pitch = data.attitude.pitch
                                print("Pitch angle: \(self.degrees(radians: pitch))")

                                    //Angles: yaw
                                yaw = data.attitude.yaw
                                print("Yaw angle: \(self.degrees(radians: yaw))")
                            }

                            //Get the accelerometer data

                            if let data = self.motion.accelerometerData {
                                let x = data.acceleration.x
                                //let y = data.acceleration.y
                                //let z = data.acceleration.z


                                // Use the accelerometer data in your app.
                                self.label.text = "x: \(x)"
                                let xCorrectedWithPitch = cos(self.degrees(radians: pitch)) * x
                                print("x: \(x)\nPitch: \(pitch)\nxCorrectedWithPitch: \(xCorrectedWithPitch)")
                                let xCorrectedWithYaw = cos(self.degrees(radians: yaw)) * xCorrectedWithPitch
                                print("x: \(x)\nYaw: \(yaw)\nxCorrectedWithYaw: \(xCorrectedWithYaw)")

                                // Use the accelerometer data in your app.

                                self.accelArrayY.append(xCorrectedWithYaw*9.81)
                                time = time + timer.timeInterval
                                self.timeArrayY.append(time)


                            }

Anyone help would be much appreciated

回答1:

CMMotionMagager gives you acceleration about the iPhone's axes and also the direction of gravity. By multiplying these two you can calculate the acceleration from/to the earth. It's the length of the result of this multiplication. I'm using Ray Wenderlich's 3D Vector utils.

In your GameViewController:

  var motionManager: CMMotionManager!

In GameViewController.viewDidLoad:

  motionManager = CMMotionManager()
  motionManager.startDeviceMotionUpdates()

In GameScene:

 override func update(_ currentTime: TimeInterval) {

    if let deviceMotion = motionManager.deviceMotion {

    let gravityVector = Vector3(x: CGFloat(deviceMotion.gravity.x),
                                y: CGFloat(deviceMotion.gravity.y),
                                z: CGFloat(deviceMotion.gravity.z))

    let userAccelerationVector = Vector3(x: CGFloat(deviceMotion.userAcceleration.x),
                                         y: CGFloat(deviceMotion.userAcceleration.y),
                                         z: CGFloat(deviceMotion.userAcceleration.z))

    // Acceleration to/from earth
    let zVector = gravityVector * userAccelerationVector
    let zAcceleration = zVector.length()
  }

To know the direction of movement:

sign(Double(zVector.x * zVector.y * zVector.z))

Don't forget to call .stopDeviceMotionUpdates() when you don't need it anymore.