Swerve VI based on the 2011 paper & excel by Ether

So I wanted to do the math in this paper/spreadsheet: Paper: 4 wheel independent drive & independent steering ("swerve") - CD-Media: Papers - Chief Delphi in LabVIEW but some of the output values don’t match up to the spreadsheet, and only when I try to add in a non-zero clockwise rotation input. I based the calculations on the paper: http://www.chiefdelphi.com/media/papers/download/3028.


Swerve excel #1.JPG

Swerve excel #2.JPG




Swerve excel #1.JPG

Swerve excel #2.JPG

You clipped your wheel speeds instead of normalizing them.

Clipping the wheel speeds makes them no longer kinematically correct.

See the discussion of normalization on page 2 of this doc.

You are running a lot of calculations in parallel. Some rely on others. You may want to double check that.

Maybe so, I’m not a LabVIEW guru. But his immediate problem (as shown in his third attachment Swervw VI FP#2.JPG), is that he is clipping the wheel speeds instead of normalizing them.

*

clipping.png




clipping.png

Thank you Ether. I updated it as shown. It’s outputs are concurrent with those of the spreadsheet. However now adding a gyro angle seems to produce incorrect values. I would expect gyro angle 180 and FWD +1 (STR and RCW are 0) to produce a wheel angle of 180 not 126.76. I would also expect gyro angle of 179 to produce something close to that instead of -175.94 for wheel angles if this LabVIEW implementation were correct.

Also the values are computed in order, that is to say that if D is equal to CPi and C = AB it will wait to get A & B compute C, compute C*Pi then give D. Nothing happens with D until everything it is dependant on happens. Or that’s what I observed at least.





Ether’s worry about clipping vs. normalization is valid, and you do need to correct your code to handle it properly, but I don’t think the input values you’re testing would cause an issue in this particular case.

Because you have separated the processing steps into disconnected parts, they will not necessarly run in the order you expect. The output-clipping section will use whatever values are in the local variables, whether or not the speed-mixing section has completed and updated the variables first.

Your first task in correcting the VI is to eliminate all local variables. Let LabVIEW do what it does best. Instead of storing a value in “A” somewhere and reading “A” elsewhere, just run a wire from the source to the destination. You can – and should – wire a single source to multiple destinations.

Then you can implement the necessary normalization. It’s a lot easier than you’re making it look, and it’s even easier in LabVIEW than the C code implies.

Finally, you can do some serious optimization of the math. There’s no need to have four copies each of “180” and “π” when converting from radians to degrees. You can just do the 180/π division once and multiply each angle by that result.

From my knowledge of LabVIEW, I think it’s pretty much impossible to be sure of the order of the calculations will happen.

You should try to do it without the variables.

I admit the locals were mainly to help me understand it better while I was writing it the first time and are clearly slower than wiring it. I also think it looked a tad cleaner. As for the execution, I get the same values either way. That said I thought things waited for their dependencies after watching it run while “Highlight Execution” was on and all the values looked like they updated after their dependencies did. Though I shouldn’t have assumed.

The way that he has his code structured, that is correct. To sequentially compute his solutions, he needs the different structures connected in some way. There are many ways to do that. He can put the dependent calculations in a flat sequence structure. Or he could ‘cheat’ and throw them in while loops that are connected by wires, with each while loop only executing once. Or he could enclose each ‘group’ in a descriptive VI, and wire them together using the error wire so they execute in sequence.

Just a quick comment on the difference between variables and wires.

A wire transfers a data value with sequencing. Variables transfer a data value without sequencing.

Another way to think of this is that wires do not have a value until they are written to. Once written to, the wire will propagate the value to all consumers, retaining it as long as necessary for the consumer to begin execution – at which point it no longer has a value. Variables, global and local alike, have a value at all times. Before being written to, they have a default value. There is no waiting or sequencing. They always return their current value.

Another difference is that a wire can have only one writer and all other connections must be readers. Variables may have many writers, many readers.

Multiple writers and lack of expected sequencing are the cause of race conditions in parallel programming. Wires have neither of these issues.

As for determining the order of execution of code such as this. Since these are simple expressions, the LV clumping algorithm will turn each section of code into a series of instructions, there is no need for data flow scheduling for simple operations. Likewise, there are no loops, no asynchronous operations that would benefit from multiple cores. So the compiler will arbitrarily determine an order and the end result will be the same each time … until the code is regenerated. Since you didn’t specify any preference, the compiler is free to reorder the code according to memory optimizations, cache alignment, order that the code was written in, or phase of the moon.

Hope this helps.
Greg McKaskle

Are you still having this issue with your code, after making the sequencing changes that other posters have recommended?

If so, here are the intermediate values you should be getting. This may help you track down where the error is:

L=24 W=24

FWDfc=1 STRfc=0

Q=180

FWDrc=-1 STRrc=1.02068e-011

RCW=0

A=1.02068e-011 B=1.02068e-011 C=-1 D=-1

wa1=180 wa2=180 wa3=180 wa4=180

ws1=1 ws2=1 ws3=1 ws4=1

ws1n=1 ws2n=1 ws3n=1 ws4n=1

I would also expect gyro angle of 179 to produce something close to that instead of -175.94 for wheel angles if this LabVIEW implementation were correct.

For FWDfc=1 STRfc=0 RCW=0, a gyro angle of +179 results in wheel angles of -179* (both gyro and wheel angles begin measured clockwise):

L=24 W=24

FWDfc=1 STRfc=0

Q=179

FWDrc=-0.999848 STRrc=-0.0174524

RCW=0

A=-0.0174524 B=-0.0174524 C=-0.999848 D=-0.999848

wa1=-179 wa2=-179 wa3=-179 wa4=-179

ws1=1 ws2=1 ws3=1 ws4=1

ws1n=1 ws2n=1 ws3n=1 ws4n=1
  • note the last paragraph in the paper where reversing wheel speeds is discussed

If you posted your code as VIs or VI Snippets, it would be much easier for people to help. Code (of any type) is hard to debug visually.

VI Snippets: Yes.

VIs: No. Cannot view them without LabVIEW installed.

Thank you for the numbers I was able to see the error was in the gyro correction of the joystick inputs. The gyro angle input is supposed to be in radians. I looked at the WPI holonomic VI and realized this. The sequencing changes (going to all wires) didn’t change any output values which is why I was confused.

Also do formula nodes execute faster than the equivalent code on the block diagram?

When you get this working the next step is to determine the shortest path to turn including a change of drive rotation. You will need a working prototype to Do the steering pids. We have so far not closed the loop for drive velocity.

They will generally be the same, however some operation such as arrays may actually take longer in the formula.

The formula and the nodes both feed into the same compiler, but with different front ends. By comparison, FPGA VIs start with the same front end and finish with different back ends.

The front end may need to parse text in order to identify types and operations. Or for nodes, it simply visits the nodes since this is already determined in the editor. The generator will then produce intermediate code for the representations, and then it will send the intermediate code through the LLVM back end.

I did a quick measure averaging random numbers and saw that the scalars were almost the same. The formula for accessing elements of four different arrays and storing in a fifth was about 50% more expensive in the formula node. I personally wouldn’t read too much into that, since I was using the indexing tunnels on the LV diagram, so that probably gave the diagram simpler code to generate.

Generally, I try to write code that is the most understandable. I measure performance quite often, but I try to achieve it by using the proper algorithms, understanding what the major operations are in the algorithm, then make minimal edits or data structure changes in order to gain performance. Sometimes the more understandable code is a formula, sometimes not.

Greg McKaskle

Now that I look more carefully at what you’re doing, I see you are running this in a loop. After two (or three, in your original code) iterations, all the values are guaranteed to have settled down. But if you were watching closely, or logging values each time through the loop, you might have noticed a glitch in the output whenever you changed input values.

I thought this was a subVI that you were calling in an actual robot program. If that had been the case, the glitchy output values could have shown up as undesired operation of the robot.