iOS Programming Recipe 19: Using Core Motion to Access Gyro and Accelerometer

One of the coolest things smartphones are capable of now is the ability to sense motion and orientation. The iPhone does this with the use of a 3-axis Accelerometers and Gyroscopes. In this tutorial we’re going to create an app that indicates the current G and speed of rotation. Not exactly a pretty app but still instructional.

Assumptions

  • You have a developer account and have provisioned a device. Unfortunately we can’t simulate this stuff without hardware. Follow the Apple documentation here to get set up.
  • Make Sure you head over our Github page and download the source code. NSCookbook Github Recipe 19
  • You have looked over some of the past tutorials and are comfortable with creating outlets and actions from the interface builder

Setting Up the Interface

Our Interface will be pretty simple as we will be displaying 12 values. We’ll show Max and Current Acceleration and Rotation about the X,Y and Z axis’.

Start with a new single view project titled “GyrosAndAccelerometers” and make sure the storyboards are selected.

Once the new project is created, open up the storyboard and drag 25 Labels onto the view. Twelve of these labels will be our outlets that will display the output data for each axis. Rename the labels and add a button as shown in the image below:

Image1

Now we’ll want to make the outlet connections so we can display the values on the screen. While still in the storyboard, change your editor view to the assistant editor view. control click and drag from each output label (the zeros) to the ViewController.h file. Title them “accX”,”maxAccX”, “accY”, “maxAccY”,”accZ”,”maxAccZ”, “rotX” , “maxRotX”, “rotY” , “marRotY”,”rotZ”, and “marRotZ” . Your button action should be titled “resetMaxValues” When you’re done, ViewController.h Should look like the following:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (strong, nonatomic) IBOutlet UILabel *accX;
@property (strong, nonatomic) IBOutlet UILabel *accY;
@property (strong, nonatomic) IBOutlet UILabel *accZ;

@property (strong, nonatomic) IBOutlet UILabel *maxAccX;
@property (strong, nonatomic) IBOutlet UILabel *maxAccY;
@property (strong, nonatomic) IBOutlet UILabel *maxAccZ;

@property (strong, nonatomic) IBOutlet UILabel *rotX;
@property (strong, nonatomic) IBOutlet UILabel *rotY;
@property (strong, nonatomic) IBOutlet UILabel *rotZ;

@property (strong, nonatomic) IBOutlet UILabel *maxRotX;
@property (strong, nonatomic) IBOutlet UILabel *maxRotY;
@property (strong, nonatomic) IBOutlet UILabel *maxRotZ;

- (IBAction)resetMaxValues:(id)sender;

@end

That’s it for the Interface! Now we can start working on making the displays work. First we might want to talk a bit more about the output data that comes from the Accelerometer and Gyroscope.

The Accelerometer And Gyroscope Explained

The Accelerometer

The Accelerometer, as mentioned previously, measures acceleration about all three axis’. This measurement is taken with respect to gravity.

When the phone sets on your desk with the screen pointed upward, The accelerometer would give you a value of -1g in the Z Direction. If you were to put the phone face down it would give a 1g value.

Holding the phone straight up and down would have a value of -1g in the Y direction, Assuming you can stay 100% still. Again, you can probably guess what happens when you hold the phone with the screen facing you and the home button on upward.

If you hold the phone with the screen facing you and then rotate it 90 degrees left, you’ll get a -1g value in the X direction. Rotate it right, And you’ll see a 1g value.

The g here stands for gravity or 9.8m/s^2, at any given time when the phone is at a stand still, the earth is pulling down with the force of gravity. In free fall, the accelerometors read 0.

The following photo shows the positive XYZ axis for the iPhone’s accelerometer. By positive I mean that if the direction of the arrow is pointed toward the sky then you’ll see a positive g reading for that axis.

Accelerometer Axis'

The Gyroscope

The gyroscope senses rotation around any of XYZ axis’. Together with the accelerometer, you will have all of the tools necessary to sense motion.

The Gryoscope outputs data in terms of the units rotations per second. If you were to rotate the iPhone 5 times around the Z axis in one second, The output would be eitehr -5 or 5 depending on wether or not you rotated it in the clockwise or counterclockwise direction.

Adding The Code

Now that we have been properly introduced to the motion tools we’ll dive right into the code.

For this tutorial we’ll be using Core Motion, So add the Core Motion Framework to the project. To do this, select the top level project in the project navigator. You should see the project summary show up in the main window. Scroll down and press the “+” button in the section title “Linked Frameworks and Libraries”. Choose the CoreMotion Framework and press “Add”.

Now we’ll want to import CoreMotion into the ViewController.h File as well as added a new property for the motion manager. Update the ViewController.h file as follows:


1
2
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>

Now we’ll want to add a few things to the ViewController.h file in order to get it working correcly. First we’ll want to add some global double variables to store the max acceleration and then we’ll want to create a CMMotionManager property.

Modify the ViewController.h file as follows:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>


double currentMaxAccelX;
double currentMaxAccelY;
double currentMaxAccelZ;
double currentMaxRotX;
double currentMaxRotY;
double currentMaxRotZ;

@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UILabel *accX;
@property (strong, nonatomic) IBOutlet UILabel *accY;
@property (strong, nonatomic) IBOutlet UILabel *accZ;

@property (strong, nonatomic) IBOutlet UILabel *maxAccX;
@property (strong, nonatomic) IBOutlet UILabel *maxAccY;
@property (strong, nonatomic) IBOutlet UILabel *maxAccZ;

@property (strong, nonatomic) IBOutlet UILabel *rotX;
@property (strong, nonatomic) IBOutlet UILabel *rotY;
@property (strong, nonatomic) IBOutlet UILabel *rotZ;

@property (strong, nonatomic) IBOutlet UILabel *maxRotX;
@property (strong, nonatomic) IBOutlet UILabel *maxRotY;
@property (strong, nonatomic) IBOutlet UILabel *maxRotZ;




- (IBAction)resetMaxValues:(id)sender;

@property (strong, nonatomic) CMMotionManager *motionManager;

@end

Next we’ll want to allocate and initialize our motion manager, give our double variables a starting values, set the update intervals, and then setup the Updaters. For this app, we want the motion data to always be on so we’ll set everything up in the viewDidLoad Section of the ViewController.m File:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    currentMaxAccelX = 0;
    currentMaxAccelY = 0;
    currentMaxAccelZ = 0;

    currentMaxRotX = 0;
    currentMaxRotY = 0;
    currentMaxRotZ = 0;

    self.motionManager = [[CMMotionManager alloc] init];
    self.motionManager.accelerometerUpdateInterval = .2;
    self.motionManager.gyroUpdateInterval = .2;

    [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue]
                                         withHandler:^(CMAccelerometerData  *accelerometerData, NSError *error) {
    [self outputAccelertionData:accelerometerData.acceleration];
                                             if(error){

                                                 NSLog(@"%@", error);
                                             }
    }];

    [self.motionManager startGyroUpdatesToQueue:[NSOperationQueue currentQueue]
                                withHandler:^(CMGyroData *gyroData, NSError *error) {
                                    [self outputRotationData:gyroData.rotationRate];
                                }];



    }

In this code, we first set the doubles to zero. After that we allocate and initialize the motionManager. Then we set the Update Intervals to a millisecond.

Next we call the startAccelerometerUpdatesToQueue method on the motionManger. This method tells the motionManger property to start sending acceleration updates which are handled by a block.

The next piece does exactly the same thing as the startAccelerometerUpdatesToQueue method but it does it for rotation instead.

In the code above, you’ll se we’re calling to methods inside the block statement:


1
    [self outputAccelertionData:accelerometerData.acceleration];

And


1
    [self outputRotationData:gyroData.rotationRate];

We’ll need to add these methods, These are going to update our acceleration and rotation data when it become available.

Add the following two methods right after the “viewDidLoad” method:


1
2
3
4
5
6
7
8
-(void)outputAccelertionData:(CMAcceleration)acceleration
{

}
-(void)outputRotationData:(CMRotationRate)rotation
{

}

Now populate those two methods with the following code:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
-(void)outputAccelertionData:(CMAcceleration)acceleration
{



self.accX.text = [NSString stringWithFormat:@" %.2fg",acceleration.x];
if(fabs(acceleration.x) > fabs(currentMaxAccelX))
{
    currentMaxAccelX = acceleration.x;
}
self.accY.text = [NSString stringWithFormat:@" %.2fg",acceleration.y];
if(fabs(acceleration.y) > fabs(currentMaxAccelY))
{
    currentMaxAccelY = acceleration.y;
}
self.accZ.text = [NSString stringWithFormat:@" %.2fg",acceleration.z];
if(fabs(acceleration.z) > fabs(currentMaxAccelZ))
{
    currentMaxAccelZ = acceleration.z;
}

self.maxAccX.text = [NSString stringWithFormat:@" %.2f",currentMaxAccelX];
self.maxAccY.text = [NSString stringWithFormat:@" %.2f",currentMaxAccelY];
self.maxAccZ.text = [NSString stringWithFormat:@" %.2f",currentMaxAccelZ];


}
-(void)outputRotationData:(CMRotationRate)rotation
{

self.rotX.text = [NSString stringWithFormat:@" %.2fr/s",rotation.x];
if(fabs(rotation.x) > fabs(currentMaxRotX))
{
    currentMaxRotX = rotation.x;
}
self.rotY.text = [NSString stringWithFormat:@" %.2fr/s",rotation.y];
if(fabs(rotation.y) > fabs(currentMaxRotY))
{
    currentMaxRotY = rotation.y;
}
self.rotZ.text = [NSString stringWithFormat:@" %.2fr/s",rotation.z];
if(fabs(rotation.z) > fabs(currentMaxRotZ))
{
    currentMaxRotZ = rotation.z;
}

self.maxRotX.text = [NSString stringWithFormat:@" %.2f",currentMaxRotX];
self.maxRotY.text = [NSString stringWithFormat:@" %.2f",currentMaxRotY];
self.maxRotZ.text = [NSString stringWithFormat:@" %.2f",currentMaxRotZ];
}

In each of these sections we are reading the retrieved data that is being passed in and setting our text labels to both the current real time value of the accelerometer. We are also checking to see if a new max has been achieved and if so, setting the max labels to the new high value.

The last thing we’ll need to do is fill in the Button action to reset the max values.


1
2
3
4
5
6
7
8
9
10
11
- (IBAction)resetMaxValues:(id)sender {

currentMaxAccelX = 0;
currentMaxAccelY = 0;
currentMaxAccelZ = 0;

currentMaxRotX = 0;
currentMaxRotY = 0;
currentMaxRotZ = 0;

}

Now if you run it you should see something that looks like this. You can’t simulate the accelerometer, so you will need to load this to a real device and try it out.

Final Image

While this app is not the most eye catching app we’ve created, it should help you get started with Core Motion. I encourage you to put it in your car and check out the acceleration. Just make sure you keep your eyes on the road! It is worth pointing out that to get a true acceleration vector, you’ll need to know the contribution of each axis in a particular direction and do a bunch of fancy math.

