EDIT:
I have stripped the program down to its basics: https://github.com/aidan-aidan/temp/blob/master/source/vpp.c
I have posted this question over at the gba-dev forums but they seem pretty dead and I have not gotten a response after many days.
Here is a video showing the problem: http://youtu.be/8gweFiSobwc (different from the code above, you can run the rom if you want to see it)
As you can see the BG rotation is unaffected, though they use the same LUT and they have the same data types in their structures as each other.
I have redone the LUT a few times to no avail, and this problem only surfaced after switching to 2048 circle divisions rather than 256.
Looking at the memory viewer in VBA, I can see that pb is not behaving the same as pa. pa ranges from 0x0100 to 0 as I would expect (same as 1 to 0), but pb ranges from 0 to 0x0096 when the gun is right from the center line, but jumps up to 0xFFFF as soon as it goes left from the center line. The only thing I can figure is that it is going negative (which makes sense, as cosine of that angle should result in a negative number), but I don't fully understand ones complement so i can't be sure. 0 "degrees" is horizontal on the right for the gun. 512 would be ninety degrees.
I have included everything I can think of, any help is appreciated.
Thanks!
As near as I can tell the program is technically working correctly. Instead your visual artifacts stem from the limited 8-bit fixed-point precision in the affine transformation used by the hardware.
Essentially the difference between a 0 and 1 in the sine tables makes a rather large and visible jump in the rotation for the right-angles in your rectangular sprite, while the 0°/90°/180°/270° corners are not that special for the round earth background.
The tangent for the (co-)sine function at 0 is high, so if you look at your tables there is only a single spot where they are precisely 0. If the rotation doesn't fall precisely on this index the result looks visibly skewed.
What you may try however is to cheat by fiddling with the rounding to produce more consecutive zeroes. At the moment you table generator is scaling by 256 and rounding towards the nearest integer:
sine[i] = floor(sin(i * M_PI / 1024.0) * 256.0 + 0.5);
As an alternative we may skew the table a little by rounding towards zero, as is the default in C, and increasing the scale to insure that the table still reaches 256 for at more than one entry.
int value = sin(i * M_PI / 1024.0) * 257.0;
if(value < -256) value = -256;
if(value > +256) value = +256;
sine[i] = value;
The table may be then flattened even further by increasing the scaling factor even further.
The problem is also that for small sprites near right angles there is nowhere for the rotation to go.
Picture a 64x1 straight line sprite pointing vertically at nearly an almost straight angle, with only the minimum 1/256th fractional step from the sine table. Keep in mind that the rotation is always centered so you get one jagged horizontal step precisely at the center.
For each of the 32 pixels from the center outwards you then add up another 1/256th fraction to the texture coordinate. However all of these together only make for 1/8th of a pixel in total and so no more horizontal steps are taken. In fact for this sprite shape you will see no visible change until the rotation angle reaches a full 8/256th fraction.
On the other hand your large earth object is sufficiently big to always show multiple visible integer "steps." This means that an increased rotation angle will always at least shift the position of the non-centered step, even if it insufficient to add up to another full pixel along entire side. The result is a smoother overall effect due to the continual animation.
Related
I am developing a SLAM algorithm in C, and I have implemented the FAST corner finding method which gives me some strong keypoints in the image. The next step is to get the center of the keypoints with a sub-pixel accuracy, therefore I extract a 3x3 patch around each of them, and do a Least Squares fit of a two dimensional quadratic:
Where f(x,y) is the corner saliency measure of each pixel, similar to the FAST score proposed on the original paper, but modified to also provide a saliency measure in non corner pixels.
And the least squares:
With being the estimated parameters.
I can now calculate the location of the peak of the fitted quadratic, by taking the gradient equal to zero, achieving my original goal.
The issue arises on some corner cases, where the local peak is closer to the edge of the window, resulting in a fit with low residuals but a peak of the quadratic way outside the window.
An example:
The corner saliency and a contour of the fitted quadratic:
The saliency (blue) and fit (red) as 3D meshes:
Numeric values of this example are (row-major ordering):
[336, 522, 483, 423, 539, 153, 221, 412, 234]
And the resulting sub pixel center of (2.6, -17.1) being wrong.
How can I constrain the fit so the center is within the window?
I'm open to alternative methods for finding the sub pixel peak.
The obvious answer is to reject 3x3 (or 5x5, whatever you use) boxes whose discrete maximum is not at the center. In other words, to use a quadratic approximation only to refine the location of a maximum that must be located inside the box.
More generally, in such cases the first questions to ask is not "How do I constrain my model-fitting procedure to shoehorn a solution for this edge case?", but rather
"Does my model apply to this edge case?" and "Is this edge case even worth spending time on, or can I just ignore it?"
I tried my own code to fit a 2D quadratic function to the 3x3 values, using a stable least-squares solving algorithm, and also found a maximum outside of the domain. The 3x3 patch of data does not match a quadratic function, and therefore the fit is not useful.
Fitting a 2D quadratic to a 3x3 neighborhood requires a degree of smoothness in the data that you don't seem to have in your FAST output.
There are many other methods to find the sub-pixel location of the maximum. One that I like because it is more stable and less computationally intensive is the fitting of a "separable" quadratic function. In short, you fit a quadratic function to the three values around the local maximum in one dimension, and then another in the other dimension. Instead of solving 6 parameters with 9 values, this solves 3 parameters with 3 values, twice. The solution is guaranteed stable, as long as the center pixel is larger or equal to all pixels in the 4-connected neighborhood.
z1 = [f(-1,0), f(0,0), f(1,0)]^T
[1,-1,0]
X = [0,0,0]
[1,1,0]
solve: X b1 = z1
and
z2 = [f(0,-1), f(0,0), f(0,1)]^T
[1,-1,0]
X = [0,0,0]
[1,1,0]
solve: X b2 = z2
Now you get the x-coordinate of the centroid from b1 and the y-coordinate from b2.
I am looking for a line drawing algorithm that only plots one pixel per scanline/row and only use integers.
Bresenham line drawing algorithm works in most of the cases, but it plots two pixel per scanline when I have a line from P1(40, 0) to P2(0, 20) where Pn(x, y).
For the record, I'm coding in C for a low end micro controller with no floating point unit.
Thanks!
To simulate floating point you can scale the integers, so in stead of "1" you have "1000" where you imagine the floating point after the third digit from the left. Then you perform Bresenham's algorithm in integer on these scaled values. Once calculated, you de-scale by integer division with this maximum. You may want to round. You must determine the maximum x value and choose your scale acordingly. Taking a power of 2, you can use shift in stead of divisision.
To ensue there will be only one pixel per line, you can use a cut-off value, where below will not draw the pixel and above will. The pixel should be drawn as black (or white) but there are no gradients (as otherwise you need two pixels).
See http://en.wikipedia.org/wiki/Bresenham's_line_algorithm:
"...This results in an algorithm that uses only integer arithmetic."
This is a hard one and although I can think of a few kludge methods of doing it, I have a feeling there is a clean mathematical method, although I am having difficulty inventing it myself.
I have a number of parameters which control (software) biquad filters for audio. Essentially there are just 3 parameters, frequency, gain and Q (or bandwidth). In audio terms, the frequency represents the center frequency of the filter. The gain represents whether this frequency is boosted or cut (a gain of 0 results in no change to the audio passing through the filter). Q represents the width of the filter - IE a very wide filter might affect frequencies far away from the center frequency, whereas a narrow (low Q) filter will only affect frequencies close to the center frequency.The filters take the form of a bell curve, or at least thats an approximation, whether its mathematically accurate I am not sure.
I want to display the characteristics of these filters graphically - display a graph of gain against frequency. There are several of these filters applied to the audio channel, and I want to be able to add the different result graphs, to produce an overall graph (IE a graph summing all the components of the combined filters). But I also want to be able to access the individual filters graphs.
I can handle adding the component graphs into a single 'total' graph, but how to produce the original x-y graph from the filter parameters escapes me. I will draw bitmaps so all I need is to be able to create arrays of the form frequency[x]=y. Im doing this in C so I don't have the mathematical tools in matlab etc. So I might have a filter with a center frequency of say 1000 (Hz), a gain of say 20 (db or linear I understand how to convert that), and a Q of say 3. The Q factor is relative and does not have to be exactly mathematically correct if that causes any complication.
It seems like a quite simple mathematical function but maths is not my strong point and I don't know enough - I have been messing round with sine functions etc but its not working and I suspect is probably wasting processing power by over complicating the maths (although I might be wrong there).
TIA, Pete
I have my doubts about the relationships between biquad filters, Q values, and bell curves. But I'll put those aside and just tell you how to draw a bell curve, since that's what you asked.
From this wikipedia article, the equation for a bell curve is
where for your application
a corresponds to the gain
b determines the center frequency
2c^2 is related to Q (larger values will make the curve wider)
The C code below computes a sample bell curve. For this example, the numbers were chosen based on drawing into a window that is 250 pixels wide by 200 pixels high, with a coordinate system where the origin {0,0} is at the bottom left corner.
int width = 250;
int height = 200;
int bellCurve[width]; // the output array that holds the f(x) values
double gain = 180; // the 'a' value, determines how high the peak is from the baseline
double offset = 10; // the 'd' value, determines the y coordinate of the baseline
double qFactor = 1000; // the '2c^2' value, determines how fat the curve is
double center = 100; // the 'b' value, determines the x coordinate of the peak
double dx;
for ( int x = 0; x < width; x++ )
{
dx = x - center;
bellCurve[x] = gain * exp( -( dx * dx ) / qFactor ) + offset;
}
Plotting the curve results in an image like this where the peak is at x=100, y=10+180=190
You could input a unit impulse (an array of all zeros, except one element=1.0) into your digital filters, treating them as black boxes. Then FFT the impulse response output array to get the frequency response of the filter. Plotting the magnitude of the complex frequency samples will give you a pretty picture. Python+numpy+matplotlib would probably be an easier way to go about it. You will need to know the sampling period to get meaningful plots.
What you really want is the bode plot of the filter. This is non trivial to calculate yourself, a cursory search for a library to do it for you in C yielded nothing. If accuracy is not important and you can approximate the shape once and stretch it based on the parameters of the particular filter. For example, you might have a normalized array of relative values and construct a new curve (array) based on the parameters of the filter and the base curve you generated earlier.
The base curve could be generated from MATLAB if you can or Wolfram Alpha or something like that.
Here is one in javascript.
http://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/
The filter you describe is the 'peak' filter. Use the log scale to display frequencies.
—Tom
I have wasted 2 days (and nights) on this specific issue and completely failed in tilt compensating my magnetometer output.
I tried about everything I could read on Google and in open source examples, nothing guided me to a proper tilt compensation for both (roll and pitch). .
The Setup:
I use calibrated values but not unity values.
I have a fused gravity vector which is working properly and exact.
The sensor is a 9dof BMX055 (http://www.mouser.com/ds/2/621/BST-BMX055-DS000-01-274824.pdf)
The magnetometer min/max are +- 512 on each axis (with small differences but all axes are zero'd out).
The hardware is Sam3X8e (Cortex M3), all written in C.
Floating point and trigonometry can be done quite fast, so that's no problem.
The BMX055 chip is aligned so that the pins 20,19,18 point to the front.
Datasheet page 159-161 show the orientation.
Pitch rises when I rise the front.
Roll rises when I rise the left side.
Example values:
Pointing at a direction my algorithm calls 305deg when leveled horizontal
Pitch: 0 , Roll 0 : MAG cal: x 132 y 93 z -364
Pitch: +24, Roll 0 : MAG cal: x -109 y 93 z -397
Pitch: +46, Roll 0 : MAG cal: x -303 y 89 z -351
Pitch: 0 , Roll -44 : MAG cal: x 151 y 352 z -235
Pitch: 0 , Roll +36 : MAG cal: x 130 y -140 z -328
Pitch: 78 , Roll -2 : MAG cal: x -503 y 93 z -199
Pitch: 7 , Roll -53 : MAG cal: x 135 y 424 z -180
The alignment should always have been around 305 degree (as good as I could do it), maybe +- 5 degree.
The formula: (same one used about everywhere)
uint16_t compass_tilt_compensation(float roll_radians, float pitch_radians,float mag_y, float mag_x, float mag_z)
{
float tilt_compensated_heading;
float MAG_X;
float MAG_Y;
float cos_roll;
float sin_roll;
float cos_pitch;
float sin_pitch;
int16_t heading;
//pitch_radians =roll_radians;
//roll_radians *= -1;
//mag_x *= -1;
//roll_radians=0.0f;
//pitch_radians=0;v
//pitch_radians *=-1;
cos_roll = cos(roll_radians);
sin_roll = sin(roll_radians);
cos_pitch = cos(pitch_radians);
sin_pitch = sin(pitch_radians);
#if 0
MAG_X = mag_x*cos_pitch+mag_y*sin_roll*sin_pitch+mag_z*cos_roll*sin_pitch;
MAG_Y = mag_y*cos_roll-mag_z*sin_roll;
tilt_compensated_heading = atan2f(-MAG_Y,MAG_X);
#else
MAG_X = mag_x * cos_pitch + mag_z * sin_pitch;
MAG_Y = mag_x * sin_roll * sin_pitch + mag_y * cos_roll - mag_z * sin_roll * cos_pitch;
tilt_compensated_heading = atan2f(-MAG_Y,MAG_X);
//tilt_compensated_heading = atan2f(-mag_y,mag_x); // works fine when leveled
#endif
//convert to degree from 0-3599
heading = tilt_compensated_heading * RAD_TO_DEG * 10;
if ( heading < 0 )
{
heading += 3600;
}
return heading;
}
I tried various combinations, I tried to only fix one axis and leave one always at 0, exchanging X/Y pitch/roll, *-1 on any of the inputs.
The results are always completely wrong.
Sometimes (depending on where I try to invert or not invert a value by trial/error) the values seem to be off nearly linear. Sometimes one axis is compensated in positive areas.
However rolling and pitching always causes 'random' jumps and changes of the heading.
Math has never been my favourite, now I regret it.
My personal guess is that the formula is right in principe, the mag is working in principe (after all I get proper degrees when leveled) but I am somehow feeding something wrong.
(Like Y and X need to be exchanged, z*-1, pitch needs to be *-1)
Could someone who is good in this subject maybe take a look and guide me how to get a proper heading ?
Would be great to have a few hours sleep this night without having to dream about a dysfunctional algorithm again :)
Update:
The tilt compensation here works for negative roll when pointing at the 305deg heading.
It is also used here: http://www.emcu.it/MEMS/Compass/MEMS_Compass_A_RTM1v3.pdf
3 days of work, finally I found the issues I had and tilt compensation is working !
I read quite a few people had such issues in various forums, so here is what I did:
I explain what I did and why I did, step by step. Maybe someone with a similar issue will find a solution that way.
Playing around with the values can help (maybe just pitch or roll has to be inverted, X or Y has to be exchanged).
However, there are quite a lot of values and the amount of combinations is too high if your problem is more than a tiny one.
The formula posted works fine (the one which is in the active #if branch.
If you have a magnetometer and the atan2(-y,x) gives you a proper heading when leveled flat, then this formula will work for you too.
What I did was to completely go through all my sensors and vectors beginning from the I2C binary logic (and the datasheet).
Important in this secial case (BMX055), the Datasheet orientation page is WRONG!
There are multiple bugs regarding orientation of axes (x,y) as well as when a rotation is positive or negative. Sometimes right hand rule applies, sometimes not and the drawings are misleading (wrong). Bosch did a bad job documenting this chip (and the previous one).
They do not seem to want people to understand it properly, they write about a fusion API several times with optimized fixed point arithmetiks and advanced calibration but it's not available for the public.
What I needed to do was to make a proper body reference system.
Decide yourself where X is and then make it so all your sensors properly change the X axis when pitched/rolled into the same direction (positive/negative).
Do this for pitch,roll and gravity/magnetic field.
Once all of them play together nicely I started all over again.
The heading formula was still dysfunctional but now I trusted the vextors the first time.
I added a vector rotation matrix function and rotated the magnetic vector back using roll and pitch and yaw=0.
Surprise: the magnetic vector was tilt compensated.
Now I knew it CAN be done :)
I went back to the original formula, replaced X with Y (because I had exchanged them to match the body reference system (X and Y of gyro/mag).
Now tilt compensation worked for pitch but not for roll.
So I inverted roll_radians and suddenly it's perfectly tilt compensated.
I have two solutions now. One is a rotation matrix solution and the other one the standard solution everyone is using. I will see if one of them performs better and maybe give a last update here if the results are worth it.
First, verify that it is indeed a software problem. Your equations seem to be correct. Just in case, generate a table populated with test data to pass to your procedure and compare the output of your function with the expected values if the code was correct.
You are using a magnetometer and these are very sensitive devices. The first thing I would check is whether there are any large metal structures near the sensor. If you have access to an oscilloscope, probe the chip's power rails and see if power going into it is stable. Adding a 1uF decoupling cap could solve power issues. I would also suggest getting a graph while having a sampling frequency larger than 100Hz and see if the jumps are periodic. If the signal is periodic with a 50Hz frequency, assuming the sensor is not being moved, that would indicate interference from your mains supply. Performing FFT analysis over the data can't hurt. We had similar problems caused by power cables running underneath our lab floor. If the sensor keeps jumping around randomly, the chip is probably dead. Did you use proper ESD protection while handling it?
Hope this helps.
Does rotation always occur about the origin (0,0,0)?
Does translation always occur relative to previous translation?
Does scaling increase the coordinates axes size?
I suggest that a good way for a beginner is to start by thinking about points rather than 3D objects. Then all the transformation can be thought of as functions to change a point position to a new position.
First imagine an XYZ cartesian coordinate space, then imagine a point (X,Y,Z) in space with origin (0, 0, 0). All OpenGL knows at this stage is the point X,Y,Z. Now you are ready to begin:
Rotation requires an angle and a center of rotation. glRotate allows you to only specify the angles. By virtue of mathematics, conceptually, the center of rotation is at the location (X-X,Y-Y,Z-Z) or (0,0,0).
Translation is just an offset from the current position. Since OpenGL knows your point (X,Y,Z) it simply adds the offest to the position vector. It is therefore more correct to say it is relative to the current position rather than previous translation.
Scaling is a multiplication of the point vector (X.m,Y.m,Z.m) hence it simply just translating that point by a factor of m. Hence conceptually one can say it doesn't change the coordinate axes size.
However, when you start to think in 3D things get abit tricky because you will realise that if you are not careful, the all the points in a single 3D object doesn't always change position in the way you desire relative to each other. You will learn for example that if you want to rotate about the object's center, you will have to "move it to the origin, rotate, and then move it back again". This process of moving it back an forth can be thought as specifying the center of rotation. These are actually mathematical "tricks" that you apply.
Does rotation always occur about the origin (0,0,0)?
Indeed this is the case.
Does translation always occur relative to previous translation?
Does scaling increase the coordinates axes size?
This requires some explanation: OpenGL, and so many other software operating with geometry data don't build a list of chained transformations. What they maintain is one single homogenous transformation matrix.
"Appending" a transformation is done by multiplying the current transformation matrix with the transformation matrix describing the "next" transformation, replacing the old transformation. This also means that a compound transformation matrix, like what you end up having in the OpenGL modelview, may be applied as transformation as well.
To make a long story short, it depends all on the transformation applied. Old OpenGL gives you some basic matrix manipulations. In OpenGL-3 they have been removed, because OpenGL is not a math library, but draws stuff.
So how does such a transformation matrix look like? Like this:
Xx Yx Zx Tx
Xy Yy Zy Ty
Xz Yz Zz Tz
_x _y _z w
Maybe you noticed that there are 3 major columns designated by capital X, Y, Z. Those columns form vectors. And in the case of 3D transformations those are the base vectors of a coordinate system, relative the one the transformation is applied upon. However vectors only give "directions" and a length. So what's needed as well is the relative point of origin of the new coordinate system, and that's what the T vector contains.
Most of the time _x = _y = _z = 0 and w = 1
Transforming a point of geometry happens by multiplying the points vector with the matrix. Let such a matrix be M, the point p, then
p' = M * p
Now assume we chain transformations:
p'' = M' * p' = M' * M * p
We can substitute M_ = M' * M, so
p'' = M_ * p
It's easy to see, that we can chain this arbitrarily long
To answer your two last questions: Transformations (not just translations) do chain. And yes, applying a scaling transform will "scale" the axes.
And to clear up some commong misunderstanding: OpenGL is not a scene graph, it does not deal with "objects", but just lists of geometry. glScale, glTranslate, glRotate don't transform objects, but "chain up" transformation operations.
someone with more experience will surely point you to a good tutorial but your question reflect that you don't understand the 3D graphical pipeline and more precisely the concept of projection matrix (I might have the wrong name here since I studied this ages ago in French lol).
Basically whenever you apply a rotation/translation/scaling you are modifying the same matix
therefor when you each operation modifies the existing state.
For example doing rotation then a translation will give you a different result that translation then rotaiton (try doing the solar system sun earth moon it will help you understand)
regarding your questions:
No the basic rotation will not always occur in 0,0,0. for example if you first translate to 2,3,4 then the rotation will happen in 2,3,4.
the simple answer is yes, you are moving your matrice form its last position.(read my comment at the end for the not the simple answer ^^)
scaling will affect all the transformations done after. example scale 1,2,2 followed by a translation 2,3,4 could be seen as a a global translation 2,6,8
now for the not so simple part:
as explained each change will be affected by the previous changes (example of the scale)
also there is a lot of ways to do the same thing or to alter the behavior, for example:
achieving absolute translation can be done like this
-translate
-create an object
-indentity (reset the matrix to 0)
-translate2
-create object2
My advice is read tutorials but also global 3D programing blogs or a book (red book is good when you start lol)