WPiLib Unit Library vs always SI units

Wpilib recommends that we use the units library in our code. However, it seems to make my code two times more complicated, especially when performing computations. I’m also concerned that the RIO’s 800mhz potato will get GC issues if objects are created and dumped repeatedly.

We use meters daily in my country, so I don’t get the point. Why don’t we just use radians and SI units throughout the code? There will never be a problem with the wrong units.

I know that imperial units are more commonly used in FRC than meters, but all we have to do is Units.inchesToMeters(), right?

Most odometry stuff is in meters, IIRC.

Radians are in units of radius. And the wheels have a 2 inch radius.

Such is life. :smiley:

1 Like

The units library really has two main purposes:

  1. Decrease the likelihood of bugs caused by unit errors, such writing down a value in terms of inches when the code that uses it assumes it’s in feet (or meters, in your case). Using units means your code will either be free of those bugs (if you use the built-in math functions, which Java does make unnecessarily verbose), or you’ll be forced to explicitly say in your code what units you’re working in (eg someDistance.in(Meters) * someAngle.in(Radians)), which makes it much easier to spot unit-related bugs.
  2. Increase visibility into what your numbers mean. This is particularly important for things like PID constants, where programmers often just punch in random numbers like 0.001 without knowing what they physically correspond to

It’s totally up to you if you feel these are useful for your team. Personally, I’d use it at the very least for storing configuration values (eg robot dimensions, wheel sizes, etc) and doing any math with those values converted to specific units.

If you’re worried about memory and GC impact, use mutable objects to avoid allocations; that’s why they exist.

4 Likes

I’ve always found it useful to just make the units part of the variable name and this there isn’t any confusion as to what you are working with, and you aren’t creating more objects than you need to

4 Likes

WPILib does this in a few spots. It makes the user code super verbose and hard to read (for example, chassisSpeeds.vx vs chassisSpeeds.vxMetersPerSecond in various expressions). I’m not sure it’s worth it.

2 Likes

On a PR you once stated that Java’s type system is a potato.

I use that quote with my 3rd level Computer Science class that’s learning Java all of the time when discussing more advanced features.

Crazy benefit of being a CS teacher and programming mentor. Though my curriculum kind of requires one of the 3 languages somewhere. Thankfully I can pick and choose.

Definitely makes things verbose. On our team we like to use a shorthand suffix for all our variables. For example:

_mps = meters per second
_s = seconds
_V = volts
_radps2 = radians per second per second

Takes a little to get used to at the start, often requiring a legend. But eventually just becomes trivial after enough reps. Might not be the best solution in the world, but it’s something to try if you don’t want to use Units

2 Likes

How do you recommend doing this? The documentation for it looks… more confusing than just having random, unitless numbers.

I’m a big fan of this Units implementation everywhere else, but the kP definition doesn’t feel natural.

Wait they aren’t supposed to be random guess and check numbers?

I know it’s related to the error and output but I didn’t think it had any unit associated with it

For kP at least, it’s volts per unit. I usually start tuning this by saying “what’s the minimum error I want to send 12V” and then do the math. If the answer to that question is 100 rotations, I say 12V = 100R * kP. Which means kP is 12V/100R, or .12 volts per rotation.

This is why Units are so important. If your error is in inches, you’ll have a relatively small kP, where as if that same error is actually in meters, you’ll end up with a very large kP.

12V = 12inches * kP; kP = 12V/12in = 1v/in
12V = .3meters * kP; kP = 12V/.3meter = 40V/meter

4 Likes

I feel compelled to point out these concerns can be proved real or imagined- “Go forth and do science!”. Tools like GCViewer can help profile how much work the garbage collector is actually doing in context, and lizzard for statically analyzing cyclomatic complexity. Lots of other tool possibilities too.

3 Likes

Personally, I perfer

double kP = 5/Math.toRadians(15)

it translates to

correction voltage is 5 when error is 15 degrees.

This also tells me what my PID constant means, and I don’t need the units library for that.

The proportional constant is just the ratio of the output effort to a given error in the measurement. For most FRC mechanisms, this looks something like the commanded voltage of a motor to a distance (for linear mechanisms like elevators and drivetrains), angle (for rotating mechanisms like arms and turrets), or a linear or angular velocity if you’re doing velocity control instead of position.

So you’d commonly see something like this (all these numbers are made up)

// Output 12 volts for every 4 inches of deviation from the setpoint
Per<VoltageUnit, DistanceUnit> kP = Volts.of(12).divide(Inches.of(4));

// Output an additional 0.04 volts for every accrued inch-second away from the setpoint
// kI is weird and doesn't really correspond to a physical property of the system like kP and kD
Per<VoltageUnit, MultUnit<DistanceUnit, TimeUnit>> kI = Volts.of(0.04).divide(Inches.mult(Seconds).of(10));

// To dampen overshoot, reduce output by 0.1 volt for every 8 inches per second of velocity
Per<VoltageUnit, LinearVelocity> kD = Volts.of(0.1).divide(InchesPerSecond.of(8));

Not quite: it relies on the reader inferring what the values mean from context clues in its usage sites. Using units would bake your meaning into the definition and leave no room for ambiguity.

1 Like

Thanks for explaining!

Also, do you mind if I ask a question irrelevant to this topic?

For double, there is Math.abs() and Math.copySign(). Is there a plan to have similar features in the unit library?

I just submitted a pull request to add abs() and copySign() methods to the units library and Rotation2d.

1 Like