Skip to main content

P2

Отлично! Создаю эталонную STEM-презентацию по практическому программированию систем следования по линии с глубоким техническим анализом:Отлично! Создаю эталонную STEM-презентацию для практической работы по программированию датчиков линии с глубоким математическим анализом алгоритмов управления.

 1title: "STEM-презентация: Программирование работы датчика линии"
 2tag: "line-following-programming"
 3description: |
 4  ## 🛤️ Создание STEM-презентации "Практическая работа: Программирование работы датчика линии"
 5
 6  ### 📋 Исходные данные
 7  **Учитель:** Ахметов Рустам  
 8  **Школа:** ГБОУ Школа № 1362  
 9  **Класс:** 6 (физмат-профиль)  
10  **Модуль:** Сенсорные системы роботов  
11  **Тип урока:** Практическая работа (90 минут)  
12  **Дата:** 2025-06-13
13
14  ### 🎯 Цель презентации
15  Создать комплексную методическую презентацию для практической работы по программированию датчиков линии с акцентом на алгоритмическую разработку, математические модели управления и оптимизацию параметров системы.
16
17  ### 🔬 STEM-интеграция урока
18  - **S (Science):** Физика сенсоров, оптика, калибровка измерительных систем
19  - **T (Technology):** Датчики линии, микроконтроллеры, системы автоматического управления
20  - **E (Engineering):** Алгоритмы управления, PID-регуляторы, оптимизация систем
21  - **M (Mathematics):** Теория управления, дифференциальные уравнения, алгоритмы
22
23  ### 📊 Структура урока по карте
24  1. **Организационный момент** (3 мин)
25  2. **Постановка задачи** (5 мин)
26  3. **Подготовка оборудования** (10 мин)
27  4. **Калибровка датчиков** (10 мин)
28  5. **Изучение алгоритмов следования** (8 мин)
29  6. **Программирование базового алгоритма** (15 мин)
30  7. **Тестирование работы робота** (10 мин)
31  8. **Усовершенствование алгоритма** (15 мин)
32  9. **Финальное тестирование** (10 мин)
33  10. **Рефлексия и итоги** (4 мин)
34
35  ### 🧠 Ключевые технические аспекты
36  - Методы калибровки и определения пороговых значений
37  - Алгоритмы управления: от простых до PID-регуляторов
38  - Математическое моделирование поведения робота
39  - Оптимизация параметров для различных условий
40  - Обработка особых случаев (перекрестки, разрывы линии)
41
42  ### 🛠️ Практические задачи
43  - Установка и калибровка датчиков линии
44  - Программирование алгоритмов следования различной сложности
45  - Настройка параметров управления
46  - Тестирование в различных условиях освещения
47  - Оптимизация производительности системы
48
49  ### 🔧 Математический аппарат
50  - Теория автоматического управления
51  - PID-регуляторы и настройка коэффициентов
52  - Алгоритмы цифровой фильтрации сигналов
53  - Методы оптимизации параметров системы  
  1---
  2title: "🛤️ Программирование работы датчика линии"
  3subtitle: "От алгоритмов управления до математической оптимизации поведения"
  4author: "Ахметов Рустам"
  5date: "2025-06-13"
  6class: "6 класс"
  7subject: "Технология • Сенсорные системы роботов"
  8duration: "90 минут"
  9layout: "list"
 10outputs:
 11  - "reveal"
 12theme: "control-algorithms"
 13transition: "slide"
 14backgroundTransition: "fade"
 15controls: true
 16progress: true
 17history: true
 18center: true
 19touch: true
 20loop: false
 21rtl: false
 22navigationMode: "default"
 23---
 24
 25# 🛤️ Программирование работы датчика линии
 26## От алгоритмов управления до математической оптимизации поведения
 27
 28**🧮 Алгоритмы • 📊 Теория управления • 💻 Программирование • 🎯 Оптимизация**  
 29*6 класс • Технология • 90 минут*
 30
 31👨‍🏫 **Учитель:** Ахметов Рустам  
 32🏫 **Школа:** ГБОУ № 1362  
 33📅 **Дата:** 2025-06-13  
 34🎯 **Цель:** Создать интеллектуальную систему навигации по линии!
 35
 36---
 37
 38## 🎯 План алгоритмической разработки
 39
 40**💻 Наша программистская миссия:**
 411. **⚠️ Подготовка системы** - безопасность и инициализация (3 мин)
 422. **🎯 Техническое задание** - формулировка алгоритмической задачи (5 мин)
 433. **🔧 Установка оборудования** - интеграция датчиков в систему (10 мин)
 444. **📐 Калибровка сенсоров** - настройка пороговых значений (10 мин)
 455. **🧮 Математические модели** - теория алгоритмов управления (8 мин)
 466. **💻 Базовое программирование** - реализация простых алгоритмов (15 мин)
 477. **🧪 Тестирование и отладка** - верификация работы системы (10 мин)
 488. **⚡ Продвинутые алгоритмы** - PID-регуляторы и оптимизация (15 мин)
 499. **🏁 Итоговые испытания** - тестирование в различных условиях (10 мин)
 5010. **🤔 Анализ результатов** - оценка эффективности алгоритмов (4 мин)
 51
 52**🎯 К концу практикума мы сможем:**
 53- 📐 Калибровать сенсорные системы для точных измерений
 54- 🧮 Программировать математические алгоритмы управления
 55- ⚡ Оптимизировать параметры для максимальной эффективности
 56- 🎛️ Создавать адаптивные системы автоматического управления
 57
 58---
 59
 60

⚠️ Подготовка инженерной лаборатории

Инициализация систем и безопасность

🛡️ Протокол безопасности

⚡ Электробезопасность при работе с датчиками:

  • Проверка правильности подключения перед подачей питания
  • Соблюдение полярности при подключении датчиков
  • Использование токоограничивающих резисторов
  • Избегание короткого замыкания сигнальных линий

💻 Безопасность программирования:

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

🤖 Безопасность тестирования роботов:

  • Ограниченная скорость при первых запусках
  • Контролируемая зона тестирования
  • Готовность к экстренной остановке
  • Защита датчиков от механических повреждений

