Timing, threads, the Field Management System, etc

I understand the basics of how FMS interacts with the robot. It calls the init function when it enters a new phase, and it calls the periodic function periodically while in the phase. I’ve read that it calls the periodic function every 10 milliseconds, but I don’t know if that’s correct, and I’ve never timed it.

Now what I’m wondering is what really happens if you do things that take up a lot of processor time, or perhaps just clock time.

For example, if you write in teleopPeriodic()
{
Thread.sleep(15);
}

when will the next call be made to teleopPeriodic() be made? 15 milliseconds after the last one? 25 milliseconds?

And then there are threads. There are certain things that come up that I know must be multithreaded behind the scenes. (E.g. PIDControllers)

Can I set up my own threads? There’s a control loop that I am thinking about using, but 10 milliseconds between updates is too slow. If I just launched a thread to take care of that, could I update it much faster?

And loops? What if I code an infinite loop in autonomous.

autonomousPeriodic()
{
while (2+2==4)
{
System.out.println(“I am still in autonomous”);
}
}

Will that mean that it will run forever, and teleopInit() will never be called?

All right, those are very general questions that could have very complicated answers. What I am really looking for is a document from FIRST/National Instruments/WPI that addresses the issue.

In case it matters, we use Java with the Iterative robot model. I probably ought to use the command based robot model, but iterative was actually closer to what I wrote professionally, so that’s what I do, and what I have taught since our team switched from Labview. I have now begun wondering if I am projecting my professional code behavior onto Java. Professionally, I wrote code that had to execute in a short time window because it was called periodically in an interrupt service routine. I’m not sure that’s how the FMS/robot code actually works, and am wondering what would happen if I spawned a thread in teleopInit() that would go out and read my sensors and update motor speed more often than the default calls to teleopPeriodic().

The robot calls periodic methods at 50hz. You can start your own threads if you want. If you write an infinite loop in a periodic method, then no other control methods will be called, as there is a single main thread that reads FMS/input and calls control methods. If you have code that takes too much time, the robot well wait until that is finished. If you want more control over timing, such as faster updates, look into the SampleRobot class.

The good news is that the WPI Library is all open source, so you can look at all the bits and pieces to find many of the answers to these questions.

You can find the source code at usfirst.collab.net.

There isn’t a set rate at which the periodic methods are called.

If you open up the IterativeRobot class, you will find that there is a big infinite loop that checks which state the robot is in, and calls the appropriate periodic method whenever nextPeriodReady() returns true.

As per the documentation on that method, this happens whenever a packet is received from the driver station, which should be about 20ms (50Hz).

Assuming the robot received a driver station packet during that 15ms that your code was sleeping, it will be called again immediately (well, as soon as execution can iterate through the control loop again).

Note that the DriverStation instance used by the RobotBase to update your robot polls for updates in a separate thread, so sleeping in your code won’t delay any incoming packets from the DS.

Yes you can create your own threads. Just as you would in any other Java program, you can simply create a new Thread object and call start() on it.

As to whether you can make it update much faster- maybe.

First, I urge you to consider the physical limitations of your robot. Even if your code is attempting to do something every 1ms, can your robot physically do anything meaningful in that time?

Second, your motor controllers have technical limitations as well. Here’s the Talon SRX documentation. The default control frame period (i.e. the maximum rate at which it accepts new input) is 10ms (see section 20.6), and the smallest you can go is 1ms. However, going down to 1ms increases CAN bus utilization by 15% (see section 16.23). There are tradeoffs to consider here.

You also have to consider RoboRIO CPU usage- the faster one thread is updating, the slower another thread is updating. Watch your CPU utilization carefully to make sure you aren’t starving more important threads of resources.

Yes that code will run forever.

Note that motor controllers may have certain safety features enabled (see here). In addition to individual speed controller watchdogs, there is a system watchdog that will disable all output if it doesn’t get feed for 125ms.

Regarding while loops:


while (2+2==4) {
    System.out.println("I am still in autonomous");
}

If you’re ever watching a match, and a Java/C++ team moves/does something in autonomous, but doesn’t do anything during teleop… most of the time it’s because they have a while loop checking for some condition that didn’t occur that they thought would occur.

While loops are evil in robot code. Prefer not to use them (with a very few limited exceptions).

Threads are also evil (once again, with exceptions) in robot code and when used improperly can lead to difficult to diagnose bugs, prefer asynchronous constructs such as state machines instead.

^^ true

A good mental model is to imagine there’s an implicit loop around your code that gets invoked each time a packet arrives from the DS. You don’t want explicit loops that repeatedly check for something as well.

You should also assume that you don’t know how long it will be between invocations of your code. You can see this by logging the actual time between invocations – that value will have quite a bit of jitter when tethered, and will be much more jittery during an actual match.

I sometimes think it would be good to add a small amount of artificial jitter to WPILib, to better simulate real conditions.

Thanks to all. I’ll put you all in for the Gracious Professionalism award.

In all seriousness, these were some very helpful replies, and answered some questions I have wondered about.

Threads are always evil. Way too easy to put yourself into a deadlock/livelock or put your code in an “impossible” situation with bad use of concurrency constructs. Really the problem is mutable state + threading + bad use of concurrency constructs.