TeslasuitDocumentation
Frameworks

System Architecture

Overview#

RapidKit uses a multi-process architecture to separate real-time data processing from the user interface. This design ensures that the critical control loop maintains consistent timing while the GUI can update at a different rate.

Architecture Diagram#

Loading diagram…

Process Architecture#

Main Process (main.py)#

The main process serves as the entry point and process manager:

  1. Initialization: Sets up Python environment and imports
  2. Queue Creation: Creates multiprocessing queues for IPC:
    • control_queue: GUI → Backend (stimulation parameters)
    • utility_queue: GUI ↔ Backend (system state flags)
  3. Process Launch: Starts backend and GUI as separate processes
  4. Cleanup: Handles graceful shutdown of both processes

Backend Process (ClosedLoopEngine)#

The backend runs in a separate process to ensure real-time performance:

Main Loop (~100 Hz, hardware-paced):

  1. DataStreamer.collect_data() — pull latest frames from Teslasuit SDK (blocking)
  2. DataStreamer.process() — optional user override for custom sensor processing
  3. DataStreamer.distribute_data() — push to SharedRingBuffer + LSLStreamer
  4. ControlStrategyBase.run_strategy() — update strategy inputs, call process(), apply EMS output
  5. QueueHandler.check_queues() — handle incoming messages from GUI

Key Components:

  • SuitHandler: Teslasuit connection, haptic/EMS subsystem, muscle map
  • DataStreamer: Three-phase collect → process → distribute cycle
  • Data Types: Python dataclasses populated by DataStreamer each cycle
  • ControlStrategyBase: User-implemented process() method — reads inputs, writes ems_output
  • Stimulator: Translates EmsData → Teslasuit SDK EMS calls (uses MuscleMap)
  • LSLStreamer: Streams 7 LSL outlets to external lab equipment
  • SharedRingBuffer: Circular shared-memory buffer for GUI data feed

GUI Process (MainWindow)#

The GUI runs in the main process (PyQt5 requirement):

Update Loop (30 Hz):

  1. Read latest data from shared memory buffer
  2. Update visualizations (plots, graphs, statistics)
  3. Handle user input (parameter changes, mode switches)
  4. Send control/utility messages to backend

Key Components:

  • MainWindow: Main application window with tabs
  • DataHandler: Manages shared memory buffer connection
  • OverviewTab: Main control interface with leg panels
  • SensorDataTab: Bilateral sensor data visualisations
  • GaitStatisticsTab: Gait phase statistics

Data Architecture#

Data Flow Hierarchy#

TeslaSuit Hardware
    ↓ (via WiFi / Teslasuit Control Center)
SuitHandler  (fes_framework/io/suit_handler.py)
    ↓ (raw ctypes frames from SDK)
DataStreamer  (fes_framework/io/data_streamer.py)
    ↓ (parsed Python dataclasses)
Data Types  (fes_framework/data/types.py)
    ↓ (typed inputs to ControlStrategyBase)
ControlStrategyBase.process()
    ↓ (EmsData written to self.ems_output)
Stimulator  (fes_framework/io/stimulator.py)
    ↓ (SDK EMS channel calls)
TeslaSuit Hardware  ← stimulation applied

SuitHandler#

Location: fes_framework/io/suit_handler.py

Purpose: Thin interface layer to Teslasuit hardware

Responsibilities:

  • Connects to Teslasuit via Teslasuit Control Center (WiFi)
  • Manages SDK connection lifecycle
  • Exposes mocap, haptic, and PPG subsystems
  • Holds the MuscleMap for channel lookup
  • Starts/stops data streaming

DataStreamer#

Location: fes_framework/io/data_streamer.py

Purpose: Three-phase sensor data cycle: collect → process → distribute

Responsibilities:

  • collect_data(): pulls raw ctypes frames from SuitHandler SDK; parses into dataclasses
  • process(): user-overridable hook for custom sensor processing (default: no-op)
  • distribute_data(): pushes populated dataclasses to SharedRingBuffer and LSLStreamer
  • Populates four data types each cycle:
    • BiomechanicalData (29 joint angles)
    • ProcessedData (skeleton bone positions/rotations)
    • StepDetectorData (foot contact states)
    • RawData (IMU sensor readings)

Data Structures (Dataclasses)#

Location: fes_framework/data/types.py

Purpose: Typed, organized data containers used throughout the system

Key Dataclasses:

BiomechanicalData#

  • 29 joint angles (pelvis, hips, knees, ankles, shoulders, elbows, wrists)
  • All angles in degrees
  • Organized by anatomical joint

ProcessedData#

  • 20 skeleton bone positions and rotations
  • 3D position + quaternion rotation per bone
  • Organized by bone name

RawData#

  • Raw IMU sensor data from 20 body segments
  • Accelerometer, gyroscope, orientation per sensor
  • Organized by body segment name

StepDetectorData#

  • Left and right foot contact states (boolean)
  • Organized for easy access

EmsData#

  • Per-muscle stimulation parameters (20 muscle groups)
  • Nested structure: EmsData → EMSParamData per muscle
  • Organized by muscle group name

ControlMessage#

  • Empty base dataclass — subclass to define application-specific fields
  • Per-muscle stimulation parameters (defined in subclass, e.g. WalkingControlMessage)
  • Timing parameters (defined in subclass, e.g. stance/swing windows)
  • Organized for control strategy consumption

UtilityMessage#

  • FES enable state (FesIsActive)
  • Recording state
  • Calibration state
  • Step detection mode (TS API, VU, model-based)
  • Folder path
  • Organized for system state management

Data Structure Flow:

  1. Initialized in ClosedLoopEngine using factory functions from init_utils.py
  2. Passed to DataStreamer at construction
  3. Populated in-place by DataStreamer.collect_data() each cycle
  4. Used by all backend components
  5. Written to SharedRingBuffer and LSLStreamer via DataStreamer.distribute_data()

Inter-Process Communication (IPC)#

Shared Memory Ring Buffer#

Purpose: High-performance, zero-copy data streaming from backend to GUI

Implementation:

  • Location: fes_framework/ipc/buffer.py
  • Type: Circular buffer in shared memory
  • Capacity: configurable (default 500 frames; application can set higher for longer plot history)
  • Data Structure: Application-defined numpy dtype (set by the ClosedLoopEngine subclass)

Data Flow:

Backend (Writer) → Shared Memory → GUI (Reader)

Frame Structure:

  • Timestamp (float64)
  • 29 biomechanical joint angles (float64)
  • Step detection flags (uint8)
  • EMS parameters per muscle (3×float32 per muscle)

Message Queues#

Purpose: Command and control messages from GUI to backend

Control Queue (control_queue):

  • Application-specific stimulation parameters (defined by ControlMessage subclass)
  • Muscle stimulation parameters (frequency, amplitude, pulse width, timing)
  • Sent when user changes parameters in GUI

Utility Queue (utility_queue):

  • Recording start/stop
  • Calibration requests
  • Step detection mode selection
  • Folder path for data storage

Implementation:

  • Location: fes_framework/ipc/queue_handler.py
  • Type: multiprocessing.Queue
  • Non-blocking reads in backend loop

Component Relationships#

Backend Data Flow#

Loading diagram…

Component Dependencies#

  • SuitHandler: Independent, wraps SDK hardware interface
  • DataStreamer: Depends on SuitHandler (reads raw frames)
  • Data Types: Independent dataclasses, populated by DataStreamer
  • ControlStrategyBase: Reads Data Types; writes EmsData to Stimulator
  • Stimulator: Depends on EmsData and SuitHandler
  • LSLStreamer: Reads all populated Data Types
  • SharedRingBuffer: Reads populated Data Types (via DataStreamer)

LSL Streaming Architecture#

LSLStreamer provides 7 LSL outlets, all disabled by default (enabled via lsl_enabled=True in ClosedLoopEngine or orchestrator.launch()):

Stream nameChannelsContent
TS_Biomechanics29Joint angles (degrees)
TS_StepDetector2Left/right foot contact (bool)
TS_EMSParameters80EMS params for 20 muscles × 4 fields
AppData_ControlMessagevariableRuntime strategy parameters
AppData_UtilityMessagevariableSystem state flags
TS_BonePosition140Skeleton bone positions/rotations
TS_RawDatavariableRaw IMU sensor readings

All streams share the same LSL source ID and are synchronized via LSL's global clock.

Error Handling and Resilience#

Backend Resilience#

  • Continues operation if GUI disconnects
  • Handles TeslaSuit connection errors gracefully (via Control Center)
  • Validates data before processing
  • Logs errors without crashing
  • Reconnects to TeslaSuit via Control Center on connection loss

GUI Resilience#

  • Handles backend disconnection (shows mock data)
  • Retries shared memory connection
  • Validates user input before sending
  • Graceful degradation if data unavailable

Process Management#

  • Backend process can be restarted independently
  • GUI cleanup on window close
  • Proper resource cleanup on shutdown
  • Timeout handling for process termination

Performance Considerations#

Backend Loop Timing#

  • Target: 10 ms per iteration (100 Hz)
  • Critical path: Data acquisition → Data organization → Step detection → Control → Stimulation
  • LSL streaming and shared memory writes are non-blocking

GUI Update Rate#

  • Target: 33 ms per iteration (30 Hz)
  • Only active tab updates continuously
  • Biomechanics tab updates only when visible (performance optimization)

Memory Management#

  • Shared memory buffer: ~10 seconds of data
  • Circular buffer prevents memory growth
  • Old data automatically overwritten
  • Dataclasses are updated in-place (no copying)

CPU Usage#

  • Backend: Single-threaded, CPU-bound
  • GUI: Event-driven, mostly idle
  • ML model: Can use GPU if available

Security Considerations#

  • No network exposure (LSL is local network only)
  • No user authentication (local application)
  • File system access limited to data directory
  • No external API calls