Gyro angle in simulation vs reality?

The gyro in the simulation gives angles between -180 to 180 and when it reaches 180 it goes to -180.
Is it like this in real life gyros?
It makes it hard to make the simulation robot turn 180 degrees or more :frowning:

See this post: http://www.chiefdelphi.com/forums/showpost.php?p=1182626&postcount=18

There are also some other great posts in that thread on this topic.

Here’s the thread: http://www.chiefdelphi.com/forums/showthread.php?p=1182626

Ok, I understand now how to solve it.
Is it like this in the KIT gyro too?

The gyro is not the issue. The issue is how the code is interpreting and displaying the data that sensor is sending to it. For all intents, any gyro running this particular could would behave the same way.

If you want the values to continue to increase/decrease, then the code must be written that way.

The gyro is just a sensor. What the code does is take the sensors output an converts it into something useable.

Ok, so for my needs continuous increase/decrease would be the best ā€œconvertionā€ of the gyro volts readings.
Now, the default code is giving me +/-180 degrees range and I can’t change it so I’ll have to use it that way.
I’d like to have a vi that gets X degrees as an input and moves the robot X degrees CW, can I have some help with that?
If my current Angle is 90 and I want to move 100 degrees CW, I should end up at -170… I don’t know how to get to that.

I’m not sure which example you are using, but here is what I found.
If you open LV and click on ā€œSupportā€. Then select Find FRC Examples, you will find a Gyro example under the Analog folder.

The Get Angle vi returns two things: The current angle relative to where the robot started, and the current angular rate of rotation.

If you use the ā€œGet Angleā€ output, I believe you will find what you are looking for.
See the attached image.





Would you mind sharing with us what you are trying to accomplish and how you determined that a continuous increase/decrease sensor output would be best?

Now, the default code is giving me +/-180 degrees range…

Interesting. In Java, the gyro angle is returned as a continuous increase/decrease from the starting value:

    /**
     * Return the actual angle in degrees that the robot is currently facing.
     *
     * The angle is based on the current accumulator value corrected by the oversampling rate, the
     * gyro type and the A/D calibration values.
     * The angle is continuous, that is can go beyond 360 degrees. This make algorithms that wouldn't
     * want to see a discontinuity in the gyro output as it sweeps past 0 on the second time around.
     *
     * @return the current heading of the robot in degrees. This heading is based on integration
     * of the returned rate from the gyro.
     */

I’d like to have a vi that gets X degrees as an input and moves the robot X degrees CW

Is there a reason you always want to rotate the robot clockwise? In other words, if ā€œXā€ is 359 degrees wouldn’t it be better to rotate the robot 1 degree counter-clockwise?

That’s the vi I’m using, but it works differently in the simulator.
I think that the cRIO version of it would work the way I want to.

