📱 Программирование пульта дистанционного управления

Создаем систему беспроводного управления роботом

🎮 Интерфейс • 📡 Связь • 💻 Программирование • 🔧 Отладка
7 класс • Технология • 45 минут
mw285748 • 15.06.2025

🎯 Цель урока

💡 Научимся:

  • Программировать пульты дистанционного управления
  • Настраивать беспроводные модули связи
  • Создавать системы обработки команд
  • Тестировать и оптимизировать управление

🤖 Результат: Функциональная система дистанционного управления роботом!

🏗️ Архитектура системы управления

📊 Компоненты системы

[ОПЕРАТОР] ←→ [ПУЛЬТ] ~~~📡~~~ [РОБОТ] ←→ [ДАТЧИКИ]
    ↑           ↑                    ↑         ↑
 Команды    Интерфейс           Исполнение  Обратная
                                            связь

Основные блоки:

  1. Пульт управления - формирование команд
  2. Канал связи - передача данных
  3. Робот - исполнение команд
  4. Обратная связь - контроль состояния

🔄 Цикл управления

Алгоритм работы системы:

while True:
    # 1. Получение команд от оператора
    user_input = read_user_interface()
    
    # 2. Формирование пакета данных
    command_packet = encode_command(user_input)
    
    # 3. Передача по беспроводному каналу
    send_wireless(command_packet)
    
    # 4. Получение и обработка на роботе
    if packet_received():
        execute_command(decode_packet())
    
    # 5. Отправка обратной связи
    telemetry = read_sensors()
    send_feedback(telemetry)
    
    delay(10)  # Период управления

⏱️ Временные характеристики:

  • Период управления: 10-100 мс
  • Задержка передачи: 5-50 мс
  • Время обработки: 1-10 мс

📱 Программирование пульта управления

🎮 Типы интерфейсов

Мобильное приложение (Android/iOS):

// Пример кода для Android
public class RobotController extends AppCompatActivity {
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothSocket socket;
    
    // Обработка джойстика
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        
        // Преобразование координат в команды
        int speed = calculateSpeed(y);
        int direction = calculateDirection(x);
        
        // Отправка команды
        sendCommand("MOVE", speed, direction);
        return true;
    }
    
    private void sendCommand(String type, int param1, int param2) {
        String command = type + ":" + param1 + ":" + param2 + "\n";
        socket.getOutputStream().write(command.getBytes());
    }
}

🖥️ Веб-интерфейс

HTML + JavaScript:

<!DOCTYPE html>
<html>
<head>
    <title>Робот Control</title>
</head>
<body>
    <div class="joystick-container">
        <div id="joystick"></div>
    </div>
    
    <div class="buttons">
        <button onclick="sendCommand('LED_ON')">💡 Свет</button>
        <button onclick="sendCommand('HORN')">🔊 Сигнал</button>
        <button onclick="sendCommand('STOP')">🛑 Стоп</button>
    </div>
    
    <script>
        // WebSocket соединение
        const ws = new WebSocket('ws://192.168.1.100:8080');
        
        function sendCommand(cmd) {
            const packet = {
                command: cmd,
                timestamp: Date.now(),
                source: "web_interface"
            };
            ws.send(JSON.stringify(packet));
        }
        
        // Виртуальный джойстик
        const joystick = new VirtualJoystick({
            container: document.getElementById('joystick'),
            mouseSupport: true,
            onMove: function(event, data) {
                const speed = Math.round(data.force * 100);
                const angle = Math.round(data.angle.degree);
                sendCommand(`MOVE:${speed}:${angle}`);
            }
        });
    </script>
</body>
</html>

🎛️ Физический пульт

Arduino-пульт с джойстиками:

// Пульт на Arduino с nRF24L01
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN пины
const byte address[6] = "Robot1";

struct ControlData {
    int leftStickX;
    int leftStickY;
    int rightStickX;
    int rightStickY;
    bool button1;
    bool button2;
    bool button3;
    unsigned long timestamp;
};

void setup() {
    Serial.begin(9600);
    radio.begin();
    radio.openWritingPipe(address);
    radio.setPALevel(RF24_PA_MIN);
    radio.stopListening();
}

