В сфере робототехники начальное обучение играет критическую роль, и я, как преподаватель робототехники, знаю, насколько важно делать сложные вещи понятными. Эта статья предназначена для школьников, студентов и энтузиастов, которые только начинают свой путь. Мы сосредоточимся на базовых аспектах: как установить ROS2 на Ubuntu 22.04 и запустить первые ноды для работы с изображениями и текстом.
Цель этой статьи — помочь вам быстро начать работу с ROS2, поэтому многие вещи, связанные с безопасностью или профессиональным использованием Linux опущены, хотя буду рад, если вы отметите их в комментариях. Мы пошагово разберем процесс установки и запуска трех нод: одна будет считывать изображения с веб-камеры и передавать картинку и текстовое сообщение, вторая — получить изображение для дальнейшей обработки, а третья — получить текстовые сообщения. Это основа, которая позволит вам в будущем создавать различные проекты.
Этот материал был проверен на Raspberry Pi 4 с Ubuntu Server 22.04.
Приступим к установке ROS2.
Установка 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
Шаг 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
, что гарантирует однозначное подключение к выбранной камере.
Создание ноды 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
Это нода будет выполнять следующие задачи:
Получение изображений: С помощью камеры, используя библиотеку OpenCV
Обработка и публикация: Используя библиотеку
cv_bridge
, нода преобразует полученные изображения в формат, совместимый с ROS, и отправляет их в топикcamera/image
. Это делает изображения доступными для других нод в ROS.Публикация информации: Она также отправляет текстовые сообщения, которые содержат информацию о времени захвата каждого изображения, в топик
camera/info
.Сохранение изображений: Функция
save_image
сохраняет захваченные изображения в определённой директории и ограничивает количество хранящихся изображений до последних 20, удаляя старые.Таймеры: Устанавливаются два таймера — один для функции
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: Скомпилируйте ноду.
cd ~/ros2_ws/
source install/setup.bash
colcon build
Шаг 7: Запустите ноду.
ros2 run capture_camera capture_camera
Теперь ваша нода запущена и работает. Откройте второй терминал, для работы со следующей нодой!
Создание ноды 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
Шаг 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
Создание ноды 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-файла:
Создание 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'
),
])
-
Добавьте 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
. Инструкция по запуску
Убедитесь, что ваша среда 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-файлы и запускать их, чтобы ноды работали вместе. Я бы сказал, что практика, работа с ошибками и тестирование — лучшие помощники в обучении. Удачи в разработке!
Комментарии (9)
wl2776
11.11.2023 17:44Статья является пересказом официальных инструкций от предыдущей версии.
Stepan_Burmistrov Автор
11.11.2023 17:44Действительно, это так в начале статьи! Т.к. без этой информации статья была бы инструкцией "с 0".
Прочитайте ее полностью и увидите, что в ней содержится много ценной для новичков информации о создании нод, принципов обмена между ними. Особенности подключения к камерам и создание лаунч файла.
Jury_78
Хотя бы пару слов, для тех кто не в теме, что за ROS?
Stepan_Burmistrov Автор
ROS2 — это второе поколение Robot Operating System, фреймворка, надстройки над операционной системой для удобной разработки робототехнических систем.
https://habr.com/ru/articles/492058/
Alesh
Ага, еле сдержался, что бы не минусануть ;)
solarplexus
Странное желание - минусить то, что не знаешь. Я много каких технологий не знаю, например ReactOS. У меня и в мыслях не было бы желания минусануть статью про реактос, где сразу с места в карьер, без описания что это и для чего.
Alesh
Тем не менее желание появляется, когда в ленту прилетает статья, в которой автор долго и упорно с чем то борется, но не называет это нечто по имени :)
solarplexus
Могу предположить, что если что-то не знакомое, то скорей всего, вас не заинтересует.
Возьмем несколько примеров.
Человек знает что такое ROS. Мыслей о том что надо бы вначале описать что это - нет.
Человек не знает что это. Человек не будет применять знания из статьи не зависимо узнает ли он предназначение ROS, или нет (похоже, что вы тут).
Человек не знает что это. Заинтересуется, начнет изучать. Да, возможно, было бы полезным оставить абзац о том, что это. Но, каковы шансы?) (А может, вы тут?).
Я не раз натыкался на подобного рода статьи без описания для чего это. Абсолютно было пофиг на отсутствие описания. Сам гуглил. А потом решал надо ли мне это.
Ну, кстати, я знаком с ROS, добавил в закладки. Но, даже тут есть сомнения, что мне это пригодится).