Простая установка ROS2 на Ubuntu 22.04: Руководство для начинающих

от автора

В сфере робототехники начальное обучение играет критическую роль, и я, как преподаватель робототехники, знаю, насколько важно делать сложные вещи понятными. Эта статья предназначена для школьников, студентов и энтузиастов, которые только начинают свой путь. Мы сосредоточимся на базовых аспектах: как установить ROS2 на Ubuntu 22.04 и запустить первые ноды для работы с изображениями и текстом.

Цель этой статьи — помочь вам быстро начать работу с ROS2, поэтому многие вещи, связанные с безопасностью или профессиональным использованием Linux опущены, хотя буду рад, если вы отметите их в комментариях. Мы пошагово разберем процесс установки и запуска трех нод: одна будет считывать изображения с веб-камеры и передавать картинку и текстовое сообщение, вторая — получить изображение для дальнейшей обработки, а третья — получить текстовые сообщения. Это основа, которая позволит вам в будущем создавать различные проекты.

Этот материал был проверен на Raspberry Pi 4 с Ubuntu Server 22.04.

Приступим к установке ROS2.

  1. Установка ROS2 Humble на Ubuntu 22.04

Шаг 1: Установка Locale

Нам нужно убедиться, что наша система использует UTF-8:

sudo apt update && sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 export LANG=en_US.UTF-8

Шаг 2: Добавление репозитория ROS 2

sudo apt update && sudo apt install curl gnupg2 lsb-release curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -

Добавьте репозиторий в список источников:

sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2.list'

Шаг 3: Установка ROS 2

Сначала обновите список пакетов:

sudo apt update

Затем установите ROS 2:

sudo apt install ros-humble-desktop #Установка полной версии с графический интерфейсом  sudo apt install ros-humble-ros-base sudo apt install ros-dev-tools #Установка базовой (консольной) версии и пакета dev-tools, необходимого для дальней работы

Шаг 4: Источник ROS 2 setup.bash в .bashrc

Это позволит ROS 2 быть доступным каждый раз, когда вы открываете новое окно терминала:

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc

Обновите существующий терминал, запустив:

source ~/.bashrc

Шаг 5: Установите пакет setuptools. Это предотвращает ошибки при компиляции пакетов.

pip3 install setuptools==58.2.0

На этом этапе установочный процесс ROS2 на Ubuntu 22.04 успешно завершен, и мы можем переходить к следующей части — созданию нод. Но перед этим давайте уточним несколько ключевых терминов, которые будут использоваться далее.

Ноды — это основные исполняемые процессы в ROS2, которые выполняют конкретные задачи. Каждая нода может считывать данные, принимать решения или управлять действиями в системе робота. В контексте нашего проекта, мы создадим ноды, которые будут работать с изображениями и текстовыми сообщениями.

Топики — это каналы именованных данных, по которым ноды могут обмениваться сообщениями (с различными типами данных). Это позволяет одной ноде публиковать информацию, которая затем читается другими нодами. Топики — это способ передачи информации между нодами без необходимости знать детали их реализации. Этот подход позволяет разграничить процессы, что существенно упрощает разработку робота в команде.

Теперь, когда мы разобрались с основными понятиями, давайте начнем с создания первой ноды. Эта нода будет называться ‘CaptureCamera‘, и её задача — считывать изображения с подключенной к системе веб-камеры.

В контексте программирования нод для робототехнических систем, выбор камеры на основе её индекса с помощью функции cv2.VideoCapture(0) может привести к нестабильности, поскольку нумерация устройств подвержена изменениям при перезагрузке системы. Такой подход осложняет однозначную идентификацию камеры, в случае, когда камер в роботе несколько

Для обеспечения правильного выбора видеоустройства предпочтительно использование статических путей к устройствам, используя адреса USB-портов, к которым они подключены. В Linux это достигается путём обращения к ссылкам в /dev/v4l/by-path/, которые не изменяются между сеансами и указывают на конкретные физические порты.

Примером такого подхода служит следующий код:

path = "/dev/v4l/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.2:1.0-video-index0" cap = cv2.VideoCapture(path, cv2.CAP_V4L)

Здесь path указывает на уникальный идентификатор устройства, связанный с конкретным портом USB. Объект VideoCapture инициализируется с использованием этого пути и флага cv2.CAP_V4L, что гарантирует однозначное подключение к выбранной камере.

  1. Создание ноды CaptureCamera

Чтобы создать ноду CaptureCamera, вам понадобится библиотека для работы с камерой. В этом примере мы будем использовать cv_bridge, который является интерфейсом между ROS2 и OpenCV.

