Автономная инвалидная коляска на ROS: сборка и программирование в 2026 | AiManual
AiManual Logo Ai / Manual.
12 Апр 2026 Гайд

Собираем автономную коляску на ROS: от железа до искусственного интеллекта

Пошаговый гайд по созданию автономной инвалидной коляски с ROS2, лидаром и AI-навигацией. Аппаратная часть, код, тонкости.

Представьте инвалидную коляску, которая сама объезжает препятствия, строит маршрут по квартире и везет пользователя к холодильнику. Это не фантастика 2010-х, а реальный проект, который можно собрать в гараже за разумные деньги. Секрет не в волшебстве, а в ROS и паре китайских сенсоров.

Почему именно Robot Operating System? Потому что все остальные фреймворки для такого проекта – либо дорогие корпоративные решения, либо самописный ад из тысяч строк кода, который сломается от взгляда. ROS – это стандарт де-факто. Он уже содержит все нужные кубики: драйверы для железа, готовые алгоритмы SLAM и навигации, инструменты визуализации. Ваша задача – правильно эти кубики сложить.

Это не медицинское устройство. Это экспериментальная платформа для обучения и прототипирования. Не сажайте в нее людей без серьезных доработок, систем резервирования и тестов. Ответственность за безопасность лежит только на вас.

Что понадобится: от коляски до лидара

Собирать будем на базе обычной электрической инвалидной коляски. Не покупайте супердорогие модели – все равно разберем половину. Главное, чтобы были исправны моторы, привод и аккумуляторы.

Компонент Конкретная модель (актуально на 04.2026) Примерная цена Зачем
Электрическая коляска Любая б/у с джойстиковым управлением 30 000 - 60 000 руб. База, шасси, моторы, батарея
Одноплатный компьютер NVIDIA Jetson Orin Nano (8GB) или более мощный AGX Orin от 45 000 руб. Мозги. Нужна производительность для ROS и AI.
2D Лидар SLAMTEC RPLIDAR A3 или YDLIDAR G4 15 000 - 25 000 руб. Глаза. Строит карту и видит препятствия.
Контроллер моторов RoboClaw 2x60A или самодельный на Arduino + драйверы моторов 10 000 - 20 000 руб. Принимает команды от ROS и крутит колеса.
IMU (Inertial Measurement Unit) SparkFun 9DoF IMU Breakout (ICM-20948) 3 000 - 5 000 руб. Ориентация в пространстве. Улучшает одометрию.
Камера глубины Intel RealSense D455 или OAK-D Pro 25 000 - 40 000 руб. Для обнаружения мелких препятствий и работы с AI.

Итоговый бюджет – в районе 100-150 тысяч рублей. Дешевле, чем новый iPhone, и в разы полезнее. Для сравнения, готовые исследовательские платформы стоят от полумиллиона. А наш подход, кстати, очень похож на тот, что использовали в проекте REXASI-PRO, только с открытым стэком.

💡
Не экономьте на лидаре. Дешевые модели (вроде A1) имеют низкую частоту обновления и разрешение. В тесной квартире с кучей мебели вы будете постоянно натыкаться на ножки стульев. A3 или аналог – минимум.

1 Подготовка коляски: разбираем и понимаем

Первым делом снимите сиденье и пластиковую облицовку. Нужно добраться до мотор-редукторов колес и штатного контроллера. Обычно там простая схема: джойстик -> плата управления -> силовые ключи -> моторы.

  • Найдите провода, идущие от моторов к контроллеру. Их нужно будет переподключить к нашему новому контроллеру (RoboClaw).
  • Определите напряжение батареи (чаще всего 24В). Убедитесь, что RoboClaw или ваш драйвер поддерживает его.
  • Закрепите площадку для одноплатника и аккумуляторов 12В/5В для периферии. Jetson Orin кушает прилично.

Самый нервный момент – отключение родной электроники. Если хотите оставить возможность ручного управления через джойстик, придется проектировать схему переключения. Я рекомендую на первом этапе полностью перейти на ROS-управление, а джойстик подключить уже как USB-устройство к Jetson.

2 Установка софта: ROS 2 и пакеты

На апрель 2026 года стабильным долгосрочным релизом ROS 2 является Jazzy Jalisco. Устанавливаем его на чистую Ubuntu 24.04 LTS. Не используйте ROS 1 (Noetic) – он уже legacy.

# Устанавливаем ROS 2 Jazzy
sudo apt update && sudo apt install curl software-properties-common
curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

sudo apt update
sudo apt install ros-jazzy-desktop python3-rosdep2
sudo rosdep init
rosdep update

# Источники в .bashrc
echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc
source ~/.bashrc

Теперь ставим обязательные для нашего проекта пакеты:

# Навигационный стек и работа с лидаром
sudo apt install ros-jazzy-nav2-bringup ros-jazzy-slam-toolbox ros-jazzy-rplidar-ros

# Драйверы для камеры (для RealSense)
sudo apt install ros-jazzy-realsense2-camera

# Утилиты
sudo apt install ros-jazzy-robot-localization ros-jazzy-tf2-tools

Если планируете использовать Vision Language Models (VLM) для семантического понимания сцены ("объехать чашку на полу"), потребуется установить дополнительные фреймворки, например, LeRobot или аналоги. Но для базовой навигации по карте хватит и лидара.

3 Создание пакета ROS и настройка конфигурации

Создаем workspace и пакет для нашей коляски. Назовем ее, например, wheelchair_bot.

mkdir -p ~/wheelchair_ws/src
cd ~/wheelchair_ws/src
ros2 pkg create wheelchair_bot --build-type ament_cmake --dependencies rclcpp sensor_msgs nav_msgs tf2 geometry_msgs
cd ~/wheelchair_ws
colcon build --symlink-install
echo "source ~/wheelchair_ws/install/setup.bash" >> ~/.bashrc
source ~/.bashrc

Сердце проекта – конфигурационные файлы. Их структура стандартна для любого мобильного робота на ROS. Вам нужно создать:

  1. URDF-файл (Unified Robot Description Format). Описывает геометрию, колеса, расположение сенсоров. Без него tf2 не сможет связать все системы координат.
  2. Конфиг для SLAM (slam_toolbox). Параметры построения карты.
  3. Конфиг для Nav2. Параметры планировщика, контроллера и recovery-поведений (что делать, если робот застрял).
  4. Launch-файлы. Чтобы запускать все одной командой.

Не пишите launch-файлы вручную с нуля. Возьмите за основу примеры из nav2_bringup и адаптируйте под свои названия топиков и фреймов. Основная ошибка новичков – несовпадение имен фреймов (frame_id). Если лидар публикует сканы во фрейме laser_frame, а URDF называет этот фрейм lidar_link, ничего работать не будет. Все должно быть согласовано.

Вот пример минимального launch-файла для запуска лидара и SLAM:

# ~/wheelchair_ws/src/wheelchair_bot/launch/slam.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare

def generate_launch_description():
    return LaunchDescription([
        # Драйвер лидара RPLIDAR A3
        Node(
            package='rplidar_ros',
            executable='rplidar_composition',
            output='screen',
            parameters=[{
                'serial_port': '/dev/ttyUSB0',
                'frame_id': 'lidar_link',
                'angle_compensate': True,
                'scan_mode': 'Standard'
            }]
        ),
        # Узел SLAM Toolbox
        Node(
            package='slam_toolbox',
            executable='async_slam_toolbox_node',
            name='slam_toolbox',
            output='screen',
            parameters=[
                PathJoinSubstitution([
                    FindPackageShare('wheelchair_bot'),
                    'config',
                    'slam_config.yaml'
                ])
            ]
        ),
    ])

4 Программирование контроллера и одометрии

Самый важный и грязный этап. Нужно написать узел, который будет слушать команды скорости от Nav2 (/cmd_vel) и преобразовывать их в PWM-сигналы или последовательные команды для RoboClaw. Параллельно этот же узел или отдельный должен читать энкодеры колес и публиковать одометрию в топик /odom.

Как НЕ надо делать? Писать все в одном цикле без фильтрации. Получите рывки и дерганья.

Вот схема правильного узла управления на Python (используем rclpy):

# ~/wheelchair_ws/src/wheelchair_bot/wheelchair_bot/motor_controller.py
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry
import serial  # для общения с RoboClaw по UART
import math

class MotorController(Node):
    def __init__(self):
        super().__init__('motor_controller')
        # Подписываемся на команды скорости
        self.subscription = self.create_subscription(
            Twist,
            '/cmd_vel',
            self.cmd_vel_callback,
            10)
        # Публикуем одометрию
        self.odom_pub = self.create_publisher(Odometry, '/odom', 10)
        # Подключаемся к контроллеру моторов
        self.ser = serial.Serial('/dev/ttyACM0', 38400, timeout=1)
        # Параметры робота: расстояние между колесами и радиус
        self.wheel_base = 0.6  # метра
        self.wheel_radius = 0.15  # метра
        self.last_time = self.get_clock().now()
        # Текущая позиция и ориентация
        self.x = 0.0
        self.y = 0.0
        self.theta = 0.0
        
        # Таймер для публикации одометрии (50 Гц)
        self.timer = self.create_timer(0.02, self.publish_odometry)
        
    def cmd_vel_callback(self, msg):
        # Преобразуем линейную и угловую скорость в скорости левого и правого колес
        linear = msg.linear.x
        angular = msg.angular.z
        
        # Дифференциальный привод: v_left = linear - angular * wheel_base/2
        v_left = linear - angular * self.wheel_base / 2.0
        v_right = linear + angular * self.wheel_base / 2.0
        
        # Конвертируем в обороты/сек и отправляем на контроллер
        left_rps = v_left / (2 * math.pi * self.wheel_radius)
        right_rps = v_right / (2 * math.pi * self.wheel_radius)
        
        # Отправка команд на RoboClaw (упрощенный пример)
        command = f"M1 {int(left_rps*1000)} M2 {int(right_rps*1000)}\n"
        self.ser.write(command.encode())
        
    def publish_odometry(self):
        # Читаем данные с энкодеров (здесь - заглушка)
        left_ticks = 0  # получить реальные значения
        right_ticks = 0
        ticks_per_rev = 2000  # зависит от энкодера
        
        # Вычисляем пройденное расстояние каждым колесом
        left_dist = (left_ticks / ticks_per_rev) * (2 * math.pi * self.wheel_radius)
        right_dist = (right_ticks / ticks_per_rev) * (2 * math.pi * self.wheel_radius)
        
        # Вычисляем изменение позиции (dead reckoning)
        dist = (left_dist + right_dist) / 2.0
        delta_theta = (right_dist - left_dist) / self.wheel_base
        
        current_time = self.get_clock().now()
        dt = (current_time - self.last_time).nanoseconds / 1e9
        self.last_time = current_time
        
        self.x += dist * math.cos(self.theta)
        self.y += dist * math.sin(self.theta)
        self.theta += delta_theta
        
        # Формируем и публикуем сообщение Odometry
        odom_msg = Odometry()
        odom_msg.header.stamp = current_time.to_msg()
        odom_msg.header.frame_id = "odom"
        odom_msg.child_frame_id = "base_link"
        odom_msg.pose.pose.position.x = self.x
        odom_msg.pose.pose.position.y = self.y
        # ... заполняем остальные поля (ориентация, ковариация, скорость)
        
        self.odom_pub.publish(odom_msg)

def main(args=None):
    rclpy.init(args=args)
    node = MotorController()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Одометрия по энкодерам дрейфует. Поэтому в серьезных проектах ее фьюзят с данными IMU и лидара через robot_localization (пакет ekf_node). Это обязательно для устойчивой навигации.

5 Настройка Nav2 и первая поездка

Когда карта построена (покатайте коляску вручную в режиме mapping), можно переходить к автономной навигации. Запускаем Nav2:

ros2 launch nav2_bringup navigation_launch.py use_sim_time:=False params_file:=/path/to/your/nav2_params.yaml

Параметры nav2_params.yaml – это целая наука. Основные моменты:

  • Planner Server: Используйте NavFn или SmacPlanner. Для коляски, которая движется только вперед-назад и вращается на месте, подойдет Smac с kinematic тип REEDS_SHEPP.
  • Controller Server: DWB (Dynamic Window Approach) – стандартный выбор. Настройте максимальные скорости и ускорения под вашу коляску. Не ставьте 2 м/с, если она едет 0.8.
  • Behavior Server: Настройте recovery behaviors. Что делать, если коляска застряла? Сначала повернуться на месте, потом отъехать назад, потом позвать на помощь.
  • Costmap: Радиус инфляции (inflation_radius) должен быть больше ширины коляски. Иначе она будет пытаться проехать вплотную к стене и зацепит ее.

Теперь через RViz можно задать цель (Goal Pose). Если все настроено верно, коляска построит путь и поедет. Сначала медленно и с постоянным наблюдением.

💡
Первые тесты проводите в пустом помещении (гараж, коридор) с мягкими препятствиями (картонные коробки). Обязательно имейте под рукой аварийный выключатель (красную кнопку), физически разрывающую цепь питания моторов. Не надейтесь только на программный стоп.

AI-дополнения: когда лидара мало

Лидар не видит стеклянные двери, провода на полу или небольшие предметы на высоте сиденья. Здесь нужна камера и AI.

Самый простой вариант – запустить детектор объектов (YOLO v11 или DETR 2026 года) на изображении с камеры глубины. Обнаруженные объекты (человек, стул, чашка) преобразуются в obstacles и добавляются в локальную costmap Nav2 как виртуальные препятствия. Это уже уровень, близкий к продвинутым мобильным роботам.

Более сложный путь – использование VLM (Vision Language Model), чтобы коляска понимала команды на естественном языке: "отвези меня к окну" или "объезди ту тумбочку". Для этого понадобится интеграция с такими фреймворками, как LeRobot или аналоги (партнерская ссылка на книгу по теме). Но будьте готовы к тому, что инференс тяжелой модели на Jetson Orin будет работать в 1-2 кадра в секунду.

Где все ломается: частые ошибки

  • Нет связи между фреймами в tf2. Запустите ros2 run tf2_tools view_frames.py и посмотрите граф. Все должно быть связано. Если где-то обрыв, проверьте, публикуете ли вы трансформ от base_link к lidar_link в узле драйвера лидара или в URDF.
  • Коляска дергается или вращается на месте. Проблема в одометрии. Неправильно рассчитано соотношение тиков энкодера к метрам. Калибруйте: проезжайте ровно 1 метр по рулетке и смотрите, сколько тиков насчитали.
  • Nav2 не может построить план. Смотрите логи planner_server. Частая причина – глобальная costmap полностью занята препятствиями (неправильно построена карта или задан огромный inflation_radius).
  • Робот игнорирует динамические препятствия. Убедитесь, что в локальной costmap включен слой obstacle_layer, который подписывается на топик лидара (/scan).

Что дальше? От прототипа к продукту

Работающий прототип – это 10% пути. Чтобы коляска могла безопасно использоваться, нужны:

  1. Система резервирования. Второй независимый контроллер (например, простой микроконтроллер), который мониторит "здоровье" основного узла и останавливает моторы при потере связи.
  2. Множество сенсоров. Один лидар – точка отказа. Добавьте второй (сзади) или стереокамеры для создания надежного поля препятствий.
  3. Сертификация. Для медицинских устройств – годы испытаний. Наш проект – база для исследований и хобби.

Технологии, опробованные здесь, – это тот же фундамент, что и для беспилотных дронов или промышленных манипуляторов. ROS – это клей, который позволяет скреплять сложные роботизированные системы. Освоив его на таком социально значимом проекте, вы получите опыт, который пригодится в любой области робототехники. Главное – начать с первого шага, точнее, с первого правильно прописанного launch-файла.

Подписаться на канал