🧰 Программные инструменты и среды

💻 Среды разработки по платформам:

Arduino IDE:

 1// Настройка среды для работы с датчиками линии
 2#define LEFT_SENSOR A0
 3#define RIGHT_SENSOR A1
 4#define LEFT_MOTOR 5
 5#define RIGHT_MOTOR 6
 6
 7void setup() {
 8    Serial.begin(9600);
 9    pinMode(LEFT_MOTOR, OUTPUT);
10    pinMode(RIGHT_MOTOR, OUTPUT);
11    
12    Serial.println("=== СИСТЕМА СЛЕДОВАНИЯ ПО ЛИНИИ ===");
13    Serial.println("Инициализация датчиков...");
14}
15
16// Функция диагностики системы
17void systemDiagnostics() {
18    Serial.println("Проверка подключения датчиков:");
19    Serial.print("Левый датчик: ");
20    Serial.println(analogRead(LEFT_SENSOR));
21    Serial.print("Правый датчик: ");
22    Serial.println(analogRead(RIGHT_SENSOR));
23}

LEGO MINDSTORMS EV3:

1Настройка среды EV3-G:
2- Подключение датчиков цвета в режиме отражения
3- Настройка портов моторов для левого и правого колеса
4- Конфигурация параметров связи для отладки
5- Установка начальной мощности моторов

Python для Raspberry Pi:

 1import RPi.GPIO as GPIO
 2import time
 3
 4# Инициализация GPIO для датчиков линии
 5LEFT_SENSOR = 18
 6RIGHT_SENSOR = 19
 7LEFT_MOTOR_PWM = 20
 8RIGHT_MOTOR_PWM = 21
 9
10def initialize_system():
11    GPIO.setmode(GPIO.BCM)
12    GPIO.setup(LEFT_SENSOR, GPIO.IN)
13    GPIO.setup(RIGHT_SENSOR, GPIO.IN)
14    GPIO.setup(LEFT_MOTOR_PWM, GPIO.OUT)
15    GPIO.setup(RIGHT_MOTOR_PWM, GPIO.OUT)
16    
17    print("=== СИСТЕМА НАВИГАЦИИ ГОТОВА ===")
18    print("Начинаем калибровку...")
19
20def system_check():
21    left_value = GPIO.input(LEFT_SENSOR)
22    right_value = GPIO.input(RIGHT_SENSOR)
23    print(f"Датчики: L={left_value}, R={right_value}")

📊 Подготовка тестового полигона

🛤️ Конфигурация трассы для тестирования:

 1Схема тестового полигона:
 2
 3┌─────────────────────────────────────────────────┐
 4│                                                 │
 5│  🏁 СТАРТ                                      │
 6│    ║                                           │
 7│    ║  ← Прямой участок (50 см)                 │
 8│    ║                                           │
 9│    ╚══╗                                        │
10│       ║  ← Плавный поворот (R=30 см)           │
11│       ║                                        │
12│    ╔══╝                                        │
13│    ║                                           │
14│    ║  ← Прямой участок (30 см)                 │
15│    ║                                           │
16│    ╚══╗                                        │
17│       ║  ← Острый поворот (R=15 см)            │
18│    ╔══╝                                        │
19│    ║                                           │
20│    ║  ← Финишный участок (20 см)               │
21│    🏁 ФИНИШ                                    │
22└─────────────────────────────────────────────────┘
23
24Технические параметры:
25- Ширина линии: 20 мм
26- Материал: черная изолента на белом фоне
27- Контрастность: > 70%
28- Общая длина трассы: 150 см

📋 Контрольные точки для тестирования:

Участок Тип испытания Критерий успеха Время прохождения
Участок 1 Прямолинейное движение Отклонение < 5 мм < 5 сек
Участок 2 Плавный поворот Без потери линии < 3 сек
Участок 3 Острый поворот Восстановление < 1 сек < 4 сек
Полная трасса Комплексное испытание Без остановок < 15 сек
61 62--- 63 64

🎯 Техническое задание

Формулировка алгоритмической задачи

🎯 Постановка задачи управления

📋 Техническое задание:

Основная цель: Разработать адаптивную систему автоматического управления роботом для точного следования по контрастной линии с оптимизацией скорости и плавности движения.

Технические требования:

  • Максимальное отклонение от центра линии: ±3 мм
  • Время восстановления после возмущения: < 0.5 сек
  • Скорость движения: 0.2-0.8 м/с (переменная)
  • Плавность управления: без резких рывков
  • Адаптация к изменению освещения: автоматическая

🎮 Функциональные требования:

Базовый уровень (обязательный):

 11. Калибровка датчиков:
 2   - Определение пороговых значений для черного/белого
 3   - Сохранение калибровочных данных
 4   - Возможность рекалибровки
 5
 62. Следование по прямой линии:
 7   - Стабильное движение по центру
 8   - Коррекция при отклонениях
 9   - Индикация состояния датчиков
10
113. Прохождение поворотов:
12   - Плавные повороты без потери линии
13   - Адаптация скорости в поворотах
14   - Восстановление после временной потери

Продвинутый уровень (дополнительный):

14. Оптимизация производительности:
2   - PID-регулятор для плавного управления
3   - Переменная скорость в зависимости от кривизны
4   - Предсказание траектории
5
65. Обработка особых случаев:
7   - Перекрестки и разветвления
8   - Прерывистые линии
9   - Изменение ширины линии

🔬 Анализ системы управления

📊 Математическая модель робота:

Робот как объект управления можно представить упрощенной моделью:

