I am testing my app, and i am meant to be getting the readings from the accelometer...pretty simple! right now , the code says, if the acceleration is above 0.005, start adding 1 to a value... however for some reason, when i rotate the ipad, , so it seems to be 'standing on one of its edges' like a diamond, the value seems to be increasing? it increases even if the ipad is completely still.
Here is the code :
motionManager.accelerometerUpdateInterval = 0.02
motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data,error) in
if let myData = data {
if myData.acceleration.y > 0.05 {
self.damian += 1
print(data)
}
I'm not sure but based on documentation it looks like you're getting gravity + userAcceleration. So it's pretty normal to get non-zero values for y-axis in some device positions.
The total acceleration of the device is equal to gravity plus the acceleration the user imparts to the device (
userAcceleration
).
To get userAcceleration you should call startDeviceMotionUpdates(to:withHandler:) - https://developer.apple.com/documentation/coremotion/cmmotionmanager/1616048-startdevicemotionupdates
Related
I apologize for not knowing the proper terminology for everything here. I'm a fairly new programmer, and entirely new to Swift. The task I'm trying to accomplish is to display a current speed in MPH. I've found that using the "CoreLocation" and storing locations in an array and using "locations.speed "to display the speed is quite slow and does not refresh as often as I want.
My thought was to get an initial speed value using the "MapKit" and "CoreLocation" method, then feed that initial speed value into a function using the accelerometer to provide a quicker responding speedometer. I would do this by integrating the accelerometer values and adding the initial velocity. This was the best solution I could come up with to get a more accurate speedometer with a better refresh rate.
I'm having a couple of issues currently:
First Issue: I don't know how to get an initial speed value from a function using location data as parameters into a function using accelerometer data as parameters.
Second Issue: Even when assuming an initial speed of 0, my current program displays a value that keeps increasing infinitely. I'm not sure what the issue is that is causing this.
I will show you the portion of my code responsible for this, and would appreciate any insight any of you may have!
For my First Issue, here is my GPS data Function:
func provideInitSpeed(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])->Double {
let location = locations[0]
return ((location.speed)*2.23693629) //returns speed value converted to MPH
}
I'm not sure how to make a function call to retrieve this value in my Accelerometer function.
For my Second Issue, here is my Accelerometer Function with assumed starting speed of 0:
motionManager.startAccelerometerUpdates(to: OperationQueue.current!) {
(data,error) in
if let myData = data {
//getting my acceleration data and rounding the values off to the hundredths place to reduce noise
xAccel = round((myData.acceleration.x * g)*100)/100
yAccel = round((myData.acceleration.y * g)*100)/100
zAccel = round((myData.acceleration.z * g)*100)/100
// Integrating accel vals to get velocity vals *Possibly where error occurs* I multiply the accel values by the change in time, which is currently set at 0.2 seconds.
xVel += xAccel * self.motionManager.accelerometerUpdateInterval
yVel += yAccel * self.motionManager.accelerometerUpdateInterval
zVel += zAccel * self.motionManager.accelerometerUpdateInterval
// Finding total speed; Magnitude of Velocity
totalSpeed = sqrt(pow(xVel,2) + pow(yVel,2) + pow(zVel,2))
// if-else statment for further noise reduction. note: "zComp" just adjusts for the -1.0 G units z acceleration value that the phone reads by default for gravity
if (totalSpeed - zComp) > -0.1 && (totalSpeed - zComp) < 0.1 {
self.view.reloadInputViews()
self.speedWithAccelLabel.text = "\(0.0)"
} else {
// Printing totalSpeed
self.view.reloadInputViews()
self.speedWithAccelLabel.text = "\(abs(round((totalSpeed - zComp + /*where initSpeed would go*/)*10)/10))"
}
}//data end
}//motionManager end
I'm not sure why but the speed this function displays is always increasing by about 4 mph every refresh of the label.
This is my first time using Stack Overflow, so I apologize for any stupid mistakes I might have made!
Thanks a lot!
I am working on a driver behavior app and I am using SOMotionDetector (Thanks to MIT). Its giving speed and Motion Type (Not Moving, Walking, Running, Automotive) of device. I will use Automotive in my case as I need to detect driver behavior. This is detecting Motion Type based on speed with some thresholds set for Walking, Running, Automotive or if available it uses M7 Chip. It updates location approximately after every second (time varies based on GPS) in [SOMotionDetector sharedInstance].locationChangedBlock To detect Aggressive speed or break I am checking is that the increase/decrease of speed in last second. If it increases from a certain threshold (I am using kAggressiveSpeedIncrementFactor 8.0f) then its aggressively increasing speed, and if there is decreasing speed (difference factor is negative in this case) then its aggressive break. For turn I am playing with angle of latitude and longitude points, following is code for my logic:
#define kAggressiveSpeedIncrementFactor 8.0f // if 8 km/h speed was increased in last second
#define kAggressiveAngleIncrementFactor 30.0f // 30 degree turn angle
#define kAggressiveTurnIncrementFactor 5.0f . // while turn the increasing speed factor in last second
SOMotionDetector *motionDetector = [SOMotionDetector sharedInstance];
motionDetector.locationChangedBlock = ^(CLLocation *location) {
if (motionDetector.motionType == MotionTypeAutomotive) {
SOLocationManager *locationManager = [SOLocationManager sharedInstance];
float currSpeed = motionDetector.currentSpeed * 3.6f;
float lastSpeed = motionDetector.lastSpeed * 3.6f;
float currAngle = locationManager.currAngle;
float lastAngle = locationManager.lastAngle;
self.speedDiff = currSpeed-lastSpeed;
self.angleDiff = currAngle-lastAngle;
if (fabs(self.speedDiff)>kAggressiveSpeedIncrementFactor && fabs(self.angleDiff)<kAggressiveAngleIncrementFactor) {
NSString *msg = #"Aggressive Speed";
if (self.speedDiff < 0)
msg = #"Aggressive Break";
NSLog(#"%#", msg);
}
if (fabs(self.angleDiff)>kAggressiveAngleIncrementFactor && currSpeed>kAggressiveTurnIncrementFactor) {
NSLog(#"aggressive turn");
}
}
};
I have created currentSpeed and lastSpeed in SOMotionDetector class (for my speed difference) and currAngle and lastAngle in SOLocationManager. Please have a look at code,
Aggressive Speed some times work perfect
My question is:
Is this right approach what I am doing?
For detecting aggressive turn with the angle some times this happens that if
my vehicle is going 50 degrees angle (calculated with current and last lat, longs) on a strait road, some times the GPS detect location right or left side of road that give a big difference to the angle (like the path becomes a zig zag). any suggestion for this?
I am creating a very simple game using Swift and SpriteKit and I am moving a ball on the screen using the accelerometer data (acceleration x,y).
I would say the code works fine but I have noticed that sometimes (often right when I open the app) the accelerometer data is not correct and delayed for few seconds.
Why is that happening?
I am using the following code to read the accelerometer data:
if motionManager.accelerometerAvailable == true {
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue(), withHandler:{
data, error in
self.accX = CGFloat(data.acceleration.x)
self.accY = CGFloat(data.acceleration.y)
})
}
And the function update to apply some impulse to the ball:
override func update(currentTime: CFTimeInterval) {
var impulse = CGVectorMake(accX, accY)
var obj = childNodeWithName("ball") as SKSpriteNode
obj.physicsBody?.applyImpulse(impulse)
}
Am i missing something?
Thank you
With any accelerometer data, it is a good idea to run it through a filter to smooth out any irregular spikes. Here is my favorite:
double filteredAcceleration[3];
memset(filteredAcceleration, 0, sizeof(filteredAcceleration));
CMAccelerometerData *newestAccel = motionManager.accelerometerData;
filteredAcceleration[0] = (filteredAcceleration[0]*(1.0-alpha)) + (newestAccel.acceleration.x*alpha);
filteredAcceleration[1] = (filteredAcceleration[1]*(1.0-alpha)) + (newestAccel.acceleration.y*alpha);
filteredAcceleration[2] = (filteredAcceleration[2]*(1.0-alpha)) + (newestAccel.acceleration.z*alpha);
alpha can be any value from 0 to 1. The closer to 1 the more responsive it will be, the closer to zero the more smooth it will be. My favorite value on the iPhone is 0.2 It is a good compromise for smooth yet responsive for a game like doodle jump, or possibly moving a ball around.
I don't know why the accelerometer data is incorrect/delayed on startup, my guess would be that the hardware has to wake up and calibrate itself, but regardless of the why, if you implement a filter, it will smooth out these irregularities, and they won't be nearly as noticeable.
I have given priority to both functions and the issue seems fixed.
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
// do some task
dispatch_async(dispatch_get_main_queue()) {
// code with priority
}
}
I'm trying to duplicate the functionality in the Compass app - and I'm stuck on a particular bit: how do I figure out which way is "up" in the interface?
I've got a label onscreen, and I've got the following code that orients it to remain horizontal as the device moves around:
self.motionManager = CMMotionManager()
self.motionManager?.gyroUpdateInterval = 1/100
self.motionManager?.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue(), withHandler: { (deviceMotion, error) -> Void in
let roll = -deviceMotion.attitude.roll
self.tiltLabel?.transform = CGAffineTransformRotate(CGAffineTransformIdentity, CGFloat(roll))
})
This effect is pretty good, but it's got a few states where it's wrong - for example, the label flips erratically when the iPhone's lightning connector is pointed up.
How do I consistently tell which direction is up using CoreMotion?
UPDATE: Apparently, roll/pitch/yaw are Euler angles, which suffer from gimbal lock - so I think the correct solution might involve using quaternions, which don't suffer from this issue, or perhaps the rotationMatrix on CMAttitude might help: https://developer.apple.com/library/ios/documentation/CoreMotion/Reference/CMAttitude_Class/index.html
It doesn't need to be quite so complicated for the 2D case. "Up" means "opposite gravity", so:
motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()) { (motion, error) in
// Gravity as a counterclockwise angle from the horizontal.
let gravityAngle = atan2(Double(motion.gravity.y), Double(motion.gravity.x))
// Negate and subtract π/2, because we want -π/2 ↦ 0 (home button down) and 0 ↦ -π/2 (home button left).
self.tiltLabel.transform = CGAffineTransformMakeRotation(CGFloat(-gravityAngle - M_PI_2))
}
But simply "opposite gravity" has less meaning if you're trying to do this in all 3 dimensions: the direction of gravity doesn't tell you anything about the phone's angle around the gravity vector (if your phone is face-up, this is the yaw angle). To correct in three dimensions, we can use the roll, pitch, and yaw measurements instead:
// Add some perspective so the label looks (roughly) the same,
// no matter what angle the device is held at.
var t = self.view.layer.sublayerTransform
t.m34 = 1/300
self.view.layer.sublayerTransform = t
motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()) { (motion, error) in
let a = motion.attitude
self.tiltLabel.layer.transform =
CATransform3DRotate(
CATransform3DRotate(
CATransform3DRotate(
CATransform3DMakeRotation(CGFloat(a.roll), 0, -1, 0),
CGFloat(a.pitch), 1, 0, 0),
CGFloat(a.yaw), 0, 0, 1),
CGFloat(-M_PI_2), 1, 0, 0) // Extra pitch to make the label point "up" away from gravity
}
I'm trying to detect three actions: when a user begins walking, jogging, or running. I then want to know when the stop. I've been successful in detecting when someone is walking, jogging, or running with the following code:
- (void)update:(CMAccelerometerData *)accelData {
[(id) self setAcceleration:accelData.acceleration];
NSTimeInterval secondsSinceLastUpdate = -([self.lastUpdateTime timeIntervalSinceNow]);
if (labs(_acceleration.x) >= 0.10000) {
NSLog(#"walking: %f",_acceleration.x);
}
else if (labs(_acceleration.x) > 2.0) {
NSLog(#"jogging: %f",_acceleration.x);
}
else if (labs(_acceleration.x) > 4.0) {
NSLog(#"sprinting: %f",_acceleration.x);
}
The problem I run into is two-fold:
1) update is called multiple times every time there's a motion, probably because it checks so frequently that when the user begins walking (i.e. _acceleration.x >= .1000) it is still >= .1000 when it calls update again.
Example Log:
2014-02-22 12:14:20.728 myApp[5039:60b] walking: 1.029846
2014-02-22 12:14:20.748 myApp[5039:60b] walking: 1.071777
2014-02-22 12:14:20.768 myApp[5039:60b] walking: 1.067749
2) I'm having difficulty figuring out how to detect when the user stopped. Does anybody have advice on how to implement "Stop Detection"
According to your logs, accelerometerUpdateInterval is about 0.02. Updates could be less frequent if you change mentioned property of CMMotionManager.
Checking only x-acceleration isn't very accurate. I can put a device on a table in a such way (let's say on left edge) that x-acceleration will be equal to 1, or tilt it a bit. This will cause a program to be in walking mode (x > 0.1) instead of idle.
Here's a link to ADVANCED PEDOMETER FOR SMARTPHONE-BASED ACTIVITY TRACKING publication. They track changes in the direction of the vector of acceleration. This is the cosine of the angle between two consecutive acceleration vector readings.
Obviously, without any motion, angle between two vectors is close to zero and cos(0) = 1. During other activities d < 1. To filter out noise, they use a weighted moving average of the last 10 values of d.
After implementing this, your values will look like this (red - walking, blue - running):
Now you can set a threshold for each activity to separate them. Note that average step frequency is 2-4Hz. You should expect current value to be over the threshold at least few times in a second in order to identify the action.
Another helpful publications:
ERSP: An Energy-efficient Real-time Smartphone Pedometer (analyze peaks and throughs)
A Gyroscopic Data based Pedometer Algorithm (threshold detection of gyro readings)
UPDATE
_acceleration.x, _accelaration.y, _acceleration.z are coordinates of the same acceleration vector. You use each of these coordinates in d formula. In order to calculate d you also need to store acceleration vector of previous update (with i-1 index in formula).
WMA just take into account 10 last d values with different weights. Most recent d values have more weight, therefore, more impact on resulting value. You need to store 9 previous d values in order to calculate current one. You should compare WMA value to corresponding threshold.
if you are using iOS7 and iPhone5S, I suggest you look into CMMotionActivityManager which is available in iPhone5S because of the M7 chip. It is also available in a couple of other devices:
M7 chip
Here is a code snippet I put together to test when I was learning about it.
#import <CoreMotion/CoreMotion.h>
#property (nonatomic,strong) CMMotionActivityManager *motionActivityManager;
-(void) inSomeMethod
{
self.motionActivityManager=[[CMMotionActivityManager alloc]init];
//register for Coremotion notifications
[self.motionActivityManager startActivityUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMotionActivity *activity)
{
NSLog(#"Got a core motion update");
NSLog(#"Current activity date is %f",activity.timestamp);
NSLog(#"Current activity confidence from a scale of 0 to 2 - 2 being best- is: %ld",activity.confidence);
NSLog(#"Current activity type is unknown: %i",activity.unknown);
NSLog(#"Current activity type is stationary: %i",activity.stationary);
NSLog(#"Current activity type is walking: %i",activity.walking);
NSLog(#"Current activity type is running: %i",activity.running);
NSLog(#"Current activity type is automotive: %i",activity.automotive);
}];
}
I tested it and it seems to be pretty accurate. The only drawback is that it will not give you a confirmation as soon as you start an action (walking for example). Some black box algorithm waits to ensure that you are really walking or running. But then you know you have a confirmed action.
This beats messing around with the accelerometer. Apple took care of that detail!
You can use this simple library to detect if user is walking, running, on vehicle or not moving. Works on all iOS devices and no need M7 chip.
https://github.com/SocialObjects-Software/SOMotionDetector
In repo you can find demo project
I'm following this paper(PDF via RG) in my indoor navigation project to determine user dynamics(static, slow walking, fast walking) via merely accelerometer data in order to assist location determination.
Here is the algorithm proposed in the project:
And here is my implementation in Swift 2.0:
import CoreMotion
let motionManager = CMMotionManager()
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()) { (accelerometerData: CMAccelerometerData?, error: NSError?) -> Void in
if((error) != nil) {
print(error)
} else {
self.estimatePedestrianStatus((accelerometerData?.acceleration)!)
}
}
After all of the classic Swifty iOS code to initiate CoreMotion, here is the method crunching the numbers and determining the state:
func estimatePedestrianStatus(acceleration: CMAcceleration) {
// Obtain the Euclidian Norm of the accelerometer data
accelerometerDataInEuclidianNorm = sqrt((acceleration.x.roundTo(roundingPrecision) * acceleration.x.roundTo(roundingPrecision)) + (acceleration.y.roundTo(roundingPrecision) * acceleration.y.roundTo(roundingPrecision)) + (acceleration.z.roundTo(roundingPrecision) * acceleration.z.roundTo(roundingPrecision)))
// Significant figure setting
accelerometerDataInEuclidianNorm = accelerometerDataInEuclidianNorm.roundTo(roundingPrecision)
// record 10 values
// meaning values in a second
// accUpdateInterval(0.1s) * 10 = 1s
while accelerometerDataCount < 1 {
accelerometerDataCount += 0.1
accelerometerDataInASecond.append(accelerometerDataInEuclidianNorm)
totalAcceleration += accelerometerDataInEuclidianNorm
break // required since we want to obtain data every acc cycle
}
// when acc values recorded
// interpret them
if accelerometerDataCount >= 1 {
accelerometerDataCount = 0 // reset for the next round
// Calculating the variance of the Euclidian Norm of the accelerometer data
let accelerationMean = (totalAcceleration / 10).roundTo(roundingPrecision)
var total: Double = 0.0
for data in accelerometerDataInASecond {
total += ((data-accelerationMean) * (data-accelerationMean)).roundTo(roundingPrecision)
}
total = total.roundTo(roundingPrecision)
let result = (total / 10).roundTo(roundingPrecision)
print("Result: \(result)")
if (result < staticThreshold) {
pedestrianStatus = "Static"
} else if ((staticThreshold < result) && (result <= slowWalkingThreshold)) {
pedestrianStatus = "Slow Walking"
} else if (slowWalkingThreshold < result) {
pedestrianStatus = "Fast Walking"
}
print("Pedestrian Status: \(pedestrianStatus)\n---\n\n")
// reset for the next round
accelerometerDataInASecond = []
totalAcceleration = 0.0
}
}
Also I've used the following extension to simplify significant figure setting:
extension Double {
func roundTo(precision: Int) -> Double {
let divisor = pow(10.0, Double(precision))
return round(self * divisor) / divisor
}
}
With raw values from CoreMotion, the algorithm was haywire.
Hope this helps someone.
EDIT (4/3/16)
I forgot to provide my roundingPrecision value. I defined it as 3. It's just plain mathematics that that much significant value is decent enough. If you like you provide more.
Also one more thing to mention is that at the moment, this algorithm requires the iPhone to be in your hand while walking. See the picture below. Sorry this was the only one I could find.
My GitHub Repo hosting Pedestrian Status
You can use Apple's latest Machine Learning framework CoreML to find out user activity. First you need to collect labeled data and train the classifier. Then you can use this model in your app to classify user activity. You may follow this series if are interested in CoreML Activity Classification.
https://medium.com/#tyler.hutcherson/activity-classification-with-create-ml-coreml3-and-skafos-part-1-8f130b5701f6