base_controller.md - dingdongdengdong/astra_ws GitHub Wiki

Base Controller (base_controller.py)

flow diagram

graph TD
    A[Instantiate BaseController] --> B[__init__ Method];
    B --> C[Log Device Name];
    C --> D[Initialize Callbacks and State Variables];
    D --> E[Create Thread Locks];
    E --> F[Initialize Position Dictionaries];
    F --> G[Create Quit Event];
    G --> H[Create Left Motor Thread];
    G --> I[Create Right Motor Thread];
    H --> J[Start Left Motor Thread];
    I --> K[Start Right Motor Thread];
    J --> L[Left odrive_can_thread Runs];
    K --> M[Right odrive_can_thread Runs];
    L --> N[Initialize Left ODrive CAN];
    M --> O[Initialize Right ODrive CAN];
    N --> P[Start Left CAN Communication];
    O --> Q[Start Right CAN Communication];
    P --> R[Clear Left ODrive Errors];
    Q --> S[Clear Right ODrive Errors];
    R --> T[Reset Left Encoder];
    S --> U[Reset Right Encoder];
    T --> V[Set Left Control Mode and Gains];
    U --> W[Set Right Control Mode and Gains];
    V --> X[Enter Left Closed-Loop Control];
    W --> Y[Enter Right Closed-Loop Control];
    X --> Z{Left Motor in Closed-Loop?};
    Y --> AA{Right Motor in Closed-Loop?};
    Z -- No --> AB[Sleep 0.1s Left];
    AB --> Z;
    AA -- No --> AC[Sleep 0.1s Right];
    AC --> AA;
    Z -- Yes --> AD[Assign feedback_cb to Left ODrive];
    AA -- Yes --> AE[Assign feedback_cb to Right ODrive];
    AD --> AF[Left Control Loop Runs];
    AE --> AG[Right Control Loop Runs];

    AF --> AH{Quit Event Set Left?};
    AH -- No --> AI[Acquire Write Lock Left];
    AI --> AJ[Set Left ODrive Input Position];
    AJ --> AK[Check Left ODrive Errors];
    AK --> AL[Sleep 0.1s Left Control];
    AL --> AF;
    AI --> AN[Release Write Lock Left];
    AN --> AK;
    AH -- Yes --> AO[Raise Quit Exception Left];
    AO --> AP[Left ODrive Stop];

    AG --> AQ{Quit Event Set Right?};
    AQ -- No --> AR[Acquire Write Lock Right];
    AR --> AS[Set Right ODrive Input Position];
    AS --> AT[Check Right ODrive Errors];
    AT --> AU[Sleep 0.1s Right Control];
    AU --> AG;
    AR --> AV[Release Write Lock Right];
    AV --> AT;
    AQ -- Yes --> AW[Raise Quit Exception Right];
    AW --> AX[Right ODrive Stop];


    J --> BA[Wait for First Feedback];
    K --> BA;
    BA --> BB[Controller Ready];

    BB --> BC[feedback_cb Triggered by ODrive CAN];
    BC --> BD{Message is Encoder Estimates?};
    BD -- Yes --> BE[Extract Pos and Vel];
    BE --> BF[Log Debug Info];
    BF --> BG[Update curpos];
    BG --> BH{Debug Callback Registered?};
    BH -- Yes --> BI[Call Debug Callback curpos];
    BI --> BJ[Record Current Time];
    BH -- No --> BJ;
    BD -- No --> BJ;

    BJ --> BK[Acquire State Lock];
    BK --> BL{First Update?};
    BL -- Yes --> BM[Initialize Last Velocities and Time];
    BM --> BN[Release State Lock];
    BN --> BP{State Callback Registered?};
    BP -- Yes --> BQ[Call State Callback with Velocities and dt];
    BQ --> BB;
    BP -- No --> BB;

    BL -- No --> BR[Calculate dt];
    BR --> BS[Adjust Wheel Vel by Install Dir];
    BS --> BT[Update Last Wheel Velocities];
    BT --> BU[Convert Wheel Vel to Robot Vel];
    BU --> BN;

    BB --> BV[Call set_vel method];
    BV --> BW[Convert Robot Vel to Wheel Vel];
    BW --> BX[Acquire Write Lock set_vel];
    BX --> BY[Update setpos with Velocity*dt];
    BY --> BZ[Clamp setpos];
    BZ --> CA[Log Clamping];
    CA --> CB{Debug Callback Registered set_vel?};
    CB -- Yes --> CC[Call Debug Callback setpos];
    CC --> CD[Release Write Lock set_vel];
    CD --> BB;
    CB -- No --> CD;
    BZ --> CB;

    BB --> CE[Call stop method];
    CE --> CF[Set Quit Event];
    CF --> CG[Allow Threads to Stop];
    CG --> CH[Controller Stopped];

    CI[Object Garbage Collected] --> CJ[__del__ Method];
    CJ --> CE;

    L --> AP;
    M --> AX;

Class Structure

