🎮 Интерфейс • 📡 Связь • 💻 Программирование • 🔧 Отладка
7 класс • Технология • 45 минут
mw285748 • 15.06.2025
💡 Научимся:
🤖 Результат: Функциональная система дистанционного управления роботом!
[ОПЕРАТОР] ←→ [ПУЛЬТ] ~~~📡~~~ [РОБОТ] ←→ [ДАТЧИКИ]
↑ ↑ ↑ ↑
Команды Интерфейс Исполнение Обратная
связь
Основные блоки:
Алгоритм работы системы:
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) # Период управления
⏱️ Временные характеристики:
Мобильное приложение (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 пакетов в секунду
}
Настройка 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);
}
}
Точка доступа (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();
}
Настройка приемника:
#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-управление
🟡 Вариант 2: Wi-Fi веб-интерфейс
🔴 Вариант 3: Радиопульт
Этап 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 минуты на команду):
❓ Вопросы для обсуждения:
📊 Оценочная матрица (20 баллов):
| Критерий | Максимум | Описание |
|---|---|---|
| Настройка связи | 5 | Стабильность, дальность, качество |
| Программирование пульта | 5 | Интерфейс, функциональность, эргономика |
| Обработка команд | 5 | Точность, плавность, обработка ошибок |
| Общий результат | 5 | Надежность, демонстрация, творчество |
🎯 Перевод в оценки:
3 вещи, которые удалось реализовать:
2 трудности, с которыми столкнулись:
1 идея для усовершенствования системы:
Оценка работы группы (1-5): ___
Разработать схему усовершенствованного пульта:
Создать проект пульта управления с дополнительными функциями:
📋 Структура проекта:
Изучить обратную связь в системах управления:
✅ Научились:
🧠 Поняли:
“Хорошая система управления = Интуитивный интерфейс + Надежная связь + Быстрая обработка команд”
🚀 Следующий шаг: Создание автономных роботов с элементами искусственного интеллекта
💡 Теперь вы можете создать пульт для любого робота!