Skip to content
Denna sida är ännu inte tillgänglig på svenska.
← Tillbaka till projekten

TinyML Arduino Posture Classifier/ Sida 4 av 4

Federated Learning

Federated learning over Bluetooth Low Energy, sharing and aggregating model weights between Arduino devices using weighted averaging based on training data.

#tinyml#embedded#machine-learning#arduino#edge-computing#federated-learning

The Setup

Two Arduino devices can share their trained output weights over Bluetooth Low Energy. One device acts as the central (initiates the connection), the other as the peripheral (waits for connection).

The central device is determined by checking if it has a valid UUID:

if (Coms.getUUID()) {
  // THIS DEV IS CENTRAL
} else {
  // THIS DEV IS PERIPHERAL
}

The Protocol

The weight sharing protocol in federated.cpp works differently for central and peripheral:

Central device:

  1. Send its weights (W_a) to peripheral
  2. Send its batch count (n_a)
  3. Receive peripheral’s weights (W_b)
  4. Receive peripheral’s batch count (n_b)

Peripheral device:

  1. Wait for central to connect
  2. Receive central’s weights (W_b)
  3. Receive central’s batch count (n_b)
  4. Send its weights (W_a)
  5. Send its batch count (n_a)

The timing is critical: there are delays between operations to ensure the BLE stack has time to complete each transfer:

// Central sends first
if (!Coms.sendModel((float *)W_a.weights, sizeof(float) * W_out_length)) {
  return false;
}

delay(5000);  // Wait before sending batch count

if (!Coms.sendNBatches((const uint16_t)n_a, sizeof(uint16_t))) {
  return false;
}

// Then receive
if (!Coms.receiveModel((float *)W_b.weights, sizeof(float) * W_out_length)) {
  return false;
}

if (!Coms.receiveNBatches(&n_b, sizeof(uint16_t))) {
  return false;
}

The peripheral has longer delays (8 seconds) because it needs to wait for the central to initiate the connection and start sending.

Weighted Averaging

After exchanging weights, both devices compute a weighted average:

n_tot = n_a + n_b;

for (size_t i = 0; i < OUTPUT_SIZE; i++) {
  for (size_t j = 0; j < RESERVOIR_SIZE; j++) {
    W_a.weights[i][j] = 
      (W_a.weights[i][j] * (float)n_a + W_b.weights[i][j] * (float)n_b) / (float)n_tot;
  }
}

If device A trained on 10 batches and device B trained on 5 batches, the aggregated weights give 2/3 weight to A and 1/3 to B. This ensures devices that have seen more data have more influence on the shared model.

Both devices end up with the same aggregated weights, so they’re synchronized after the exchange.

The Trigger

Federated weight sharing is triggered by the CMD_SHARE_WEIGHTS command in engine.cpp:

case CMD_SHARE_WEIGHTS: {
  turnOffLED();
  
  // Get total batches processed (including current session)
  uint16_t mem;
  getNProcessedBatches(&mem);
  uint16_t curr = mem + nBatchesProcessed;
  
  // Perform weight sharing
  shareW_out(&curr);
  
  // Update batch count
  nBatchesProcessed = curr - mem;
  
  // Visual feedback via LED
  communicateUSBMode();
  delay(1000);
  communicateSuccess();
  delay(1000);
  communicateBLEMode();
  delay(1000);
  turnOffLED();
  
  // Reset communication mode
  CommunicationMode coms = getCommunicationMode();
  Coms.setBackend(coms);
  break;
}

The LED blinks to show the mode (USB → success → BLE) during the exchange, then the device prompts for a new communication mode selection.

What Gets Shared

Only the output weights (W_out) are shared. The reservoir weights (W_in, W_res) remain fixed and identical across devices (they were initialized with the same random seed). The EMA values are not shared, so each device maintains its own normalization parameters.

The shared structure is defined in esn.h:

typedef struct {
  float weights[OUTPUT_SIZE][RESERVOIR_SIZE];
} shareableWeights;

This is 3 × 20 = 60 floats = 240 bytes -> small enough to transfer quickly over BLE.

The Python Helper

The python_helper/ble_shell.py script provides a command-line interface for managing federated learning. It can:

  • Connect to devices over BLE
  • Send training commands
  • Trigger weight sharing
  • Monitor the exchange process

The script uses the bleak library for cross-platform BLE support.

Limitations

The current implementation has some constraints:

  • Pairwise only: Only two devices can exchange weights at a time. Multi-device federation would require multiple rounds.
  • Manual triggering: Weight sharing must be explicitly triggered - it doesn’t happen automatically.
  • No conflict resolution: If devices have diverged significantly, the weighted average might not produce a better model.
  • BLE reliability: Connection drops can interrupt the exchange, requiring retry logic.

Despite these limitations, the implementation demonstrates that federated learning is feasible on resource-constrained microcontrollers using only BLE communication.