Behaviours are Curtin FRC’s solution to the control problem. In a robot where many actions can occur at once, how do we make sure only one place has control of a system at a particular time? Moreover, how can we combine small elements of control into one much larger, cohesive functionality - such as chaining small autonomous actions together to make a full autonomous routine?
A Behaviour is a small action or piece of work that describes how to control a robot system or collection of systems. Examples may include:
Teleoperated control of the drivetrain
Automatic spinup of the shooter
Setting the elevator to go to a specific height
Performing motor calibration
… and many more.
The key to Behaviours is that they are small, and are used together to build into a much larger system.
Implementing Behaviours
Behaviours are created by inheriting from the Behaviour class. Let’s look at an example Behaviour that prints a message when it starts, and then immediately stops.
We can run this behaviour by doing the following:
Controlling systems
Let’s look at how we might control a system with Behaviours. For this example, we’re going to create a Behaviour that tells a flywheel shooter to spin up to a certain speed.
First, we need to make our shooter system able to have a Behaviour. To do this, we make it implement from HasBehaviour - that’s it, no methods to override or anything else.
Go ahead and register the Shooter with the BehaviourScheduler:
Next, we create our Behaviour. Just for fun, let’s do some PIDF while we’re at it.
See how we’ve taken a complex action and broken it down into a single piece of code that we can reuse? Just have a look how much easier it is to tell the shooter to spinup to an arbitrary speed:
We can also assign a timeout to this behaviour, in case we want to move on if it’s not ready in time:
We can also tell this behaviour to run faster by calling SetPeriod:
Using Behaviours Together
As we mentioned, Behaviours are small, compartmentalised units of work that we can use together to make complex routines. In order to achieve this, Wombat provides some ways to combine behaviours together into larger sequences.
Sequential Execution
Behaviours can be executed in sequence by using the << operator.
Parallel Execution
Behaviours can be executed together (at the same time) by using the & or | operators. Only behaviours which control different systems can be run at the same time. & will run until both are complete, whereas | will stop after either is complete (known as a “race”).
You can also run in parallel, waiting until a specific behaviour is complete with the Until function.
Waiting
Wombat provides WaitTime and WaitFor to produce simple waits in the behaviour chain.
Making Decisions
Making decisions in the behaviour chain is easy, as Wombat provides If, Switch, and Decide.
Switch is similar to a switch-case statement, allowing you to choose from one of many options.
Decide is a special case of Switch, but without an argument - using predicates on all branches.
Big picture: designing Behaviours from the Top Down.
Let’s say we come up with a plan to do a really awesome (but really complicated) autonomous. The team decides the following routine is our best strategic option:
While spinning up the shooter:
Move forward 2m
Intake a ball
Shoot the ball
Turn 90 degrees
Wait 2 seconds
Move forward 1.5m
Intake a ball
While driving backwards 3m:
Spinup the shooter
Wait until vision is ready
Shoot a ball
Intake another ball
Spinup & Shoot the last ball
Complex, right? Let’s look at how we break it down. First of all, notice that it’s already in a sequence of steps for us - small chunks of work that we can harness to complete our goals. Also notice that there’s some common behaviour across this routine - there’s multiple times where we move forward, intake a ball, etc. We can use this to our advantage by reducing the amount of code we need to write.
We can use the steps we’ve already outlined to build our overall behaviour sequence that describes the autonomous routine.
Let’s go ahead and mock up what we think our autonomous routine above will look like in code:
See how we can just flow on from the individual steps of our big, complex example? Now, instead of implementing 15 different steps in the autonomous routine, we only have to implement 5 behaviours: