Are loops ok in FRC Programming

Hi All, When i joined my team, I was told using loops was bad. Their reasoning for it is because teleop runs the code ever 20 milliseconds and a loop can hold that time up. So i have a couple questions? Is it bad to use loops when programming the robot. If so why is it bad? Also how does the FMS interact with the code.

Code inside of the Periodic functions in the main robot class will automatically loop every 20ms. In general it is probably a good idea to avoid using while() loops especially ones that don’t exit but for() loops can be very useful in a robot program.

7 Likes

While it would be wrong to say that loops are bad in every situation in FRC Programming, they should definitely be avoided when possible. This is especially true for any kind of control loop or other structure where you cannot guarantee it will exit, or exit swiftly.

A case where a loop would be acceptable, is where you want to perform the same action to an array of similar items, like say a sensor. If you have three sensors, have put them in an array, and want to zero all of them out. it would be perfectly acceptable in my eyes to use a for loop over the indexes of the array and zero out each sensor, or collect the data from them, ect.

An unacceptable case is where you want to reach a specific state, that could for any reason never be reached, or take an extended period of time to reach, like a control loop. You shouldn’t create your own while loops to say while not at a certain angle,turn left in place, because if anything went wrong, like the sensor being messed up, or a drive motor being backwards, it may take a while, or never reach the angle and the robot would be stuck forever or a long time in that loop, unable to respond or do any other actions.

Such control loops should either be in their own threads, or integrated such that they utilize the system periodic loop and give everything else their fair time to run as well.

3 Likes

Note that the RoboRIO has more than one core. If you start a thread, it can use the second core and then a loop can be useful. For example, maybe you want something to happen for vision in that other thread and it can loop checking if you are on target. Even better is a loop that yields/sleeps so other threads can run. (Not in your main control thread of course).

Another good use case for loops is during auton. (Assuming auton comes back.) There’s no need to be receptive to the controller then.

Also, do make sure you have an exit point. For example end the loop if you have been if more than a few seconds.

Said another way - the software architecture of the periodic methods in Timed or Iterative robot base classes is designed such that the periodic function must be run every 20ms to maintain proper control of the robot.

If the function itself takes too long, it will not be possible to call it every 20ms.

If you are to use a loop, you must be able to prove that it will never cause any particular iteration to take too long. This is easier to do with a for loop than a while loop.

Therefor, in almost all embedded code development, while loops are considered a no-no. Basically, if you can’t do what you’re trying to do in a normally constructed for loop, you probably shouldn’t be doing it in the first place.

True long-running tasks can be placed in a different thread, a programming construct which tells the operating system that pieces of code are allowed to execute in parallel. This can be effective, but brings additional challenges with thread synchronization.

To the FMS question - it can vary year to year. In general the FMS only controls your driver station, which in turn broadcasts control packets to the robot. It is up to your robot code to run fast enough to accept and respond to those control packets - one of the reasons for requiring that 20ms periodic update loop. FIRST does publish some details in the FMS Whitepaper.

3 Likes

Just to be clear, there are four different loops in Java: for loop (c style for loop), while loop, do while loop, and the for each loop. The for each loop is great because unless you hijack an Iterable, that loop is basically guaranteed to exit. Now, with for, while, and do-while loops, these are not guaranteed to exit.

I can agree with this. If you use a for loop to count to 10, it will exit. Doing the same thing in a while loop is less readable.

I don’t agree with this. There are plenty of places that you can use a while loop in without trouble. Such as an Iterator to append to a list while iterating through it. I’ll admit you won’t need to use the while loop very much in FRC applications (we didn’t use any in our code this year), but you can’t just say that using a while loop is straight up bad.

I agree with most of what with everyone is saying. Use for-each loops as much as possible, use for loops (c style) frequently and use while or do-while loops only when you need to.

1 Like

My use of weasel words “almost”, “basically”, and “probably” was purposeful. :slight_smile:

I do agree with you - there are plenty of ways you can use a while loop without getting in trouble. My point is that there are also ways where you will get in trouble - often in subtle and not predictable ways.

To simply say “No while loops” is just an engineering tradeoff to make - the code is a bit harder to write, but it reduces the code review effort and potentially prevents some types of bugs.

More often than not, it’s driven by history: If your team has had an issue using while loops before, they’ll likely be banned in the future.

However, if you have another mechanism of guaranteeing a predictable iteration count for every while loop (perhaps just code inspection if your source is small enough), such a hard-and-fast rule is not needed.

My comments are heavily influenced by this line of thought: code inspection gets harder as code gets larger and more complex. Auditing for all instances of the word “while” is comparatively easier for an arbitrarily large codebase.

1 Like

