🔲 ArUco-навигация detect_aruco_coordinates.py

Координаты ArUco-метки

aruco_examples/detect_aruco_coordinates.py

Рассчитывает положение метки относительно камеры через solvePnP и печатает координаты.

Использует data.yml с калибровкой. Для первой найденной метки (сторона 0.05 м) по углам вычисляется поза cv2.solvePnP, координаты x, y, z печатаются в терминал, а оси метки рисуются на кадре. Поток — aruco_coordinates.

Как работает

  1. Загрузить коэффициенты калибровки из data.yml.
  2. Создать детектор ArUco, Camera() и ImageViewer().
  3. В цикле искать метки.
  4. Для первой метки вызвать cv2.solvePnP.
  5. Вывести x, y, z и нарисовать оси cv2.drawFrameAxes.
  6. Публиковать кадр в поток aruco_coordinates.

Используемые методы SDK

Ключевые параметры

ПараметрЗначениеОписание
size_of_marker0.05 мСторона метки.
timeout1.0 сОжидание кадра.

Запуск

cd aruco_examples
python3 detect_aruco_coordinates.py

Результат

  • Строки x=…, y=…, z=… в терминале.
  • Поток rtsp://10.42.0.1:8889/aruco_coordinates/.

Видеопотоки

rtsp://10.42.0.1:8889/aruco_coordinates/

Примечания

  • Размер метки в этом примере — 0.05 м, в отличие от aruco_flight.py (0.1 м).

Требования

  • data.yml с калибровкой.
  • ArUco-метка 0.05 м.

Полный исходный код

Показать detect_aruco_coordinates.py
from pioneer_sdk2 import Camera, ImageViewer # импортируем классы Camera и ImageViewer из библиотеки pioneer_sdk2
import cv2                                  # библиотека cv2 содержит функции для работы с изображениями
import numpy as np                          # библиотека numpy нужна для работы с массивами
from pathlib import Path                    # класс Path нужен для работы с путем к файлу калибровки


DATA_PATH = Path(__file__).with_name("data.yml") # путь к файлу калибровки рядом со скриптом


def load_coefficients(path):               # функция для загрузки коэффициентов калибровки камеры
    cv_file = cv2.FileStorage(str(path), cv2.FILE_STORAGE_READ) # открываем файл для чтения данных калибровки

    if not cv_file.isOpened():             # проверяем, что файл калибровки удалось открыть
        raise FileNotFoundError("Не удалось открыть файл калибровки: " + str(path)) # сообщаем, какой файл не найден

    camera_matrix = cv_file.getNode("mtx").mat() # считываем матрицу камеры из файла
    dist_coeffs = cv_file.getNode("dist").mat()  # считываем коэффициенты искажений из файла

    cv_file.release()                      # закрываем файл после чтения

    if camera_matrix is None or dist_coeffs is None: # проверяем, что файл содержит нужные данные
        raise ValueError("Файл калибровки не содержит mtx или dist") # сообщаем о неверном файле калибровки

    return camera_matrix, dist_coeffs      # возвращаем матрицу камеры и коэффициенты искажений


aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_ARUCO_ORIGINAL) # выбираем словарь ArUco-меток
aruco_params = cv2.aruco.DetectorParameters()                         # создаем параметры для поиска ArUco-меток
aruco_detector = cv2.aruco.ArucoDetector(aruco_dict, aruco_params)    # создаем детектор ArUco-меток

camera_matrix, dist_coeffs = load_coefficients(DATA_PATH) # загружаем коэффициенты калибровки камеры

size_of_marker = 0.05                    # задаем размер стороны ArUco-метки в метрах

points_of_marker = np.array([            # задаем координаты углов ArUco-метки
    (size_of_marker / 2, -size_of_marker / 2, 0),
    (-size_of_marker / 2, -size_of_marker / 2, 0),
    (-size_of_marker / 2, size_of_marker / 2, 0),
    (size_of_marker / 2, size_of_marker / 2, 0)
], dtype=np.float32)                      # используем тип float32, который подходит для функций OpenCV

camera = Camera()                         # создаем экземпляр класса Camera для получения кадров с камеры
viewer = ImageViewer()                    # создаем экземпляр класса ImageViewer для трансляции изображения

try:                                      # основной код находится внутри блока try
    while True:                           # запускаем бесконечный цикл
        frame = camera.get_cv_frame(timeout=1.0) # получаем один кадр с камеры
                                                 # timeout=1.0 - время ожидания кадра в секундах

        if frame is None:                 # проверяем, что кадр не был получен
            continue                      # пропускаем текущую итерацию цикла

        corners, ids, rejected = aruco_detector.detectMarkers(frame) # ищем ArUco-метки на изображении

        if ids is not None and len(corners) > 0: # проверяем, что хотя бы одна ArUco-метка найдена
            image_points = corners[0].reshape(-1, 2) # получаем координаты углов первой найденной метки

            success, rvecs, tvecs = cv2.solvePnP(points_of_marker, image_points, camera_matrix, dist_coeffs) # считаем положение метки

            if success:                   # проверяем, что положение метки успешно рассчитано
                print(
                    f"x={tvecs.item(0):.2f},", # координата метки по оси X
                    f"y={tvecs.item(1):.2f},", # координата метки по оси Y
                    f"z={tvecs.item(2):.2f}"   # координата метки по оси Z
                )                         # выводим координаты ArUco-метки в терминал

                cv2.drawFrameAxes(frame, camera_matrix, dist_coeffs, rvecs, tvecs, 0.1) # рисуем оси координат метки

        cv2.aruco.drawDetectedMarkers(frame, corners, ids) # рисуем найденные ArUco-метки на изображении
        viewer.imshow("aruco_coordinates", frame, fps=30)  # запускаем трансляцию изображения

finally:                                  # блок finally выполнится при завершении программы
    camera.stop()                         # останавливаем получение кадров с камеры
    viewer.close()                        # останавливаем видеопоток