 # MATHS help reqired for HSI project.

Hi folks.

I bought a “neopixel ring” from adafruit and for now have a BASIC accelerometer to do X Y and Z. Though Z isn’t used.

The ring has 24 LEDs in it and I am trying to get the code working.

My early efforts were rather bad and I realised that I should first calculate the ROLL, then then display the ROLL and PITCH later.

The PITCH is a formula that is pretty easy, but when I was then trying to do the ROLL it became difficult.

So I did it the other way around and did the ROLL first.

The maths I am using is basically this:

Y = ROLL (in degrees) (kind of)

There are 16 LEDs in the loop.

So 90 / (16/4) is how many LEDs do 90 degrees.

When LEVEL, LEDs, 4 and 12 should be on.
This value is added as you will see.

Or: 90 / 4

22.5

ROLL_LED = (y/(90/4))+4
ROLL_LED2 = ROLL_LED + 8

That should make sense and it seems to work.

Then I include the PITCH which is similar to that.

Instead of writing it, it is in the code, but I would like to say here what I am doing and why.

With that “working”, the pitch is “added” to one LED and “subtracted” form the other one.

Yet, LEDs 3 and 13 are mostly on.

Tilting the sensor I can sometime only quickly/briefly get 4 and 12.

Nose down it is 5 and 11.

I’m guessing I am goofing in the formula somewhere.

There is also an CALx and CALy value.

That is because I noticed that the values went above 90 before going down again.

the accelerometer is the 7361 board.

Anyway, help would be appreciated.

my V4b.zip (2.82 KB)

Just touching base.

As far as I know/understand/believe, I have uploaded the sketch.

I don't understand why I got an e-mail reply saying someone had responded but there are no replies I can see.

There is/was a problem with this site about replies.

After a few tries, I still can't grasp what you're trying to do. It looks like this:- read an accelerometer, - make some calculations, and - turn on some neopixels based on the results. Beyond that, I'm befuddled.

Here's something, though it's almost certainly not the primary problem. In the sketch, I find this in loop():`  x, y, z = attitude();`and this in attribute():`  return (x,y,z);`It looks like that code is intended to return three values from attribute(). That won't work - C++ allows a straightforwared return of only a single value from a function. I'm fairly certain that code doesn't do what's intended. The function appears to work, because the values x, y and z are global values, and they're directly modified in attribute(). But, they're not returned to loop(); in fact, only z is returned. To see it break, use local variables rather than global inside attribute(), return them, and watch what happens to the values of the first two variables.

I can't be certain, but I don't see any evidence that you've tested the various tasks that you're trying to program separately. Things like: - reading the accelerometer, printing the results to the terminal, and confirming that they match the expected results based on the accelerometer's orientation - running calculations on known test data rather than the accelerometer's output, printing those, and observing that the results are what's expected, and - running the display routines on known test data, and observing that the expected lights come on while the others stay off. As things stand, there could be problems - maybe more than one - with any of those tasks. If you test in smaller pieces, there will be fewer hurdles to get past in order to help. Right now, we have to decipher the post - something I haven't been able to do - analyze the program, find the libraries, analyze them, and get up to speed on this particular accelerometer. The list of people who will do that is a short list; I'm not on it.

Can you post a minimal sketch that compiles, and illustrates a specific problem, and then describe specifically what you expected, and what you got?

Thanks TMD3.

I am silly expecting it to work with three returns - but I'll plead ignorance to that.

I thought it could be done, but can't.

The sketch worked by virtue that the X Y and Z were global as you pointed out.

I thought there was a little "display data" part where it serial.print() the data.

That is working fine enough that as I PITCH and ROLL the unit, the numbers reflect that happening.

I'll clean up the "wiping" part with a loop() to clear ALL the LEDs. That was suggested by someone else.

The idea is to make a HSI (attitude indicator)

As you ROLL the two LEDs remain on the level.

As you climb, the two LEDs descend from their "level" position to (in this example) 5 and 11. then 6 and 10. then 7 and 9. 8 when vertical.

Likewise when you descend, but the other way.

I have learned that if X is 0.01 the value from the INT() will yield a 1 rather than a 0.

This is throwing the whole thing out the window as 0 is where I want to be and have it shown.

When I get that part working, I will make the brightness of the adjacent LEDs turn on as you are between the values of the resolution of the loop.

I think each LED is 22.5 degrees from memory.

So for instance: at 12 degrees PITCH up: LEDs 4 and 5, ad 12 and 11 would be glowing. But for now that is WAY in the future. I really only get two days a week to look at this and alas recently not even that as LIFE() is a bit busy just now.

I have tried several ways of doing the function to no avail.

Hope that helps you understand what I am wanting to do.

Oh and I just realised that I said the ring has 24 LEDs.

Sorry, it is 16.

Ooops.

lost_and_confused:
Just touching base.

As far as I know/understand/believe, I have uploaded the sketch.

I don’t understand why I got an e-mail reply saying someone had responded but there are no replies I can see.

There is/was a problem with this site about replies.

Sorry, that was me. I didn’t see the attachment until after I’d posted my comment. I then deleted the post.

That code you posted looks like Matlab or something. No C/C++ code looks anything remotely like that.

That accelerometer is an analog device. The “standard” example codes for it, return an integer for each axis, apparently normalised to 100 representing 1g.

You mentioned that you are not using the z axis, so I assume that you have your x and y axes horizontally and your z vertical. If you have your x axis pointing “forwards” and your y axis pointing sideways, then, for very small angles of pitch and roll, the x and y readings will represent 100 x the sine of the pitch and roll axes, in radians, respectively.

Note the limitations here. That calculation will only work if the device is not actually accelerating anyway, and also not subjected to a rotational acceleration. It’s going to work for tilted objects, not spinning objects. And it is not going to work for large angles of pitch and roll.

You also need to be clear on what part of your calculations require integer and floating point arithmetic. If you don’t want to use floating point types, you can work around that but you need to be clear on what you are doing.

your statement that int(0.1) results in 1, seems to be wrong. Actually, to the best of my knowledge, C doesn’t even have an INT( ) function. Matlab and Fortran do… You can cast numbers to an int, like this

``````float f = 4.7 ;
int i = (int) f ;                                  //  the result of this is (integer) 4
``````

but that is not actually a function call.

This is how you would calculate the pitch and roll, representing the tilt of your device in degrees, where the x and y directions are oriented forwards and sideway, and z vertical, provided that the device has no accelerations and therefore that the numbers coming from the device represent only the static effect of gravity, and that the numbers are integers normalised to 100 = 1g :

``````int x ;  // accelerometer reading
int y ;  // accelerometer reading
int z ;  // accelerometer reading

void loop()
{
x= accelero.getXAccel() ;
y = accelero.getYAccel( );

float  pitch_sine = ((float)x)/100.0 ;       //  this is the sine of the pitch angle

//  Check that the sine of the pitch angle is within the legal range between -1 and 1
//  If either part of this section is true,  it means that the device has a substantial actual acceleration,
//  or that the device is poorly calibrated.

if ( pitch_sine > 1.0 ) pitch_sine = 1.0 ;
else if ( pitch_sine < -1.0 ) pitch_sine = -1.0 ;

float pitch_radians = asin ( pitch_sine )   ;             //  calculates the pitch angle,  in radians
float pitch_degrees = pitch_radians * 57.3 ;         //  convert result to degrees, multiply by 180/pi

}
``````

For SMALL angles of pitch and roll, less than maybe about 20 degrees, you can use the approximation that the sine of the angle ( in radians ), is not very different to the angle in radians. Therefore, you could use this calculation

``````int x = accerelo.getXAccel( ) ;              //  x direction component,  normalized to 100 = 1g
int pitch_degrees = x * 57 / 100 ;
``````

This calculation uses no float math at all. However, it will overflow the arduino 16 bit int if the value of x exceeds about 55 . To be clear why this is, the product x times 57 must be less than 32767, the limit for ints.

However, this approach is only valid for the range of tilt for which sine(y) is approximately equal to y ( in radians ) , which is for angles less than about +/- 20 degrees, depending on your tolerance for “approximately”. If you are using this model to measure the range of tilt for which it is appropriate, then the overflow would not be an issue.

I was going through the code and found a couple of mistakes. My eyes are obviously failing more than I realise.

I’ll have to look into how you said I should apply what you said.

I thought the values given from the accelerometer were the DEGREES of PITCH/ROLL.

Anyway, this is my latest code and for the sake of prototyping, it works.

I pitch the unit, the LEDs do what I want/expect.
Same with ROLL and both PITCH and ROLL.

The problem is the values given go from 0 to +/- 100. So I am guessing that is because I am reading them as DEGREES and not doing what you said.

I need a bit of help with “tweeking” it but thought for the sake of it, I would post it here and see what others have to say.

Now, on to the “variables”.

I don’t know if this “language” does support multiple variables on a function, but it is weird how, when I made them ALL global and removed all the stuff in the calls, it was annoyed with me and I really had to do some re-coding.

From there I went back to making them “local” and got it working.

Be it “accepted” or not: It works.

I know also that I am not changing the names of them in the function, as they are already have the names, IT WORKS.
And that is really ONLY for the serial_data part which is ONLY for debugging anyway.

I did it like that so when I am done debugging, I just delete the call and the tab, rather than having to go through the code and delete all the “serial.print…” lines.

Where I am at now:

Well, it is determined that EACH LED is 22.5 degrees resolution.

“Straight and level” the correct two LEDs are glowing.

If I go to 12 degrees (pitch up for instance) I would like the two LEDs above the main two to come one at a reduced brightness.
Maybe proportional to the MOD(22.5) value.

I can sort of work out how to do that, but not when it is pitch DOWN.

I haven’t even got into the ROLL side of it yet.

So I wouldn’t mind a bit of help there please.

See attached.

V5.zip (2.56 KB)

I am still stuck.

This is a text file showing the output.

Granted the unit could be faulty, but I can’t say for sure.

Anyone mind helping?

Attitude data.txt (7.23 KB)

You need to read all three values x, y, z. Then calculate the angles properly using trig:

``````pitch = atan2 (x, z) ;
roll = atan2 (y, z) ;
``````

Hi Markt.

This is what I tried in reply to mitchinyon’s post.

``````//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  New code here.///////////////////////////////////////////////////////////////////////////////////////////////////////////

//  Roll.
float  roll_sine = ((float)y)/100;       //  this is the sine of the pitch angle

//  Check that the sine of the pitch angle is within the legal range between -1 and 1
//  If either part of this section is true,  it means that the device has a substantial actual acceleration,
//  or that the device is poorly calibrated.
if ( roll_sine > 1.0 ) roll_sine = 1.0 ;
else if ( roll_sine < -1.0 ) roll_sine = -1.0 ;

float roll_radians = asin ( roll_sine )   ;             //  calculates the pitch angle,  in radians
float roll_degrees = roll_radians * 57.3 ;         //  convert result to degrees, multiply by 180/pi

int(_y) = (int) roll_degrees;

Serial.print("y: ");
Serial.print(y);

Serial.print("    _y: ");
Serial.print(_y);

//-------------------------------------------

//  Easy/quick way to X value.
//   int(_x) = int(x * 57 / 100);

//  Pitch.
float  pitch_sine = ((float)x)/z;       //  this is the sine of the pitch angle

//  Check that the sine of the pitch angle is within the legal range between -1 and 1
//  If either part of this section is true,  it means that the device has a substantial actual acceleration,
//  or that the device is poorly calibrated.
if ( pitch_sine > 1.0 ) pitch_sine = 1.0 ;
else if ( pitch_sine < -1.0 ) pitch_sine = -1.0 ;

float pitch_radians = asin ( pitch_sine )   ;             //  calculates the pitch angle,  in radians
float pitch_degrees = pitch_radians * 57.3 ;         //  convert result to degrees, multiply by 180/pi

int(_x) = (int) pitch_degrees;

Serial.print("   X ");
Serial.print(x);
Serial.print("  _X ");
Serial.print(_x);

Serial.println(" ");

//-------------------------------------------

//  Work out the LED numbers now.
int(_RollLED1) = (_y/(CALy/QL))+ZVO;               //  So this LED is set.
int(_RollLED2) = _RollLED1+QL+QL;                 //  This LED is half the circle offset.

