LSL Data Streams
This document describes all Lab Streaming Layer (LSL) data streams provided by RapidKit.
Overview#
RapidKit provides 7 LSL outlets from LSLStreamer (fes_framework/io/lsl_streamer.py):
TS_Biomechanics— Biomechanical joint anglesTS_API_StepDetector— Teslasuit SDK step detectorTS_EMSParameters— EMS stimulation parametersAppData_ControlMessage— Application runtime parametersAppData_UtilityMessage— System state flagsTS_BonePosition— Skeleton bone positions and rotationsTS_RawData— Raw IMU sensor data
All streams are disabled by default and must be enabled via lsl_enabled=True in ClosedLoopEngine.
All streams run at 100 Hz nominal sample rate.
Walking FES example note: The Walking FES application (examples/walking_fes/) also provides
an 8th stream — ModelBasedStepDetector — from its ML-based step detector. This stream
is not part of RapidKit; see Walking FES-specific streams below.
Framework Streams (LSLStreamer)#
1. Biomechanical Data Stream#
Stream Info:
- Name:
TS_Biomechanics - Type:
Biomechanical - Source ID:
{source_id}_biomech - Sample Rate: 100 Hz
- Format:
cf_double64(64-bit float) - Channel Count: 29 channels
Description: Streams processed joint angles in degrees from the OpenSim biomechanical analysis. Contains all major body joint angles including pelvis, hips, knees, ankles, shoulders, elbows, and wrists. More about biomechnical data in Teslasuit you find here
Channel Structure:
All fields from BiomechanicalData dataclass (excluding timestamp):
PelvisTilt,PelvisList,PelvisRotationHipFlexExtL/R,HipAddAbdL/R,HipRotL/RKneeFlexExtL/RAnkleFlexExtL/R,FootProSupL/RShoulderFlexExtL/R,ShoulderAddAbdL/R,ShoulderRotL/RElbowFlexExtL/RForearmProSupL/RWristFlexExtL/R,WristUlnRadDevL/R
Metadata per Channel:
label: Field name (e.g., "HipFlexExtL")unit: "degrees"type: "angle"id: Channel index
2. Tesla Suit API Step Detector Stream#
Stream Info:
- Name:
TS_API_StepDetector - Type:
StepDetection - Source ID:
{source_id}_steps - Sample Rate: 100 Hz
- Format:
cf_float32(32-bit float) - Channel Count: 2
Description: Streams foot contact detection from the Tesla Suit SDK's built-in step detector algorithm.
Channels:
left_foot_contact- Boolean flag (0.0/1.0) for left foot ground contactright_foot_contact- Boolean flag (0.0/1.0) for right foot ground contact
Metadata per Channel:
label: "left_foot_contact" or "right_foot_contact"unit: "boolean"type: "contact"id: Channel index
3. EMS Parameters Stream#
Stream Info:
- Name:
TS_EMSParameters - Type:
EMSParameters - Source ID:
{source_id}_ems - Sample Rate: 100 Hz
- Format:
cf_float32(32-bit float) - Channel Count: 80 channels (20 muscles × 4 parameters)
Description:
Streams electrical muscle stimulation parameters for all 20 muscle groups in EmsData.
More about EMS parameters in Teslasuit you find here
Channel Structure:
All 20 muscles from EmsData (10 lower body + 10 upper body), each with 4 parameters:
{Muscle}IsMuted- Boolean (0.0/1.0){Muscle}Amplitude- Percentage (0–100){Muscle}Period- Milliseconds (period between pulses){Muscle}PulseWidth- Microseconds (μs)
Metadata per Channel:
label: Field name (e.g., "LeftQuadAmplitude")unit: "boolean", "percent", "Hz", or "us" (depending on parameter type)type: "flag", "amplitude", "frequency", or "pulse_width"id: Channel index
4. Control Message Stream#
Stream Info:
- Name:
AppData_ControlMessage - Type:
ControlMessage - Source ID:
{source_id}_control - Sample Rate: 100 Hz
- Format:
cf_float32(32-bit float) - Channel Count: Application-dependent (determined by your
ControlMessagesubclass fields)
Description:
Streams the runtime control parameters defined by your application's ControlMessage subclass.
The base ControlMessage class is empty; fields are added by subclassing it in your application.
Channel count and structure therefore depend entirely on what fields your subclass defines.
Walking FES example: The Walking FES application's WalkingControlMessage subclass defines 80 channels
(10 muscle groups × 8 stimulation parameters per muscle). These are
Walking FES-specific; your application's channel structure will differ.
Metadata per Channel:
label: Field name from yourControlMessagesubclassunit: Depends on field type (e.g., "boolean", "Hz", "percent", "us")id: Channel index
5. Utility Message Stream#
Stream Info:
- Name:
AppData_UtilityMessage - Type:
UtilityMessage - Source ID:
{source_id}_utility - Sample Rate: 100 Hz
- Format:
cf_float32(32-bit float) - Channel Count: 6 channels
Description: Streams system utility flags including the global FES enable state, recording/calibration status, and step detection modes.
Channels:
From UtilityMessage dataclass (excluding _on_change and FolderPath):
FesIsActive- Boolean flag (0.0/1.0)RecordingIsActive- Boolean flag (0.0/1.0)CalibrationLoopIsActive- Boolean flag (0.0/1.0)TSAPIStepDetectionIsActive- Boolean flag (0.0/1.0)VUStepDetectionIsActive- Boolean flag (0.0/1.0)ModelBasedStepDetectionIsActive- Boolean flag (0.0/1.0)
Metadata per Channel:
label: Field nameunit: "boolean"type: "recording_flag", "calibration_flag", or "status_flag"id: Channel index
6. Bone Position Stream#
Stream Info:
- Name:
TS_BonePosition - Type:
BonePosition - Source ID:
{source_id}_boneposition - Sample Rate: 100 Hz
- Format:
cf_double64(64-bit float) - Channel Count: 140 channels (20 bones × 7 channels/bone)
Description: Streams processed skeleton data including 3D position and quaternion rotation for each bone in the Tesla Suit's skeletal model. More about processed skeleton data in Teslasuit you find here
Channel Structure:
For each bone in ProcessedData dataclass (excluding timestamp), 7 channels:
{bone}_pos_x- X position (meters or suit units){bone}_pos_y- Y position{bone}_pos_z- Z position{bone}_rot_w- Quaternion W component{bone}_rot_x- Quaternion X component{bone}_rot_y- Quaternion Y component{bone}_rot_z- Quaternion Z component
Example bones:
- Pelvis, Spine, Chest, Neck, Head
- Shoulders, Upper Arms, Forearms, Hands (L/R)
- Upper Legs, Lower Legs, Feet (L/R)
Metadata per Channel:
label: Flattened bone and component (e.g., "Pelvis_pos_x")unit: "meters" for position, "quaternion" for rotationtype: "position" or "rotation"id: Channel index
7. Raw Data Stream#
Stream Info:
- Name:
TS_RawData - Type:
RawData - Source ID:
{source_id}_raw - Sample Rate: 100 Hz
- Format:
cf_double64(64-bit float) - Channel Count: 280 channels (20 sensors × 14 channels/sensor)
Description: Streams raw IMU (Inertial Measurement Unit) sensor data from Teslasuit sensors, including accelerometer, gyroscope, and orientation data. More about biomechnical data in Teslasuit you find here
Channel Structure:
For each sensor in RawData dataclass (excluding timestamp), 14 channels:
{sensor}_boneId- Integer bone identifier{sensor}_q6_w- Quaternion W component{sensor}_q6_x- Quaternion X component{sensor}_q6_y- Quaternion Y component{sensor}_q6_z- Quaternion Z component{sensor}_accel_x- Acceleration X (raw units){sensor}_accel_y- Acceleration Y{sensor}_accel_z- Acceleration Z{sensor}_gyro_x- Gyroscope X (degrees/s or raw units){sensor}_gyro_y- Gyroscope Y{sensor}_gyro_z- Gyroscope Z{sensor}_linear_accel_x- Linear acceleration X (gravity removed){sensor}_linear_accel_y- Linear acceleration Y{sensor}_linear_accel_z- Linear acceleration Z
Metadata per Channel:
label: Flattened sensor and component (e.g., "LeftThigh_accel_x")unit: "id", "quaternion", "raw", "deg/s", or "m/s^2" (depending on data type)type: "boneId", "quaternion", "accel", "gyro", or "linear_accel"id: Channel index
Walking FES-Specific Streams#
The following stream is not part of RapidKit. It is provided by the Walking FES
example application (examples/walking_fes/).
8. ML Model-Based Step Detector (Walking FES example only)#
Stream Info:
- Name:
ModelBasedStepDetector - Type:
StepDetection - Source:
examples/walking_fes/(PyTorch ML model, not part ofRapidKit) - Sample Rate: 100 Hz
- Format:
cf_float32(32-bit float) - Channel Count: 2
Description: A PyTorch-based step detector that analyzes IMU data from 8 body sensors with a 30-sample rolling window. Outputs binary foot contact states after temporal smoothing and a 0.6 classification threshold.
Channels:
left_contact— Left foot contact (0.0 / 1.0)right_contact— Right foot contact (0.0 / 1.0)
Data Flow Summary#
Teslasuit Hardware
↓ (WiFi via Teslasuit Control Center)
SuitHandler (fes_framework/io/suit_handler.py)
↓ (raw ctypes frames)
DataStreamer (fes_framework/io/data_streamer.py)
↓ (populated dataclasses)
┌───────────────────────────────────────────────────────┐
│ BiomechanicalData ProcessedData StepDetectorData │
│ RawData EmsData ControlMessage UtilityMessage │
└──────────────────────┬────────────────────────────────┘
↓
LSLStreamer (7 framework streams)
↓
Lab Streaming Layer Network
↓
External Applications (LabRecorder, MATLAB, Python, etc.)LabRecorder#
All 8 streams are automatically discoverable in LabRecorder. Simply:
- Open LabRecorder
- Click "Update" to scan for streams
- Select desired streams
- Click "Record" to save to XDF format
Technical Notes#
Timestamp Handling#
- Streams use
pylsl.local_clock()for timestamping - Some dataclasses provide their own timestamps (biomechanical, skeleton, raw)
- All timestamps are in LSL's global time domain for synchronization
Performance Considerations#
- Total bandwidth: ~398 KB/s (569 channels, mixed float32/double64 formats)
- Stream sizes:
- Raw Data: 280 channels, 219 KB/s (largest)
- Bone Position: 140 channels, 109 KB/s
- Control Message: 80 channels, 31 KB/s
- EMS Parameters: 32 channels, 13 KB/s
- Biomechanical: 29 channels, 23 KB/s
- Utility Message: 6 channels, 2 KB/s (smallest)
- Step Detectors: 2 channels, 1 KB/s total
Data Types#
cf_float32: 32-bit float (4 bytes per sample) - Most streamscf_double64: 64-bit float (8 bytes per sample) - Biomechanical, Skeleton, Rawcf_int32: 32-bit integer (not currently used)
Troubleshooting#
Stream Not Appearing#
- Ensure
lsl_enabled=Truewas passed toClosedLoopEngineororchestrator.launch() - Ensure the backend engine is running
- Check firewall settings (LSL uses UDP multicast)
- Verify network connectivity if using remote streams
High Latency#
- Check system CPU usage
- Reduce number of active streams
- Increase LSL buffer sizes
Data Discontinuities#
- Check for Teslasuit connection issues
- Verify model buffer warmup completed (30 samples)
- Monitor for dropped samples in LSL inlet
See also#
- Architecture — where the LSLStreamer fits in the data flow
- IPC and processes — the other backend ↔ outside-world channels
- Implementation Guide → Step 6 — LSL inlets walkthrough
- Implementation Guide → Step 7 — recording with LabRecorder
- Source:
fes_framework/io/lsl_streamer.py
External references#
- LSL Protocol: https://github.com/sccn/labstreaminglayer
- Teslasuit Python API documentation: Python API overview
- XDF file format: https://github.com/sccn/xdf
- LabRecorder releases: https://github.com/labstreaminglayer/App-LabRecorder/releases
