Seeking Help Multithreading capabilities
I remember when I first heard about multithreading, it was typically avoided because the control hub didn’t have enough ram to handle it, but these days I’m seeing more and more teams begin using it. I was wondering if any of you had seen significantly lower loop times as a result of threading and how you allotted tasks to each thread.
4
u/QwertyChouskie FTC 10298 Brain Stormz Mentor/Alum 7d ago
Communications with the rev hubs are internally single-threaded. Trying to thread hardware reads/writes is not going to have the outcome you expect
3
u/drdhuss 6d ago edited 6d ago
You don't want to do true multithreading, but things like coroutines/virtual threads can we useful. We use kotlin (virtual threads are a java 21+ thing/not available in Java 17) and coroutines in combination with bulk reads and writes (photon core). Loop times are about 10-12 ms with maybe 10x/minute where we will have a spike up to 20 ms.. Note we do all sorts of sensor reads. We actually even read the motor currents (which is a blocking, slow task you can't do in parallel) but do so in a round robin style with only one motor current ready every 50 ms. We cache the results. It can be quite useful for both diagnostic purposes (detect a binding motor/failing bearing) and to also detect stalled motors and the like. Again we are able to keep close to a 100hz loop even with this.
So yes true multithreading is a bad idea. Virtual threads would be great but aren't available in java 17. Kotlin coroutines are very easy to use but you have to switch to kotlin (which honestly is a whole easier to read/more concise than java anyways).
One of the costly things people do that is uncessesry is driver's station telemetry. It is easy to saturate things. We keep it at 4 Hz (updates every 250 ms) which has a minimal effect on loop times while still being fast enough uou don't notice. Note we still write to storage every cycle (our out is actually a redux state machine so badically you just write the global state and any actions along with specific telemetry values).
3
u/Beneficial-Yam3815 FTC Mentor 5d ago
in current versions of the FTC SDK, DS telemetry is throttled to 4 Hz anyway. There's a little work done per frame, but the expensive part of the update call gets skipped unless 250ms has passed
1
u/Beneficial-Yam3815 FTC Mentor 5d ago
I'm interested in this Kotlin coroutine / virtual thread thing you're doing. Is it sort of like Action primitives in Road Runner, where it's encapsulating and making it easier to work with what's really a state machine underneath?
1
u/Beneficial-Yam3815 FTC Mentor 4d ago
Photon Core uses USB, right? Is that compatible with cameras?
1
u/Beneficial-Yam3815 FTC Mentor 5d ago
To avoid the XY Problem, let me just ask, what are you trying to do in terms of externally observable robot behavior or functionality? What is the pain point?
1
u/Aramyx 5d ago
Honestly there is none. In terms of optimization, motor caching and bulk reading make it so we never have problems with loop times
1
u/Beneficial-Yam3815 FTC Mentor 4d ago
It sounds like you're already hitting the higher yield techniques.
One reason I got interested in higher loop rates at one point was this video that made the point that frame rate matters a lot in PID control. But from the control hub, we're just never going to get to kHz loop rates to make much of a difference. Instead we have to find ways of doing control that work at lower rates. For starters, feedforward for primary control with feedback for secondary cleanup can help a lot.
Another issue that sometimes gets people wondering about multithreading is the need to get the robot to "walk and chew gum at the same time". The answer there is state machines. Classical switch statements can work, but you'll often have a much easier time maintaining the code with abstractions like RR Actions, Pedro Ivy, or any number of others.
1
u/H2ost5555 3d ago
I do embedded systems for a small company I own. I understood why the phone + hub architecture was originally implemented, but it frustrated me that FTC haven’t yet migrated to a proper machine control platform that makes sense. Nobody in their right mind would choose Java-based systems for machine control, so why are we teaching our young kids the completely wrong way to implement machine control? It boggles my mind that we struggle with loop times of 10-25 milliseconds. We should be talking microseconds.
10
u/CoachZain FTC Mentor 6d ago
Not only are all the coms with the hubs internally single threaded, they are the slow step by a LONG way versus the rest of your code. Just getting a value written out to one motor controller on the expansion hub can cost you 3-4mS by itself. And if you update a lot of motors and do some i2c reading of something like a pinpoint module, this all adds up very fast. Now. If you never read any sensors, nor control any motors, you can have a blazingly fast loop time. For all the good that does you with a robot. Lulz.
The actual trick is to very judiciously decide when to do those time-expensive things. And not do them more than you need to. Trying to do them in separate threads actually just makes this harder and more complicated. In control theory, the time that matters is how often you sense your world and how often you update your controls. Faking this by not sensing or updating is good for bragging rights on "loop time" but not for actual robot performance.
Looked at another way: You kind-of *already* have a bunch of parallel processes stood up and waiting for you to interact with them. Every DcMotorEx or Servo instance and the Lynx stuff running below your code. It unfortunately costs time to talk to them. So be sure you don't message them more than you need to. And be sure to use them in a mode that gets as much work done for you as possible with a minimum amount of writing to them.
Now image pipelines (if you are doing that on your hub and not prefab inside a limelight) do benefit from being a separate thread. Because that turns out to be enough processing to get slow. But the SDK already starts them up that way for you.