int(_PitchLED) = (_x/(CALx/QL));                 //  Calculate how much Pitch there is.
int(_PitchLED1) = _RollLED1 + _PitchLED;          //  Apply to one LED
int(_PitchLED2) = _RollLED2 - _PitchLED;          //  Apply to opposite side LED

//  End new code./////////////////////////////////////////////////////////////////////////////////////////////////////////////
``````

But from memory the LEDs went crazy.

I shall post the data at some other stage.

I’m not sure if you formula is “Better” or “Worse” than the one here, but I am confused why the maths part doesn’t seem to work.

I fully admit in my earlier codes it WAS me who goofed with the formula.
Typo’s. What can I say? It sort of works, but not fully, and I do agree that the problem is something to do with what I am doing with the numbers.

I shall try your way soon and see what that does for the problem.

``````   int(_y) = (int) roll_degrees;
``````

What do you think this code is doing?

PaulS,,

Well this is what is going on:

I has my code and all the numbers were declared as INT.

Someone helped me with a problem and explained that I needed to include the Z axis and showed me a formula to resolve the "correct" angles.

Alas this required FLOAT( ) which isn't compatible with INT( ) numbers (as far as I know)

My code was all written with INT( ) values.

That line put the integer value of "roll_degrees" into the variable _y for later use in the code I had written.

Yes, I am probably wrong in what I did -or there is an easier way of doing it.

That is why I am posting and asking for help.

For now I have given up on it as I am getting no where fast and I am now working on another project for a while until I can better understand what is going on.

That line put the integer value of "roll_degrees" into the variable _y for later use in the code I had written.

No, that is NOT what it is doing. The Arduino team did you NO favors by creating those stupid macros that hide how a cast works.

A cast is properly performed by using the type to cast to in parentheses by themselves:

``````int y = (int)someFloatValue;
``````

Now, you should note that this cast is unnecessary, as an implicit cast would be performed in the absence of an explicit cast.

Who knows what the f**k that macro is doing. Since you clearly don't, you should NOT be using it. Get rid of ALL uses of float(), int(), etc. in your code. If you NEED an explicit cast (you don't), do it properly.

Ok.

I'll try it.

But don't be angry with me.

Be angry with the people with whom I got my initial help.

I can only know what I am told/taught. NOTHING MORE.

I was not born to understand this stuff.

But don't be angry with me.

I'm not.

Be angry with the people with whom I got my initial help.

My anger is directed at the Arduino team that thinks programming is trivial, and when they learn that it isn't, they develop stupid macros to hide things.

Then I shall have to admit that I do not understand a word of what you are meaning.

I have "developed" my programming methods from what they were back in the 80's with BASIC and a bit of REXX to what it is now.

Alas I do see that I have become a bit "macro"/"function" mad with some of my code.

This is probably because in the early days of this, I was told that you don't write "programs" but a bunch of functions to do VERY SPECIFIC THINGS.

These send SIMPLE variables between each other to tell when they have done their job or as a result of their job.

The "bigger picture" is that all these "blocks" work as a whole to produce the desired result.

Because I have never been taught C, C++, or any other language, I am SELF TAUGHT. I look at other's codes and TRY to understand them.

I take them apart and TRY to understand that (recursive error just happened there).

When I find code which does what I want, I try to use it - as is - to do other things.

I am lucky if I get 6 hours a week to do code. The only real time is on the weekends. I try to enjoy what I do and NOT waste time.

If I get stuck I ask for help. I don't want to be spoon fed, but when I COMPLETELY don't understand what is going on, it is frustrating for me even more than you could imagine.

Alas I do see that I have become a bit "macro"/"function" mad with some of my code.

Functions are one thing (good). Macros are "code" that the preprocessor substitutes in place of text in the sketch. They are evil, as far as I'm concerned.

The preprocessor finds int() in your code, and replaces it with (int). Now, why is that needed? It's so you can have a "function" that does what a cast does, without having to understand what a cast is. That is NOT a good thing. When magic happens without you understanding what is happening, bad things happen. Just ask Harry Potter.

@Paul + 1

Doc

Well, I have come a fair way with the sketcth.