classDiagram
    class BaseController {
        -device_name: str
        -curpos: dict
        -setpos: dict
        -last_left_vel: float
        -last_right_vel: float
        +LEFT_NODE_ID: int
        +RIGHT_NODE_ID: int
        +WHEEL_BASE: float
        +WHEEL_D: float
        +__init__(name: str)
        +set_vel(linear_vel: float, angular_vel: float)
        +stop()
        -feedback_cb(msg, caller)
        -odrive_can_thread(node_id: int)
    }

    class ODriveCAN {
        +start()
        +clear_errors()
        +set_controller_mode()
        +set_input_pos()
        +get_encoder()
        +get_velocity()
    }

    BaseController --> ODriveCAN : uses

Component Interaction

flowchart TD
    subgraph BaseController
        VH[Velocity Handler]
        KM[Kinematics Module]
        PH[Position Handler]
        FB[Feedback Handler]
    end

    subgraph ODrive
        LC[Left Controller]
        RC[Right Controller]
        LE[Left Encoder]
        RE[Right Encoder]
    end

    subgraph External
        VC[Velocity Commands]
        SF[State Feedback]
    end

    VC -->|cmd_vel| VH
    VH -->|compute| KM
    KM -->|wheel velocities| PH
    PH -->|position commands| LC & RC
    LE & RE -->|encoder data| FB
    FB -->|state| SF

    style BaseController fill:#bbf,stroke:#333,stroke-width:2px
    style ODrive fill:#fbf,stroke:#333,stroke-width:2px
    style External fill:#fbb,stroke:#333,stroke-width:2px

Control Flow

sequenceDiagram
    participant App
    participant Controller
    participant Kinematics
    participant ODriveLeft
    participant ODriveRight

    App->>Controller: set_vel(linear, angular)
    Controller->>Kinematics: compute_wheel_velocities()
    Kinematics-->>Controller: left_vel, right_vel
    Controller->>ODriveLeft: set_input_pos()
    Controller->>ODriveRight: set_input_pos()
    
    loop Feedback
        ODriveLeft-->>Controller: encoder_feedback
        ODriveRight-->>Controller: encoder_feedback
        Controller->>App: state_callback
    end

State Machine

stateDiagram-v2
    [*] --> Initializing
    Initializing --> ClearingErrors: Start
    ClearingErrors --> ConfiguringControllers: Errors Cleared
    ConfiguringControllers --> Ready: Configuration Complete
    Ready --> Moving: Velocity Command
    Moving --> Ready: Stop Command
    Ready --> [*]: Shutdown
    Moving --> ErrorState: Error Detected
    ErrorState --> ClearingErrors: Recovery

Differential Drive Kinematics

graph LR
    subgraph Inputs
        LV[Linear Velocity]
        AV[Angular Velocity]
    end

    subgraph Parameters
        WB[Wheel Base]
        WD[Wheel Diameter]
    end

    subgraph Computation
        K[Kinematics]
    end

    subgraph Outputs
        LW[Left Wheel Velocity]
        RW[Right Wheel Velocity]
    end

    LV --> K
    AV --> K
    WB --> K
    WD --> K
    K --> LW
    K --> RW

    style Inputs fill:#bbf
    style Parameters fill:#fbf
    style Computation fill:#bfb
    style Outputs fill:#fbb

Error Handling

flowchart TD
    A[Error Detected] --> B{Error Type}
    B -->|Communication| C[Reset CAN]
    B -->|Controller| D[Clear ODrive Error]
    B -->|Position| E[Reset Position]
    C & D & E --> F{Recovery Success?}
    F -->|Yes| G[Resume Operation]
    F -->|No| H[Emergency Stop]

Thread Management

graph TD
    subgraph Main Thread
        A[Command Processing]
        B[State Management]
    end

    subgraph CAN Threads
        C[Left ODrive Thread]
        D[Right ODrive Thread]
    end

    subgraph Callback Thread
        E[Feedback Processing]
    end

    A -->|commands| C & D
    C & D -->|feedback| E
    E -->|updates| B

    style Main Thread fill:#bbf
    style CAN Threads fill:#fbf
    style Callback Thread fill:#bfb

Configuration Parameters

  • LEFT_NODE_ID: CAN ID for left motor
  • RIGHT_NODE_ID: CAN ID for right motor
  • WHEEL_BASE: Distance between wheels (m)
  • WHEEL_D: Wheel diameter (m)
  • Position gain: 20.0
  • Velocity gains: [0.1, 0.0]
  • Velocity limit: 20 turns/s
  • Acceleration limit: 1 turn/s²

Dependencies

  • odrive_can: ODrive CAN interface
  • threading: Thread management
  • math: Mathematical computations
  • time: Timing functions
  • logging: Error and debug logging

Notes

  • Uses position control with trajectory for smooth motion
  • Implements velocity commands through position integration
  • Thread-safe velocity commands and state updates
  • Automatic error recovery
  • Real-time feedback processing
  • Independent control threads per motor
  • Position difference limiting for safety