void loop() {
    ControlData data;
    
    // Чтение аналоговых джойстиков
    data.leftStickX = analogRead(A0);
    data.leftStickY = analogRead(A1);
    data.rightStickX = analogRead(A2);
    data.rightStickY = analogRead(A3);
    
    // Чтение цифровых кнопок
    data.button1 = digitalRead(2);
    data.button2 = digitalRead(3);
    data.button3 = digitalRead(4);
    
    // Временная метка
    data.timestamp = millis();
    
    // Отправка данных
    bool result = radio.write(&data, sizeof(data));
    
    if (result) {
        Serial.println("Данные отправлены успешно");
    } else {
        Serial.println("Ошибка передачи");
    }
    
    delay(50); // 20 пакетов в секунду
}

📡 Настройка беспроводных модулей

📶 Bluetooth (HC-05/ESP32)

Настройка HC-05:

// AT-команды для настройки HC-05
void setupBluetooth() {
    Serial.begin(9600);
    
    // Вход в режим AT-команд
    delay(1000);
    
    // Настройка имени устройства
    Serial.println("AT+NAME=RobotController");
    delay(1000);
    
    // Настройка скорости передачи
    Serial.println("AT+UART=115200,0,0");
    delay(1000);
    
    // Настройка пин-кода
    Serial.println("AT+PSWD=1234");
    delay(1000);
    
    // Роль - ведущий
    Serial.println("AT+ROLE=1");
    delay(1000);
}

ESP32 Bluetooth Classic:

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

void setup() {
    Serial.begin(115200);
    SerialBT.begin("ESP32_Robot"); // Имя Bluetooth устройства
    Serial.println("Устройство готово к сопряжению");
}

void loop() {
    if (SerialBT.available()) {
        String command = SerialBT.readString();
        command.trim();
        
        Serial.println("Получена команда: " + command);
        processCommand(command);
        
        // Отправка подтверждения
        SerialBT.println("OK:" + command);
    }
}

📶 Wi-Fi (ESP32/ESP8266)

Точка доступа (AP режим):

#include <WiFi.h>
#include <WebSocketsServer.h>

const char* ssid = "Robot_Control";
const char* password = "robot123";

WebSocketsServer webSocket = WebSocketsServer(81);

void setup() {
    Serial.begin(115200);
    
    // Создание точки доступа
    WiFi.softAP(ssid, password);
    Serial.println("AP IP адрес: " + WiFi.softAPIP().toString());
    
    // Настройка WebSocket
    webSocket.begin();
    webSocket.onEvent(webSocketEvent);
}

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
    switch(type) {
        case WStype_DISCONNECTED:
            Serial.printf("Клиент [%u] отключен\n", num);
            break;
            
        case WStype_CONNECTED:
            Serial.printf("Клиент [%u] подключен\n", num);
            break;
            
        case WStype_TEXT:
            String command = String((char*)payload);
            Serial.println("Получена команда: " + command);
            processCommand(command);
            
            // Отправка ответа
            webSocket.sendTXT(num, "OK");
            break;
    }
}

void loop() {
    webSocket.loop();
}

📻 Радиомодули (nRF24L01)

Настройка приемника:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN
const byte address[6] = "Robot1";

struct CommandPacket {
    char command[10];
    int param1;
    int param2;
    unsigned long timestamp;
    byte checksum;
};

void setup() {
    Serial.begin(9600);
    radio.begin();
    radio.openReadingPipe(0, address);
    radio.setPALevel(RF24_PA_MAX); // Максимальная мощность
    radio.setDataRate(RF24_250KBPS); // Низкая скорость = больше дальность
    radio.startListening();
}

void loop() {
    if (radio.available()) {
        CommandPacket packet;
        radio.read(&packet, sizeof(packet));
        
        // Проверка целостности
        if (validateChecksum(packet)) {
            Serial.println("Команда: " + String(packet.command));
            executeCommand(packet);
        } else {
            Serial.println("Ошибка целостности пакета");
        }
    }
}

bool validateChecksum(CommandPacket& packet) {
    // Простая проверка контрольной суммы
    byte calculated = 0;
    for (int i = 0; i < sizeof(packet) - 1; i++) {
        calculated ^= ((byte*)&packet)[i];
    }
    return calculated == packet.checksum;
}

🛠️ Практическая работа

📋 Варианты заданий

🟢 Вариант 1: Bluetooth-управление

  • Создать Android/iOS приложение
  • Настроить HC-05 модуль
  • Реализовать виртуальный джойстик
  • Добавить кнопки дополнительных функций

🟡 Вариант 2: Wi-Fi веб-интерфейс

  • Создать ESP32 точку доступа
  • Разработать веб-страницу управления
  • Использовать WebSocket для связи
  • Добавить видеопоток с камеры

🔴 Вариант 3: Радиопульт

  • Собрать физический пульт на Arduino
  • Настроить nRF24L01 модули
  • Создать эргономичный интерфейс
  • Реализовать обратную связь

⏱️ План работы (90 минут)

Этап 1: Выбор и планирование (10 мин)
• Выбор типа системы управления
• Распределение ролей в команде
• Планирование архитектуры

Этап 2: Настройка связи (15 мин)
• Подключение беспроводного модуля
• Настройка параметров связи
• Тест базового соединения

Этап 3: Программирование пульта (20 мин)
• Создание интерфейса управления
• Программирование отправки команд
• Тестирование элементов управления

Этап 4: Программирование робота (25 мин)
• Код приема и обработки команд
• Преобразование команд в действия
• Обработка ошибок и потери связи

Этап 5: Тестирование и отладка (15 мин)
• Проверка всех функций
• Оптимизация параметров
• Измерение дальности и задержек

Этап 6: Демонстрация (5 мин)
• Показ работы системы
• Объяснение реализации

💻 Примеры кода для робота

🤖 Обработка команд движения

// Универсальная система обработки команд
class RobotController {
private:
    int leftMotorPin1 = 3, leftMotorPin2 = 5;
    int rightMotorPin1 = 6, rightMotorPin2 = 9;
    unsigned long lastCommandTime = 0;
    const unsigned long TIMEOUT = 3000; // 3 секунды
    
public:
    void processCommand(String command) {
        lastCommandTime = millis();
        
        if (command.startsWith("MOVE:")) {
            // Формат: MOVE:speed:direction
            int firstColon = command.indexOf(':');
            int secondColon = command.indexOf(':', firstColon + 1);
            
            int speed = command.substring(firstColon + 1, secondColon).toInt();
            int direction = command.substring(secondColon + 1).toInt();
            
            move(speed, direction);
        }
        else if (command == "STOP") {
            stop();
        }
        else if (command == "LED_ON") {
            digitalWrite(13, HIGH);
        }
        else if (command == "LED_OFF") {
            digitalWrite(13, LOW);
        }
        else if (command.startsWith("SERVO:")) {
            int angle = command.substring(6).toInt();
            setServoAngle(angle);
        }
    }
    
    void move(int speed, int direction) {
        // Преобразование скорости и направления в команды моторам
        int leftSpeed = speed;
        int rightSpeed = speed;
        
        // Коррекция для поворота
        if (direction < 0) { // Поворот влево
            leftSpeed = speed * (100 + direction) / 100;
        } else if (direction > 0) { // Поворот вправо
            rightSpeed = speed * (100 - direction) / 100;
        }
        
        setMotorSpeed(leftMotorPin1, leftMotorPin2, leftSpeed);
        setMotorSpeed(rightMotorPin1, rightMotorPin2, rightSpeed);
    }
    
    void checkTimeout() {
        if (millis() - lastCommandTime > TIMEOUT) {
            stop(); // Аварийная остановка при потере связи
        }
    }
};

📊 Система обратной связи

// Отправка телеметрии
void sendTelemetry() {
    StaticJsonDocument<200> telemetry;
    
    telemetry["battery"] = readBatteryVoltage();
    telemetry["distance"] = readUltrasonic();
    telemetry["temperature"] = readTemperature();
    telemetry["position"]["x"] = robotX;
    telemetry["position"]["y"] = robotY;
    telemetry["timestamp"] = millis();
    
    String output;
    serializeJson(telemetry, output);
    
    // Отправка через выбранный канал связи
    if (bluetoothConnected) {
        SerialBT.println(output);
    }
    if (wifiConnected) {
        webSocket.broadcastTXT(output);
    }
}

float readBatteryVoltage() {
    int raw = analogRead(A7);
    return (raw * 5.0 / 1023.0) * 2; // Делитель напряжения
}

int readUltrasonic() {
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    
    long duration = pulseIn(echoPin, HIGH);
    return duration * 0.034 / 2; // Расстояние в см
}

🔧 Оптимизация системы

⚡ Повышение надежности