As always, questions and comments are welcome!

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Comments

  1. Thanks, very helpfull :)

  2. Matthew says:

    Thanks!

  3. Benjamin Ngo says:

    Hello Joe,

    Thank you for this tutorial, I have a much better understanding of how coremotion, accelerometer, and gyroscope works. I didn’t have any prior knowledge of it from before. But how would you use this to count the number of circles a person makes with his or her iPhone? And I don’t mean small circles but like big ones with their arm extended all the way out and doing circular motion. Thanks again.

  4. very good guide how to use gyro and accelerometer sensors. I add this tutorial on my article where a long list with sensors and tutorials about how to interface and programming accelerometer, gyro and IMU sensors

  5. Thank you for the article – thorough, concise and clearly-written !

  6. Javi Marmota says:

    Hello Joe!
    This tutorial was very easy to follow and simple, thank you so much, really helped me. But I need to use CoreMotion to move an ImageVIew only in the x axis. Have any idea?
    Thanks for any help (:

    • Joe Hoffman says:

      Hi Javi,

      I don’t think this should be too hard, but before I answer, How would you like it to move in the x direction? Would you like it to move based on the current acceleration or rotations per minute. Or would you prefer it moved with the respect to the current pitch, yaw, or roll?

      • Javi Marmota says:

        Hey Joe! Thank for your fast reply, I thought you were on holydays ;)
        Yeah, I would like it to move based on the current pitch, is for game and you have to avoid obstacles tilting your device in the x axis only, because I don’t want the image to be able to move up or down on the screen, only left and right.
        Ay idea? Thank you so much.

        • Joe Hoffman says:

          No Problem Javi! These days, there are no such thing as holidays for me.

          To Answer your question, You will want to take advantage of the CMDeviceMotion class. This class handles some filtering and comibines the accelerometer data with the magnetometer to give you a static pitch, roll, or yaw values. For the x axis I believe you will be most concerned with the Roll value. I would simply scale the x coordinates of your screen view with the roll value.

          after you’ve initialized your motion manager property(A good place to do this may be your initializer) you can call it with something similar to:

          if([self.motionManager isDeviceMotionAvailable])
          {
              //set update interval to twice per seceond
              [self.motionManager setDeviceMotionInterval:1.0/2.0];
              [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue] withHandler:^(CMDeviceMotion *deviceMotion, NSError *error)
              {

                  self.rollValue = deviceMotion.attitude.roll;
              }];


          }

          forgive me if this erroneous, I’m trying to type this without my mac handy.

          • Javi Marmota says:

            Hey Joe! The code was aright except for one or two grammar errors, it was very helpful for me and I appreciate it so much. Hope see more tutorials soon!
            Thank you for the attention man! (:

          • Joe Hoffman says:

            Thank you Javi! Appreciate the feedback. You mind pointing out these errors so I can fix them?

  7. Chalamphong Pandey says:

    Hey Joe, Awesome tutorial. I have learnt a lot from this. I am researching on gyroscope and accelerometer so I can match one motion made using the device and then compare it to another motion and decide if they both are the same. By “same” I mean not exactly the same but similar. For example, if a user makes a “S” motion, the next time user makes an S motion, it does not have to be the same size of “S”, the speed of the motion does not matter.
    Thank you for your tutorial.

    • Hello Joe,

      thank you for this tutorial, i will start leaning IOS programming from scratch. Could you please direct me to some of the beginners tutorials.

      My intention is the write an app to document motion trend for various types of motions. Later compare other motions with the documented ones. It’s similar to what Chalamphong is trying to do. Chalamphong, would you mind to share your app???

      I am excited to get started.

  8. How would I get the sensor to record data in the x,y,z from one arm movement forward. I just want to get the initial and the final value of that arm movement and eventually the displacement

  9. This is a great tutorial! I am trying to develop a game similar to “heads up” and want to use the devices motion on the X axis to either add or subtract to the user’s score – as well as change the word from a list. I have the list and can randomly choose words – I would just like to use the device’s motion to trigger the two actions (add/sub from score and move to the next word) — any help is greatly appreciated

  10. Great tut! THannks!!!!!

  11. Hi, is there a way to detect if the the device is in a free fall . I know most users won’t dare to throw their phone around. But I need this for a demo I am planning. Thanks in advance

    • Joe Hoffman says:

      Yes,

      I’m pretty sure this is doable. It might take some fancy math though depending on how high you drop it. If the phone is in free fall but accelerating at a rate of gravity then the vector math from the accelerometers should equal 0. The tricky bit is when it hits terminal velocity, then the devices wind resistance equals the pull of gravity so it is no longer accelerating and the acceleration would be 1. So when you drop it, the vector would be 1 then at the height of acceleration the vector would be 0 , then when terminal velocity is reached the vector would be 1 again. so what you would have is a vector magnitude that resembled a parabola over time.

      This is my best guess at the behavior. Interesting problem though, would be fun to explore more if I have time. Here’s a nice little primer on vector math incase you are unfamiliar: Vector math

  12. Nice tutorial..can u tell me how to detect if the phone is dropped? sample code would be very helpful. Thanks in advance

  13. Ankur Arora says:

    Hi Joel,

    I found your tutorial very helpfull. I have just started learning iOS nad was wondering if there is any way i can send “core motion data” out of the device to some microcontroller via USB or Wifi.

    Thank you.

  14. Joe Hoffman says:

    Hi Ankur!

    Thanks for reading! You would definitely want Bluetooth or Wifi for this I would think. I would start with the Arduino platform and get either a bluetooth shield or a wifi shield for it. I’m not a 100% sure, but I think you can send specific stuff of IP/TCP using the iPhone. Otherwise, you could create restful web service.

    You’ll probably want one of these: http://www.makershed.com/Arduino_Uno_Rev_3_Make_SMD_Version_p/mksp99.htm
    Then one of these: http://www.makershed.com/Arduino_WiFi_Shield_p/mksp18.htm
    or one of these: http://www.makershed.com/Bluetooth_Low_Energy_BLE_Shield_for_Arduino_2_0_p/mkrbl1.htm

Trackbacks

  1. [...] iOS Programming Recipe 19: Using Core Motion to Access Gyro and Accelerometer – guide how to create an application to indicate the G and rotation of an iPhone; [...]

Speak Your Mind

*

css.php
Privacy Policy