Перейти к содержанию
Скачать руководство PDF

Python-клиент

В репозитории есть готовый клиент examples/ros/control.py. Он вызывает сервис управления и умеет подписываться на результаты. Его можно запускать на компьютере SmartCamera или на другом компьютере с ROS 2 Jazzy и собранным пакетом smart_camera_interfaces.

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

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

cd ~/SmartCamera
source /opt/ros/jazzy/setup.bash
source install/setup.bash
python3 examples/ros/control.py --help

Если проект установлен в другом каталоге, замените ~/SmartCamera. На удалённой машине согласуйте ROS_DOMAIN_ID с сервером. При разных доменах сервис не будет обнаружен, даже если веб-панель открывается по HTTP.

От списка камер до первого результата

Получите камеры и модели:

python3 examples/ros/control.py cameras
python3 examples/ros/control.py models

В первом ответе найдите cameras[].id, во втором — models[].id. Название модели помогает выбрать запись, но в запрос передаётся её ID. Подставьте свои значения:

CAMERA_ID=workbench
MODEL_ID=вставьте_id_модели
python3 examples/ros/control.py create --json \
  "{\"camera_id\":\"$CAMERA_ID\",\"model_id\":\"$MODEL_ID\",\"enabled\":true,\"fps\":5,\"device\":\"cpu\",\"parameters\":{\"conf\":0.4,\"iou\":0.6}}"

Сохраните id созданного запуска. Ниже RUN_ID — этот ID, а не ID модели.

RUN_ID=вставьте_id_запуска
python3 examples/ros/control.py get --target "$RUN_ID"
python3 examples/ros/control.py watch --target "$RUN_ID"

watch печатает кадры по мере обработки: номер, систему координат, обнаруженные классы, confidence, рамки и ID треков. Для завершения просмотра нажмите Ctrl+C. Подписка остановится, а модель продолжит работать.

Для остановки самой модели сначала перечитайте её состояние и возьмите текущую ревизию. Например, если revision равна 1:

python3 examples/ros/control.py set --target "$RUN_ID" --revision 1 \
  --json '{"enabled":false}'

Если настройки уже менялись, число будет другим. Ошибка revision_conflict означает, что нужно повторить get и использовать новую ревизию. Не подставляйте постоянно одно и то же число в управляющую программу.

Подписчик для своей программы

Этот пример не управляет моделями. Он слушает уже работающие запуски и выводит рамки объектов. Сохраните его как listen_detections.py и запустите после source install/setup.bash:

import rclpy
from rclpy.node import Node
from rclpy.qos import qos_profile_sensor_data
from smart_camera_interfaces.msg import InferenceResult


class Detections(Node):
    def __init__(self):
        super().__init__("my_detections")
        self.subscription = self.create_subscription(
            InferenceResult,
            "/smart_camera/inference/results",
            self.on_frame,
            qos_profile_sensor_data,
        )

    def on_frame(self, message):
        # При нескольких запусках здесь можно отфильтровать message.run_id.
        stamp = message.header.stamp
        print(message.camera_id, message.sequence,
              f"{stamp.sec}.{stamp.nanosec:09d}", message.header.frame_id)
        for obj in message.objects:
            if obj.has_box:
                left, top, right, bottom = obj.xyxy
                print(obj.class_name, round(obj.confidence, 3),
                      (left, top, right, bottom), obj.track_id)


rclpy.init()
node = Detections()
try:
    rclpy.spin(node)
except KeyboardInterrupt:
    pass
finally:
    node.destroy_node()
    rclpy.shutdown()
python3 listen_detections.py

qos_profile_sensor_data совместим с издателем результатов: best-effort, volatile. Если задать подписчику reliable, сообщения от best-effort издателя не придут. Наличие топика в ros2 topic list само по себе не подтверждает совместимость QoS.

Полные выходы модели

Массив objects удобен для обычной детекции. Для вероятностей классификации, растровых масок и нестандартных выходов прочитайте JSON:

import json

payload = json.loads(message.data_json)
raw = payload["raw"]
outputs = payload["outputs"]
probabilities = raw.get("probs", {}).get("data")

Доступные поля зависят от задачи модели. Проверяйте их наличие; классификатор может вернуть вероятности и пустой objects. Сервис уже заменяет ссылки на временные NumPy-файлы значениями массивов, поэтому клиенту не нужен доступ к каталогу записей сервера.

Систему координат и ограничения размеров сообщений смотрите в описании ROS-интерфейса. Перевод рамки в 3D требует калибровки и глубины; сами четыре числа xyxy являются пикселями.