Контроль целостности данных:

struct SecurePacket {
    char command[16];
    int params[4];
    unsigned long timestamp;
    uint16_t crc16;
};

uint16_t calculateCRC16(uint8_t* data, size_t length) {
    uint16_t crc = 0xFFFF;
    for (size_t i = 0; i < length; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

Автоматическое переподключение:

void maintainConnection() {
    if (!bluetooth.connected()) {
        Serial.println("Потеря Bluetooth соединения. Переподключение...");
        bluetooth.reconnect();
    }
    
    if (WiFi.status() != WL_CONNECTED) {
        Serial.println("Потеря Wi-Fi. Переподключение...");
        WiFi.reconnect();
    }
}

📡 Оптимизация дальности

Настройки для максимальной дальности:

// nRF24L01 настройки
radio.setPALevel(RF24_PA_MAX);        // Максимальная мощность
radio.setDataRate(RF24_250KBPS);      // Низкая скорость
radio.setChannel(76);                 // Свободный канал
radio.setRetries(15, 15);             // Максимум повторов
radio.enableAckPayload();             // Подтверждение доставки
radio.enableDynamicPayloads();        // Динамический размер пакета

// ESP32 Wi-Fi настройки
WiFi.setTxPower(WIFI_POWER_19_5dBm);  // Максимальная мощность
wifi_config_t conf;
esp_wifi_get_config(WIFI_IF_AP, &conf);
conf.ap.max_connection = 1;           // Один клиент
esp_wifi_set_config(WIFI_IF_AP, &conf);

🎤 Демонстрация проектов

📊 Защита работ

План презентации (3 минуты на команду):

  1. Тип системы - какую технологию выбрали?
  2. Интерфейс - как выглядит пульт управления?
  3. Функции - что умеет делать робот?
  4. Демонстрация - показ в действии
  5. Особенности - уникальные решения

❓ Вопросы для обсуждения:

  • Почему выбрана именно эта технология связи?
  • Какая дальность действия достигнута?
  • Как система ведет себя при помехах?
  • Какие функции можно добавить?

🏆 Критерии оценки

📊 Оценочная матрица (20 баллов):

КритерийМаксимумОписание
Настройка связи5Стабильность, дальность, качество
Программирование пульта5Интерфейс, функциональность, эргономика
Обработка команд5Точность, плавность, обработка ошибок
Общий результат5Надежность, демонстрация, творчество

🎯 Перевод в оценки:

  • 18-20: “5” (отлично)
  • 14-17: “4” (хорошо)
  • 10-13: “3” (удовлетворительно)

🤔 Рефлексия “3-2-1”

📝 Анализ работы

3 вещи, которые удалось реализовать:

  • 2 трудности, с которыми столкнулись:

  • 1 идея для усовершенствования системы:

    Оценка работы группы (1-5): ___

    🏠 Домашнее задание

    🎯 Основное задание

    Разработать схему усовершенствованного пульта:

    Создать проект пульта управления с дополнительными функциями:

    • Дисплей для отображения телеметрии
    • Кнопки быстрого доступа к функциям
    • Регулировка чувствительности управления
    • Запись и воспроизведение макросов команд

    📋 Структура проекта:

    • Схема расположения элементов
    • Список необходимых компонентов
    • Описание функций каждого элемента
    • Оценка стоимости реализации

    🌟 Исследовательское задание

    Изучить обратную связь в системах управления:

    • Передача видеопотока с камеры робота
    • Телеметрия датчиков в реальном времени
    • Тактильная обратная связь (вибрация)
    • Звуковая индикация состояния робота

    🎉 Итоги урока

    🏆 Что освоили

    ✅ Научились:

    • Программировать интерфейсы дистанционного управления
    • Настраивать различные типы беспроводной связи
    • Создавать системы обработки команд
    • Тестировать и оптимизировать управление

    🧠 Поняли:

    • Программирование пульта требует понимания интерфейсов
    • Настройка связи влияет на качество управления
    • Обработка команд должна быть надежной и быстрой
    • Система управления нуждается в постоянной оптимизации

    🌟 Главный принцип

    “Хорошая система управления = Интуитивный интерфейс + Надежная связь + Быстрая обработка команд”

    🚀 Следующий шаг: Создание автономных роботов с элементами искусственного интеллекта

    💡 Теперь вы можете создать пульт для любого робота!