What I’m trying to accomplish is a vi which turns X degrees (rather than the shortest way to a certain angle), it doesn’t even matter in which direction because I get stuck whenever the (current angle + degrees to rotate) is bigger than 180 or smaller than -180.
(The problem is the same for "rotate the shortest way to angle X, but leave it for now)
I hope I’ve explained it well :slight_smile:

The WPILiib VIs should behave the same for a simulated, analog, and digital gyro. The conventions of the simulator were quite different and had to be manipulated in order to fit into WPILib, and it seems like you’ve discovered a bug.

If you select the gyro VI in your code and choose Edit>>Create SubVI, it will wrap the WPILib version in a simple subVI. Try to modify it to make the simulated one behave the same as real-world, then use that instead until the bug gets fixed.

Greg McKaskle

I can’t really change the code inside the gyro VI, I get to a level where I need to enter a password in order to make changes to the VI.
But i noticed that the angle value is coming from a a Honeywell Compass reference.
I don’t get it… why would the VI called gyro and inside it’ll be a compass?
The gyro output really does behave like a compass would.

It’s still not clear to me what you are trying to do. If it doesn’t matter which direction, what’s wrong with taking the shortest angle to get to the target?

Let’s say at some point in time your gyro reads ā€œgā€ degrees and you want the robot to rotate X degrees CW from that reading.

Compute target_angle = g + X at that point in time. Then use that target_angle in each iteration of your closed-loop controller as follows:

error = target_angle - gyro_reading;
error = error - 360.0*floor(0.5 + error/360.0);

Then set your SetPoint and ProcessVariable inputs to your closed-loop controller as follows:

SetPoint= error;
ProcessVariable = 0;

or

SetPoint=0;
Process_Variable= -error;

( choose one or the other depending on how your closed-loop controller handles d/dt(error) )

Once you’ve turned the desired X degrees, you can receive a new X command and compute a new target_angle and start the process anew. Or you can re-start the process if you receive a new X command before the previous one has completed.

Example:

The gyro reads 179 degrees and you want to rotate 10 degrees CW.

Compute target_angle = 179 + 10 = 189 degrees

first iteration:

error = 189 - 179 = 10
error = 10 - 360.0*floor(0.5 + 10/360.0) = 10
SetPoint= 10;
ProcessVariable = 0;

assume that the gyro angle increases by 2 degrees (from 179 to -179) and now look at the second iteration of the closed-loop controller:

error = 189 - (-179) = 368
error = 368 - 360.0*floor(0.5 + 368/360.0) = 8
SetPoint= 8;
ProcessVariable = 0;

… and so on until the target of 189 is reached (at which point the gyro will read -171 degrees).

Greg’s suggestion was to make your own VI that looked for a rollover from 180 to -180, and when that happened, add 360 to the gyro output. If you kept a running total of gyro rollovers, this would work forever. This doesn’t require any changes to the simulation.

The trick in simulation is to make something that isn’t real work ā€œclose enoughā€ to something that is real. In this case, most teams are interested in the angle from the gyro. Rather then spend thousands of dollars to make a computer model of a gyro, they used a compass that was already implemented, and produced very similar data. As you noted, it is different in the rollover, however that is easily compensated for.

*Here’s a possible implementation of Joe’s suggestion.

// initialization:
alpha=Go=getGyroReading();  // range -180 to +180

// iteration:
G=getGyroReading();
d=G-Go;
Go=G;
if (d>180) d-=360;
else if (d<-180) d+=360;
alpha+=d; // alpha is the continuous angle reading you've been asking for

Thank you! that’s what I was looking for.
My goal was to make the simulated gyro work like a real FRC gyro for new programmers training.
I’ve attached my implementation (this is in periodic tasks) :slight_smile:





I implemented the simulation code. The gyro should work the same as a real robot gyro. You should try a real one - if they differ I will fix the simulated. The reason you see compass VIs down within the simulated gyro is because I used subVIs that already existed as part of our robotics module (a separate commercial module that is not part of FRC). For simulation it doesn’t matter what is in the bowels of the code, only that the top layer acts like a gyro.

A real gyro accumulates continuously.

*At runtime if the result of the comparison circled in green is TRUE, does the LABview-generated code still perform the computations circled in red?
*
*





There are two types of data flow diagrams, pull and push.

The pull type works from the answer backwards. It determines what it needs in order to compute the result and back propagates to the next layer of what it needs to know. Therefore, it doesn’t need to calculate the red value.

The push type executes the other way around. It starts with what it knows and pushes it to the downstream areas until it has produced the result.

Because LV diagrams contain as much I/O as they do, it was decided to use the push model. This means that side-effects such as I/O in the red area will happen even if the results are not used. If you wish to avoid the red calculations, the technique is to use the switch/case statement.

The other way to determine this …

When a LV diagram executes, it executes all nodes it contains exactly once. That requires both red and green to execute. The case/switch simply decides not to execute one of its diagrams. The for loop, by contrast, executes its diagram N times. Hope that helps.

Greg McKaskle

Thanks for the informative post Greg.

Applying the above at face value as a general principle: if the portion circled in red was made into a separate diagram, then would that diagram not be executed if the portion in green evaluated to TRUE?

I suspect the answer is that it would still be executed, and the reason is that the green decision is in parallel with the red computation, whereas with the case/switch the diagrams are in series downstream. If that wording is awkward/incorrect, what’s the proper way to articulate this distinction, in LABview-speak?

One more thing: wouldn’t it be reasonably straightforward for the code generator to recognize these T/F decisions, and generate conditional code like the case/switch does? Or would that effectively mean abandoning the push model?

Let me elaborate on the statement I made about node and diagram execution. I don’t remember why, but had to cut that short.

When a LV diagram executes, it executes all nodes it contains exactly once. That requires both red and green to execute. The case/switch simply decides not to execute one of its diagrams. The for loop, by contrast, executes its diagram N times. Hope that helps.

Completing the thought …

The for loop and case/switch are nodes. They must execute exactly once. Normal nodes such as + represent a closed operation. There is no sub diagram to schedule for execution. The control structures in LV that bring additional scheduling structure to the language are the loop, switch, sequence, and subVI. Each of these executes by scheduling one or more subDiagrams in a structured fashion. This is the same as how a procedural language structure such a loop or case will manipulate the execution of its ā€œbodyā€ expression.

So to be more exact, the case/switch is a node and must execute exactly once for each execution of its owning diagram. It performs conditional execution by scheduling one diagram and ignoring the rest. A loop only contains one diagram, and it performs repetition by scheduling it multiple times. A sequence contains N diagrams and enforces sequencing by scheduling then in sequence. A subVI doesn’t look much like a flow structure, but it acts to coordinate the execution of the calling and called diagrams so that they behave as a procedure call.

While LV supports, promotes, and promises parallelism, it can’t change the fundamental characteristics of the CPU(s) it runs on. Code such as you’ve drawn, with simple operators do not benefit from parallelism. They are synchronous atomic operations and one of the optimizations LV makes is actually to serialize that code to avoid scheduling overhead. The ā€œclumpingā€ algorithm’s job is to identify when to use the parallelism expressed in your diagram and when to arbitrarily serialize it to improve performance.

The compiler does eliminate code and move code around to eliminate unnecessary operations. It has to be careful that the code is functional and has no side-effects. Also, realtime users would often prefer that we calculate both and not introduce jitter in the calculation.

Greg McKaskle