Idea
My idea is to write a little application for my iOS-Device to record a motion after start recording by touching an UIButton and save the data from the accelerometer for this motion. After the recording, I can save this data to use it in the finished Application to detect this motion again.
So, what I'm looking for is a way to compare two NSMutableArrays, each as a set of motion data, and want to check if the current NSMutableArray is the same motion as the recorded one.
Problems
To realize this idea, I found three problems, which I can't solve myself:
How to compare two NSMutableArrays and get e.g. an index-level of the similarity or a percentage e.g. 97.32% equivalent.
What if the user makes the motion in different speeds? E.g. slowly or fast.
The last problem: How to handle different device-orientations? Is there a way to calculate the data from the accelerometer to a neutral level in any orientation? I think I have a solution approach for this last problem, but don't know how to bring this to code: We have the attitude data for pitch, roll and yaw. Maybe we can calculate with these values the neutral data for the accelerometer?
Code
At this moment, I only have the code to get the data from the accelerometer and the attitude data, both from the MotionManager. I played multiple hours with my code and searched a lot on Google, but can't find the right way...
import UIKit
import CoreMotion
var motionManager = CMMotionManager()
var x:Float = 0.0
var y:Float = 0.0
var z:Float = 0.0
var roll:Float = 0.0
var pitch:Float = 0.0
var yaw:Float = 0.0
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
motionManager.accelerometerUpdateInterval = 1
motionManager.deviceMotionUpdateInterval = 1
}
#IBAction func startRecording() {
accelerometerData = NSMutableArray()
motionManager.startDeviceMotionUpdates(using: CMAttitudeReferenceFrame.xMagneticNorthZVertical, to:
OperationQueue.current!, withHandler: {
(deviceMotion, error) -> Void in
let attitude = deviceMotion?.attitude
let roll = self.degrees(radians: attitude!.roll)
let pitch = self.degrees(radians: attitude!.pitch)
let yaw = self.degrees(radians: attitude!.yaw)
self.roll = Float(roll)
self.pitch = Float(pitch)
self.yaw = Float(yaw)
let accX = deviceMotion?.userAcceleration.x
let accY = deviceMotion?.userAcceleration.y
let accZ = deviceMotion?.userAcceleration.z
self.x = Float(accX!)
self.y = Float(accY!)
self.z = Float(accZ!)
})
}
}
Update
I will update this question, when I know more.
Problem 2: Solution:
With the Attribute CMAttitudeReferenceFrame.xMagneticNorthZVertical for the motionManager.startDeviceMotionUpdates-Method you can neutralize all sensors data to a defined orientation. You can access the accelerometer-data here too. I've updated the code above, too.
Related
I can't get my head around how to get out simple status data, like the current gimbal pitch for example.
I have not found a solid connection between the DJI SDK and what actually works in xcode. The SDK gives me hints and together with xcode autocompletion a go forwards, slowly..
Class GimbalState has member getAttitudeInDegrees() with description:
"The current gimbal attitude in degrees. Roll, pitch and yaw are 0 if the gimbal is level with the aircraft and points in the forward direction of North Pole." - Great!
However, it does not autocomplete in xcode nor does it compile.
Other approaches tested:
var gimbalStateInformation = DJIGimbalState()
print(gimbalStateInformatoin.attitudeInDegrees.pitch.description)
--> All pitch roll and yaw values come out as 0.0
var gimbalStateInformation = DJUGimbalAttitude()
print(gimbalStateInformatoin.pitch.description)
--> All pitch roll and yaw values come out as 0.0
I've tried to reach the information via keys, but my app crashes when I run the code.
func getGimbalAttitude(){
// Get the key
guard let gimbalAttitudeKey = DJIGimbalKey(param: DJIGimbalParamAttitudeInDegrees) else {
print("Could not create DJIGimbalParamAttitudeInDegrees key")
return
}
// Get the keyManager
guard let keyManager = DJISDKManager.keyManager() else {
print("Could not get the key manager, manke sure you are registered")
return
}
// Test if key is available
let testing = keyManager.isKeySupported(gimbalAttitudeKey)
self.statusLabel.text = String(testing) // This comes out true
// Use key to retreive info
let gimbalAttitudeValue = keyManager.getValueFor(gimbalAttitudeKey)
let gimbalAttitude = gimbalAttitudeValue?.value as! DJIGimbalState
_ = gimbalAttitude.attitudeInDegrees.pitch
// --> Application crashes on the line above
}
I'm working towards a Mavic Mini.
Please advise in general terms how to connect the DJI Mobile SDK to Swift and specifically how I can read out the current gimbal pitch value.
You can access the current Gimbal pitch through its didUpdate state delegate function
import DJISDK
class GimbalController: NSObject, DJIGimbalDelegate {
let gimbal: DJIGimbal
init(gimbal: DJIGimbal) {
self.gimbal = gimbal
super.init()
gimbal.delegate = self
}
func gimbal(_ gimbal: DJIGimbal, didUpdate state: DJIGimbalState) {
print(state.attitudeInDegrees.pitch)
}
}
// create an instance of the custom gimbal controller in some other class
// and pass it the gimbal instance
if let aircraft = DJISDKManager.product() as? DJIAircraft {
if let gimbal = aircraft.gimbal {
let gimbalController = GimbalController(gimbal: gimbal)
}
}
Via the code below I am getting accelerometer data, now I want to work with it to track the user's movement, specifically speed. Looking around at code using Core Motion on the iPhone they are using a motionManager object which can set a value for accelerometerUpdateInterval as well as get the .acceleration.x value for example. How can I work with the raw data I'm given back so that I can determine e.g. how fast a person is moving or how fast their arm is swinging?
//Record the data
if CMSensorRecorder.isAccelerometerRecordingAvailable() {
print("Accelerometer available")
recorder.recordAccelerometer(forDuration: 20 * 60) // Record for 20 minutes
}
//Read the data
if CMSensorRecorder.isAccelerometerRecordingAvailable() {
let accelerometerData = recorder.accelerometerData(from: startDate, to: endDate)
for (index, data) in (accelerometerData?.enumerated())! {
print(index, data)
}
}
Prints:
0 388, 208409.082611, 529770182.607276, (0.038574, -0.762207, -0.652832)
1 388, 208409.102722, 529770182.627387, (0.027588, -0.763184, -0.660889)
2 388, 208409.122863, 529770182.647528, (0.027100, -0.763184, -0.661865)
3 388, 208409.142974, 529770182.667639, (0.030029, -0.756836, -0.661865)
4 388, 208409.163116, 529770182.687781, (0.026611, -0.764648, -0.665039)
Edit: I found this lib which looks like it would be perfect but hasn't been updated in 3 years...anything similar that is still maintained? https://github.com/MHaroonBaig/MotionKit
I found that using a CMMotionManager on the watch works just as good as it does on iPhone. This way you can implement startAccelerometerUpdates in awake and receive real time feed back on watch positions for X Y Z coordinates so that you can get a better grasp of the data;
var motionManager = CMMotionManager()
override func awake(withContext context: Any?) {
super.awake(withContext: context)
manager.accelerometerUpdateInterval = 0.2
manager.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
if let myData = data {
print("x: \(myData.acceleration.x) y: \(myData.acceleration.y) z: \(myData.acceleration.z)")
if myData.acceleration.x > 1.5 && myData.acceleration.y > 1.5 {
}
}
}
}
There is a new heading property of CMDeviceMotion in iOS 11.
I'm trying to use it but find that it's always -1.0. It's supposed to hold degrees as a Double from 0.0 to 360.0.
My app targets iOS 11+, and I'm testing on a physical device (iPhone) running iOS 11.
let mmgr = CMMotionManager()
mmgr.showsDeviceMovementDisplay = true // for calibrating magnetometer, maybe not needed?
mmgr.deviceMotionUpdateInterval = 0.1
mmgr.startDeviceMotionUpdates(to: .main, withHandler: { (motionData: CMDeviceMotion?, error: Error?) in
if let motion = motionData {
print("heading:", motion.heading) // always -1.0
}
})
I can get other properties just fine, such as motion.attitude.roll. Is there something I'm missing?
The issue is that I needed to start motion updates with a different method signature which includes the CMAttitudeReferenceFrame option:
let mmgr = CMMotionManager()
mmgr.deviceMotionUpdateInterval = 0.1
mmgr.startDeviceMotionUpdates(using: .xMagneticNorthZVertical, to: .main, withHandler: { (motionData: CMDeviceMotion?, error: Error?) in
if let motion = motionData {
print("heading:", motion.heading) // works
}
})
The What's New in iOS 11 guide states that if you use xArbitraryZVertical (default) or xArbitraryCorrectedZVertical for the CMAttitudeReferenceFrame option, the heading will always be -1.
This helpful tidbit is not stated in the heading property reference.
In Swift or Objective-C, we can easily create a CLLocationmanager and start tracking our position. We can also set a distanceFilter, which defines the minimum distance (measured in meters) a device must move horizontally before an update event is generated. My Swift code:
override func viewDidLoad() {
super.viewDidLoad()
manager = CLLocationManager()
manager.delegate = self
manager.distanceFilter = 10
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.startUpdatingLocation()
}
But in certain circumstances, with a lower gps accuracy (ie in woods, tunnels, between high buildings...) incorrect spikes are generated. The CLLocationManager lacks a property or method to define a maximum distance for an update in order to clean up this sort of spikes.
Right now, I'm handling the max distance myself storing usable locations into a seperate array:
var myLocations: [CLLocation] = []
func calculateDistanceBetweenTwoLocations(start:CLLocation,destination:CLLocation) {
var distanceInMeters = start.distanceFromLocation(destination)
if distanceInMeters < 20 {
myLocations.append(destination)
}
}
Is there a more accurate way?
What you're looking for is a Data cleansing method applied for GPS data.
If you have some time ahead, you can read this PDF about it
Some of the ways explained is to take 5 concurrents points and make sure it's part of a cluster with a certain precision parameter. If not, remove the points that are out.
I'm trying to get a label to rotate based on the tit of the device in an app I'm making in swift. Given that I should be fine with rotating the label, does anybody know how to find the current accelerometer values of an iPhone? For example, getting it to print its x, y and z values once every second or so.
Thanks in advance.
Sample code:
Add the framework
Add the following code in the appropriate places
import CoreMotion
var motionManager = CMMotionManager()
var AccelX: CGFloat = 0
// Setup accelemeter detection
if motionManager.accelerometerAvailable == true {
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue(), withHandler: { (data, error) -> Void in
self.outputAccelertionData(data.acceleration)
})
}
func outputAccelertionData (acceleration: CMAcceleration) {
AccelX = 0
if fabs(CGFloat(acceleration.x)) > fabs(AccelX) {
AccelX = CGFloat(acceleration.x)
}
}