Installation · Why PythFinder · Usage · Trajectory API · Documentation
| Generate reliable, pre-calculated robot trajectories for autonomous routines. | |
| Build → simulate → inspect → export → run on the robot. | |
| EV3 with MicroPython and the PythFinder quick-start. | |
| Hardware-independent output that can be adapted for EV3, SPIKE Prime, NXT or other compatible processors. |
Before diving in, make sure you have:
- Python 3.10 or newer (the latest version is recommended);
- pip installed on your device—usually
pip3for Python 3; - the team's Graffiti Youth font installed, as it is used by the interface.
Installation is then a single command in your terminal:
Teams commonly use blocks for autonomous routines because of the limited MicroPython and Python documentation available online. This approach may be faster to compile, but it sacrifices reliability.
With this in mind, we chose MicroPython as the main language for our EV3 brick. Throughout the 2023–2024 MASTERPIECE season, we experimented with on-the-go motion calculations and concluded that they were
Because the processors inside LEGO®-approved bricks cannot perform these calculations quickly, moving that work to a separate tool was the practical solution.
PythFinder is a .txt file containing everything the robot needs to reproduce the planned motion. Copy that file into the robot's code folder and load it during initialization.
On the robot, a simplified follower reconstructs trajectories from the exported .txt data.
For an average maximum-points routine with seven or eight launches, loading all trajectory data can take approximately one to three minutes. The exact time depends on the amount of exported data and the generation settings described later.
$\color{#62E39E}{\textsf{Competition note:}}$ Start the program at least four minutes before the match.
Why use this method?
It's a small price to have one of the most reliable autonomous programs in the FLL competition.
PythFinder is SPIKE Prime, NXT and other microprocessors capable of reading a .txt file can use the exported data—including robots outside FLL.
The plug-and-play PythFinder quick-start currently targets
If you need implementation help, have an improvement in mind or simply want to learn more, contact @omega.core on Instagram.
The original roadmap aimed to create a quick-start for every FLL-legal brick, including a SPIKE Prime version around the launch of the
To start using this library in your environment, simply create a new python file and import the library:
import pythfinderTo enable the robot-visualization elements, create a Simulator object. This class encapsulates every component into one control center, taking care of the pygame window, joystick input and other pygame events (see Advanced usage).
sim = pythfinder.Simulator()This creates a simulator with Constants object with your desired values and pass it to the constructor:
# pass your values here
custom_constants = pythfinder.Constants(...)
sim = pythfinder.Simulator(custom_constants)Every time you run the simulator, it starts with your dataset of constants. You'll learn another way to change them in Interface settings. Finally, display your simulation:
while sim.RUNNING():
sim.update()The code runs until you exit the simulator window. Connecting a supported controller allows you to move freely on the field.
PythFinder is built on top of pygame's functionalities, from which it inherits support for XBOX, PS4, and PS5 controllers.
Connecting them is as easy as plugging in through
All of the Nintendo controllers are currently not supported and will raise an error.
As of version 0.0.5.0-alpha, the latest release introduces enhanced functionality for controlling settings, robot movement not included. In addition to the existing controller-based controls, users can now also utilize keyboard buttons to access and operate most of the functionalities previously limited to the controller interface.
The controls used to manipulate the simulator are listed below. Button order is
| Controller | Keyboard | Action |
|---|---|---|
△ / Y |
Space |
Move forward or backward when field-centric control is enabled. |
□ / X |
Escape |
Enter or exit the interface settings menu. |
○ / B |
Tab |
Reset the robot pose to the origin, or press buttons while the menu is active. |
X / A |
— | Show or hide the trail. |
| Left bumper | Delete |
Erase the trail, or restore default values while the menu is active. |
| Right bumper | — | Hold to enter selection mode. |
| D-pad | Arrow keys | Navigate the interface or select robot orientation in selection mode. |
| Left joystick | — | Control linear and angular velocity when field-centric control is enabled. |
| Right joystick | — | Control angular velocity only when field-centric control is disabled. |
| Options / Start | S |
Save a screenshot to the library's local Screenshots folder. |
First, we define a specific set of data regarding the robot's position, speed, and distance traveled as a
Multiple states of motion that exhibit certain similarities are referred to as Primitives denote movements with a single degree of freedom (1D), such as pure rotation, pure linear movement, or even stationary states (waiting). These primitives serve as building blocks for complex segments, which incorporate two or more primitives and characterize movements with two or three degrees of freedom, primarily intended for
Ultimately, all motion segments and auxiliary elements that perform other functions—known as trajectory.
Trajectories are constructed using the TrajectoryBuilder class. This class requires a Simulator object as a parameter, and optionally, a starting position and a preset to use. By default, the initial position is set at the origin of the Cartesian coordinate system.
The constructor offers intuitive methods for crafting precise trajectories, incorporating personalised
The constructor identifies the type of chassis in use and tangent to the trajectory, whereas holonomic chassis are given the option to interpolate orientation.
Here is a list of available motion functions:
wait()inLineCM()turnToDeg()toPoint()ortoPointTangentHead()toPose(),toPoseTangentHead()ortoPoseLinearHead()
These functionalities can be integrated with multithreading techniques. Markers can be configured to activate after a certain
| Type | Purpose |
|---|---|
| Break trajectory continuity at a selected time or distance, similar to sudden braking. | |
| Change the speed of selected trajectory sections without sacrificing continuity. |
Available marker methods:
-
interruptTemporal()orinterruptDisplacement() -
addTemporalMarker()oraddDisplacementMarker() -
addRelativeTemporalMarker()oraddRelativeDisplacementMarker() -
addRelativeTemporalConstraints()oraddRelativeDisplacementConstraints()InterruptsandConstraintsare$\color{#62E39E}{\textsf{strictly relative}}$ , as we have observed that users find it$\color{#62E39E}{\textsf{difficult}}$ to visualize the trajectory segments to which they apply. They modify the trajectory's course itself, as opposed to markers that call functions and might adversely affect the trajectory's construction. However, if users$\color{#62E39E}{\textsf{request}}$ it, I will reintroduce these functionalities, as they were included in the library's initial prototypes.
Markers can also include
After specifying the desired motion, call .build() to compute the trajectory values.
Putting it all together, we obtain:
# first launch from our Masterpiece code
START_POSE = Pose(-47, 97, -45)
PRESET = 1
trajectory = (TrajectoryBuilder(sim, START_POSE, PRESET)
.inLineCM(75)
.addRelativeDisplacementMarker(35, lambda: print('womp womp'))
.addRelativeDisplacementMarker(-12, lambda: print('motor goes brr'))
.addRelativeDisplacementConstraints(cm = 30,
constraints2d = Constraints2D(linear = Constraints(
vel = 10,
dec = -50)))
.addRelativeDisplacementConstraints(cm = 36,
constraints2d = Constraints2D(linear = Constraints(
vel = 27.7,
acc = 35,
dec = -30)))
.interruptDisplacement(cm = 66)
.wait(2600)
.addRelativeTemporalMarker(-1, lambda: print('motor goes :('))
.inLineCM(-30)
.turnToDeg(90)
.inLineCM(-20)
.turnToDeg(105)
.inLineCM(-47)
.turnToDeg(20)
.wait(ms = 1200)
.addRelativeTemporalMarker(0, lambda: print("spin'n'spin'n'spin.."))
.addRelativeTemporalMarker(-1, lambda: print("the party's over :("))
.turnToDeg(80)
.inLineCM(-120)
.build())After creating your trajectory, call the .follow() method and pass the Simulator object to see your code in action.
The follower supports two modes:
| Mode | Behaviour |
|---|---|
perfect |
Iterates through each motion state and displays the robot at its pre-calculated position. Increasing the step size makes the on-screen robot move faster. |
real |
Sends the calculated powers to the simulated robot, reproducing real-time behaviour. This is the recommended visualisation mode. |
The last optional parameter is wait. When set to True, it waits until the simulator is fully rendered before beginning the trajectory. This is useful with perfect following and a large step value because it keeps the beginning visible. Our fifth run looks something like this:
# default values
PERFECT_STEPS = 40
PERFECT_FOLLOWING = False
WAIT = True
trajectory.follow(sim, PERFECT_FOLLOWING, WAIT, PERFECT_STEPS)To facilitate the understanding of the 'trajectory' concept, I have implemented an easy-to-use graphical visualization method for motion profiles.
I truly believe that this library represents one of the best ways to begin learning concepts
Calling the .graph() function will display a Matplotlib graph of the
An interesting aspect is the connect parameter. By default, it is set to True, causing lines to be drawn between points. Setting it to False reveals discontinuities in acceleration, as velocity is optimized for continuity.
# default values
CONNECT = True
VELOCITY = True
ACCELERATION = True
WHEEL_SPEEDS = True
trajectory.graph(CONNECT, VELOCITY, ACCELERATION, WHEEL_SPEEDS)To make the robot move like it does in the simulator, you need to .txt file. This is accomplished with the .generate() method. Pass the file name or path and the step size:
STEPS = 6
FILE_NAME = 'test'
WHEEL_SPEEDS = True
SEPARATE_LINES = False
trajectory.generate(FILE_NAME, STEPS, WHEEL_SPEEDS, SEPARATE_LINES)Now you can copy the '.txt' file and load it into the quick-start to see it running!
There are two main ways you can manipulate your simulator environment through constants.
The first way is to pass a new instance of Constants when creating the simulator object, changing any of the following values:
# constants.py -- simplification
# all modifiable values:
class Constants():
def __init__(self,
pixels_to_dec,
fps,
robot_img_source,
robot_scale,
robot_width,
robot_height,
text_color,
text_font,
max_trail_len,
max_trail_segment_len,
draw_trail_threshold,
trail_color,
trail_loops,
trail_width,
background_color,
axis_color,
grid_color,
width_percent,
backing_distance,
arrow_offset,
time_until_fade,
fade_percent,
real_max_velocity,
max_power,
screen_size,
constraints2d,
kinematics):
...As described in the Create a Robot section, these changes will be automatically applied at the start of the simulation. For an in-depth explanation of the constants, see the documentation.
The second way is through the interface menu with joystick control. This is
A presets. These allow you to completely transform the interface appearance, robot configuration, and chassis type with the press of a button. You can utilize the number keys from 1 to 9 on the keyboard, each assigned to a distinct set of constants that adjust the simulation in various ways. The 0 key resets the interface to its
Beyond these predefined options, you can create your interface, robot behavior, and simulator parameters.
By default, button FLL field and button FTC field:
In response to a community request, we have implemented a new feature that allows users to draw shapes on the screen. This feature proves to be particularly useful when engaging in discussions or explaining strategies to team members or judges.
To access the drawing functionality, toggle the HAND DRAWING option in the
Accessing the painting tools can be done using keyboard shortcuts. Simply press the designated keys to activate the desired painting tool:
E— erase tool;L— line tool;R— rectangle tool;C— circle tool;T— triangle tool;Enter— exit tools.
The functionality is similar to that of a painting program. Selecting different tools
Explore the complete PythFinder documentation for the deeper API reference and implementation details.
| Libraries | pygame · Matplotlib · Pybricks |
| Robot model | BrickLink Studio 2.0 |
| Visual design | Adobe Illustrator |
| Motion-planning inspiration | Road Runner FTC |
| Interface font | Graffiti Youth |
| Field imagery |
PythFinder is available under the MIT License.
Developed by Omega Core for the 2023–2024 FIRST LEGO League MASTERPIECE season.
v0.0.5.0-alpha