The real goal in FRC programming and real-time-ish embedded systems in general is to pay some attention to the temporal requirements. Paying some attention to time makes programming embedded systems much more complex than enterprise applications.

So can you use loops? That is really a syntax question. If you use them properly, making sure they cannot consume too much time, sure you can use loops. They are not of the devil! But you should NEVER structure your code in such a way that there is any chance of missing communications from the FMS. I think if you miss 3 messages you get shut down by the FMS!

Some of the app wizards hide the fact that you are already in a loop. Many posters in this thread allude to that when they say do NOT take more than 20ms. This is because the FMS communicates with the robot at a roughly 50Hz rate.

Properly designed loops are not such a big problem. But there are some classic problems that cause non-determinism (your “loop” taking varying amounts of time). Avoid doing memory allocation or formatted output in your loops that demand deterministic performance. That is tricky
in Java, less so in C++ and straightforward in C. This is one of the reasons
I tend to shy away from Java in embedded systems. So do not create new objects in your loops. Avoid calling printf or using cout or the Java equivalents. Memory allocation and formatted outputs are classic temporal problems. Create all your objects BEFORE getting into your tight loops. Use the smart dashboard libs (which really send messages) instead of printing things to the console in your tight loops.

Good luck!

3 Likes

//If you’re using a loop to do a
System.out.println(“ExplosivesLead2412 is awesome!”);
/*Then I think that is a very good use off loops.
Jokes aside, there are various uses for them. Like, when you have rollers for intake, you could use a while loop for the button to continually intake until the button is no longer pressed.
*/

This is not something you should do because it won’t allow the periodic functions to be run until the button is no longer pressed. This will also not work because the joystick won’t be able to update its button state.

Instead of

while(buttonIsPressed){
    intake()
}
stopIntake()

Do

if(buttonIsPressed){
    intake()
else {
    stopIntake()
}

You will have to run the code for that in a periodic function.

7 Likes

Definitely part of the question.

In addition, classical discrete-time control theory principles assume a constant sample rate. Keeping your loop times consistent is how you keep the math working right.

Same. FRC has this unique requirement of being able to teach students to code quickly, making Java a good choice from the learning perspective. However, we’ve had issues with tight timing on loops getting messed up by the garbage collector coming along and running, throwing off timings.

I’m old-school. C and C++ are where it’s at for production-quality real-time embedded system software development.

Indeed. This is exactly the case I would not use any sort of loop, because it would halt any other processing until the button is released. Unless of course this halting is inside some separate thread (as already mentioned). I would definitely state this explicitly if it was your assumption.

1 Like

Truth! WPIlib hides this from you by timing your PID loops etc for you plus leveraging the FPGA to help. But there are many teams closing servos etc in control loops and they need to be deterministic for the math to work out.

ExplosivesLead2412:

Like, when you have rollers for intake, you could use a while loop for the button to continually intake until the button is no longer pressed.

This is poor temporal design. Just record the button transition from ‘not pushed’ to ‘pushed’ and vice versa. There is no need for a loop in this case. Indeed it will kill you since by definition a button cannot be depressed and released by a human in less time than it takes the FMS to drop you.

Loops are okay in just about every Programming. (Asynchronous programming does exist but it is a very complicated topic.)

Most CPUs use loops. The Robot uses a CPU. (Like asynchronous programming, there does exist asynchronous hardware.)(There does exist asynchronous parts in synchronous CPUs but don’t worry about them)
There are two main types of input handing.
“Polling” - the good ol’ if(button.is_pressed){do.this} //This code is constantly executed again and again.
“Event Based” - thisProcedureIsCalledWhenButtonPressed(){do.this} //This code is only executed when that thing happens. But something else is likely looping again and again.

Polling is faster, and 100% reliable since you have scan code buffers in Windows and Mac and Linux and every decent Operating System.

At the heart of things, both will generally use a loop. They are virtually necessary for multitasking and doing stuff and whatnot. CPUs are fast and given the code you will likely write, you will not need to worry about execution time in general unless you are doing something super advanced. Or having weird detours like counting to a megamillion in the middle of the main loop for no reason. Remove the weird detours. As long as everything in the loop has a purpose and makes sense, you will not need to worry about optimization. Premature optimization is the devil. Like Internet Explorer.

The easiest thing to improve execution time is to use C++. Minecraft Bedrock runs faster for that reason :slight_smile:

I said virtually.
I know you can probably pull sommething off with CPU interrupts and redoing the Robot Operating system but you would probably lose more than gained in desigining a new Robot OS, reading the hardware programmer manuals and whatnot.

Loops are needed to control the robot. Even if you are using events, a loop is used to handle each event beneath the clockworks.

Extra But Relevant Stuff:

I have been learning how to program operating systems and “Event based programming” stems from CPU interrupts. Basically, an interrupt is a signal sent to the cpu to tell it to stop doing something and execute code to handle that event. For example, when you press a key, the Operating System will place the scan codes into a buffer which will be handled in the next loop cycle. It is technically possible to not need a key buffer in an OS, but you need to grant a certain program a lot of permissions and part of the program will be inside the kernel code. This reduces security, multitasking capabilities, and the interrupt code gets bloated.

Don’t know what half of those things mean? Don’t. There’s a reason why we have the almighty WPIlib(although their default differential drive is sub-par). And that’s the reason why we have operating systems and libraries to abstract all those details. Once in a blue moon, you might need to adjust one of the gears, but 99.99% of the time, you just want to add two numbers and put them in a variable… And 99.99% of the time, hand sanitizer works.

When i began programming, I was worried about getting the most optimized code. The best way to write code is to get messy and learn from your mistakes like they say in the Magic School Bus. You will write sub-optimal code, but you have gone a step farther if it does what it is supposed to do. And eventually, you will be able to optimize/refactor it. But you never will if you are too afriad to start writing code even if it is messy.

No. Loops are not okay in FRC programming.

Why is it so bad?

It is entirely possible for a loop’s exit criteria to not be met (for any number of reasons) and that can cause a robot to behave dangerously and erratically. If the loop exit condition is not met then the robot control will continue to do everything it had been assigned to do, which may include drive or actuate a mechanism. Until the code exits that loop the robot will not respond to any inputs or controls on the RoboRio. It might not even respond to the e-stop.

Learn from my fails.

1 Like

If your robot doesn’t respond to the E-Stop, there’s something wrong with something else besides your own code. NI has put enough safeguards in place (the watchdog) to stop infinite loops from making the robot do dangerous things.

There’s wrong ways to use loops, but that doesn’t mean you should avoid them altogether when it’s the best solution.

2 Likes

My experience strongly disagrees with this statement.

The lack of e-stop response was correlated to many ‘loop overrun’ warnings on smart dashboard. When we used any version of the code without the while loop we observed no issues.

When there was no mechanical issue that stopped the loop’s exit conditions from being met we observed no issues.

1 Like

There are some correct statements here, and some very incorrect ones.

I’ll speak for labview. Auto does not run in a loop. You need to use a single loop that contains your code.

Teleop, on the other hand, runs a loop once every time the robot receives FMS data. Around once every 20 ms. You should not be running loops in teleop.

Periodic tasks can be used to run your own loops. However, you can ABSOLUTELY max out the cpu on the Rio and should be critically careful of your loops, their frequency, and the amount of data and processing you are doing.

We often DO have to worry about optimization, based on cpu load.

You do not need to add additional loops to run your robot. The one that runs teleop is enough to make a functional robot.

The correct way to program is to follow conventions. They are there for a reason - usually to help you not make mistakes that have already been made by other people. Not to throw your code at a wall and see what sticks. All three languages in FRC have primers to explain their basic programming and conventions. And there are very knowledgeable people here ready to help.

1 Like

I’d avoid speaking in absolutes in cases like this.

If you don’t know/understand how to write a loop that exits, maybe it’s something to avoid (or learn). A basic for (int i = 0; i < array.length; i++){} or for (Object o : list){} is absolutely okay as long as it doesn’t take too much time. It all depends on the use case.

100% of teams on Houston 2019 Einsteins Finals with a primarily blue logo used loops :flushed:

My advice when teaching FRC programming to a group of people that are brand new to OOP/Java?
“You probably won’t need them for a while, but when you know you need to use one, you’re ready to learn to use one”

1 Like

That’s fair.

So I’m not talking about WPILib’s loop overrun here. I believe our robot code has loop overruns right now and we have no issues. Those warnings just mean that the loop is going over the 20ms time limit for an iteration to complete.

If you were to actually put a while(true) or Thread.sleep(1000) in your code, the code in the RobotBase class (I think) would not be able to call the HAL.observe*** and obviously other parts of your code wouldn’t update. Now, I don’t know how long you can wait before feeding it, but it’s way more than 20ms.

If the watchdog that’s at a lower level (not the WPI one) doesn’t get fed, I believe the robot gets disabled temporarily. It takes a couple of seconds before it disables, but it still disables. I believe the E-Stop button should always be immediate. If it isn’t, that’s something you should report to NI.

It sounds like your code is just spending a lot of time in one of your periodic functions. There are plenty of reasons this could be happening and using loops the wrong way is one reason for that. That doesn’t mean you shouldn’t use loops altogether. It just means you have to be careful when creating a loop that needs an exit condition.