\[\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 - координаты робота

🎛️ Переменные состояния системы:

 1Входные переменные:
 2- sensor_left: значение левого датчика [0-1023]
 3- sensor_right: значение правого датчика [0-1023]
 4- sensor_center: значение центрального датчика [0-1023] (опционально)
 5
 6Промежуточные переменные:
 7- line_position: позиция линии относительно робота [-1.0...+1.0]
 8- line_detected: флаг обнаружения линии [true/false]
 9- error: ошибка позиционирования [-1.0...+1.0]
10
11Выходные переменные:
12- motor_left_speed: скорость левого мотора [0-255]
13- motor_right_speed: скорость правого мотора [0-255]
14- 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

🔄 Алгоритм оценки качества:

 1class PerformanceAnalyzer {
 2private:
 3    float position_errors[1000];
 4    float control_signals[1000];
 5    int sample_count = 0;
 6    
 7public:
 8    void addSample(float error, float control) {
 9        if(sample_count < 1000) {
10            position_errors[sample_count] = error;
11            control_signals[sample_count] = control;
12            sample_count++;
13        }
14    }
15    
16    float calculateRMSE() {
17        float sum_squares = 0;
18        for(int i = 0; i < sample_count; i++) {
19            sum_squares += position_errors[i] * position_errors[i];
20        }
21        return sqrt(sum_squares / sample_count);
22    }
23    
24    float calculateSmoothness() {
25        float sum_changes = 0;
26        for(int i = 1; i < sample_count; i++) {
27            sum_changes += abs(control_signals[i] - control_signals[i-1]);
28        }
29        return sum_changes / (sample_count - 1);
30    }
31    
32    void printReport() {
33        Serial.println("=== ОТЧЕТ О ПРОИЗВОДИТЕЛЬНОСТИ ===");
34        Serial.print("RMSE ошибки: ");
35        Serial.println(calculateRMSE());
36        Serial.print("Плавность: ");
37        Serial.println(calculateSmoothness());
38        Serial.print("Количество образцов: ");
39        Serial.println(sample_count);
40    }
41};
65 66--- 67 68

🔧 Установка и интеграция оборудования

Системная интеграция датчиков

📐 Оптимальное размещение датчиков

🔍 Конфигурации датчиков для различных задач:

Двухсенсорная система (базовая):

 1Схема размещения:
 2     [L]     [R]
 3      │       │
 4      ├───────┤ ← 15-20 мм
 5   ═══●═══════●═══ ← Линия (20 мм)
 6      │       │
 7    ПЛАТФОРМА
 8
 9Логика работы:
10L=0, R=0  → Линия потеряна или между датчиками
11L=1, R=0  → Линия слева, поворот направо
12L=0, R=1  → Линия справа, поворот налево
13L=1, R=1  → Широкая линия или перекресток
14
15Преимущества:
16✓ Простота программирования
17✓ Низкая стоимость
18✓ Быстрая реакция
19
20Ограничения:
21✗ Низкое разрешение
22✗ Рывки в движении
23✗ Сложность точной калибровки

Трехсенсорная система (рекомендуемая):

 1Схема размещения:
 2   [L]   [C]   [R]
 3    │     │     │
 4    ├─────┼─────┤ ← 10 мм каждый
 5 ═══●═════●═════●═══ ← Линия
 6    │     │     │
 7      ПЛАТФОРМА
 8
 9Состояния и реакции:
10001 → Поворот налево (сильный)
11010 → Движение прямо
12100 → Поворот направо (сильный)
13011 → Поворот налево (слабый)
14110 → Поворот направо (слабый)
15111 → Перекресток или широкая линия
16000 → Поиск линии
17
18Преимущества:
19✓ Плавное управление
20✓ Хорошая точность
21✓ Простая калибровка

Пятисенсорная система (профессиональная):

 1Схема размещения:
 2[L2][L1][C][R1][R2]
 3 │   │   │   │   │
 4 ├───┼───┼───┼───┤ ← 6 мм между соседними
 5═●═══●═══●═══●═══●═ ← Линия
 6 │   │   │   │   │
 7      ПЛАТФОРМА
 8
 9Расчет позиции линии:
10position = (L2×(-2) + L1×(-1) + C×0 + R1×1 + R2×2) / total_active
11
12Преимущества:
13✓ Максимальная точность
14✓ Плавное движение
15✓ Возможность интерполяции
16✓ Хорошее восстановление

⚡ Электрическое подключение

🔌 Схемы подключения для Arduino:

 1Подключение трех аналоговых датчиков TCRT5000:
 2
 3Arduino Uno        Датчик линии (Left)    Датчик линии (Center)   Датчик линии (Right)
 4┌─────────────┐    ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
 5│    5V       ├────┤ VCC             │    │ VCC             │    │ VCC             │
 6│    GND      ├────┤ GND             │    │ GND             │    │ GND             │
 7│    A0       ├────┤ Analog Out      │    │                 │    │                 │
 8│    A1       ├────┼─────────────────┘    │ Analog Out      │    │                 │
 9│    A2       ├────┼──────────────────────┤                 │    │ Analog Out      │
10│             │    └─────────────────────────────────────────┘    └─────────────────┘
11│   Pin 5     ├──── Left Motor (PWM)
12│   Pin 6     ├──── Right Motor (PWM)
13│   Pin 7     ├──── Left Motor Direction
14│   Pin 8     ├──── Right Motor Direction
15│   Pin 13    ├──── Status LED
16└─────────────┘

📊 Проверка подключения:

 1void verifyConnections() {
 2    Serial.println("=== ПРОВЕРКА ПОДКЛЮЧЕНИЯ ДАТЧИКОВ ===");
 3    
 4    // Проверка питания
 5    for(int i = 0; i < 3; i++) {
 6        int value = analogRead(A0 + i);
 7        Serial.print("Датчик ");
 8        Serial.print(i);
 9        Serial.print(": ");
10        Serial.print(value);
11        
12        if(value < 50) {
13            Serial.println(" ❌ Возможно не подключен");
14        } else if(value > 950) {
15            Serial.println(" ⚠️ Возможно нет нагрузки");
16        } else {
17            Serial.println(" ✅ Подключение OK");
18        }
19    }
20    
21    // Проверка моторов
22    digitalWrite(LEFT_MOTOR_DIR, HIGH);
23    analogWrite(LEFT_MOTOR_PWM, 100);
24    delay(500);
25    analogWrite(LEFT_MOTOR_PWM, 0);
26    
27    digitalWrite(RIGHT_MOTOR_DIR, HIGH);
28    analogWrite(RIGHT_MOTOR_PWM, 100);
29    delay(500);
30    analogWrite(RIGHT_MOTOR_PWM, 0);
31    
32    Serial.println("Проверка моторов завершена");
33}

🛠️ Механическое крепление

🔧 Требования к установке датчиков:

 1Критические параметры установки:
 2
 31. Высота над поверхностью:
 4   - Оптимальная: 3-5 мм
 5   - Минимальная: 2 мм
 6   - Максимальная: 8 мм
 7   
 82. Угол наклона:
 9   - Рекомендуемый: 0° (перпендикулярно)
10   - Допустимый: ±5°
11   - Критический: ±15°
12
133. Расстояние от центра робота:
14   - Минимальное: 30 мм (для маневренности)
15   - Оптимальное: 50-70 мм
16   - Максимальное: 100 мм (для больших роботов)
17
184. Защита от вибраций:
19   - Жесткое крепление к раме
20   - Амортизация при необходимости
21   - Защита проводов от перегибов

📐 Проверочный тест установки:

 1void testSensorPlacement() {
 2    Serial.println("=== ТЕСТ КАЧЕСТВА УСТАНОВКИ ===");
 3    
 4    // Тест стабильности показаний
 5    float readings[3][20];
 6    
 7    for(int sample = 0; sample < 20; sample++) {
 8        for(int sensor = 0; sensor < 3; sensor++) {
 9            readings[sensor][sample] = analogRead(A0 + sensor);
10        }
11        delay(50);
12    }
13    
14    // Анализ стабильности
15    for(int sensor = 0; sensor < 3; sensor++) {
16        float mean = 0, stddev = 0;
17        
18        // Среднее значение
19        for(int i = 0; i < 20; i++) {
20            mean += readings[sensor][i];
21        }
22        mean /= 20;
23        
24        // Стандартное отклонение
25        for(int i = 0; i < 20; i++) {
26            stddev += pow(readings[sensor][i] - mean, 2);
27        }
28        stddev = sqrt(stddev / 19);
29        
30        Serial.print("Датчик ");
31        Serial.print(sensor);
32        Serial.print(": среднее=");
33        Serial.print(mean);
34        Serial.print(", σ=");
35        Serial.print(stddev);
36        
37        if(stddev < 5) {
38            Serial.println(" ✅ Стабильно");
39        } else if(stddev < 15) {
40            Serial.println(" ⚠️ Умеренные колебания");
41        } else {
42            Serial.println(" ❌ Нестабильно - проверить крепление");
43        }
44    }
45}
69 70--- 71 72## 📐 Калибровка сенсорной системы 73 74### 🎛️ Методология точной калибровки 75 76**🔬 Теоретические основы калибровки:** 77 78Калибровка датчиков линии - критически важный процесс, определяющий точность работы всей системы. Цель калибровки - установить соответствие между физическими характеристиками поверхности и цифровыми значениями датчиков. 79 80**📊 Математическая модель калибровки:** 81 82 \[V_{norm} = \frac{V_{raw} - V_{min}}{V_{max} - V_{min}}\] 83 84где: 85- V_norm - нормализованное значение [0...1] 86- V_raw - сырое значение АЦП 87- V_min - минимальное значение (черная поверхность) 88- V_max - максимальное значение (белая поверхность) 89 90**🎯 Определение порогового значения:** 91 92 \[V_{threshold} = V_{min} + k \times (V_{max} - V_{min})\] 93 94где k - коэффициент порога (обычно 0.4-0.6 для максимальной помехоустойчивости) 95 96--- 97 98### 📋 Пошаговая процедура калибровки 99 100**🔧 Автоматическая калибровка:** 101 102```cpp 103class SensorCalibration { 104private: 105 int sensor_pins[3] = {A0, A1, A2}; 106 int min_values[3] = {1023, 1023, 1023}; 107 int max_values[3] = {0, 0, 0}; 108 int threshold_values[3]; 109 bool calibrated = false; 110 111public: 112 void performCalibration() { 113 Serial.println("=== АВТОМАТИЧЕСКАЯ КАЛИБРОВКА ==="); 114 Serial.println("Поместите робота на белую поверхность"); 115 Serial.println("Нажмите кнопку для начала..."); 116 117 waitForButtonPress(); 118 119 // Калибровка на белой поверхности 120 Serial.println("Калибровка белого... (5 сек)"); 121 calibrateWhiteSurface(); 122 123 Serial.println("Поместите робота на черную линию"); 124 Serial.println("Нажмите кнопку для продолжения..."); 125 126 waitForButtonPress(); 127 128 // Калибровка на черной поверхности 129 Serial.println("Калибровка черного... (5 сек)"); 130 calibrateBlackSurface(); 131 132 // Расчет пороговых значений 133 calculateThresholds(); 134 135 // Сохранение результатов 136 saveCalibrationData(); 137 138 calibrated = true; 139 Serial.println("✅ Калибровка завершена успешно!"); 140 printCalibrationResults(); 141 } 142 143private: 144 void calibrateWhiteSurface() { 145 unsigned long start_time = millis(); 146 int sample_count = 0; 147 148 while(millis() - start_time < 5000) { 149 for(int i = 0; i < 3; i++) { 150 int value = analogRead(sensor_pins[i]); 151 if(value > max_values[i]) { 152 max_values[i] = value; 153 } 154 } 155 sample_count++; 156 delay(10); 157 } 158 159 Serial.print("Образцов обработано: "); 160 Serial.println(sample_count); 161 } 162 163 void calibrateBlackSurface() { 164 unsigned long start_time = millis(); 165 int sample_count = 0; 166 167 while(millis() - start_time < 5000) { 168 for(int i = 0; i < 3; i++) { 169 int value = analogRead(sensor_pins[i]); 170 if(value < min_values[i]) { 171 min_values[i] = value; 172 } 173 } 174 sample_count++; 175 delay(10); 176 } 177 178 Serial.print("Образцов обработано: "); 179 Serial.println(sample_count); 180 } 181 182 void calculateThresholds() { 183 for(int i = 0; i < 3; i++) { 184 // Адаптивный порог с учетом контрастности 185 int range = max_values[i] - min_values[i]; 186 float threshold_factor = 0.5; // Базовое значение 187 188 // Адаптация в зависимости от контрастности 189 if(range > 500) { 190 threshold_factor = 0.4; // Высокая контрастность 191 } else if(range < 200) { 192 threshold_factor = 0.6; // Низкая контрастность 193 } 194 195 threshold_values[i] = min_values[i] + threshold_factor * range; 196 } 197 } 198 199public: 200 float getNormalizedValue(int sensor_index) { 201 if(!calibrated || sensor_index < 0 || sensor_index > 2) { 202 return 0.5; // Безопасное значение 203 } 204 205 int raw = analogRead(sensor_pins[sensor_index]); 206 207 // Ограничение значений в калибровочном диапазоне 208 raw = constrain(raw, min_values[sensor_index], max_values[sensor_index]); 209 210 // Нормализация 211 float normalized = (float)(raw - min_values[sensor_index]) / 212 (max_values[sensor_index] - min_values[sensor_index]); 213 214 return normalized; 215 } 216 217 bool isOnLine(int sensor_index) { 218 if(!calibrated) return false; 219 220 int raw = analogRead(sensor_pins[sensor_index]); 221 return raw < threshold_values[sensor_index]; 222 } 223 224 void printCalibrationResults() { 225 Serial.println("\n=== РЕЗУЛЬТАТЫ КАЛИБРОВКИ ==="); 226 for(int i = 0; i < 3; i++) { 227 Serial.print("Датчик "); 228 Serial.print(i); 229 Serial.print(": MIN="); 230 Serial.print(min_values[i]); 231 Serial.print(", MAX="); 232 Serial.print(max_values[i]); 233 Serial.print(", ПОРОГ="); 234 Serial.print(threshold_values[i]); 235 Serial.print(", КОНТРАСТ="); 236 Serial.print(max_values[i] - min_values[i]); 237 Serial.println(); 238 } 239 240 // Оценка качества калибровки 241 analyzeCalibrationQuality(); 242 } 243 244private: 245 void analyzeCalibrationQuality() { 246 Serial.println("\n=== АНАЛИЗ КАЧЕСТВА КАЛИБРОВКИ ==="); 247 248 for(int i = 0; i < 3; i++) { 249 int contrast = max_values[i] - min_values[i]; 250 251 Serial.print("Датчик "); 252 Serial.print(i); 253 Serial.print(": "); 254 255 if(contrast > 400) { 256 Serial.println("✅ Отличная контрастность"); 257 } else if(contrast > 200) { 258 Serial.println("✅ Хорошая контрастность"); 259 } else if(contrast > 100) { 260 Serial.println("⚠️ Удовлетворительная контрастность"); 261 } else { 262 Serial.println("❌ Плохая контрастность - проверить установку"); 263 } 264 } 265 } 266};

📊 Адаптивная калибровка

🤖 Самообучающаяся система:

 1class AdaptiveCalibration {
 2private:
 3    float adaptation_rate = 0.01; // Скорость адаптации
 4    float confidence_threshold = 0.8;
 5    
 6    struct CalibrationStats {
 7        float running_min;
 8        float running_max;
 9        int sample_count;
10        float confidence;
11    } stats[3];
12    
13public:
14    void initializeAdaptive() {
15        for(int i = 0; i < 3; i++) {
16            stats[i].running_min = 1023;
17            stats[i].running_max = 0;
18            stats[i].sample_count = 0;
19            stats[i].confidence = 0;
20        }
21    }
22    
23    void updateAdaptiveCalibration() {
24        for(int i = 0; i < 3; i++) {
25            float current_value = analogRead(A0 + i);
26            
27            // Медленная адаптация к экстремальным значениям
28            if(current_value < stats[i].running_min) {
29                stats[i].running_min = current_value * (1 - adaptation_rate) + 
30                                      stats[i].running_min * adaptation_rate;
31            }
32            
33            if(current_value > stats[i].running_max) {
34                stats[i].running_max = current_value * (1 - adaptation_rate) + 
35                                      stats[i].running_max * adaptation_rate;
36            }
37            
38            stats[i].sample_count++;
39            
40            // Обновление уверенности в калибровке
41            float range = stats[i].running_max - stats[i].running_min;
42            stats[i].confidence = min(1.0, range / 500.0);
43        }
44    }
45    
46    bool isCalibrationReliable() {
47        for(int i = 0; i < 3; i++) {
48            if(stats[i].confidence < confidence_threshold) {
49                return false;
50            }
51        }
52        return true;
53    }
54    
55    void printAdaptiveStatus() {
56        static unsigned long last_print = 0;
57        if(millis() - last_print > 2000) {
58            Serial.println("=== АДАПТИВНАЯ КАЛИБРОВКА ===");
59            for(int i = 0; i < 3; i++) {
60                Serial.print("S");
61                Serial.print(i);
62                Serial.print(": MIN=");
63                Serial.print(stats[i].running_min, 1);
64                Serial.print(", MAX=");
65                Serial.print(stats[i].running_max, 1);
66                Serial.print(", CONF=");
67                Serial.print(stats[i].confidence * 100, 0);
68                Serial.println("%");
69            }
70            last_print = millis();
71        }
72    }
73};

🧮 Математические модели управления

📈 Теория автоматического управления

🎯 Классификация алгоритмов управления:

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 - коэффициенты регулятора

🎛️ Реализация алгоритмов управления

🔧 Простой релейный алгоритм:

 1class BangBangController {
 2private:
 3    SensorCalibration* calibration;
 4    int base_speed = 150;
 5    int turn_speed = 200;
 6    
 7public:
 8    BangBangController(SensorCalibration* cal) : calibration(cal) {}
 9    
10    void update() {
11        bool left_on_line = calibration->isOnLine(0);
12        bool center_on_line = calibration->isOnLine(1);
13        bool right_on_line = calibration->isOnLine(2);
14        
15        // Простая логика управления
16        if(center_on_line) {
17            // Линия по центру - движение прямо
18            setMotorSpeeds(base_speed, base_speed);
19            setStatusLED(GREEN);
20        }
21        else if(left_on_line) {
22            // Линия слева - поворот направо
23            setMotorSpeeds(base_speed + turn_speed, base_speed - turn_speed);
24            setStatusLED(YELLOW);
25        }
26        else if(right_on_line) {
27            // Линия справа - поворот налево
28            setMotorSpeeds(base_speed - turn_speed, base_speed + turn_speed);
29            setStatusLED(YELLOW);
30        }
31        else {
32            // Линия потеряна - поиск
33            executeSearchPattern();
34            setStatusLED(RED);
35        }
36    }
37    
38private:
39    void executeSearchPattern() {
40        static unsigned long search_start = 0;
41        static int search_direction = 1;
42        
43        if(search_start == 0) {
44            search_start = millis();
45        }
46        
47        // Зигзагообразный поиск
48        if(millis() - search_start < 500) {
49            setMotorSpeeds(100 * search_direction, -100 * search_direction);
50        } else {
51            search_direction *= -1;
52            search_start = millis();
53        }
54    }
55};

⚖️ Пропорциональный регулятор:

 1class ProportionalController {
 2private:
 3    SensorCalibration* calibration;
 4    float kp = 2.0;              // Пропорциональный коэффициент
 5    int base_speed = 150;        // Базовая скорость
 6    int max_correction = 100;    // Максимальная коррекция
 7    
 8public:
 9    ProportionalController(SensorCalibration* cal) : calibration(cal) {}
10    
11    void update() {
12        float line_position = calculateLinePosition();
13        
14        if(line_position != -999) { // Линия обнаружена
15            float error = 0.0 - line_position; // Цель - центр (0)
16            float correction = kp * error;
17            
18            // Ограничение коррекции
19            correction = constrain(correction, -max_correction, max_correction);
20            
21            // Применение коррекции к моторам
22            int left_speed = base_speed - correction;
23            int right_speed = base_speed + correction;
24            
25            // Ограничение скоростей моторов
26            left_speed = constrain(left_speed, 0, 255);
27            right_speed = constrain(right_speed, 0, 255);
28            
29            setMotorSpeeds(left_speed, right_speed);
30            
31            // Отладочная информация
32            Serial.print("Pos: ");
33            Serial.print(line_position, 2);
34            Serial.print(", Err: ");
35            Serial.print(error, 2);
36            Serial.print(", Corr: ");
37            Serial.print(correction, 1);
38            Serial.print(", Motors: ");
39            Serial.print(left_speed);
40            Serial.print(",");
41            Serial.println(right_speed);
42        } else {
43            // Линия потеряна
44            executeSearchPattern();
45        }
46    }
47    
48private:
49    float calculateLinePosition() {
50        float left_norm = calibration->getNormalizedValue(0);
51        float center_norm = calibration->getNormalizedValue(1);
52        float right_norm = calibration->getNormalizedValue(2);
53        
54        // Инверсия для работы с черной линией (0 = белый, 1 = черный)
55        left_norm = 1.0 - left_norm;
56        center_norm = 1.0 - center_norm;
57        right_norm = 1.0 - right_norm;
58        
59        float total_activation = left_norm + center_norm + right_norm;
60        
61        if(total_activation < 0.1) {
62            return -999; // Линия не обнаружена
63        }
64        
65        // Взвешенное среднее: -1 (крайне слева) до +1 (крайне справа)
66        float weighted_sum = left_norm * (-1.0) + center_norm * 0.0 + right_norm * (+1.0);
67        float position = weighted_sum / total_activation;
68        
69        return position;
70    }
71    
72    void setKp(float new_kp) {
73        kp = constrain(new_kp, 0.1, 10.0);
74        Serial.print("Новый Kp: ");
75        Serial.println(kp);
76    }
77};

🎛️ PD-регулятор (продвинутый):

  1class PDController {
  2private:
  3    SensorCalibration* calibration;
  4    
  5    // Параметры регулятора
  6    float kp = 2.5;              // Пропорциональный коэффициент
  7    float kd = 0.8;              // Дифференциальный коэффициент
  8    
  9    // Переменные состояния
 10    float previous_error = 0;
 11    unsigned long previous_time = 0;
 12    
 13    // Параметры управления
 14    int base_speed = 180;
 15    int max_correction = 120;
 16    
 17    // Фильтр для производной
 18    float derivative_filter = 0.1;
 19    float filtered_derivative = 0;
 20    
 21public:
 22    PDController(SensorCalibration* cal) : calibration(cal) {
 23        previous_time = millis();
 24    }
 25    
 26    void update() {
 27        unsigned long current_time = millis();
 28        float dt = (current_time - previous_time) / 1000.0; // Время в секундах
 29        
 30        if(dt > 0.001) { // Минимальный интервал времени
 31            float line_position = calculateLinePosition();
 32            
 33            if(line_position != -999) {
 34                float error = 0.0 - line_position;
 35                
 36                // Пропорциональная составляющая
 37                float proportional = kp * error;
 38                
 39                // Дифференциальная составляющая с фильтрацией
 40                float raw_derivative = (error - previous_error) / dt;
 41                filtered_derivative = derivative_filter * raw_derivative + 
 42                                    (1 - derivative_filter) * filtered_derivative;
 43                float differential = kd * filtered_derivative;
 44                
 45                // Общее управляющее воздействие
 46                float control_signal = proportional + differential;
 47                
 48                // Ограничение сигнала управления
 49                control_signal = constrain(control_signal, -max_correction, max_correction);
 50                
 51                // Применение к моторам
 52                int left_speed = base_speed - control_signal;
 53                int right_speed = base_speed + control_signal;
 54                
 55                left_speed = constrain(left_speed, 0, 255);
 56                right_speed = constrain(right_speed, 0, 255);
 57                
 58                setMotorSpeeds(left_speed, right_speed);
 59                
 60                // Обновление переменных состояния
 61                previous_error = error;
 62                previous_time = current_time;
 63                
 64                // Отладочная информация
 65                printControllerStatus(line_position, error, proportional, 
 66                                    differential, control_signal);
 67            } else {
 68                executeAdvancedSearchPattern();
 69            }
 70        }
 71    }
 72    
 73private:
 74    void printControllerStatus(float position, float error, 
 75                             float prop, float diff, float control) {
 76        static unsigned long last_print = 0;
 77        if(millis() - last_print > 100) { // Каждые 100 мс
 78            Serial.print("Pos:");
 79            Serial.print(position, 2);
 80            Serial.print(" E:");
 81            Serial.print(error, 2);
 82            Serial.print(" P:");
 83            Serial.print(prop, 1);
 84            Serial.print(" D:");
 85            Serial.print(diff, 1);
 86            Serial.print(" U:");
 87            Serial.println(control, 1);
 88            last_print = millis();
 89        }
 90    }
 91    
 92    void tuneParameters(float new_kp, float new_kd) {
 93        kp = constrain(new_kp, 0.1, 10.0);
 94        kd = constrain(new_kd, 0.0, 5.0);
 95        
 96        Serial.print("Новые параметры - Kp:");
 97        Serial.print(kp);
 98        Serial.print(", Kd:");
 99        Serial.println(kd);
100        
101        // Сброс фильтра при изменении параметров
102        filtered_derivative = 0;
103        previous_error = 0;
104    }
105    
106    void executeAdvancedSearchPattern() {
107        static unsigned long search_start = 0;
108        static float search_amplitude = 50;
109        static float search_frequency = 2.0; // Гц
110        
111        if(search_start == 0) {
112            search_start = millis();
113        }
114        
115        float time_elapsed = (millis() - search_start) / 1000.0;
116        float search_signal = search_amplitude * sin(2 * PI * search_frequency * time_elapsed);
117        
118        int left_speed = base_speed/2 - search_signal;
119        int right_speed = base_speed/2 + search_signal;
120        
121        left_speed = constrain(left_speed, 0, 255);
122        right_speed = constrain(right_speed, 0, 255);
123        
124        setMotorSpeeds(left_speed, right_speed);
125    }
126};

💻 Программирование базовых алгоритмов

🚀 Архитектура программной системы

🏗️ Модульная структура программы:

  1// ================== ГЛАВНЫЙ ФАЙЛ ПРОГРАММЫ ==================
  2#include "SensorCalibration.h"
  3#include "LineFollowingController.h"
  4#include "PerformanceAnalyzer.h"
  5#include "UserInterface.h"
  6
  7// Глобальные объекты системы
  8SensorCalibration calibration;
  9PDController controller(&calibration);
 10PerformanceAnalyzer analyzer;
 11UserInterface ui;
 12
 13// Конфигурация системы
 14struct SystemConfig {
 15    bool auto_calibration_enabled = true;
 16    bool performance_logging = true;
 17    bool debug_output = false;
 18    int target_loop_frequency = 50; // Гц
 19} config;
 20
 21void setup() {
 22    Serial.begin(115200);
 23    
 24    // Инициализация аппаратуры
 25    initializeHardware();
 26    
 27    // Приветствие пользователя
 28    ui.displayWelcomeMessage();
 29    
 30    // Проверка системы
 31    if(!performSystemSelfCheck()) {
 32        Serial.println("❌ Ошибка инициализации системы!");
 33        while(1) { delay(1000); }
 34    }
 35    
 36    // Калибровка датчиков
 37    if(config.auto_calibration_enabled) {
 38        calibration.performCalibration();
 39    } else {
 40        calibration.loadFromEEPROM();
 41    }
 42    
 43    // Готовность к работе
 44    ui.displayReadyMessage();
 45    delay(1000);
 46    
 47    Serial.println("🚀 СИСТЕМА СЛЕДОВАНИЯ ПО ЛИНИИ ЗАПУЩЕНА");
 48}
 49
 50void loop() {
 51    unsigned long loop_start = micros();
 52    
 53    // Основной цикл управления
 54    controller.update();
 55    
 56    // Сбор данных о производительности
 57    if(config.performance_logging) {
 58        analyzer.collectSample();
 59    }
 60    
 61    // Обработка пользовательского интерфейса
 62    ui.processInput();
 63    
 64    // Адаптивная калибровка
 65    if(config.auto_calibration_enabled) {
 66        calibration.updateAdaptive();
 67    }
 68    
 69    // Контроль частоты цикла
 70    maintainLoopFrequency(loop_start);
 71}
 72
 73// ================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ==================
 74void initializeHardware() {
 75    // Инициализация пинов моторов
 76    pinMode(LEFT_MOTOR_PWM, OUTPUT);
 77    pinMode(RIGHT_MOTOR_PWM, OUTPUT);
 78    pinMode(LEFT_MOTOR_DIR, OUTPUT);
 79    pinMode(RIGHT_MOTOR_DIR, OUTPUT);
 80    
 81    // Инициализация индикации
 82    pinMode(STATUS_LED, OUTPUT);
 83    pinMode(BUTTON_PIN, INPUT_PULLUP);
 84    
 85    // Установка начального состояния
 86    digitalWrite(LEFT_MOTOR_DIR, HIGH);
 87    digitalWrite(RIGHT_MOTOR_DIR, HIGH);
 88    analogWrite(LEFT_MOTOR_PWM, 0);
 89    analogWrite(RIGHT_MOTOR_PWM, 0);
 90}
 91
 92bool performSystemSelfCheck() {
 93    Serial.println("=== САМОДИАГНОСТИКА СИСТЕМЫ ===");
 94    
 95    // Проверка датчиков
 96    bool sensors_ok = true;
 97    for(int i = 0; i < 3; i++) {
 98        int value = analogRead(A0 + i);
 99        if(value < 10 || value > 1020) {
100            Serial.print("❌ Датчик ");
101            Serial.print(i);
102            Serial.println(" не отвечает");
103            sensors_ok = false;
104        }
105    }
106    
107    // Проверка моторов
108    bool motors_ok = testMotors();
109    
110    // Проверка памяти
111    bool memory_ok = (getFreeMemory() > 500);
112    
113    Serial.print("Датчики: ");
114    Serial.println(sensors_ok ? "✅" : "❌");
115    Serial.print("Моторы: ");
116    Serial.println(motors_ok ? "✅" : "❌");
117    Serial.print("Память: ");
118    Serial.print(getFreeMemory());
119    Serial.println(" байт");
120    
121    return sensors_ok && motors_ok && memory_ok;
122}
123
124void maintainLoopFrequency(unsigned long loop_start) {
125    unsigned long target_period = 1000000 / config.target_loop_frequency; // мкс
126    unsigned long loop_duration = micros() - loop_start;
127    
128    if(loop_duration < target_period) {
129        delayMicroseconds(target_period - loop_duration);
130    } else if(config.debug_output) {
131        Serial.print("⚠️ Превышение времени цикла: ");
132        Serial.print(loop_duration);
133        Serial.println(" мкс");
134    }
135}

🎛️ Пользовательский интерфейс

📟 Интерактивное управление системой:

  1class UserInterface {
  2private:
  3    enum UIState {
  4        MAIN_MENU,
  5        CALIBRATION_MENU,
  6        PARAMETER_TUNING,
  7        PERFORMANCE_VIEW,
  8        RUNNING
  9    } current_state = MAIN_MENU;
 10    
 11    unsigned long last_status_update = 0;
 12    
 13public:
 14    void displayWelcomeMessage() {
 15        Serial.println("╔═══════════════════════════════════════╗");
 16        Serial.println("║     СИСТЕМА СЛЕДОВАНИЯ ПО ЛИНИИ      ║");
 17        Serial.println("║           Версия 2.0 STEM            ║");
 18        Serial.println("║      ГБОУ Школа № 1362 - 6 класс     ║");
 19        Serial.println("╚═══════════════════════════════════════╝");
 20        Serial.println();
 21    }
 22    
 23    void displayMainMenu() {
 24        Serial.println("=== ГЛАВНОЕ МЕНЮ ===");
 25        Serial.println("1. Запустить следование по линии");
 26        Serial.println("2. Калибровка датчиков");
 27        Serial.println("3. Настройка параметров");
 28        Serial.println("4. Просмотр производительности");
 29        Serial.println("5. Диагностика системы");
 30        Serial.println("0. Экстренная остановка");
 31        Serial.println();
 32        Serial.print("Выберите опцию: ");
 33    }
 34    
 35    void processInput() {
 36        if(Serial.available()) {
 37            char input = Serial.read();
 38            handleMenuInput(input);
 39        }
 40        
 41        // Обновление статуса во время работы
 42        if(current_state == RUNNING && millis() - last_status_update > 1000) {
 43            displayRunningStatus();
 44            last_status_update = millis();
 45        }
 46    }
 47    
 48private:
 49    void handleMenuInput(char input) {
 50        switch(current_state) {
 51            case MAIN_MENU:
 52                handleMainMenuInput(input);
 53                break;
 54            case CALIBRATION_MENU:
 55                handleCalibrationMenuInput(input);
 56                break;
 57            case PARAMETER_TUNING:
 58                handleParameterTuningInput(input);
 59                break;
 60            case PERFORMANCE_VIEW:
 61                handlePerformanceViewInput(input);
 62                break;
 63            case RUNNING:
 64                handleRunningInput(input);
 65                break;
 66        }
 67    }
 68    
 69    void handleMainMenuInput(char input) {
 70        switch(input) {
 71            case '1':
 72                Serial.println("🚀 Запуск системы следования...");
 73                current_state = RUNNING;
 74                break;
 75            case '2':
 76                Serial.println("📐 Меню калибровки:");
 77                displayCalibrationMenu();
 78                current_state = CALIBRATION_MENU;
 79                break;
 80            case '3':
 81                Serial.println("⚙️ Настройка параметров:");
 82                displayParameterMenu();
 83                current_state = PARAMETER_TUNING;
 84                break;
 85            case '4':
 86                Serial.println("📊 Анализ производительности:");
 87                analyzer.displayDetailedReport();
 88                current_state = PERFORMANCE_VIEW;
 89                break;
 90            case '5':
 91                Serial.println("🔧 Диагностика системы:");
 92                performSystemSelfCheck();
 93                displayMainMenu();
 94                break;
 95            case '0':
 96                emergencyStop();
 97                break;
 98            default:
 99                Serial.println("❌ Неверный ввод. Попробуйте снова.");
100                displayMainMenu();
101        }
102    }
103    
104    void displayRunningStatus() {
105        Serial.println("┌─────────────────────────────────┐");
106        Serial
107
108
109# Пересобрать урок и уменьшить код
110// Достаточно для образовательных целей (урок 90 мин)
111#define LEFT_SENSOR A0
112#define CENTER_SENSOR A1  
113#define RIGHT_SENSOR A2
114
115int threshold = 500; // Простое фиксированное значение для начала
116
117void setup() {
118    Serial.begin(9600);
119    Serial.println("🤖 Робот-следопыт запущен!");
120    
121    // Быстрая автокалибровка (30 сек)
122    Serial.println("Калибровка... поставьте на линию");
123    delay(3000);
124    autoCalibrate();
125    
126    Serial.println("✅ Готов! Показания датчиков:");
127}
128
129void loop() {
130    // Основной алгоритм
131    followLine();
132    
133    // Простая отладка
134    debugPrint();
135    delay(100);
136}
137
138void debugPrint() {
139    Serial.print("Датчики: ");
140    Serial.print(analogRead(LEFT_SENSOR) > threshold ? "●" : "○");
141    Serial.print(" ");
142    Serial.print(analogRead(CENTER_SENSOR) > threshold ? "●" : "○");  
143    Serial.print(" ");
144    Serial.print(analogRead(RIGHT_SENSOR) > threshold ? "●" : "○");
145    Serial.print(" | Движение: ");
146    Serial.println(getMovementDescription());
147}