black_grayscale_line_following.py
# Эта работа лицензирована по лицензии MIT.
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# Пример следования по чёрной линии в оттенках серого
#
# Создание робота, следующего по линии, требует немало усилий. Этот пример скрипта
# показывает, как реализовать часть машинного зрения для робота, следующего по линии. Вы
# можете использовать выходные данные этого скрипта для управления роботом с дифференциальным приводом, чтобы он
# следовал по линии. Этот скрипт просто генерирует одно значение поворота, которое сообщает
# вашему роботу, ехать влево или вправо.
#
# Чтобы этот скрипт работал правильно, направьте камеру на линию под углом
# примерно 45 градусов. Убедитесь, что в поле зрения камеры находится
# только линия.
import csi
import time
import math
# Отслеживает чёрную линию. Используйте [(128, 255)] для отслеживания белой линии.
GRAYSCALE_THRESHOLD = [(0, 64)]
# Каждый roi имеет вид (x, y, w, h). Алгоритм обнаружения линии будет пытаться найти
# центроид самого большого блоба в каждом roi. Положение x центроидов
# затем будет усреднено с разными весами, где наибольший вес присваивается
# roi ближе к низу изображения, а меньший - следующему roi и так далее.
ROIS = [ # [ROI, вес]
(0, 100, 160, 20, 0.7), # Вам нужно будет настроить веса для вашего приложения
(0, 50, 160, 20, 0.3), # в зависимости от того, как устроен ваш робот.
(0, 0, 160, 20, 0.1),
]
# Вычислить делитель веса (мы вычисляем его, чтобы вам не пришлось делать сумму весов равной 1).
weight_sum = 0
for r in ROIS:
weight_sum += r[4] # r[4] - это вес roi.
# Настройка камеры...
csi0 = csi.CSI()
csi0.reset() # Инициализация датчика камеры.
csi0.pixformat(csi.GRAYSCALE) # использовать оттенки серого.
csi0.framesize(csi.QQVGA) # использовать QQVGA для скорости.
csi0.snapshot(time=2000) # Дайте новым настройкам вступить в силу.
csi0.auto_gain(False) # должно быть отключено для отслеживания цвета
csi0.auto_whitebal(False) # должно быть отключено для отслеживания цвета
clock = time.clock() # Отслеживает FPS.
while True:
clock.tick() # Отслеживание количества миллисекунд, прошедших между snapshots().
img = csi0.snapshot() # Сделать снимок и вернуть изображение.
centroid_sum = 0
for r in ROIS:
blobs = img.find_blobs(
GRAYSCALE_THRESHOLD, roi=r[0:4], merge=True
) # r[0:4] - это кортеж roi.
if blobs:
# Найти блоб с наибольшим числом пикселей.
largest_blob = max(blobs, key=lambda b: b.pixels)
# Нарисовать прямоугольник вокруг блоба.
img.draw_detection(largest_blob)
centroid_sum += largest_blob.cx * r[4] # r[4] - это вес roi.
center_pos = centroid_sum / weight_sum # Определить центр линии.
# Преобразовать center_pos в угол отклонения. Мы используем нелинейную
# операцию, чтобы отклик становился сильнее по мере того, как мы дальше от линии,
# к которой стремимся. Нелинейные операции хорошо использовать на выходе таких алгоритмов,
# чтобы вызвать эффект "триггера" отклика.
deflection_angle = 0
# 80 - это половина X-разрешения, 60 - половина Y-разрешения. Уравнение
# ниже просто вычисляет угол треугольника, где
# противоположная сторона треугольника - это отклонение положения центра
# от центра, а прилежащая сторона - половина Y-разрешения. Это ограничивает
# выходной угол диапазоном примерно от -45 до 45. (Не совсем -45 и 45).
deflection_angle = -math.atan((center_pos - 80) / 60)
# Преобразовать угол из радиан в градусы.
deflection_angle = math.degrees(deflection_angle)
# Теперь у вас есть угол, показывающий, насколько нужно повернуть робота, который
# учитывает часть линии, ближайшую к роботу, и части
# линии, находящиеся дальше от робота, для более точного прогноза.
print("Turn Angle: %f" % deflection_angle)
print(clock.fps()) # Примечание: ваша камера OpenMV Cam работает примерно вдвое медленнее, пока
# подключена к компьютеру. После отключения частота кадров должна увеличиться.