Mapping IMU/Angle data to a line on-screen.
Or, display an angle as a line on screen.
For my Inertial Measurement Unit to have some kind of graphical display, I need to somehow map the data (In this case, a relative angle generated from both the accelerometer and a gyroscope) to a useful line on the LCD.
Now I’ve never been very good at maths, but with some googling and reference to my uni textbooks I was able to figure out how to do this programmatically (Rather than manually mapping each degree to a certain number of pixels).
I thought about busting out the ol’ Computational Mathematics textbook and delving into linear transformations of the plane, but that seemed like over-complicating matters.
There are a few things we need to calculate in order to do this. The end point of the positive line, and the end point of the negative line (If we’re rotating around one pixel). I decided to display text in the middle of the line, so I also need to calculate the start point of both lines.
A few pieces of information we need to start are:
- The X coordinate of the centre pixel (X[0])
- The Y coordinate of the centre pixel (X[1]);
- How long you want the line to be in pixels (linelength)
- The conversion for radians to degrees. To do this we divide by 57.29 (The ATMega on the Arduino Uno/Mega only supports a length of 4 bytes for a float, so we lose some precision).
- The filtered angle from the IMU (xAngle/yAngle)
So we need to calculate where to put the endpoints o f the line on both the X and Y axis of the display. To do this, we can use the sine and cosine functions respectively.
Sine = OPP/HYP = X Axis
Cosine = ADJ/HYP = Y Axis
We calculate where to place the X coordinate:
// Calculate X display coord using sine X[2] = (X[0] + linelength * (sin(xAngle/RAD_TO_DEG)));
And where to put the Y coordinate:
// Calc Y display coord using cosine X[3] = (X[1] + linelength * (cos(xAngle/RAD_TO_DEG)));
This should give you a nice line that rotates from it’s point of origin (X[0],X[1]), but that’s really only half the picture. To get the full line we simply need to invert the calculation for the X and Y coords, so one way you could do this is just to multiply the original calculation by negative one:
//Calculate the other end... // Calculate X display coord using sine X_inv[2] = (X[0] + linelength * (sin(xAngle/RAD_TO_DEG))-1); // Calc Y display coord using cosine X_inv[3] = (X[1] + linelength * (cos(xAngle/RAD_TO_DEG))-1);
And there you should have a nice line that rotates around the centre point. I have gone one step forward and added an offset so that the middle of the line is a blank sphere, which allows me to print the current angle in there:
To do this you simply need to calculate the start point of each line, which is the same as before, except substituting the linelength for the amount of offset that you want:
X_offset[1] = (X[1] + lineOffset * (cos(xAngle/RAD_TO_DEG)));
Hopefully this helps someone, and if you see a better way of doing it, please let me know!
The experimental code for this part I’m playing with can be found here: IMU.ino