P2
Отлично! Создаю эталонную STEM-презентацию по практическому программированию систем следования по линии с глубоким техническим анализом:Отлично! Создаю эталонную STEM-презентацию для практической работы по программированию датчиков линии с глубоким математическим анализом алгоритмов управления.
title: "STEM-презентация: Программирование работы датчика линии"
tag: "line-following-programming"
description: |
## 🛤️ Создание STEM-презентации "Практическая работа: Программирование работы датчика линии"
### 📋 Исходные данные
**Учитель:** Ахметов Рустам
**Школа:** ГБОУ Школа № 1362
**Класс:** 6 (физмат-профиль)
**Модуль:** Сенсорные системы роботов
**Тип урока:** Практическая работа (90 минут)
**Дата:** 2025-06-13
### 🎯 Цель презентации
Создать комплексную методическую презентацию для практической работы по программированию датчиков линии с акцентом на алгоритмическую разработку, математические модели управления и оптимизацию параметров системы.
### 🔬 STEM-интеграция урока
- **S (Science):** Физика сенсоров, оптика, калибровка измерительных систем
- **T (Technology):** Датчики линии, микроконтроллеры, системы автоматического управления
- **E (Engineering):** Алгоритмы управления, PID-регуляторы, оптимизация систем
- **M (Mathematics):** Теория управления, дифференциальные уравнения, алгоритмы
### 📊 Структура урока по карте
1. **Организационный момент** (3 мин)
2. **Постановка задачи** (5 мин)
3. **Подготовка оборудования** (10 мин)
4. **Калибровка датчиков** (10 мин)
5. **Изучение алгоритмов следования** (8 мин)
6. **Программирование базового алгоритма** (15 мин)
7. **Тестирование работы робота** (10 мин)
8. **Усовершенствование алгоритма** (15 мин)
9. **Финальное тестирование** (10 мин)
10. **Рефлексия и итоги** (4 мин)
### 🧠 Ключевые технические аспекты
- Методы калибровки и определения пороговых значений
- Алгоритмы управления: от простых до PID-регуляторов
- Математическое моделирование поведения робота
- Оптимизация параметров для различных условий
- Обработка особых случаев (перекрестки, разрывы линии)
### 🛠️ Практические задачи
- Установка и калибровка датчиков линии
- Программирование алгоритмов следования различной сложности
- Настройка параметров управления
- Тестирование в различных условиях освещения
- Оптимизация производительности системы
### 🔧 Математический аппарат
- Теория автоматического управления
- PID-регуляторы и настройка коэффициентов
- Алгоритмы цифровой фильтрации сигналов
- Методы оптимизации параметров системы
---
title: "🛤️ Программирование работы датчика линии"
subtitle: "От алгоритмов управления до математической оптимизации поведения"
author: "Ахметов Рустам"
date: "2025-06-13"
class: "6 класс"
subject: "Технология • Сенсорные системы роботов"
duration: "90 минут"
layout: "list"
outputs:
- "reveal"
theme: "control-algorithms"
transition: "slide"
backgroundTransition: "fade"
controls: true
progress: true
history: true
center: true
touch: true
loop: false
rtl: false
navigationMode: "default"
---
# 🛤️ Программирование работы датчика линии
## От алгоритмов управления до математической оптимизации поведения
**🧮 Алгоритмы • 📊 Теория управления • 💻 Программирование • 🎯 Оптимизация**
*6 класс • Технология • 90 минут*
👨🏫 **Учитель:** Ахметов Рустам
🏫 **Школа:** ГБОУ № 1362
📅 **Дата:** 2025-06-13
🎯 **Цель:** Создать интеллектуальную систему навигации по линии!
---
## 🎯 План алгоритмической разработки
**💻 Наша программистская миссия:**
1. **⚠️ Подготовка системы** - безопасность и инициализация (3 мин)
2. **🎯 Техническое задание** - формулировка алгоритмической задачи (5 мин)
3. **🔧 Установка оборудования** - интеграция датчиков в систему (10 мин)
4. **📐 Калибровка сенсоров** - настройка пороговых значений (10 мин)
5. **🧮 Математические модели** - теория алгоритмов управления (8 мин)
6. **💻 Базовое программирование** - реализация простых алгоритмов (15 мин)
7. **🧪 Тестирование и отладка** - верификация работы системы (10 мин)
8. **⚡ Продвинутые алгоритмы** - PID-регуляторы и оптимизация (15 мин)
9. **🏁 Итоговые испытания** - тестирование в различных условиях (10 мин)
10. **🤔 Анализ результатов** - оценка эффективности алгоритмов (4 мин)
**🎯 К концу практикума мы сможем:**
- 📐 Калибровать сенсорные системы для точных измерений
- 🧮 Программировать математические алгоритмы управления
- ⚡ Оптимизировать параметры для максимальной эффективности
- 🎛️ Создавать адаптивные системы автоматического управления
---
⚠️ Подготовка инженерной лаборатории
Инициализация систем и безопасность
🛡️ Протокол безопасности
⚡ Электробезопасность при работе с датчиками:
- Проверка правильности подключения перед подачей питания
- Соблюдение полярности при подключении датчиков
- Использование токоограничивающих резисторов
- Избегание короткого замыкания сигнальных линий
💻 Безопасность программирования:
- Создание резервных копий перед каждым изменением кода
- Поэтапное тестирование новых функций
- Использование отладочных выводов для мониторинга
- Документирование всех изменений в коде
🤖 Безопасность тестирования роботов:
- Ограниченная скорость при первых запусках
- Контролируемая зона тестирования
- Готовность к экстренной остановке
- Защита датчиков от механических повреждений
🧰 Программные инструменты и среды
💻 Среды разработки по платформам:
Arduino IDE:
// Настройка среды для работы с датчиками линии
#define LEFT_SENSOR A0
#define RIGHT_SENSOR A1
#define LEFT_MOTOR 5
#define RIGHT_MOTOR 6
void setup() {
Serial.begin(9600);
pinMode(LEFT_MOTOR, OUTPUT);
pinMode(RIGHT_MOTOR, OUTPUT);
Serial.println("=== СИСТЕМА СЛЕДОВАНИЯ ПО ЛИНИИ ===");
Serial.println("Инициализация датчиков...");
}
// Функция диагностики системы
void systemDiagnostics() {
Serial.println("Проверка подключения датчиков:");
Serial.print("Левый датчик: ");
Serial.println(analogRead(LEFT_SENSOR));
Serial.print("Правый датчик: ");
Serial.println(analogRead(RIGHT_SENSOR));
}
LEGO MINDSTORMS EV3:
Настройка среды EV3-G:
- Подключение датчиков цвета в режиме отражения
- Настройка портов моторов для левого и правого колеса
- Конфигурация параметров связи для отладки
- Установка начальной мощности моторов
Python для Raspberry Pi:
import RPi.GPIO as GPIO
import time
# Инициализация GPIO для датчиков линии
LEFT_SENSOR = 18
RIGHT_SENSOR = 19
LEFT_MOTOR_PWM = 20
RIGHT_MOTOR_PWM = 21
def initialize_system():
GPIO.setmode(GPIO.BCM)
GPIO.setup(LEFT_SENSOR, GPIO.IN)
GPIO.setup(RIGHT_SENSOR, GPIO.IN)
GPIO.setup(LEFT_MOTOR_PWM, GPIO.OUT)
GPIO.setup(RIGHT_MOTOR_PWM, GPIO.OUT)
print("=== СИСТЕМА НАВИГАЦИИ ГОТОВА ===")
print("Начинаем калибровку...")
def system_check():
left_value = GPIO.input(LEFT_SENSOR)
right_value = GPIO.input(RIGHT_SENSOR)
print(f"Датчики: L={left_value}, R={right_value}")
📊 Подготовка тестового полигона
🛤️ Конфигурация трассы для тестирования:
Схема тестового полигона:
┌─────────────────────────────────────────────────┐
│ │
│ 🏁 СТАРТ │
│ ║ │
│ ║ ← Прямой участок (50 см) │
│ ║ │
│ ╚══╗ │
│ ║ ← Плавный поворот (R=30 см) │
│ ║ │
│ ╔══╝ │
│ ║ │
│ ║ ← Прямой участок (30 см) │
│ ║ │
│ ╚══╗ │
│ ║ ← Острый поворот (R=15 см) │
│ ╔══╝ │
│ ║ │
│ ║ ← Финишный участок (20 см) │
│ 🏁 ФИНИШ │
└─────────────────────────────────────────────────┘
Технические параметры:
- Ширина линии: 20 мм
- Материал: черная изолента на белом фоне
- Контрастность: > 70%
- Общая длина трассы: 150 см
📋 Контрольные точки для тестирования:
Участок Тип испытания Критерий успеха Время прохождения Участок 1 Прямолинейное движение Отклонение < 5 мм < 5 сек Участок 2 Плавный поворот Без потери линии < 3 сек Участок 3 Острый поворот Восстановление < 1 сек < 4 сек Полная трасса Комплексное испытание Без остановок < 15 сек
---
🎯 Техническое задание
Формулировка алгоритмической задачи
🎯 Постановка задачи управления
📋 Техническое задание:
Основная цель: Разработать адаптивную систему автоматического управления роботом для точного следования по контрастной линии с оптимизацией скорости и плавности движения.
Технические требования:
- Максимальное отклонение от центра линии: ±3 мм
- Время восстановления после возмущения: < 0.5 сек
- Скорость движения: 0.2-0.8 м/с (переменная)
- Плавность управления: без резких рывков
- Адаптация к изменению освещения: автоматическая
🎮 Функциональные требования:
Базовый уровень (обязательный):
1. Калибровка датчиков:
- Определение пороговых значений для черного/белого
- Сохранение калибровочных данных
- Возможность рекалибровки
2. Следование по прямой линии:
- Стабильное движение по центру
- Коррекция при отклонениях
- Индикация состояния датчиков
3. Прохождение поворотов:
- Плавные повороты без потери линии
- Адаптация скорости в поворотах
- Восстановление после временной потери
Продвинутый уровень (дополнительный):
4. Оптимизация производительности:
- PID-регулятор для плавного управления
- Переменная скорость в зависимости от кривизны
- Предсказание траектории
5. Обработка особых случаев:
- Перекрестки и разветвления
- Прерывистые линии
- Изменение ширины линии
🔬 Анализ системы управления
📊 Математическая модель робота:
Робот как объект управления можно представить упрощенной моделью:
\[\frac{d\theta}{dt} = \frac{v_r - v_l}{L}\]
\[\frac{dx}{dt} = \frac{v_r + v_l}{2} \cos(\theta)\]
\[\frac{dy}{dt} = \frac{v_r + v_l}{2} \sin(\theta)\]где:
- θ - угол поворота робота
- v_r, v_l - скорости правого и левого колеса
- L - расстояние между колесами
- x, y - координаты робота
🎛️ Переменные состояния системы:
Входные переменные:
- sensor_left: значение левого датчика [0-1023]
- sensor_right: значение правого датчика [0-1023]
- sensor_center: значение центрального датчика [0-1023] (опционально)
Промежуточные переменные:
- line_position: позиция линии относительно робота [-1.0...+1.0]
- line_detected: флаг обнаружения линии [true/false]
- error: ошибка позиционирования [-1.0...+1.0]
Выходные переменные:
- motor_left_speed: скорость левого мотора [0-255]
- motor_right_speed: скорость правого мотора [0-255]
- status_led: индикация состояния системы
🎯 Критерии качества управления
📈 Показатели эффективности системы:
Точность следования:
\[\text{RMSE} = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(e_i)^2}\]где e_i - отклонение от центра линии в i-й момент времени
Плавность управления:
\[\text{Smoothness} = \frac{1}{n-1}\sum_{i=1}^{n-1}|u_{i+1} - u_i|\]где u_i - управляющее воздействие в i-й момент
Скорость прохождения:
\[\text{Speed} = \frac{L_{total}}{T_{total}}\]где L_total - общая длина трассы, T_total - время прохождения
📊 Целевые значения показателей:
Показатель Единица Базовый уровень Продвинутый уровень RMSE отклонения мм < 5 < 2 Плавность Δu/dt < 50 < 20 Скорость см/с > 15 > 25 Время восстановления мс < 1000 < 300
🔄 Алгоритм оценки качества:
class PerformanceAnalyzer {
private:
float position_errors[1000];
float control_signals[1000];
int sample_count = 0;
public:
void addSample(float error, float control) {
if(sample_count < 1000) {
position_errors[sample_count] = error;
control_signals[sample_count] = control;
sample_count++;
}
}
float calculateRMSE() {
float sum_squares = 0;
for(int i = 0; i < sample_count; i++) {
sum_squares += position_errors[i] * position_errors[i];
}
return sqrt(sum_squares / sample_count);
}
float calculateSmoothness() {
float sum_changes = 0;
for(int i = 1; i < sample_count; i++) {
sum_changes += abs(control_signals[i] - control_signals[i-1]);
}
return sum_changes / (sample_count - 1);
}
void printReport() {
Serial.println("=== ОТЧЕТ О ПРОИЗВОДИТЕЛЬНОСТИ ===");
Serial.print("RMSE ошибки: ");
Serial.println(calculateRMSE());
Serial.print("Плавность: ");
Serial.println(calculateSmoothness());
Serial.print("Количество образцов: ");
Serial.println(sample_count);
}
};
---
🔧 Установка и интеграция оборудования
Системная интеграция датчиков
📐 Оптимальное размещение датчиков
🔍 Конфигурации датчиков для различных задач:
Двухсенсорная система (базовая):
Схема размещения:
[L] [R]
│ │
├───────┤ ← 15-20 мм
═══●═══════●═══ ← Линия (20 мм)
│ │
ПЛАТФОРМА
Логика работы:
L=0, R=0 → Линия потеряна или между датчиками
L=1, R=0 → Линия слева, поворот направо
L=0, R=1 → Линия справа, поворот налево
L=1, R=1 → Широкая линия или перекресток
Преимущества:
✓ Простота программирования
✓ Низкая стоимость
✓ Быстрая реакция
Ограничения:
✗ Низкое разрешение
✗ Рывки в движении
✗ Сложность точной калибровки
Трехсенсорная система (рекомендуемая):
Схема размещения:
[L] [C] [R]
│ │ │
├─────┼─────┤ ← 10 мм каждый
═══●═════●═════●═══ ← Линия
│ │ │
ПЛАТФОРМА
Состояния и реакции:
001 → Поворот налево (сильный)
010 → Движение прямо
100 → Поворот направо (сильный)
011 → Поворот налево (слабый)
110 → Поворот направо (слабый)
111 → Перекресток или широкая линия
000 → Поиск линии
Преимущества:
✓ Плавное управление
✓ Хорошая точность
✓ Простая калибровка
Пятисенсорная система (профессиональная):
Схема размещения:
[L2][L1][C][R1][R2]
│ │ │ │ │
├───┼───┼───┼───┤ ← 6 мм между соседними
═●═══●═══●═══●═══●═ ← Линия
│ │ │ │ │
ПЛАТФОРМА
Расчет позиции линии:
position = (L2×(-2) + L1×(-1) + C×0 + R1×1 + R2×2) / total_active
Преимущества:
✓ Максимальная точность
✓ Плавное движение
✓ Возможность интерполяции
✓ Хорошее восстановление
⚡ Электрическое подключение
🔌 Схемы подключения для Arduino:
Подключение трех аналоговых датчиков TCRT5000:
Arduino Uno Датчик линии (Left) Датчик линии (Center) Датчик линии (Right)
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 5V ├────┤ VCC │ │ VCC │ │ VCC │
│ GND ├────┤ GND │ │ GND │ │ GND │
│ A0 ├────┤ Analog Out │ │ │ │ │
│ A1 ├────┼─────────────────┘ │ Analog Out │ │ │
│ A2 ├────┼──────────────────────┤ │ │ Analog Out │
│ │ └─────────────────────────────────────────┘ └─────────────────┘
│ Pin 5 ├──── Left Motor (PWM)
│ Pin 6 ├──── Right Motor (PWM)
│ Pin 7 ├──── Left Motor Direction
│ Pin 8 ├──── Right Motor Direction
│ Pin 13 ├──── Status LED
└─────────────┘
📊 Проверка подключения:
void verifyConnections() {
Serial.println("=== ПРОВЕРКА ПОДКЛЮЧЕНИЯ ДАТЧИКОВ ===");
// Проверка питания
for(int i = 0; i < 3; i++) {
int value = analogRead(A0 + i);
Serial.print("Датчик ");
Serial.print(i);
Serial.print(": ");
Serial.print(value);
if(value < 50) {
Serial.println(" ❌ Возможно не подключен");
} else if(value > 950) {
Serial.println(" ⚠️ Возможно нет нагрузки");
} else {
Serial.println(" ✅ Подключение OK");
}
}
// Проверка моторов
digitalWrite(LEFT_MOTOR_DIR, HIGH);
analogWrite(LEFT_MOTOR_PWM, 100);
delay(500);
analogWrite(LEFT_MOTOR_PWM, 0);
digitalWrite(RIGHT_MOTOR_DIR, HIGH);
analogWrite(RIGHT_MOTOR_PWM, 100);
delay(500);
analogWrite(RIGHT_MOTOR_PWM, 0);
Serial.println("Проверка моторов завершена");
}
🛠️ Механическое крепление
🔧 Требования к установке датчиков:
Критические параметры установки:
1. Высота над поверхностью:
- Оптимальная: 3-5 мм
- Минимальная: 2 мм
- Максимальная: 8 мм
2. Угол наклона:
- Рекомендуемый: 0° (перпендикулярно)
- Допустимый: ±5°
- Критический: ±15°
3. Расстояние от центра робота:
- Минимальное: 30 мм (для маневренности)
- Оптимальное: 50-70 мм
- Максимальное: 100 мм (для больших роботов)
4. Защита от вибраций:
- Жесткое крепление к раме
- Амортизация при необходимости
- Защита проводов от перегибов
📐 Проверочный тест установки:
void testSensorPlacement() {
Serial.println("=== ТЕСТ КАЧЕСТВА УСТАНОВКИ ===");
// Тест стабильности показаний
float readings[3][20];
for(int sample = 0; sample < 20; sample++) {
for(int sensor = 0; sensor < 3; sensor++) {
readings[sensor][sample] = analogRead(A0 + sensor);
}
delay(50);
}
// Анализ стабильности
for(int sensor = 0; sensor < 3; sensor++) {
float mean = 0, stddev = 0;
// Среднее значение
for(int i = 0; i < 20; i++) {
mean += readings[sensor][i];
}
mean /= 20;
// Стандартное отклонение
for(int i = 0; i < 20; i++) {
stddev += pow(readings[sensor][i] - mean, 2);
}
stddev = sqrt(stddev / 19);
Serial.print("Датчик ");
Serial.print(sensor);
Serial.print(": среднее=");
Serial.print(mean);
Serial.print(", σ=");
Serial.print(stddev);
if(stddev < 5) {
Serial.println(" ✅ Стабильно");
} else if(stddev < 15) {
Serial.println(" ⚠️ Умеренные колебания");
} else {
Serial.println(" ❌ Нестабильно - проверить крепление");
}
}
}
---
## 📐 Калибровка сенсорной системы
### 🎛️ Методология точной калибровки
**🔬 Теоретические основы калибровки:**
Калибровка датчиков линии - критически важный процесс, определяющий точность работы всей системы. Цель калибровки - установить соответствие между физическими характеристиками поверхности и цифровыми значениями датчиков.
**📊 Математическая модель калибровки:**
\[V_{norm} = \frac{V_{raw} - V_{min}}{V_{max} - V_{min}}\]
где:
- V_norm - нормализованное значение [0...1]
- V_raw - сырое значение АЦП
- V_min - минимальное значение (черная поверхность)
- V_max - максимальное значение (белая поверхность)
**🎯 Определение порогового значения:**
\[V_{threshold} = V_{min} + k \times (V_{max} - V_{min})\]
где k - коэффициент порога (обычно 0.4-0.6 для максимальной помехоустойчивости)
---
### 📋 Пошаговая процедура калибровки
**🔧 Автоматическая калибровка:**
```cpp
class SensorCalibration {
private:
int sensor_pins[3] = {A0, A1, A2};
int min_values[3] = {1023, 1023, 1023};
int max_values[3] = {0, 0, 0};
int threshold_values[3];
bool calibrated = false;
public:
void performCalibration() {
Serial.println("=== АВТОМАТИЧЕСКАЯ КАЛИБРОВКА ===");
Serial.println("Поместите робота на белую поверхность");
Serial.println("Нажмите кнопку для начала...");
waitForButtonPress();
// Калибровка на белой поверхности
Serial.println("Калибровка белого... (5 сек)");
calibrateWhiteSurface();
Serial.println("Поместите робота на черную линию");
Serial.println("Нажмите кнопку для продолжения...");
waitForButtonPress();
// Калибровка на черной поверхности
Serial.println("Калибровка черного... (5 сек)");
calibrateBlackSurface();
// Расчет пороговых значений
calculateThresholds();
// Сохранение результатов
saveCalibrationData();
calibrated = true;
Serial.println("✅ Калибровка завершена успешно!");
printCalibrationResults();
}
private:
void calibrateWhiteSurface() {
unsigned long start_time = millis();
int sample_count = 0;
while(millis() - start_time < 5000) {
for(int i = 0; i < 3; i++) {
int value = analogRead(sensor_pins[i]);
if(value > max_values[i]) {
max_values[i] = value;
}
}
sample_count++;
delay(10);
}
Serial.print("Образцов обработано: ");
Serial.println(sample_count);
}
void calibrateBlackSurface() {
unsigned long start_time = millis();
int sample_count = 0;
while(millis() - start_time < 5000) {
for(int i = 0; i < 3; i++) {
int value = analogRead(sensor_pins[i]);
if(value < min_values[i]) {
min_values[i] = value;
}
}
sample_count++;
delay(10);
}
Serial.print("Образцов обработано: ");
Serial.println(sample_count);
}
void calculateThresholds() {
for(int i = 0; i < 3; i++) {
// Адаптивный порог с учетом контрастности
int range = max_values[i] - min_values[i];
float threshold_factor = 0.5; // Базовое значение
// Адаптация в зависимости от контрастности
if(range > 500) {
threshold_factor = 0.4; // Высокая контрастность
} else if(range < 200) {
threshold_factor = 0.6; // Низкая контрастность
}
threshold_values[i] = min_values[i] + threshold_factor * range;
}
}
public:
float getNormalizedValue(int sensor_index) {
if(!calibrated || sensor_index < 0 || sensor_index > 2) {
return 0.5; // Безопасное значение
}
int raw = analogRead(sensor_pins[sensor_index]);
// Ограничение значений в калибровочном диапазоне
raw = constrain(raw, min_values[sensor_index], max_values[sensor_index]);
// Нормализация
float normalized = (float)(raw - min_values[sensor_index]) /
(max_values[sensor_index] - min_values[sensor_index]);
return normalized;
}
bool isOnLine(int sensor_index) {
if(!calibrated) return false;
int raw = analogRead(sensor_pins[sensor_index]);
return raw < threshold_values[sensor_index];
}
void printCalibrationResults() {
Serial.println("\n=== РЕЗУЛЬТАТЫ КАЛИБРОВКИ ===");
for(int i = 0; i < 3; i++) {
Serial.print("Датчик ");
Serial.print(i);
Serial.print(": MIN=");
Serial.print(min_values[i]);
Serial.print(", MAX=");
Serial.print(max_values[i]);
Serial.print(", ПОРОГ=");
Serial.print(threshold_values[i]);
Serial.print(", КОНТРАСТ=");
Serial.print(max_values[i] - min_values[i]);
Serial.println();
}
// Оценка качества калибровки
analyzeCalibrationQuality();
}
private:
void analyzeCalibrationQuality() {
Serial.println("\n=== АНАЛИЗ КАЧЕСТВА КАЛИБРОВКИ ===");
for(int i = 0; i < 3; i++) {
int contrast = max_values[i] - min_values[i];
Serial.print("Датчик ");
Serial.print(i);
Serial.print(": ");
if(contrast > 400) {
Serial.println("✅ Отличная контрастность");
} else if(contrast > 200) {
Serial.println("✅ Хорошая контрастность");
} else if(contrast > 100) {
Serial.println("⚠️ Удовлетворительная контрастность");
} else {
Serial.println("❌ Плохая контрастность - проверить установку");
}
}
}
};📊 Адаптивная калибровка
🤖 Самообучающаяся система:
class AdaptiveCalibration {
private:
float adaptation_rate = 0.01; // Скорость адаптации
float confidence_threshold = 0.8;
struct CalibrationStats {
float running_min;
float running_max;
int sample_count;
float confidence;
} stats[3];
public:
void initializeAdaptive() {
for(int i = 0; i < 3; i++) {
stats[i].running_min = 1023;
stats[i].running_max = 0;
stats[i].sample_count = 0;
stats[i].confidence = 0;
}
}
void updateAdaptiveCalibration() {
for(int i = 0; i < 3; i++) {
float current_value = analogRead(A0 + i);
// Медленная адаптация к экстремальным значениям
if(current_value < stats[i].running_min) {
stats[i].running_min = current_value * (1 - adaptation_rate) +
stats[i].running_min * adaptation_rate;
}
if(current_value > stats[i].running_max) {
stats[i].running_max = current_value * (1 - adaptation_rate) +
stats[i].running_max * adaptation_rate;
}
stats[i].sample_count++;
// Обновление уверенности в калибровке
float range = stats[i].running_max - stats[i].running_min;
stats[i].confidence = min(1.0, range / 500.0);
}
}
bool isCalibrationReliable() {
for(int i = 0; i < 3; i++) {
if(stats[i].confidence < confidence_threshold) {
return false;
}
}
return true;
}
void printAdaptiveStatus() {
static unsigned long last_print = 0;
if(millis() - last_print > 2000) {
Serial.println("=== АДАПТИВНАЯ КАЛИБРОВКА ===");
for(int i = 0; i < 3; i++) {
Serial.print("S");
Serial.print(i);
Serial.print(": MIN=");
Serial.print(stats[i].running_min, 1);
Serial.print(", MAX=");
Serial.print(stats[i].running_max, 1);
Serial.print(", CONF=");
Serial.print(stats[i].confidence * 100, 0);
Serial.println("%");
}
last_print = millis();
}
}
};
🧮 Математические модели управления
📈 Теория автоматического управления
🎯 Классификация алгоритмов управления:
1. Релейный алгоритм (Bang-Bang Control): \[u(t) = \begin{cases} U_{max} & \text{если } e(t) > 0 \\ -U_{max} & \text{если } e(t) < 0 \end{cases}\]
2. Пропорциональный регулятор (P-Controller): \[u(t) = K_p \cdot e(t)\]
3. Пропорционально-дифференциальный (PD-Controller): \[u(t) = K_p \cdot e(t) + K_d \cdot \frac{de(t)}{dt}\]
4. PID-регулятор: \[u(t) = K_p \cdot e(t) + K_i \int_0^t e(\tau)d\tau + K_d \cdot \frac{de(t)}{dt}\]
где:
- e(t) - ошибка управления (отклонение от желаемой траектории)
- u(t) - управляющее воздействие
- K_p, K_i, K_d - коэффициенты регулятора
🎛️ Реализация алгоритмов управления
🔧 Простой релейный алгоритм:
class BangBangController {
private:
SensorCalibration* calibration;
int base_speed = 150;
int turn_speed = 200;
public:
BangBangController(SensorCalibration* cal) : calibration(cal) {}
void update() {
bool left_on_line = calibration->isOnLine(0);
bool center_on_line = calibration->isOnLine(1);
bool right_on_line = calibration->isOnLine(2);
// Простая логика управления
if(center_on_line) {
// Линия по центру - движение прямо
setMotorSpeeds(base_speed, base_speed);
setStatusLED(GREEN);
}
else if(left_on_line) {
// Линия слева - поворот направо
setMotorSpeeds(base_speed + turn_speed, base_speed - turn_speed);
setStatusLED(YELLOW);
}
else if(right_on_line) {
// Линия справа - поворот налево
setMotorSpeeds(base_speed - turn_speed, base_speed + turn_speed);
setStatusLED(YELLOW);
}
else {
// Линия потеряна - поиск
executeSearchPattern();
setStatusLED(RED);
}
}
private:
void executeSearchPattern() {
static unsigned long search_start = 0;
static int search_direction = 1;
if(search_start == 0) {
search_start = millis();
}
// Зигзагообразный поиск
if(millis() - search_start < 500) {
setMotorSpeeds(100 * search_direction, -100 * search_direction);
} else {
search_direction *= -1;
search_start = millis();
}
}
};
⚖️ Пропорциональный регулятор:
class ProportionalController {
private:
SensorCalibration* calibration;
float kp = 2.0; // Пропорциональный коэффициент
int base_speed = 150; // Базовая скорость
int max_correction = 100; // Максимальная коррекция
public:
ProportionalController(SensorCalibration* cal) : calibration(cal) {}
void update() {
float line_position = calculateLinePosition();
if(line_position != -999) { // Линия обнаружена
float error = 0.0 - line_position; // Цель - центр (0)
float correction = kp * error;
// Ограничение коррекции
correction = constrain(correction, -max_correction, max_correction);
// Применение коррекции к моторам
int left_speed = base_speed - correction;
int right_speed = base_speed + correction;
// Ограничение скоростей моторов
left_speed = constrain(left_speed, 0, 255);
right_speed = constrain(right_speed, 0, 255);
setMotorSpeeds(left_speed, right_speed);
// Отладочная информация
Serial.print("Pos: ");
Serial.print(line_position, 2);
Serial.print(", Err: ");
Serial.print(error, 2);
Serial.print(", Corr: ");
Serial.print(correction, 1);
Serial.print(", Motors: ");
Serial.print(left_speed);
Serial.print(",");
Serial.println(right_speed);
} else {
// Линия потеряна
executeSearchPattern();
}
}
private:
float calculateLinePosition() {
float left_norm = calibration->getNormalizedValue(0);
float center_norm = calibration->getNormalizedValue(1);
float right_norm = calibration->getNormalizedValue(2);
// Инверсия для работы с черной линией (0 = белый, 1 = черный)
left_norm = 1.0 - left_norm;
center_norm = 1.0 - center_norm;
right_norm = 1.0 - right_norm;
float total_activation = left_norm + center_norm + right_norm;
if(total_activation < 0.1) {
return -999; // Линия не обнаружена
}
// Взвешенное среднее: -1 (крайне слева) до +1 (крайне справа)
float weighted_sum = left_norm * (-1.0) + center_norm * 0.0 + right_norm * (+1.0);
float position = weighted_sum / total_activation;
return position;
}
void setKp(float new_kp) {
kp = constrain(new_kp, 0.1, 10.0);
Serial.print("Новый Kp: ");
Serial.println(kp);
}
};
🎛️ PD-регулятор (продвинутый):
class PDController {
private:
SensorCalibration* calibration;
// Параметры регулятора
float kp = 2.5; // Пропорциональный коэффициент
float kd = 0.8; // Дифференциальный коэффициент
// Переменные состояния
float previous_error = 0;
unsigned long previous_time = 0;
// Параметры управления
int base_speed = 180;
int max_correction = 120;
// Фильтр для производной
float derivative_filter = 0.1;
float filtered_derivative = 0;
public:
PDController(SensorCalibration* cal) : calibration(cal) {
previous_time = millis();
}
void update() {
unsigned long current_time = millis();
float dt = (current_time - previous_time) / 1000.0; // Время в секундах
if(dt > 0.001) { // Минимальный интервал времени
float line_position = calculateLinePosition();
if(line_position != -999) {
float error = 0.0 - line_position;
// Пропорциональная составляющая
float proportional = kp * error;
// Дифференциальная составляющая с фильтрацией
float raw_derivative = (error - previous_error) / dt;
filtered_derivative = derivative_filter * raw_derivative +
(1 - derivative_filter) * filtered_derivative;
float differential = kd * filtered_derivative;
// Общее управляющее воздействие
float control_signal = proportional + differential;
// Ограничение сигнала управления
control_signal = constrain(control_signal, -max_correction, max_correction);
// Применение к моторам
int left_speed = base_speed - control_signal;
int right_speed = base_speed + control_signal;
left_speed = constrain(left_speed, 0, 255);
right_speed = constrain(right_speed, 0, 255);
setMotorSpeeds(left_speed, right_speed);
// Обновление переменных состояния
previous_error = error;
previous_time = current_time;
// Отладочная информация
printControllerStatus(line_position, error, proportional,
differential, control_signal);
} else {
executeAdvancedSearchPattern();
}
}
}
private:
void printControllerStatus(float position, float error,
float prop, float diff, float control) {
static unsigned long last_print = 0;
if(millis() - last_print > 100) { // Каждые 100 мс
Serial.print("Pos:");
Serial.print(position, 2);
Serial.print(" E:");
Serial.print(error, 2);
Serial.print(" P:");
Serial.print(prop, 1);
Serial.print(" D:");
Serial.print(diff, 1);
Serial.print(" U:");
Serial.println(control, 1);
last_print = millis();
}
}
void tuneParameters(float new_kp, float new_kd) {
kp = constrain(new_kp, 0.1, 10.0);
kd = constrain(new_kd, 0.0, 5.0);
Serial.print("Новые параметры - Kp:");
Serial.print(kp);
Serial.print(", Kd:");
Serial.println(kd);
// Сброс фильтра при изменении параметров
filtered_derivative = 0;
previous_error = 0;
}
void executeAdvancedSearchPattern() {
static unsigned long search_start = 0;
static float search_amplitude = 50;
static float search_frequency = 2.0; // Гц
if(search_start == 0) {
search_start = millis();
}
float time_elapsed = (millis() - search_start) / 1000.0;
float search_signal = search_amplitude * sin(2 * PI * search_frequency * time_elapsed);
int left_speed = base_speed/2 - search_signal;
int right_speed = base_speed/2 + search_signal;
left_speed = constrain(left_speed, 0, 255);
right_speed = constrain(right_speed, 0, 255);
setMotorSpeeds(left_speed, right_speed);
}
};
💻 Программирование базовых алгоритмов
🚀 Архитектура программной системы
🏗️ Модульная структура программы:
// ================== ГЛАВНЫЙ ФАЙЛ ПРОГРАММЫ ==================
#include "SensorCalibration.h"
#include "LineFollowingController.h"
#include "PerformanceAnalyzer.h"
#include "UserInterface.h"
// Глобальные объекты системы
SensorCalibration calibration;
PDController controller(&calibration);
PerformanceAnalyzer analyzer;
UserInterface ui;
// Конфигурация системы
struct SystemConfig {
bool auto_calibration_enabled = true;
bool performance_logging = true;
bool debug_output = false;
int target_loop_frequency = 50; // Гц
} config;
void setup() {
Serial.begin(115200);
// Инициализация аппаратуры
initializeHardware();
// Приветствие пользователя
ui.displayWelcomeMessage();
// Проверка системы
if(!performSystemSelfCheck()) {
Serial.println("❌ Ошибка инициализации системы!");
while(1) { delay(1000); }
}
// Калибровка датчиков
if(config.auto_calibration_enabled) {
calibration.performCalibration();
} else {
calibration.loadFromEEPROM();
}
// Готовность к работе
ui.displayReadyMessage();
delay(1000);
Serial.println("🚀 СИСТЕМА СЛЕДОВАНИЯ ПО ЛИНИИ ЗАПУЩЕНА");
}
void loop() {
unsigned long loop_start = micros();
// Основной цикл управления
controller.update();
// Сбор данных о производительности
if(config.performance_logging) {
analyzer.collectSample();
}
// Обработка пользовательского интерфейса
ui.processInput();
// Адаптивная калибровка
if(config.auto_calibration_enabled) {
calibration.updateAdaptive();
}
// Контроль частоты цикла
maintainLoopFrequency(loop_start);
}
// ================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ==================
void initializeHardware() {
// Инициализация пинов моторов
pinMode(LEFT_MOTOR_PWM, OUTPUT);
pinMode(RIGHT_MOTOR_PWM, OUTPUT);
pinMode(LEFT_MOTOR_DIR, OUTPUT);
pinMode(RIGHT_MOTOR_DIR, OUTPUT);
// Инициализация индикации
pinMode(STATUS_LED, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Установка начального состояния
digitalWrite(LEFT_MOTOR_DIR, HIGH);
digitalWrite(RIGHT_MOTOR_DIR, HIGH);
analogWrite(LEFT_MOTOR_PWM, 0);
analogWrite(RIGHT_MOTOR_PWM, 0);
}
bool performSystemSelfCheck() {
Serial.println("=== САМОДИАГНОСТИКА СИСТЕМЫ ===");
// Проверка датчиков
bool sensors_ok = true;
for(int i = 0; i < 3; i++) {
int value = analogRead(A0 + i);
if(value < 10 || value > 1020) {
Serial.print("❌ Датчик ");
Serial.print(i);
Serial.println(" не отвечает");
sensors_ok = false;
}
}
// Проверка моторов
bool motors_ok = testMotors();
// Проверка памяти
bool memory_ok = (getFreeMemory() > 500);
Serial.print("Датчики: ");
Serial.println(sensors_ok ? "✅" : "❌");
Serial.print("Моторы: ");
Serial.println(motors_ok ? "✅" : "❌");
Serial.print("Память: ");
Serial.print(getFreeMemory());
Serial.println(" байт");
return sensors_ok && motors_ok && memory_ok;
}
void maintainLoopFrequency(unsigned long loop_start) {
unsigned long target_period = 1000000 / config.target_loop_frequency; // мкс
unsigned long loop_duration = micros() - loop_start;
if(loop_duration < target_period) {
delayMicroseconds(target_period - loop_duration);
} else if(config.debug_output) {
Serial.print("⚠️ Превышение времени цикла: ");
Serial.print(loop_duration);
Serial.println(" мкс");
}
}
🎛️ Пользовательский интерфейс
📟 Интерактивное управление системой:
class UserInterface {
private:
enum UIState {
MAIN_MENU,
CALIBRATION_MENU,
PARAMETER_TUNING,
PERFORMANCE_VIEW,
RUNNING
} current_state = MAIN_MENU;
unsigned long last_status_update = 0;
public:
void displayWelcomeMessage() {
Serial.println("╔═══════════════════════════════════════╗");
Serial.println("║ СИСТЕМА СЛЕДОВАНИЯ ПО ЛИНИИ ║");
Serial.println("║ Версия 2.0 STEM ║");
Serial.println("║ ГБОУ Школа № 1362 - 6 класс ║");
Serial.println("╚═══════════════════════════════════════╝");
Serial.println();
}
void displayMainMenu() {
Serial.println("=== ГЛАВНОЕ МЕНЮ ===");
Serial.println("1. Запустить следование по линии");
Serial.println("2. Калибровка датчиков");
Serial.println("3. Настройка параметров");
Serial.println("4. Просмотр производительности");
Serial.println("5. Диагностика системы");
Serial.println("0. Экстренная остановка");
Serial.println();
Serial.print("Выберите опцию: ");
}
void processInput() {
if(Serial.available()) {
char input = Serial.read();
handleMenuInput(input);
}
// Обновление статуса во время работы
if(current_state == RUNNING && millis() - last_status_update > 1000) {
displayRunningStatus();
last_status_update = millis();
}
}
private:
void handleMenuInput(char input) {
switch(current_state) {
case MAIN_MENU:
handleMainMenuInput(input);
break;
case CALIBRATION_MENU:
handleCalibrationMenuInput(input);
break;
case PARAMETER_TUNING:
handleParameterTuningInput(input);
break;
case PERFORMANCE_VIEW:
handlePerformanceViewInput(input);
break;
case RUNNING:
handleRunningInput(input);
break;
}
}
void handleMainMenuInput(char input) {
switch(input) {
case '1':
Serial.println("🚀 Запуск системы следования...");
current_state = RUNNING;
break;
case '2':
Serial.println("📐 Меню калибровки:");
displayCalibrationMenu();
current_state = CALIBRATION_MENU;
break;
case '3':
Serial.println("⚙️ Настройка параметров:");
displayParameterMenu();
current_state = PARAMETER_TUNING;
break;
case '4':
Serial.println("📊 Анализ производительности:");
analyzer.displayDetailedReport();
current_state = PERFORMANCE_VIEW;
break;
case '5':
Serial.println("🔧 Диагностика системы:");
performSystemSelfCheck();
displayMainMenu();
break;
case '0':
emergencyStop();
break;
default:
Serial.println("❌ Неверный ввод. Попробуйте снова.");
displayMainMenu();
}
}
void displayRunningStatus() {
Serial.println("┌─────────────────────────────────┐");
Serial
# Пересобрать урок и уменьшить код
// Достаточно для образовательных целей (урок 90 мин)
#define LEFT_SENSOR A0
#define CENTER_SENSOR A1
#define RIGHT_SENSOR A2
int threshold = 500; // Простое фиксированное значение для начала
void setup() {
Serial.begin(9600);
Serial.println("🤖 Робот-следопыт запущен!");
// Быстрая автокалибровка (30 сек)
Serial.println("Калибровка... поставьте на линию");
delay(3000);
autoCalibrate();
Serial.println("✅ Готов! Показания датчиков:");
}
void loop() {
// Основной алгоритм
followLine();
// Простая отладка
debugPrint();
delay(100);
}
void debugPrint() {
Serial.print("Датчики: ");
Serial.print(analogRead(LEFT_SENSOR) > threshold ? "●" : "○");
Serial.print(" ");
Serial.print(analogRead(CENTER_SENSOR) > threshold ? "●" : "○");
Serial.print(" ");
Serial.print(analogRead(RIGHT_SENSOR) > threshold ? "●" : "○");
Serial.print(" | Движение: ");
Serial.println(getMovementDescription());
}
