I have a problem getting pitch, roll and yaw angles from CMAttitude class.
First, I did a normal Gyro using 'CMMotionManager' class and atributes x,y,z and worked fine.
Then, I tried to use CMAttitude for "absolute angles", but I doesn't work because It seems that is not updating data. Angles are always 0 (but the isn't errors or warnings)
I have searched a lot in stackoverflow, and used some solutions I find, but I have the same problem.
Here's my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
motionManager = [[CMMotionManager alloc] init];
CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
CMAttitude *attitude = deviceMotion.attitude;
referenceAttitude = attitude;
[motionManager startGyroUpdates];
timer = [NSTimer scheduledTimerWithTimeInterval:1/30.0
target:self
selector:#selector(doGyroUpdate)
userInfo:nil
repeats:YES];
}
-(void)doGyroUpdate {
//cambia el frame de referencia
[motionManager.deviceMotion.attitude multiplyByInverseOfAttitude: referenceAttitude];
double rRotation = motionManager.deviceMotion.attitude.roll*180/M_PI;
double pRotation = motionManager.deviceMotion.attitude.pitch*180/M_PI;
double yRotation = motionManager.deviceMotion.attitude.yaw*180/M_PI;
NSString *myString = [NSString stringWithFormat:#"%f",rRotation];
self.angYaw.text = myString;
myString = [NSString stringWithFormat:#"%f",pRotation];
self.angPitch.text = myString;
myString = [NSString stringWithFormat:#"%f",yRotation];
self.angRoll.text = myString;
}
Thanks a lot! :D
motionManager has 4 modes: Accelerometer, Gyroscope, Magnetometer and Device motion.
Depending on which one you need, you need to start appropriate mode: startAccelerometerUpdates, startGyroUpdates, startMagnetometerUpdates or startDeviceMotionUpdates.
You are starting startGyroUpdates but reading deviceMotion property. In your case only gyroData will be available.
do this instead and you will be getting the deviceMotion data:
[motionManager startDeviceMotionUpdates];
Related
I want to create a project that reads the user's gesture (accelerometer-based) and recognise it, I searched a lot but all I found was too old, I neither have problems in classifying nor in recognition, I will use 1 dollar recogniser or HMM, I just want to know how to read the user's gesture using the accelerometer.
Is the accelerometer data (x,y,z values) enough or should i use other data with it like Attitude data (roll, pitch, yaw), Gyro data or magnitude data, I don't even understand anyone of them so explaining what does these sensors do will be useful.
Thanks in advance !
Finally i did it, i used userAcceleration data which is device acceleration due to device excluding gravity, i found a lot of people use the normal acceleration data and do a lot of math to remove gravity from it, now it's already done by iOS 6 in userAcceleration.
And i used 1$ recognizer which is a 2D recongnizer (i.e. point(5, 10), no Z).Here's a link for 1$ recognizer, there's a c++ version of it in the downloads section.
Here are the steps of my code...
Read userAcceleration data with frequancy 50 HZ.
Apply low pass filter on it.
Take a point into consideration only if its x or y values are greater than 0.05 to reduce noise. (Note: The next step depends on your code and on the recognizer you use).
Save x and y points into array.
Create a 2D path from this array.
Send this path to the recognizer to weather train it or recongize it.
Here's my code...
#implementation MainViewController {
double previousLowPassFilteredAccelerationX;
double previousLowPassFilteredAccelerationY;
double previousLowPassFilteredAccelerationZ;
CGPoint position;
int numOfTrainedGestures;
GeometricRecognizer recognizer;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
previousLowPassFilteredAccelerationX = previousLowPassFilteredAccelerationY = previousLowPassFilteredAccelerationZ = 0.0;
recognizer = GeometricRecognizer();
//Note: I let the user train his own gestures, so i start up each time with 0 gestures
numOfTrainedGestures = 0;
}
#define kLowPassFilteringFactor 0.1
#define MOVEMENT_HZ 50
#define NOISE_REDUCTION 0.05
- (IBAction)StartAccelerometer
{
CMMotionManager *motionManager = [CMMotionManager SharedMotionManager];
if ([motionManager isDeviceMotionAvailable])
{
[motionManager setDeviceMotionUpdateInterval:1.0/MOVEMENT_HZ];
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error)
{
CMAcceleration lowpassFilterAcceleration, userAcceleration = motion.userAcceleration;
lowpassFilterAcceleration.x = (userAcceleration.x * kLowPassFilteringFactor) + (previousLowPassFilteredAccelerationX * (1.0 - kLowPassFilteringFactor));
lowpassFilterAcceleration.y = (userAcceleration.y * kLowPassFilteringFactor) + (previousLowPassFilteredAccelerationY * (1.0 - kLowPassFilteringFactor));
lowpassFilterAcceleration.z = (userAcceleration.z * kLowPassFilteringFactor) + (previousLowPassFilteredAccelerationZ * (1.0 - kLowPassFilteringFactor));
if (lowpassFilterAcceleration.x > NOISE_REDUCTION || lowpassFilterAcceleration.y > NOISE_REDUCTION)
[self.points addObject:[NSString stringWithFormat:#"%.2f,%.2f", lowpassFilterAcceleration.x, lowpassFilterAcceleration.y]];
previousLowPassFilteredAccelerationX = lowpassFilterAcceleration.x;
previousLowPassFilteredAccelerationY = lowpassFilterAcceleration.y;
previousLowPassFilteredAccelerationZ = lowpassFilterAcceleration.z;
// Just viewing the points to the user
self.XLabel.text = [NSString stringWithFormat:#"X : %.2f", lowpassFilterAcceleration.x];
self.YLabel.text = [NSString stringWithFormat:#"Y : %.2f", lowpassFilterAcceleration.y];
self.ZLabel.text = [NSString stringWithFormat:#"Z : %.2f", lowpassFilterAcceleration.z];
}];
}
else NSLog(#"DeviceMotion is not available");
}
- (IBAction)StopAccelerometer
{
[[CMMotionManager SharedMotionManager] stopDeviceMotionUpdates];
// View all the points to the user
self.pointsTextView.text = [NSString stringWithFormat:#"%d\n\n%#", self.points.count, [self.points componentsJoinedByString:#"\n"]];
// There must be more that 2 trained gestures because in recognizing, it gets the closest one in distance
if (numOfTrainedGestures > 1) {
Path2D path = [self createPathFromPoints]; // A method to create a 2D path from pointsArray
if (path.size()) {
RecognitionResult recongnitionResult = recognizer.recognize(path);
self.recognitionLabel.text = [NSString stringWithFormat:#"%s Detected with Prob %.2f !", recongnitionResult.name.c_str(),
recongnitionResult.score];
} else self.recognitionLabel.text = #"Not enough points for gesture !";
}
else self.recognitionLabel.text = #"Not enough templates !";
[self releaseAllVariables];
}
I have a for....loop and the UI then is blocked when in the loop.
Is the accelerometer also blocked?
I have following code in viewDidLoad
- (void)viewDidLoad{
[super viewDidLoad];
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 1.0f/60.0f;
}
and further down
- (void)accelerometer:(UIAccelerometer *)acel didAccelerate:(UIAcceleration *)acceleration {
acc=acceleration.x;
}
then an IBAction connected to a button which starts a loop
- (IBAction)doSomething {
for (int n = 1; n<=100;n++) {
// do someting in the loop
NSLog(#"x: %g", acc);
}
}
when logging the accelerometer x value it shows only the first value when the button is pressed and does not update when the loop is running. The same first value is repeating continuously.
Is there a way to log the acceleration when in the loop?
It would seem that UIAccelerometer needs the main thread so is getting blocked by the for...loop. Core Motion also has an accelerometer property in the CMMotionManager object that can report acceleration, and does better backgrounding since it's what Nike+ uses. There are two ways to access it - a pull method where you get the data directly, or a call-back. To be honest, I just tried it and couldn't get the call-back to work (mostly because it requires NSOperationQueue and I don't have much experience with that so I'm probably putting it on the wrong queue), but if you're ok with pulling data, which your approach seems to need, then this works:
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
[motionManager startAccelerometerUpdates];
for (int i = 0; i < 10000; i++)
{
NSLog(#"Acceleration: %f", motionManager.accelerometerData.acceleration.x);
}
I just tried it on a device and the values reported in the for...loop update with the current acceleration.
Source: UIBackgroundModes and UIAccelerometer
I'm using deviceMotion for getting useracceleration(x, y, z). My aim is to create a filetext where in each iteration my application writes the 3 components in a row.
I'm using MotionGraphs code sample.
How is it possible - directly, or is necessary to create an array first?
This array; is it NSMutableArray or NSMutableNumber?
I've been looking for this question and I'm lost. :-(
I'm not an Objective-C expert but I remember Pascal code where I opened a file, and then I was writing in each iteration, but I checked: that programming has changed.
At the beginning we don't take into account different filters or discrimination window. For them, I've implemented freescale procedure. I'm just looking to save accelerometer data / to store data from accelerometer using deviceMotion userAcceleration.
float minX = 1.0f;
float minY = 1.0f;
float minZ = 1.0f;
NSMutableArray *container = [[NSMutableArray alloc] init];
-(void)startUpdatesWithSliderValue:(int)sliderValue
{
NSTimeInterval delta = 0.005;
NSTimeInterval updateInterval = deviceMotionMin + delta * sliderValue;
CMMotionManager *mManager = [(APLAppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
APLDeviceMotionGraphViewController * __weak weakSelf = self;
[container addObject:[NSNumber numberWithFloat:deviceMotion.userAcceleration.x]];
[container addObject:[NSNumber numberWithFloat:deviceMotion.userAcceleration.y]];
[container addObject:[NSNumber numberWithFloat:deviceMotion.userAcceleration.z]];
}
//Finally we have to dump data to filetext, this is I donĀ“t know correctly.
1 Create NSMutableArray *container = [[NSMutableArray alloc] init]; to be your container.
2 Within the Accelerometer delegate method for did detect motion be sure to set a min for each of the 3 axis. e.g. float min_X = 1.0f; float min_y =1.0f; float min_Z = 1.0f
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
}
3 Use Simple Filter Logic as in: (keep in mind the Acceleration is maxed at +- 2.3g so both positive and negative thresholds need consideration.
if ((acceleration.x > min_X || acceleration.x < -min_X) && (Y's..) && (Z's...) ) {
[container addObject:[NSNumber numberWithFloat:acceleration.x]];
[container addObject:[NSNumber numberWithFloat:acceleration.y]];
[container addObject:[NSNumber numberWithFloat:acceleration.z]];
}
4 The Array should be full of NSNumbers in groups of three (x,y,z).
5 The filter is needed, otherwise the accelerometers can pick small vibrations just sitting on the table.
WARNING: The Array will fill up fast, so Set the Sample Rate to an acceptable range based on how long you want to record data.
I'm trying to make a simple app utilizing gyroscope, where a character moves according to the rotation of the iPad 1.
My code is not working, so I tested to see the values of raw,pitch,yaw,
and they actually stay as zero however I move the device.
I'm sure iPad 1 supports CMMotionManager, so I'm not sure what's causing it...
My code is as follows
- (id) init{
if((self=[super init])){
self.isTouchEnabled = YES;
winSize = [[CCDirector sharedDirector] winSize];
[self createRabbitSprite];
self.motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 1.0/60.0;
if(motionManager.isDeviceMotionAvailable){
[motionManager startDeviceMotionUpdates];
}
[self scheduleUpdate];
//[self registerWithTouchDispatcher];
}
return self;
}
-(void)update:(ccTime)delta{
CMDeviceMotion *currentDeviceMotion = motionManager.deviceMotion;
CMAttitude *currentAttitude = currentDeviceMotion.attitude;
if(referenceFrame){
[currentAttitude multiplyByInverseOfAttitude:referenceFrame];
}
float roll = currentAttitude.roll;
float pitch = currentAttitude.pitch;
float yaw = currentAttitude.yaw;
NSLog(#"%.2f and %.2f and %.2f",roll,pitch,yaw);
rabbit.rotation = CC_RADIANS_TO_DEGREES(yaw);
}
Please help me out..
and thanx in advance.
(edit)
Apparently, motionManager.isDeviceMotionAvailable is returning FALSE...
which must mean that iPad 1 doesn't support CoreMotion???
Could it be something with the setting?
The iPad First generation does support CMMotionManager (as it has an accelerometer), but won't return any gyroscopic data - it doesn't have a gyroscope! You'll need to check the gyroAvailable property of a CMMotionManager instance.
I'm trying to use CMDeviceMotion to track when the iPhone is tilted backwards, and then do something. I've successfully created the CMMotionManager, I'm getting motion data from the system, and I'm filtering for results above a certain acceleration.
What I want to do though, is detect when the device is being tilted backwards or forwards above a certain speed. How do I do that?
Here's the code I have so far:
UPDATE: I think I solved it. I was looking for the rotationRate property, CMRotationRate. I've paired them together, I really only need the x value, so I'll keep working on it. If anyone has some tips at the below code, it's much appreciated.
- (void)startMotionManager{
if (motionManager == nil) {
motionManager = [[CMMotionManager alloc] init];
}
motionManager.deviceMotionUpdateInterval = 1/15.0;
if (motionManager.deviceMotionAvailable) {
NSLog(#"Device Motion Available");
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error){
//CMAttitude *attitude = motion.attitude;
//NSLog(#"rotation rate = [%f, %f, %f]", attitude.pitch, attitude.roll, attitude.yaw);
[self performSelectorOnMainThread:#selector(handleDeviceMotion:) withObject:motion waitUntilDone:YES];
}];
//[motionManager startDeviceMotionUpdates];
} else {
NSLog(#"No device motion on device.");
[self setMotionManager:nil];
}
}
- (void)handleDeviceMotion:(CMDeviceMotion*)motion{
CMAttitude *attitude = motion.attitude;
float accelerationThreshold = 0.2; // or whatever is appropriate - play around with different values
CMAcceleration userAcceleration = motion.userAcceleration;
float rotationRateThreshold = 7.0;
CMRotationRate rotationRate = motion.rotationRate;
if ((rotationRate.x) > rotationRateThreshold) {
if (fabs(userAcceleration.x) > accelerationThreshold || fabs(userAcceleration.y) > accelerationThreshold || fabs(userAcceleration.z) > accelerationThreshold) {
NSLog(#"rotation rate = [Pitch: %f, Roll: %f, Yaw: %f]", attitude.pitch, attitude.roll, attitude.yaw);
NSLog(#"motion.rotationRate = %f", rotationRate.x);
[self showMenuAnimated:YES];
}
}
else if ((-rotationRate.x) > rotationRateThreshold) {
if (fabs(userAcceleration.x) > accelerationThreshold || fabs(userAcceleration.y) > accelerationThreshold || fabs(userAcceleration.z) > accelerationThreshold) {
NSLog(#"rotation rate = [Pitch: %f, Roll: %f, Yaw: %f]", attitude.pitch, attitude.roll, attitude.yaw);
NSLog(#"motion.rotationRate = %f", rotationRate.x);
[self dismissMenuAnimated:YES];
}
}
}
Have you looked at CMAttitude? Sounds like what you need, plus some mathematics I guess.
EDIT: Ok you did.
Quoting Apple documentation, at chapter "Getting the Current Device Orientation" :
If you need to know only the general
orientation of the device, and not the
exact vector of orientation, you
should use the methods of the UIDevice
class to retrieve that information.
Using the UIDevice interface is simple
and does not require that you
calculate the orientation vector
yourself.
Great, they do the maths for us. Then I guess that tracking acceleration like you do + tracking orientation, as above documentation explains, should do the job, using UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown ?
If none of the UIDeviceOrientation fit your needs, what you will need is to calculate some spatial angles from two CMAttitude references, which provides a rotationMatrix and a quaternion... these school maths are far away for me... I would then suggest to maybe search/ask a math only question with these.