Скан-зависание (stop-and-scan) — карта в помещении без GPS
Скан-зависание — самый дешёвый способ построить карту окружения для маленького дрона, у которого нет ни GPS, ни 2D-лидара, ни вычислителя под визуальный SLAM. Идея простая: дрон зависает на одной точке, серво крутит однолучевой дальномер по горизонту, на каждом шаге снимается одно расстояние. Получается веер расстояний 180°. Перелетев на следующую точку и повторив, можно по нескольким веерам собрать сетку занятости (occupancy grid) или 2D-карту локального окружения.

Зачем нужно
Когда дрон летает без GPS внутри помещения, у него нет источника абсолютной локализации. Барометр дрейфует, оптический поток ловит низкочастотный снос, IMU интегрирует ошибку. Чтобы построить карту препятствий и хотя бы локально привязаться к ней, нужен датчик дальности.
«Правильное» решение — 2D-лидар вроде RPLidar A1: сканирует 360° непрерывно, 5–10 раз в секунду, отдаёт LaserScan напрямую. Минусы: вес 170 граммов, цена ~100 USD, отдельное питание.
«Дешёвое» решение — однолучевой дальномер на серво. Вес 15 граммов вместе с серво, цена ~35 USD, питание от того же 5-вольтового шины. Минус — скан не непрерывный: дрон должен зависнуть на ~22 секунды, пока серво проходит весь горизонт.
Скан-зависание — это компромисс: жертвуем частотой скана ради массы, цены и сложности. Для разведки в помещении без GPS на учебном дроне — оправдано.
Как устроен цикл
- Зависание. Дрон входит в режим стабилизации высоты и положения. На квадрокоптерах с ArduPilot это обычно
LOITERилиALT_HOLDплюс ручная подкрутка по позиции с барометра/оптотока. Стабильность критична — если дрон уплывает на 20 см за время скана, веер расстояний оказывается размазан по нескольким позам. - Команда
/drone/sweep/start. Хост-нодаsweep_nodeпринимает пустое сообщение и инициирует проход. - Шаг по углу. Серво двигается на 1° (от 0° к 180°), хост ждёт выдержку 120 мс — за это время серво доезжает и стабилизируется, плюс приходит свежий замер с дальномера.
- Запись сэмпла. Текущее расстояние от дальномера складывается в массив. Параллельно публикуется
/scan/status: SCANNINGи/drone/sweep/progress(доля прохода 0.0–1.0). - Конец прохода. На 181-м замере (углы 0°, 1°, 2°, …, 180°) собирается единый
sensor_msgs/LaserScanи публикуется в/drone/sweep/result. Статус переключается наCOMPLETE. - Кулдаун. Внешняя нода
autoscan_nodeждёт ~10 секунд (даёт дрону восстановить позицию) и снова посылает/drone/sweep/start. - Остановка по команде. Если приходит
/drone/sweep/stop, текущий проход дорабатывается, новый не стартует. Статус —STOPPED. Команда/drone/sweep/resumeснимает блокировку.
Полный цикл — около 22 секунд скана плюс 10 секунд кулдауна. Один полный веер каждые ~30 секунд.
Что на выходе
Веер из 181 числа: расстояние до препятствия по азимутам 0°, 1°, …, 180° относительно носа дрона. В формате ROS 2 это sensor_msgs/LaserScan со следующими полями:
angle_min = 0,angle_max = π,angle_increment = π/180ranges[181]— массив расстояний в метрахrange_min = 0.2,range_max = 8.0(характеристики TF-Luna)frame_id = iris_claudedrone/sg90_arm/tf_luna_sweep— координатная цепочка через TF, луч уже привязан к углу серво
Дальше веер обычно превращают в локальную сетку занятости: по каждому лучу клеточки до точки попадания помечаются как «свободно», конечная клеточка — как «занято», за ней — «неизвестно». При нескольких пройденных позах сетки накладываются, и появляется грубая 2D-карта окружения.
Ограничения
- Время. 22 секунды на проход — это вечность для динамической сцены. Если в помещении что-то двигается (человек прошёл), полупроход видит его, второй полупроход — нет. Карта получается противоречивой.
- Стабильность зависания. Нужен либо очень хороший контур стабилизации, либо опорные сенсоры (оптический поток, лазерный высотомер). Без этого веер размазан.
- Один уровень. Скан только в горизонтальной плоскости на текущей высоте дрона. Препятствия выше или ниже линии скана не видны. Двухосевой подвес (pan + tilt) расширяет до 3D, но удваивает время.
- Минимальное расстояние сенсора. TF-Luna слепнет ближе 0.2 метра, VL53L0X — ближе 5 см. Стены вплотную дрон не видит.
- Узкий луч. 2° угол обзора у TF-Luna — между точками скана 1° есть мёртвые зоны. Тонкие провода, тросы могут ускользнуть.
- Дискретный шаг. Шаг 1° — компромисс между разрешением и временем. Уменьшение шага кратно увеличивает время прохода.
Альтернативы
| Подход | Скорость | Цена | Когда лучше |
|---|---|---|---|
| 2D-лидар (RPLidar A1) | 5–10 Гц | ~100 USD | подвижные сцены, динамичный полёт |
| Визуальный SLAM (RealSense T265) | 30–60 Гц | ~200 USD | есть текстура, нужна 6DoF поза |
| Cartographer / slam_toolbox | непрерывно | требует 2D-лидара | нужна согласованная глобальная карта |
| Стереокамера + глубинная сеть | 10–30 Гц | ~50–150 USD | бортовой GPU есть, нужна детальная сцена |
| Скан-зависание (это статья) | 0.05 Гц | ~35 USD | минимум массы и цены, статичная сцена |
Как используется в проекте claudeDrone
В нашей симуляции скан-зависание реализовано связкой SG90 + TF-Luna на iris_claudedrone (TASK-001 и TASK-002). Конечный автомат имеет четыре состояния — IDLE (ждёт /drone/sweep/start), SCANNING (идёт проход), COMPLETE (проход завершён, результат опубликован), STOPPED (остановлен по команде). Состояния транслируются в топик /scan/status (std_msgs/String) — внешний код видит, идёт ли сейчас скан, без подписки на сами данные.
Тестировалось в Gazebo на сцене indoor_room.sdf с параметрами полёта indoor.parm (GPS_TYPE=0, AHRS_EKF_TYPE=10): дрон взлетал на 1.5 метра по LOITER режиму через MAVROS, после стабилизации запускал autoscan_node и проходил несколько последовательных скан-циклов с командами STOP/RESUME между ними. Записи и снимки — в ~/drone_media/sim/task002-status/.
Текущая реализация делает скан только по азимуту. Для следующей итерации обсуждается двухосевой подвес (добавить pan-серво) — даст 3D-облако точек, но удвоит время прохода и потребует второго
sweep_node.