← Back to Main Portfolio

main.py

Purpose

This file is the main integration point for the entire robot. It imports all required modules, initializes the hardware interfaces, creates the shared variables used for inter-task communication, instantiates the task objects, adds them to the cooperative scheduler, and then continuously runs the robot through round-robin task scheduling. In practical terms, main.py is what turns the individual subsystems into one complete autonomous robot.

Main Role in the Project

While the detailed behavior of the robot lives inside separate files such as task_motor.py, task_line.py, task_imu.py, and task_course.py, this file is responsible for wiring everything together. It creates the exact objects used by the system, determines how those tasks communicate, and defines how often each task runs. Because of that, it is one of the most important files in the entire project.

Hardware Initialization Performed in This File

Important Shared Variables

A major part of main.py is the creation of Shares. These are what allow the independent tasks to communicate without directly reaching into each other’s local variables.

Motion and Motor Shares

Line-Following and Course Shares

IMU and Estimation Shares

Default Initialization Values

This file also establishes the startup state of the full robot. The PI gains are initialized to Kp = 0.06 and Ki = 0.6, the line-follow base speed is initialized to 220.0, and all enable flags such as line-following, course mode, estimator streaming, bump event status, and motor-go flags are set to false initially. This ensures the robot powers up in a safe and predictable state rather than immediately starting to move.

Task Objects Created

After hardware and Shares are initialized, the file creates the main task objects that make up the full software system:

Scheduler Setup

This file adds all major tasks to the cooperative scheduler using the Task class and task_list.append(). The final scheduler configuration uses different priorities and update periods depending on the importance and timing needs of each subsystem:

This priority structure is important. The bump task has the highest priority because safety and immediate collision response matter the most. The motor tasks also run frequently because wheel control needs to be updated quickly. Line following runs slightly slower, and user interaction or garbage collection can run at lower urgency.

Main Runtime Loop

At the bottom of the file, the system enters an infinite loop and repeatedly calls task_list.rr_sched(). This is what keeps the cooperative scheduler running. If a KeyboardInterrupt occurs, both motors are disabled before the program exits, which provides a clean and safe shutdown procedure. After exiting, the file prints task and share information for debugging.

Why This File Matters

This file is critical because it transforms a collection of independent modules into one complete robot. Without main.py, the individual tasks would still exist, but they would not know how to communicate, when to run, or which hardware objects to use. It is the file that determines how the robot boots, how the software system is assembled, and how the runtime behavior is scheduled.

Engineering Significance

From a systems perspective, main.py demonstrates one of the most important ideas in this project: integration. A robot like this is not just a line sensor, not just an IMU, and not just a motor controller. It is the interaction of all of those subsystems running together. This file captures that system-level design by showing how sensing, estimation, control, event handling, and user interaction are combined into a single autonomous platform.

Click to view a representative scheduler snippet
task_list.append(Task(leftMotorTask.run,  name="Left Mot. Task",  priority=3, period=5,  profile=True))
task_list.append(Task(rightMotorTask.run, name="Right Mot. Task", priority=3, period=5,  profile=True))
task_list.append(Task(lineFollowTask.run, name="Line Follow Task", priority=2, period=10, profile=False))
task_list.append(Task(userTask.run,       name="User Int. Task",   priority=2, period=20, profile=False))
task_list.append(Task(imuTask.run,        name="IMU Task",         priority=1, period=20, profile=True))
task_list.append(Task(courseTask.run,     name="Course Task",      priority=4, period=20, profile=False))
task_list.append(Task(bumpTask.run,       name="Bump Task",        priority=5, period=5,  profile=False))
task_list.append(Task(startBtnTask.run,   name="Start Button",     priority=3, period=20, profile=False))
task_list.append(Task(garbage,            name="Garbage",          priority=0, period=100, profile=False))
Click to view a representative hardware and share initialization snippet
pwm_tim = Timer(3, freq=20000)

leftMotor  = motor_driver(Pin.cpu.B1, Pin.cpu.B15, Pin.cpu.B14, pwm_tim, 4)
rightMotor = motor_driver(Pin.cpu.B0, Pin.cpu.B5,  Pin.cpu.B4,  pwm_tim, 3)

tim_enc_left  = Timer(1, period=0xFFFF, prescaler=0)
tim_enc_right = Timer(2, period=0xFFFF, prescaler=0)

leftEncoder  = encoder(tim_enc_left,  Pin.cpu.A8, Pin.cpu.A9)
rightEncoder = encoder(tim_enc_right, Pin.cpu.A0, Pin.cpu.A1)

leftMotorGo  = Share("b", name="Left Mot Go")
rightMotorGo = Share("b", name="Right Mot Go")
velSetpoint_L = Share("f", name="Vel Set L")
velSetpoint_R = Share("f", name="Vel Set R")

Open raw main.py file