🌐 Часть 34: Распределенные вычисления на нескольких Arduino (Кластер из 8 плат)

Одна Arduino слаба, но 8 Arduino, объединенных в кластер, могут решать задачи, недоступные одному контроллеру! Мы создадим параллельный вычислительный кластер на основе I2C или SPI.

Архитектура кластера:

  • Мастер (Master): Arduino Mega 2560 — распределяет задачи, собирает результаты.
  • Слейвы (Slaves): 7 плат Arduino Uno/Nano — выполняют вычисления.

Протокол обмена данными:

cpp

// master.ino - Код для главной платы
#include <Wire.h>

#define SLAVE_COUNT 7
#define SLAVE_ADDR_START 8

// Структура задачи
struct Task {
    uint8_t id;
    uint8_t type;      // 0=math, 1=matrix, 2=sort
    uint8_t data[32];
    uint8_t size;
};

// Структура результата
struct Result {
    uint8_t task_id;
    uint8_t status;    // 0=ok, 1=error
    uint8_t data[32];
    uint8_t size;
};

Task taskQueue[10];
uint8_t taskCount = 0;
uint8_t slaveStatus[7];

void setup() {
    Wire.begin();
    Serial.begin(115200);
    
    // Инициализация слейвов
    for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
        Wire.beginTransmission(SLAVE_ADDR_START + i);
        Wire.write(0x01);  // Команда: инициализация
        Wire.endTransmission();
        slaveStatus[i] = 0;
    }
}

// Отправка задачи на слейв
uint8_t sendTask(uint8_t slave, Task* task) {
    Wire.beginTransmission(SLAVE_ADDR_START + slave);
    Wire.write(0x02);  // Команда: выполнить задачу
    Wire.write(task->id);
    Wire.write(task->type);
    Wire.write(task->size);
    Wire.write(task->data, task->size);
    uint8_t err = Wire.endTransmission();
    
    if (err == 0) {
        slaveStatus[slave] = 1;  // Занят
    }
    return err;
}

// Получение результата от слейва
uint8_t getResult(uint8_t slave, Result* result) {
    Wire.requestFrom(SLAVE_ADDR_START + slave, 34);
    if (Wire.available()) {
        result->task_id = Wire.read();
        result->status = Wire.read();
        result->size = Wire.read();
        for (uint8_t i = 0; i < result->size; i++) {
            result->data[i] = Wire.read();
        }
        slaveStatus[slave] = 0;  // Свободен
        return 1;
    }
    return 0;
}

// Пример задачи: умножение матриц (разбивка на части)
void distributeMatrixMultiplication(uint8_t* matrixA, uint8_t* matrixB, uint8_t size) {
    // Разбиваем матрицу на 7 частей
    uint8_t rowsPerSlave = size / SLAVE_COUNT;
    
    for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
        Task task;
        task.id = i;
        task.type = 1;  // Matrix multiplication
        task.size = rowsPerSlave * size * 2;  // A и B части
        
        // Копируем часть матрицы A
        for (uint8_t r = 0; r < rowsPerSlave; r++) {
            for (uint8_t c = 0; c < size; c++) {
                task.data[r * size + c] = matrixA[(i * rowsPerSlave + r) * size + c];
            }
        }
        // Копируем всю матрицу B
        for (uint8_t r = 0; r < size; r++) {
            for (uint8_t c = 0; c < size; c++) {
                task.data[rowsPerSlave * size + r * size + c] = matrixB[r * size + c];
            }
        }
        
        sendTask(i, &task);
    }
    
    // Сбор результатов
    Result results[7];
    for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
        while (!getResult(i, &results[i])) {
            delay(10);
        }
        // Объединяем результаты в итоговую матрицу
        // ...
    }
}

void loop() {
    // Периодически проверяем статус слейвов
    for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
        Wire.requestFrom(SLAVE_ADDR_START + i, 1);
        if (Wire.available()) {
            uint8_t status = Wire.read();
            if (status == 0 && slaveStatus[i] == 1) {
                // Слейв завершил задачу, получаем результат
                Result result;
                getResult(i, &result);
                // Обрабатываем результат
            }
        }
    }
}



---------------------------------------------------------------------------
cpp

// slave.ino - Код для каждой дочерней платы
#include <Wire.h>

#define SLAVE_ADDR 8

// Обработчик команд от мастера
void receiveEvent(int count) {
    uint8_t cmd = Wire.read();
    
    switch (cmd) {
        case 0x01:  // Инициализация
            // Настройка
            break;
            
        case 0x02: {  // Выполнить задачу
            uint8_t taskId = Wire.read();
            uint8_t taskType = Wire.read();
            uint8_t size = Wire.read();
            
            uint8_t data[32];
            for (uint8_t i = 0; i < size; i++) {
                data[i] = Wire.read();
            }
            
            // Выполняем задачу
            uint8_t result[32];
            uint8_t resultSize = 0;
            
            switch (taskType) {
                case 0:  // Math
                    resultSize = performMath(data, size, result);
                    break;
                case 1:  // Matrix
                    resultSize = performMatrix(data, size, result);
                    break;
                case 2:  // Sort
                    resultSize = performSort(data, size, result);
                    break;
            }
            
            // Сохраняем результат в буфер
            resultBuffer[0] = taskId;
            resultBuffer[1] = 0;  // Status OK
            resultBuffer[2] = resultSize;
            memcpy(&resultBuffer[3], result, resultSize);
            resultReady = true;
            break;
        }
    }
}

// Обработчик запроса результата
void requestEvent() {
    if (resultReady) {
        Wire.write(resultBuffer, resultSize + 3);
        resultReady = false;
    }
}

void setup() {
    Wire.begin(SLAVE_ADDR);
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);
}

void loop() {
    // Слейв может выполнять фоновые задачи
}

Вам также может понравиться

About the Author: ардуинчиков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *