Хорошая модель начинается с хорошего датасета, а датасет для робототехники — это синхронные потоки RGB, глубины и облака точек, снятые на реальной сцене. Формат MCAP для этого сделан идеально: один файл, индекс по времени, сжатие, читается из Python без ROS. Записывать будем через rosbag2 — штатный механизм ROS 2 — с промышленной камерой Gemini 435Le: Ethernet-интерфейс держит стабильный поток на длинных линиях, дальность до 20 метров позволяет снимать складскую сцену целиком, а IP-исполнение не боится пыли логистического цеха — типичный контур для AMR/AGV-датасетов. В гайде: разовая запись из командной строки, программный рекордер с фильтрацией и оценкой объёма, и правильный реплей, после которого ваши пайплайны не отличат запись от живой камеры.
Немного стратегии сбора, прежде чем жать кнопки. Датасет ценнее, когда сессии структурированы: короткие прогоны по сценариям («пустой проход», «человек в проходе», «паллета в зоне погрузки») с заметками в имени файла размечаются и фильтруются в разы быстрее, чем один часовой монолит. Снимайте одни и те же сцены при разном освещении — модели, обученные на одном свете, разваливаются на утренней смене. И записывайте «скучные» минуты пустой сцены: именно они потом служат калибровкой порогов детектора и материалом для фоновых классов.
Что понадобится
- Ubuntu 22.04 с ROS 2 Humble;
- камера Orbbec (пример на Gemini 435Le с Ethernet; на USB-моделях всё то же самое);
- драйвер OrbbecSDK_ROS2 (см. наш гайд про rviz2 — установка та же);
- плагин хранилища MCAP:
ros-humble-rosbag2-storage-mcap; - NVMe-диск: запись потока камер — нагрузка, на флешке будут потери;
- для программной записи — Python-пакеты
rosbag2_py,rclpy.
Пошаговая настройка
Шаг 1. Поднимаем камеру и выбираем топики
Запустите драйвер как в гайде про rviz2 (для 435-й серии у драйвера свой launch-файл — сверяйте с README вашей ревизии). Не включайте лишнее: каждый топик — это гигабайты. Для датасета детектора обычно хватает связки «RGB + depth + camera_info обоих»; облако точек записывайте только если оно нужно как продукт:
ros2 launch orbbec_camera gemini_330_series.launch.py enable_point_cloud:=false
ros2 topic list | grep camera
Для Ethernet-камеры проверьте, что порт хоста и камера согласованы по гигабиту, а линия не идёт через медленный коммутатор: недостаток канала проявится как ровные провалы частоты, которые вы потом не вылечите никаким постпроцессингом.
Ethernet даёт запас, но не бесконечный: гигабитная линия в реальности пропускает в районе сотни мегабайт в секунду, и полный профиль с облаком точек может упереться именно в неё. Если канал общий (камера и остальные датчики в одном коммутаторе), договоритесь о квоте: для записи датасета выделите отдельный VLAN или прямую линию в хост. Логирование сетевых ошибок на интерфейсе во время записи — полезная привычка: счётчик дропов, растущий ровно во время съёмки, объясняет «загадочные» провалы частоты в bag.
Шаг 2. Разовая запись в MCAP
sudo apt install ros-humble-rosbag2-storage-mcap
ros2 bag record -s mcap -o dataset_run01 \
/camera/color/image_raw \
/camera/depth/image_rect_raw \
/camera/color/camera_info \
/camera/depth/camera_info
Останов — Ctrl+C, файл ляжет в каталог dataset_run01. Полезные флаги: --compression-mode message --compression-format zstd включит покадровое сжатие (глубина сжимается в разы), --max-bag-size разобьёт запись на части, -b 0 запрещает буферизацию размера. Информацию о записанном покажет ros2 bag info dataset_run01 — сразу видно фактическую частоту по каждому топику; если она ниже ожидаемой, вы уже потеряли кадры, читайте раздел проблем.
Шаг 3. Программный рекордер
Когда запись нужно гейтить событиями (начали движение — пишем), удобнее свой узел на rosbag2_py:
import rclpy
from rclpy.node import Node
from rclpy.qos import qos_profile_sensor_data
from rclpy.serialization import serialize_message
import rosbag2_py
from sensor_msgs.msg import Image, CameraInfo
TOPICS = {
'/camera/color/image_raw': Image,
'/camera/depth/image_rect_raw': Image,
'/camera/depth/camera_info': CameraInfo,
}
class Recorder(Node):
def __init__(self):
super().__init__('dataset_recorder')
writer = rosbag2_py.SequentialWriter()
writer.open(
rosbag2_py.StorageOptions(uri='dataset_run02',
storage_id='mcap'),
rosbag2_py.ConverterOptions('', ''))
for t, typ in TOPICS.items():
writer.create_topic(
rosbag2_py.TopicMetadata(name=t, type='sensor_msgs/msg/' + typ.__name__,
serialization_format='cdr'))
self.create_subscription(typ, t, self.make_cb(writer, t),
qos_profile_sensor_data)
self.writer = writer
def make_cb(self, writer, topic):
def cb(msg):
writer.write(topic, serialize_message(msg),
self.get_clock().now().nanoseconds)
return cb
def main():
rclpy.init()
node = Recorder()
try:
rclpy.spin(node)
finally:
rclpy.shutdown()
if __name__ == '__main__':
main()
Такой рекордер легко расширить: счётчик кадров до старта записи, словарь сцен, отметки разметчика в отдельный топик — всё это потом лежит в одном файле с данными.
Шаг 4. Оцениваем объём заранее
Прикиньте поток до записи, а не после. Глубина 1280×800 в uint16 при 30 Гц — это около 61 МБ/с сырых данных; 4K RGB при 30 Гц — почти 400 МБ/с без сжатия. Zstd на глубине даёт 3–5-кратное сжатие, на RGB — зависит от сцены. NVMe-диск держит такие потоки, SATA SSD — на пределе, USB-накопитель — нет. Если объём пугает, снижайте разрешение depth (для многих задач хватает 640×400) и частоту color до 15 Гц — для обучения детекторов этого часто достаточно, а поток падает в разы. Типовые комбинации — в таблице.
Шаг 5. Реплей
ros2 bag play dataset_run01 --rate 1.0
Чтобы ваши узлы подписались на переигрываемые топики, следите за двумя вещами: QoS записанных сообщений сохраняется, и если ваш подписчик стоит в reliable, а поток был best effort, соединения не будет — используйте сенсорный профиль. Второе: топики реплея публикуются с метками времени записи; если узлы завязаны на use_sim_time, запустите плеер с --clock и включите параметр — тогда логика, чувствительная к времени, останется корректной.
Шаг 6. Читаем датасет без ROS
Финальная проверка удобства MCAP — открыть его на машине без ROS. Питоновская библиотека mcap читает сообщения из файла, а декодирование ROS-сообщений подключается ридером rosidl (или вашим десериализатором CDR):
from mcap.reader import make_reader
with open("dataset_run01/db3.mcap", "rb") as f: # путь внутри каталога bag
reader = make_reader(f)
for schema, channel, message in reader.iter_messages():
# здесь декодируем message.data по схеме
print(channel.topic, message.log_time)
break
Даже этот минимальный цикл уже даёт индекс топиков и тайминги — достаточно для проверки полноты записи и для разметчика, которому нужны только временные интервалы. Полную декодацию подключайте по мере надобности; сверьте сигнатуры с документацией вашей версии библиотеки mcap.
Разбор кода
В рекордере три решения, на которых обычно спотыкаются. Первое — хранилище задаётся явно: storage_id='mcap'; без установленного плагина rosbag2 молча уйдёт в sqlite3, и вы получите много мелких файлов вместо одного индексируемого. Второе — подписки создаются с qos_profile_sensor_data: камера публикует сенсорный профиль, и reliable-подписка либо не подключится, либо станет узким местом с переполнением очереди. Третье — штамп времени берём на момент прихода сообщения (self.get_clock().now()); альтернатива — msg.header.stamp, который точнее отражает момент съёма кадра на камере. Для датасета, где вы будете синхронизировать RGB и depth, берите header-stamp: разница «приход в сеть» и «момент экспозиции» на Ethernet-камере может достигать единиц миллисекунд, и на 30 Гц это заметно.
Отдельно про MCAP вне ROS: файл читается питон-библиотекой mcap без установки ROS, а rosbag2-сообщения декодируются через rosidl — поэтому датасет, снятый на роботе, открывается на ноутбуке разметчика, где ROS не установлен. Это главная практическая причина выбрать mcap вместо дефолтного хранилища.
И ещё одна деталь дисциплины данных: рядом с записью rosbag2 кладёт metadata.yaml со списком топиков, длительностью и размерами. Допишите туда свой контекст — камеру и её серийник, версию драйвера, параметры профиля, ответственного и сценарий сессии. Через полгода именно эти строки скажут, что за данные перед вами, и спасут от повторной съёмки. Храните рядом и контрольные суммы: датасет, который никто не боится трогать, потому что его легко проверить, живёт дольше.
| Профиль записи | Поток (сырой) | Куда годится |
|---|---|---|
| depth 640×400@30 + RGB 1280×800@15 | ~65 МБ/с | повседневные датасеты, длинные сессии |
| depth 1280×800@30 + RGB 1280×800@30 | ~160 МБ/с | детекторы с плотной глубиной, NVMe |
| + облако точек 1280×800@30 | +~190 МБ/с | только если облако — продукт |
| 4K RGB@30 + depth 1280×800@30 | ~450 МБ/с | короткие съёмки качества, RAID/NVMe |
Цифры — ориентир для планирования диска; фактический поток покажет ros2 bag info по вашей сцене и настройкам драйвера.
Частые проблемы
Фактическая частота в bag ниже, чем у камеры
Диагноз — потери на пути к диску. Проверяйте по очереди: ros2 topic hz на живом потоке (если уже тут провалы — проблема в линии/USB, до записи дело не доходит), затем нагрузку диска во время записи. Помогают покадровое сжатие zstd (меньше записи на диск), отключение лишних топиков, отдельный NVMe под данные. Если проблема в самой линии Ethernet — начните с проверки согласования скорости портов и замены патч-корда.
После реплея узлы молчат
Классика QoS: запись шла в сенсорном профиле, реплей публикует с ним же, а ваш узел подписан reliable. Ставьте подписчикам qos_profile_sensor_data — тогда они совместимы и с живой камерой, и с записью. Вторая причина — ремаппинг: ваши узлы ждут /camera/..., а плеер публикует с исходными именами — тут всё совпадает, но если вы записывали из namespace, не забудьте тот же namespace и в реплее.
Файл растёт слишком быстро, диск кончается
Вернитесь к бюджету потока: чаще всего лишний ноль приносит облако точек, которое записали «на всякий случай». Проверьте фактический состав по ros2 bag info — там видно, какой топик съел объём. Лекарства по убыванию эффективности: не записывать облако вовсе (оно восстанавливается из depth и camera_info), понизить разрешение depth, снизить частоту color, включить zstd. Если данных всё равно много, разбивайте запись на сессии и удаляйте дубли сцены сразу, на этапе отбора, — хранить «всё подряд» дороже, чем один раз прогнать ручной отбор.
Часы в датасете «прыгают»
Если между живой камерой и записью вы переключаетесь туда-обратно, узлы с use_sim_time ловят скачки времени. Правило: исследовательские скрипты — на header-stamp сообщений, системное время — только там, где вы сравниваете данные с другими логами хоста. Для мультикамерных записей посмотрите наш разбор аппаратной синхронизации Femto — без неё межкамерные метки времени расходятся на миллисекунды, что для склейки потоков много.
Оборудование для сбора датасетов ищите в каталоге Orbbec на luwam.ru. Под склад и логистику отлично подходят Gemini 435Le (Ethernet, дальность до 20 м) и Gemini 335L для настольных и манипуляционных сцен. Консультация по SDK, счёт для юрлица, гарантия 1 год.
Камеры Orbbec в наличии на LUWAM
- Orbbec Femto Bolt — iToF 4K, замена Azure Kinect — 99 900 ₽
- Orbbec Gemini 335L — стерео для роботов и SLAM — 99 900 ₽
- Orbbec Astra 2 — структурированный свет, сканирование помещений — 99 900 ₽
Весь каталог Orbbec · консультация по SDK · счёт для юрлица в день обращения · гарантия 1 год.