Шаг 1: Создайте новый пакет

mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src ros2 pkg create --build-type ament_python capture_camera

Шаг 2: Установите зависимости

sudo apt install ros-humble-cv-bridge

Шаг 3: Создайте новый файл Python в вашем пакете

nano ~/ros2_ws/src/capture_camera/capture_camera/capture_camera.py

Шаг 4: Добавьте следующий код в capture_camera.py

Это нода будет выполнять следующие задачи:

  1. Получение изображений: С помощью камеры, используя библиотеку OpenCV

  2. Обработка и публикация: Используя библиотеку cv_bridge, нода преобразует полученные изображения в формат, совместимый с ROS, и отправляет их в топик camera/image. Это делает изображения доступными для других нод в ROS.

  3. Публикация информации: Она также отправляет текстовые сообщения, которые содержат информацию о времени захвата каждого изображения, в топик camera/info.

  4. Сохранение изображений: Функция save_image сохраняет захваченные изображения в определённой директории и ограничивает количество хранящихся изображений до последних 20, удаляя старые.

  5. Таймеры: Устанавливаются два таймера — один для функции capture_and_publish, вызываемой каждую секунду для захвата и публикации изображений, и второй для функции save_image, вызываемой каждые 5 секунд для сохранения изображений.

Все эти действия обеспечивают поток данных от камеры к нодам робота и позволяют разработчикам использовать эти данные для различных задач.

# Импортируем необходимые библиотеки import rclpy from rclpy.node import Node from cv_bridge import CvBridge from sensor_msgs.msg import Image from std_msgs.msg import String import cv2 import time import os import glob  # Определяем класс CaptureCameraNode, который является ROS2-нодой для захвата изображений с камеры class CaptureCameraNode(Node):     def __init__(self):         # Инициализируем родительский класс Node с именем 'capture_camera'         super().__init__('capture_camera')                  # Создаем объект CvBridge для преобразования изображений между OpenCV и ROS         self.bridge = CvBridge()          # Создаем издателей для публикации изображений и текстовых сообщений         self.publisher = self.create_publisher(Image, 'camera/image', 10)         self.text_publisher = self.create_publisher(String, 'camera/info', 10)          # Определяем путь к устройству камеры         path = "/dev/v4l/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.2:1.0-video-index0"         # Инициализируем объект VideoCapture для захвата видео с камеры         self.cap = cv2.VideoCapture(path, cv2.CAP_V4L)         print("Camera created", self.cap)          # Создаем таймер, который вызывает функцию capture_and_publish каждую секунду         self.timer = self.create_timer(1.0, self.capture_and_publish)          # Определяем путь для сохранения изображений         self.image_save_path = "/home/pi/images"         # Если папка для сохранения изображений не существует, создаем ее         os.makedirs(self.image_save_path, exist_ok=True)                  # Создаем таймер, который вызывает функцию save_image каждые 5 секунд         self.save_timer = self.create_timer(5.0, self.save_image)      # Функция для захвата и публикации изображения     def capture_and_publish(self):         # Захватываем изображение с камеры         ret, frame = self.cap.read()         if ret:             # Выводим текущее время захвата на консоль             print("Last time of get image:", time.ctime())                          # Преобразуем изображение в формат ROS и публикуем его             msg = self.bridge.cv2_to_imgmsg(frame, "bgr8")             self.publisher.publish(msg)              # Подготавливаем и публикуем текстовое сообщение с временем захвата изображения             msg = String()             msg.data = f"Last time of get image: {time.ctime()}"             self.text_publisher.publish(msg)      # Функция для сохранения изображения и обеспечения хранения только 20 последних изображений     def save_image(self):         ret, frame = self.cap.read()         if ret:             # Сохраняем изображение с учетом временной метки             timestamp = time.strftime("%Y%m%d-%H%M%S")             filename = os.path.join(self.image_save_path, f"image_{timestamp}.jpg")             cv2.imwrite(filename, frame)             print(f"Saved image to {filename}")                          # Получаем список всех сохраненных изображений             all_images = glob.glob(os.path.join(self.image_save_path, "*.jpg"))             # Сортируем список изображений по дате создания             sorted_images = sorted(all_images, key=os.path.getmtime)             # Удаляем старые изображения, оставляя только 20 последних             while len(sorted_images) > 20:                 os.remove(sorted_images[0])                 del sorted_images[0]  def main(args=None):     # Инициализируем ROS     rclpy.init(args=args)     # Создаем объект нашей ноды     capture_camera = CaptureCameraNode()     # Запускаем цикл обработки ROS     rclpy.spin(capture_camera)  if __name__ == '__main__':     main() 

Шаг 5: Измените следующий код в ~/ros2_ws/src/capture_camera/setup.py

Это необходимо для правильной сборки пакета и последующего запуска.

from setuptools import find_packages, setup  package_name = 'capture_camera'  setup(     name=package_name,     version='0.0.0',     packages=find_packages(exclude=['test']),     data_files=[         ('share/ament_index/resource_index/packages',             ['resource/' + package_name]),         ('share/' + package_name, ['package.xml']),     ],     install_requires=['setuptools'],     zip_safe=True,     maintainer='pi',     maintainer_email='pi@todo.todo',     description='TODO: Package description',     license='TODO: License declaration',     tests_require=['pytest'],     entry_points={         'console_scripts': [                 'capture_camera = capture_camera.capture_camera:main'         ],     }, ) 

Шаг 6: Скомпилируйте ноду.

sudo apt install python3-colcon-common-extensions cd ~/ros2_ws/ colcon build source install/setup.bash

Шаг 7: Запустите ноду.

ros2 run capture_camera capture_camera

Теперь ваша нода запущена и работает. Откройте второй терминал, для работы со следующей нодой!

  1. Создание ноды ProcessingCamera

Теперь создадим ноду ProcessingCamera, которая будет получать изображение и обрабатывать его.

Эта нода будет подписана на топик camera/image, где она будет ожидать поступления изображений от ноды захвата камеры.

В этой ноде также используется CvBridge для преобразования изображений из формата, понятного ROS, в формат, с которым может работать библиотека OpenCV. Каждый раз, когда в топик приходит новое изображение, вызывается функция image_callback. В этой функции изображение преобразуется в формат OpenCV, что позволяет ноде ProcessingCamera анализировать и обрабатывать изображение.
В нашем случае мы просто будем читать разрешение картинки и выводить его в консоль.

Шаг 1: Создайте новый пакет

cd ~/ros2_ws/src ros2 pkg create --build-type ament_python processing_camera 

Шаг 2: Создайте новый файл Python в вашем пакете

nano ~/ros2_ws/src/processing_camera/processing_camera/processing_camera.py

Шаг 4: Добавьте следующий код в processing_camera.py

import rclpy from rclpy.node import Node from cv_bridge import CvBridge from sensor_msgs.msg import Image  class ImageReaderNode(Node):      def __init__(self):         super().__init__('image_reader_node')  # Инициализируем родительский класс Node с именем 'image_reader_node'          # Создаем объект CvBridge для преобразования изображений между OpenCV и ROS         self.bridge = CvBridge()          # Подписываемся на тему 'camera/image' для чтения изображений         self.subscription = self.create_subscription(             Image,              'camera/image',              self.image_callback,              10         )         self.subscription      def image_callback(self, msg):         # Преобразуем изображение из формата ROS в формат OpenCV         cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")          # Получаем разрешение изображения         height, width, _ = cv_image.shape         print(f"Image resolution: {width}x{height}")  def main(args=None):     rclpy.init(args=args)     image_reader = ImageReaderNode()     rclpy.spin(image_reader)  if __name__ == '__main__':     main()  

Шаг 5: Измените следующий код в ~/ros2_ws/src/processing_camera/setup.py

from setuptools import find_packages, setup  package_name = 'processing_camera'  setup(     name=package_name,     version='0.0.0',     packages=find_packages(exclude=['test']),     data_files=[         ('share/ament_index/resource_index/packages',             ['resource/' + package_name]),         ('share/' + package_name, ['package.xml']),     ],     install_requires=['setuptools'],     zip_safe=True,     maintainer='pi',     maintainer_email='pi@todo.todo',     description='TODO: Package description',     license='TODO: License declaration',     tests_require=['pytest'],     entry_points={         'console_scripts': [                 'processing_camera = processing_camera.processing_camera:main'         ],     }, ) 

Шаг 6: Скомпилируйте обе ноды

cd ~/ros2_ws/ source install/setup.bash colcon build

Шаг 7: Запустите ноды в разных терминалах

cd ~/ros2_ws/ source install/setup.bash ros2 run capture_camera capture_camera   cd ~/ros2_ws/ source install/setup.bash ros2 run processing_camera processing_camera
  1. Создание ноды InfoReaderNode

Теперь создадим ноду InfoReaderNode , которая читать текстовые сообщения из топика camera/info в выводить их в консоль.

Все шаги аналогичны предыдущим. Код для ноды:

import rclpy from rclpy.node import Node from std_msgs.msg import String  class InfoReaderNode(Node):      def __init__(self):         super().__init__('info_reader_node')  # Инициализируем родительский класс Node с именем 'info_reader_node'                  # Подписываемся на топик 'camera/info' для чтения текстовых сообщений         self.subscription = self.create_subscription(             String,              'camera/info',              self.info_callback,              10         )         self.subscription      def info_callback(self, msg):         # Выводим полученное текстовое сообщение в консоль         print(f"Received info: {msg.data}")  def main(args=None):     rclpy.init(args=args)     info_reader = InfoReaderNode()     rclpy.spin(info_reader)  if __name__ == '__main__':     main() 

Для удобного запуска нод, находящихся в разных пакетах ROS2, вам нужно создать launch-файл. Обычно launch-файл размещается в папке launch внутри одного из ваших пакетов ROS2 или в отдельном пакете, предназначенном только для launch-файлов.

Вот шаги по созданию и запуску launch-файла:

  1. Создание launch-файла

Создайте файл с именем camera_launch.py (или другим на ваше усмотрение)

в папке launch одного из ваших пакетов:

from launch import LaunchDescription from launch_ros.actions import Node  def generate_launch_description():     return LaunchDescription([         # Запуск ноды захвата изображений         Node(             package='capture_camera',             executable='capture_camera',             name='capture_camera'         ),         # Запуск ноды для чтения изображений и вывода их размеров         Node(             package='processing_camera',             executable='processing_camera',             name='processing_camera'         ),         # Запуск ноды для чтения текстовой информации из camera/info         Node(             package='info_reader',             executable='info_reader_node',             name='info_reader_node'         ),     ])  
  1. Добавьте launch-файл в ваш пакет: Убедитесь, что launch-файл добавлен в data_files в setup.py файлах соответствующих пакетов, чтобы он устанавливался вместе с пакетом.

    Предположим, что ваш launch-файл называется camera_launch.py и находится в папке launch внутри пакета capture_camera. Вот как может выглядеть соответствующий раздел setup.py:

    from setuptools import find_packages, setup import os  package_name = 'capture_camera'  setup(     name=package_name,     version='0.0.0',     packages=find_packages(exclude=['test']),  # Exclude any directories named 'test'     data_files=[         ('share/ament_index/resource_index/packages',             ['resource/' + package_name]),         ('share/' + package_name, ['package.xml']),         # Включаем launch файлы         (os.path.join('share', package_name, 'launch'), ['launch/camera_launch.py']),     ],     install_requires=['setuptools'],     zip_safe=True,     maintainer='pi',     maintainer_email='pi@todo.todo',     description='TODO: Package description',     license='TODO: License declaration',     tests_require=['pytest'],     entry_points={         'console_scripts': [             'capture_camera = capture_camera.capture_camera:main',         ],     }, ) 

    Обратите внимание на следующую строку:

    (os.path.join('share', package_name, 'launch'), ['launch/camera_launch.py']),

    Эта строка говорит setuptools, что нужно включить файл camera_launch.py в папку share/<package_name>/launch внутри установочного каталога. Когда вы устанавливаете пакет с помощью colcon build, этот файл будет скопирован в указанное место, и вы сможете запустить его через ros2 launch.

  2. Инструкция по запуску

  • Убедитесь, что ваша среда ROS 2 активирована:

source /opt/ros/humble/setup.bash  # замените "humble" на вашу версию ROS 2, если она другая
  • Перейдите в корневую папку вашего workspace:

cd ~/ros2_ws
  • Запустите colcon build, чтобы пересобрать ваш пакет:

colcon build
  • Выполните source install/setup.bash, это обновляет текущую сессию терминала с необходимыми переменными среды и конфигурациями для работы с ROS. Это важно делать каждый раз при открытии нового терминала, если вы собираетесь работать с ROS, так как без этого команды ROS могут быть недоступны или работать некорректно.

source install/setup.bash
  • Запустите launch-файл (при необходимости измените имя пакета или файла):

ros2 launch capture_camera camera_launch.py 

И вот мы завершили основные действия с ROS2 для начала работы. Мы научились устанавливать и настраивать ноды, создавать launch-файлы и запускать их, чтобы ноды работали вместе. Я бы сказал, что практика, работа с ошибками и тестирование — лучшие помощники в обучении. Удачи в разработке!


ссылка на оригинал статьи https://habr.com/ru/articles/768048/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *