Watchdog и Избыточность. Как не зависнуть и не сломаться
Представьте, что робот-пожарный должен найти людей в горящем здании. Что случится, если его программа зависнет? Или если один датчик закоптится? Watchdog и избыточность — это системы, которые спасают в таких ситуациях.
1. Watchdog Timer — электронный “сторожевой пёс”
Что это такое?
Это отдельный таймер внутри микроконтроллера, который не зависит от основной программы. Если программа зависнет и перестанет “кормить” этого пса, он “кусает” микроконтроллер — перезагружает его.
Как работает:
Нормальная работа:
[Программа] → каждые 0.5 сек → [WDT: сброс] → [Программа]
(всё хорошо)
Зависание:
[Программа ЗАВИСЛА] → ... → [WDT: таймер 0] → ПЕРЕЗАГРУЗКА!
Код для Arduino:
#include <avr/wdt.h> // Библиотека для Watchdog
void setup() {
Serial.begin(9600);
Serial.println("Робот запускается...");
// Включаем Watchdog на 2 секунды
// WDTO_2S значит: перезагрузить через 2 секунды тишины
wdt_enable(WDTO_2S);
}
void loop() {
// Делаем свою работу
readSensors();
makeDecision();
controlMotors();
// ВАЖНО: "Кормим собаку" - сбрасываем таймер
// Если эта строка не выполнится за 2 секунды -> перезагрузка
wdt_reset();
}
// Пример зависания - ЗАКОММЕНТИРУЙТЕ для теста!
void badFunction() {
while(true) {
// Бесконечный цикл!
// Watchdog перезагрузит Arduino через 2 секунды
}
}
Для ESP32 (более сложно):
#include "esp_task_wdt.h"
void setup() {
// Подключаем Watchdog к главной задаче (Task)
esp_task_wdt_init(2, true); // Таймаут 2 секунды
esp_task_wdt_add(NULL); // Добавляем текущую задачу
// Теперь если задача зависнет >2 сек -> перезагрузка
}
void loop() {
// Делаем что-то...
// Сбрасываем Watchdog для этой задачи
esp_task_wdt_reset();
// Если добавили другие задачи, их тоже нужно сбрасывать
}
2. Избыточность — если один сломался, есть запасной
Типы избыточности:
А) Дублирование датчиков
Пример: Робот с двумя ультразвуковыми датчиками спереди.
float getDistance() {
float dist1 = ultrasonic1.read();
float dist2 = ultrasonic2.read();
// Если датчики показывают примерно одинаково
if (abs(dist1 - dist2) < 10) {
return (dist1 + dist2) / 2; // Среднее значение
}
// Если один сломался (показывает 0 или 1000)
else if (dist1 < 1 || dist1 > 500) {
return dist2; // Используем только рабочий
}
else if (dist2 < 1 || dist2 > 500) {
return dist1;
}
// Если оба показывают разное, но в пределах
else {
return min(dist1, dist2); // Берем ближайший (безопаснее)
}
}
Б) Разные типы датчиков
Пример: Измерение расстояния тремя способами:
Для робота-исследователя:
1. Ультразвуковой датчик: точен на 20-200 см
2. Инфракрасный датчик: точен на 10-80 см
3. Лазерный дальномер (LiDAR): точен везде, но дорогой
Если один запылился или сломался — используем другие!
В) Дублирование моторов
Гусеничный робот с 2 моторами на борт:
[Левый борт] Мотор1 → Мотор2 (резервный)
[Правый борт] Мотор3 → Мотор4 (резервный)
Если Мотор1 сломался → включаем Мотор2
Робот продолжает движение, только медленнее
Практические примеры для школы
Проект 1: Робот для соревнований “Следование по линии”
Проблема: ИК-датчик может загрязниться или сломаться
Решение: 5 датчиков вместо 3
Логика:
1. Если все 5 работают → используем все
2. Если 1 сломался → используем 4
3. Если 2 сломался → используем 3 (минимум)
4. Если 3 сломалось → останавливаемся, сигнал ошибки
Код:
int sensors[5] = {0, 0, 0, 0, 0};
int workingCount = 0;
float average = 0;
for(int i = 0; i < 5; i++) {
int val = readSensor(i);
if(val >= 100 && val <= 900) { // Рабочий диапазон
sensors[i] = val;
workingCount++;
average += val;
}
}
if(workingCount >= 3) {
average /= workingCount;
followLine(average); // Работаем
} else {
emergencyStop(); // Слишком много сломалось
}
Проект 2: Робот-метеостанция
Избыточность данных:
1. Температура: 2 датчика (DHT22 + DS18B20)
2. Давление: 2 датчика (BMP280 + BME280)
3. Влажность: 2 датчика (DHT22 + отдельный)
Если датчики показывают разное:
• Берём среднее
• Отмечаем в логе "расхождение"
• Если расхождение большое → используем более надежный
Проект 3: Робот с манипулятором
Защита от сбоев:
1. Watchdog на 500 мс (быстрая реакция)
2. 2 концевых выключателя на каждом суставе
3. Датчик тока на каждом моторе
4. Резервный источник питания для контроллера
При любой ошибке:
1. Watchdog перезагружает если завис
2. Концевик останавливает мотор физически
3. Датчик тока отключает при перегрузке
4. Резервное питание держит 5 сек для безопасной остановки
Как настроить Watchdog правильно
Настройка времени:
Короткое время (100-500 мс):
• Для быстрых роботов (дроны, гоночные)
• Быстрое обнаружение зависаний
• Но нужно часто сбрасывать
Длинное время (1-8 секунд):
• Для медленных роботов (манипуляторы, метеостанции)
• Можно делать долгие операции (измерения, отправка данных)
• Медленнее реагирует на зависания
Что делать после перезагрузки:
#include <avr/wdt.h>
void setup() {
// Проверяем причину перезагрузки
if (MCUSR & (1 << WDRF)) { // Перезагрузились из-за Watchdog
Serial.println("ПЕРЕЗАГРУЗКА Watchdog! Программа зависла.");
logError("Watchdog reset");
// Можно восстановить состояние
recoverFromCrash();
}
MCUSR = 0; // Сбрасываем флаг
wdt_enable(WDTO_2S); // Включаем снова
}
Избыточность в действии: реальный пример
Ситуация: Автономный робот-уборщик
Компоненты:
• Основной контроллер: Raspberry Pi (планирование)
• Резервный контроллер: Arduino (аварийное управление)
• Датчики расстояния: 8 штук (4 основных + 4 резервных)
• Моторы: 4 (каждый может работать в одиночку)
При отказе:
1. Если Raspberry Pi завис → Arduino берет управление
2. Если 1-2 датчика сломались → используем остальные
3. Если 1 мотор сломался → едем на 3-х, медленнее
4. Если 2 мотора сломались → останавливаемся, зовем помощь
Код управления моторами:
void controlMotors() {
// Проверяем работоспособность каждого мотора
bool motorsOK[4] = {true, true, true, true};
for(int i = 0; i < 4; i++) {
if (getMotorCurrent(i) > MAX_CURRENT) motorsOK[i] = false;
if (getMotorTemperature(i) > 80) motorsOK[i] = false;
}
// Сколько моторов работает?
int working = 0;
for(int i = 0; i < 4; i++) if(motorsOK[i]) working++;
if (working == 4) {
// Все хорошо - нормальная работа
setAllMotors(normalSpeed);
}
else if (working >= 2) {
// Работаем в ограниченном режиме
setWorkingMotors(reducedSpeed);
sendAlert("Некоторые моторы не работают");
}
else {
// Слишком много сломалось
emergencyStop();
}
}
Частые ошибки с Watchdog и избыточностью
Ошибка 1: “Слишком длинные delay()”
Проблема: delay(5000) на 5 секунд
Watchdog сработает через 2 секунды → перезагрузка
Решение:
unsigned long start = millis();
while(millis() - start < 5000) {
wdt_reset(); // Сбрасываем в цикле
// Можно делать что-то полезное
}
Ошибка 2: “Дублирование одинаковых слабых мест”
Проблема: 2 одинаковых дешёвых датчика
Оба выходят из строя в одинаковых условиях
Решение: Разные типы датчиков
Ультразвуковой + инфракрасный + лазерный
Ошибка 3: “Нет проверки после дублирования”
Проблема: Включили резервный мотор, но не проверили
Через 5 минут и он сломался
Решение: Проверять резервные системы тоже
testBackupSystems();
Ошибка 4: “Watchdog срабатывает на нормальную работу”
Проблема: Программа делает сложные вычисления 3 секунды
Watchdog настроен на 2 секунды → постоянные перезагрузки
Решение:
1. Увеличить время Watchdog
2. Разбить сложную задачу на части
3. Сбрасывать Watchdog внутри длинной операции
Эксперимент: Создай неубиваемый мигающий светодиод
Что нужно: Arduino, светодиод, резистор 220 Ом
Задача: Светодиод должен мигать ВСЕГДА, даже если мы “ломаем” программу.
Код с защитой:
#include <avr/wdt.h>
void dangerousFunction() {
// ОПАСНО: может зависнуть
while(random(100) != 42) {
// Иногда зависает здесь надолго
}
}
void setup() {
wdt_enable(WDTO_1S); // Строгий Watchdog: 1 секунда
pinMode(LED_BUILTIN, OUTPUT);
randomSeed(analogRead(0));
}
void loop() {
// Нормальная работа: мигаем
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
// Иногда вызываем опасную функцию
if (random(10) == 0) {
dangerousFunction();
}
// Сбрасываем Watchdog
// Если dangerousFunction() зависнет >1 сек -> перезагрузка
// Но светодиод СНОВА начнет мигать после перезагрузки!
wdt_reset();
}
Что увидите: Светодиод почти всегда мигает. Иногда на секунду остановится (перезагрузка), и снова начнет мигать.
Проверь свой робот
Тест на надежность:
- Тест Watchdog: Заставьте программу зависнуть (while(1)). Перезагрузился ли робот сам через несколько секунд?
- Тест датчиков: Закройте один датчик рукой. Продолжает ли робот работать?
- Тест моторов: Отключите один мотор. Может ли робот продолжать движение?
- Тест питания: На короткое время отключите питание. Восстанавливается ли робот сам?
Если на все 4 теста ответ “да” — у вас надежный робот!
Что дальше?
Освоили Watchdog и избыточность? Теперь можно:
- Failsafe системы — что делать при критических отказах
- Тестирование надежности — как проверять робота на устойчивость к сбоям
- Логирование ошибок — как записывать, что пошло не так
Совет: Добавляйте Watchdog в КАЖДЫЙ проект с самого начала. Проще сразу написать wdt_reset() в loop(), чем потом искать, почему робот зависает на соревнованиях. И помните: один резервный датчик может спасти весь проект.
