Architecture Overview

LibSTP is a modular robotics SDK written in C++20 with Python bindings. It runs on a Raspberry Pi inside the Wombat controller. You write mission code in Python; the heavy lifting (control loops at 100 Hz, kinematics math, motor drivers) happens in compiled C++ underneath.

The Layer Cake

Every piece of robot code sits on a specific layer. Higher layers are simpler to use but less flexible. Lower layers give you full control.

graph TB
    subgraph "Your Code"
        M["Missions"]
        S["Steps (DSL)"]
    end

    subgraph "Motion Layer"
        MO["Motion Controller
LinearMotion, TurnMotion, ArcMotion"] end subgraph "Control Layer" DR["Drive
Velocity PID + Feedforward"] KI["Kinematics
Differential / Mecanum"] OD["Odometry
Fused / STM32"] end subgraph "Hardware Abstraction (HAL)" HAL_M["Motor"] HAL_S["Servo"] HAL_IMU["IMU"] HAL_IR["IR Sensor"] HAL_D["Digital Sensor"] HAL_A["Analog Sensor"] end subgraph "Platform" WOM["Wombat Driver
LCM-based IPC to firmware"] MOCK["Mock Driver
For testing without hardware"] end M --> S S --> MO MO --> DR DR --> KI KI --> HAL_M DR --> HAL_IMU MO --> OD OD --> HAL_IMU OD --> KI S --> HAL_S S --> HAL_IR S --> HAL_D S --> HAL_A HAL_M --> WOM HAL_S --> WOM HAL_IMU --> WOM HAL_IR --> WOM HAL_D --> WOM HAL_A --> WOM HAL_M --> MOCK HAL_S --> MOCK HAL_IMU --> MOCK style M fill:#4CAF50,color:#fff style S fill:#66BB6A,color:#fff style MO fill:#42A5F5,color:#fff style DR fill:#42A5F5,color:#fff style KI fill:#42A5F5,color:#fff style OD fill:#42A5F5,color:#fff style HAL_M fill:#AB47BC,color:#fff style HAL_S fill:#AB47BC,color:#fff style HAL_IMU fill:#AB47BC,color:#fff style HAL_IR fill:#AB47BC,color:#fff style HAL_D fill:#AB47BC,color:#fff style HAL_A fill:#AB47BC,color:#fff style WOM fill:#FF7043,color:#fff style MOCK fill:#FF7043,color:#fff

What Each Layer Does

Missions & Steps (Green) — Your Code

This is where you spend 90% of your time. Missions define what the robot should do. Steps are the building blocks — drive_forward(25), turn_right(90), servo(arm, 160). You compose them into sequences without worrying about control theory.

Motion Layer (Blue) — Trajectory Execution

When you call drive_forward(25), the motion layer plans a trapezoidal velocity profile (accelerate → cruise → decelerate) and feeds velocity targets to the drive system at 100 Hz. You don’t interact with this layer directly unless you’re tuning performance.

Control Layer (Blue) — Velocity & Position

  • Drive: Takes a desired chassis velocity (forward, lateral, angular) and uses PID + feedforward control to achieve it
  • Kinematics: Translates chassis velocity into individual wheel speeds (and back). Knows the geometry of your drivetrain — differential (2 wheels) or mecanum (4 wheels)
  • Odometry: Tracks the robot’s position on the field by integrating wheel encoder ticks and IMU heading

Hardware Abstraction Layer (Purple)

Abstract interfaces (Motor, Servo, IMU, IRSensor, etc.) that hide platform-specific details. The same mission code works whether you’re running on a real Wombat or a mock driver for testing.

Platform Layer (Orange)

Concrete drivers that talk to actual hardware. The Wombat driver communicates with the STM32 firmware over LCM (a lightweight inter-process messaging protocol). The mock driver returns simulated values for testing.

Module Map

LibSTP is split into 21 independent C++ modules, each with its own headers, source, and optional Python bindings:

ModuleLayerPurpose
libstp-foundationCoreTypes (Pose, ChassisVelocity), PID, math, logging
libstp-halHALAbstract hardware interfaces
libstp-platformsPlatformWombat and Mock drivers
libstp-driveControlChassis velocity controller
libstp-kinematicsControlDifferential and mecanum models
libstp-odometryControlPosition tracking (fused, STM32)
libstp-motionMotionLinear, turn, and arc motion profiles
libstp-sensor-irSensorIR line sensor with calibration
libstp-sensor-etSensorAdditional sensor types
libstp-servoActuatorServo control
libstp-buttonInputDigital button handling
libstp-camSensorCamera interface
libstp-calibration-storeSupportPersistent calibration data
libstp-timingSupportTiming and synchronization
libstp-asyncSupportAsync/coroutine utilities
libstp-kmeansAlgorithmK-means clustering
libstp-debugSupportDebug utilities
libstp-screenOutputDisplay rendering
libstp-stepFrameworkStep execution framework (Python)
libstp-missionFrameworkMission lifecycle (Python)
libstp-robotFrameworkRobot integration point (Python)

Data Flow: What Happens When You Call drive_forward(25)

sequenceDiagram
    participant You as Your Mission
    participant Step as DriveForward Step
    participant Motion as LinearMotion
    participant Drive as Drive Controller
    participant Kin as Kinematics
    participant Motor as Motors (HAL)

    You->>Step: drive_forward(25)
    Step->>Motion: start(target=0.25m)
    loop Every 10ms (100 Hz)
        Motion->>Motion: Compute velocity profile
        Motion->>Drive: setDesiredVelocity(vx=0.2, vy=0, wz=0)
        Drive->>Drive: PID + Feedforward
        Drive->>Kin: applyCommand(corrected velocity)
        Kin->>Motor: setVelocity(left_wheel, right_wheel)
    end
    Motion-->>Step: Target reached
    Step-->>You: Step complete, next step runs

Getting Started

If you haven’t set up a robot yet, follow the Quick Start guide first. It walks you through creating a project, configuring hardware in the YAML file, generating code, calibrating, and running your first mission.

Key Design Decisions

Python-first API, C++ core: You never need to write C++ to build a competition robot. The Python API exposes everything you need. C++ is used where performance matters (100 Hz control loops, real-time odometry).

DSL with factory functions: Instead of DriveForward(cm=25, speed=0.8), you write drive_forward(25, 0.8). The underlying classes are hidden — you interact with clean factory functions that return builder objects. This keeps the API surface small and discoverable.

Platform abstraction: The HAL layer means your code doesn’t depend on the Wombat hardware. You can test mission logic against mock drivers, and the same code runs on any future platform that implements the HAL interfaces.

Composition over inheritance: Missions are composed of steps. Steps are composed of smaller steps via Sequential and parallel. There’s no deep class hierarchy to learn — just combine building blocks.