Загрузка данных
PYTHON CODE:
import os
import serial
import tkinter as tk
from PIL import Image, ImageTk
# --- КОНФИГУРАЦИЯ ПОДКЛЮЧЕНИЯ ---
PORT = 'COM3' # Замените на ваш актуальный порт
BAUD_RATE = 9600
try:
ser = serial.Serial(PORT, BAUD_RATE, timeout=0.01)
except Exception as e:
ser = None
print(f"Внимание: Не удалось подключиться к {PORT}. Запуск в демо-режиме. Ошибка: {e}")
# --- ФУНКЦИОНАЛЬНАЯ ЛОГИКА ---
def send(cmd):
"""Отправка символьных команд в Arduino."""
if ser and ser.is_open:
ser.write(cmd.encode())
else:
print(f"[Демо] Отправлена команда: {cmd}")
def update_dist():
"""Чтение данных датчика из COM-порта в реальном времени."""
if ser and ser.is_open:
while ser.in_waiting:
try:
line = ser.readline().decode().strip()
if line.startswith("DIST:"):
# Выделяем число после двоеточия
d = line.split(":")[1]
dist_label.config(text=f"{d} см")
except Exception:
pass
root.after(50, update_dist)
def set_manual():
"""Переключение в ручной режим."""
send('M')
status_label.config(text="Режим: Ручной", fg="#2980b9")
def set_auto():
"""Переключение в автоматический режим."""
send('A')
status_label.config(text="Режим: Автомат", fg="#27ae60")
# --- СОЗДАНИЕ ИНТЕРФЕЙСА ---
root = tk.Tk()
root.title("Управление дроном")
root.geometry("360 rounded")
root.geometry("380x620")
root.configure(bg="#f8f9fa")
# =========================================================================
# МЕСТО ДЛЯ ДОБАВЛЕНИЯ ФОТО ДРОНА
# Положите изображение 'drone.png' в папку со скриптом. Размер подгонится сам.
# =========================================================================
IMAGE_PATH = 'drone.png'
if os.path.exists(IMAGE_PATH):
try:
img = Image.open(IMAGE_PATH).resize((220, 140), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(img)
img_label = tk.Label(root, image=photo, bg="#f8f9fa")
img_label.pack(pady=(15, 5))
except Exception:
pass
else:
# Заглушка, если фото не найдено
placeholder = tk.Label(root, text="[ Добавьте 'drone.png' в папку проекта ]",
font=("Arial", 9, "italic"), fg="#7f8c8d", bg="#f8f9fa")
placeholder.pack(pady=(20, 10))
# =========================================================================
# --- БЛОК ТЕЛЕМЕТРИИ (ДАТЧИК) ---
tk.Label(root, text="Расстояние до препятствия", font=("Arial", 10), fg="#7f8c8d", bg="#f8f9fa").pack()
dist_label = tk.Label(root, text="0 см", font=("Arial", 24, "bold"), fg="#2c3e50", bg="#f8f9fa")
dist_label.pack(pady=(0, 10))
# --- БЛОК ВЫБОРА РЕЖИМА ---
mode_frame = tk.Frame(root, bg="#f8f9fa")
mode_frame.pack(pady=5)
tk.Button(mode_frame, text="Ручной", font=("Arial", 10, "bold"), bg="#ebf5fb", fg="#2980b9",
command=set_manual, width=12, height=1, relief="groove").pack(side="left", padx=8)
tk.Button(mode_frame, text="Автомат", font=("Arial", 10, "bold"), bg="#e8f8f5", fg="#27ae60",
command=set_auto, width=12, height=1, relief="groove").pack(side="left", padx=8)
status_label = tk.Label(root, text="Режим: Ручной", font=("Arial", 11, "italic"), fg="#2980b9", bg="#f8f9fa")
status_label.pack(pady=(5, 15))
# --- ДЖОЙСТИК УПРАВЛЕНИЯ ---
btn_frame = tk.Frame(root, bg="#f8f9fa")
btn_frame.pack(pady=10)
# Стилизация навигационных кнопок
btn_style = {"font": ("Arial", 10, "bold"), "bg": "#ffffff", "fg="#34495e", "width": 9, "height": 2, "relief": "raised"}
# Сетка кнопок (Исправлена накладка кнопок Назад/Вниз)
tk.Button(btn_frame, text="Вперёд", command=lambda: send('F'), **btn_style).grid(row=0, column=1, padx=6, pady=6)
tk.Button(btn_frame, text="Влево", command=lambda: send('L'), **btn_style).grid(row=1, column=0, padx=6, pady=6)
tk.Button(btn_frame, text="Вверх", command=lambda: send('U'), **btn_style).grid(row=1, column=1, padx=6, pady=6)
tk.Button(btn_frame, text="Вправо", command=lambda: send('R'), **btn_style).grid(row=1, column=2, padx=6, pady=6)
tk.Button(btn_frame, text="Назад", command=lambda: send('B'), **btn_style).grid(row=2, column=1, padx=6, pady=6)
tk.Button(btn_frame, text="Вниз", command=lambda: send('D'), **btn_style).grid(row=3, column=1, padx=6, pady=6)
# --- ЗАПУСК ПОТОКОВ ---
root.after(100, update_dist)
root.mainloop()
ARDUINO IDE CODE:
#include <Servo.h>
// --- НАСТРОЙКА ПИНОВ И ПЕРИФЕРИИ ---
Servo servoYaw;
Servo servoPitch;
const int motorPin = 5;
const int trigPin = 12;
const int echoPin = 13;
// --- ПЕРЕМЕННЫЕ СОСТОЯНИЯ ---
int motorSpeed = 150;
int yaw = 90;
int pitch = 90;
char mode = 'M';
void setup() {
Serial.begin(9600);
// Инициализация сервоприводов и пинов
servoYaw.attach(9);
servoPitch.attach(6);
pinMode(motorPin, OUTPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// Начальное положение и запуск мотора
servoYaw.write(yaw);
servoPitch.write(pitch);
analogWrite(motorPin, motorSpeed);
}
void loop() {
// 1. Измерение расстояния ультразвуковым датчиком
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
int dist = constrain(duration * 0.034 / 2, 0, 150);
// Отправка телеметрии в Python-приложение
Serial.print("DIST:");
Serial.println(dist);
// 2. Обработка команд от Python (Смена режимов и ручное управление)
if (Serial.available()) {
char cmd = Serial.read();
// Смена режима работы дрона
if (cmd == 'M') mode = 'M';
if (cmd == 'A') mode = 'A';
// ЛОГИКА РУЧНОГО РЕЖИМА ('M')
if (mode == 'M') {
switch (cmd) {
case 'U': motorSpeed = min(255, motorSpeed + 20); break; // Вверх (Газ +)
case 'D': motorSpeed = max(0, motorSpeed - 20); break; // Вниз (Газ -)
case 'L': yaw = max(0, yaw - 15); break; // Влево
case 'R': yaw = min(180, yaw + 15); break; // Вправо
case 'F': pitch = min(180, pitch + 15); break; // Вперед (Тангаж)
case 'B': pitch = max(0, pitch - 15); break; // Назад (Тангаж)
}
// Применение ручных команд к исполнительным механизмам
servoYaw.write(yaw);
servoPitch.write(pitch);
analogWrite(motorPin, motorSpeed);
}
}
// 3. ЛОГИКА АВТОМАТИЧЕСКОГО РЕЖИМА ('A')
if (mode == 'A') {
if (dist < 30) {
// Обход препятствия (ближе 30 см)
analogWrite(motorPin, 200);
servoYaw.write(120);
servoPitch.write(110);
} else {
// Стабильный полет (путь свободен)
analogWrite(motorPin, 150);
servoYaw.write(90);
servoPitch.write(90);
}
}
delay(50); // Частота работы цикла 20 Гц
}