Step Framework

The Step Framework lets you build complex mission sequences from composable building blocks. Instead of writing everything as one long sequence method, you describe your mission as a tree of steps that can run in sequence or in parallel.


Why Use Steps?

The main reasons to use steps:

  • Parallel execution — run two things at the same time (e.g., drive and monitor a sensor simultaneously)
  • Reusability — define a step once and use it in multiple missions
  • Readability — complex missions become a readable list of named actions

Sequential Steps

Sequential runs a list of steps one after another:

from libstp.step import Sequential
from libstp.step.motion import Drive, Turn

mission = Sequential([
    Drive(500),      # drive 500 mm forward
    Turn(90),        # turn right 90 degrees
    Drive(300),      # drive 300 mm forward
])

mission.run()

Each step waits for the previous one to finish before starting.


Parallel Steps

Parallel runs multiple steps at the same time and waits until all of them finish:

from libstp.step import Sequential, Parallel, Wait
from libstp.step.motion import Drive, Turn

mission = Sequential([
    Drive(500),
    Parallel([
        Turn(90),       # turn while...
        Wait(2.0),      # ...also waiting 2 seconds (whichever finishes last)
    ]),
    Drive(200),
])

Built-in Steps

StepDescription
Drive(distance_mm)Drive forward (or backward with negative value)
Turn(angle_deg)Rotate in place (positive = right)
Strafe(distance_mm)Move sideways — mecanum only
Wait(seconds)Pause for the given duration

[TODO: Confirm the complete list of built-in steps from libstp — check src/step/ in the raccoon-lib project]


Writing Custom Steps

You can create your own reusable steps in src/steps/:

from libstp.step import Step

class PickUpBall(Step):
    def run(self, robot):
        robot.servo(port=0).set_position(0)    # open claw
        robot.motion.drive(distance_mm=100)    # drive onto ball
        robot.servo(port=0).set_position(90)   # close claw

Then use it in a mission:

from libstp.step import Sequential
from libstp.step.motion import Drive
from steps.pick_up_ball import PickUpBall

mission = Sequential([
    Drive(200),
    PickUpBall(),
    Drive(-100),
])

Using Steps Inside a Mission

You can call steps from inside a mission’s sequence method:

from libstp.mission import Mission
from libstp.step import Sequential
from libstp.step.motion import Drive, Turn

class CollectMission(Mission):
    def sequence(self):
        plan = Sequential([
            Drive(500),
            Turn(90),
            Drive(300),
        ])
        plan.run()

Or mix steps with direct motion calls — whatever is clearest for the task.