Navegación autónoma de un robot móvil.

Hay una gran cantidad de formas en que un robot puede recibir información del mundo exterior para interactuar con él. Además, dependiendo de las tareas que se le asignen, los métodos de procesamiento de esta información difieren. En este artículo describiré las principales etapas del trabajo realizado como parte del proyecto escolar, cuyo objetivo es sistematizar la información sobre varios métodos de navegación autónoma del robot y aplicar los conocimientos adquiridos en el proceso de creación del robot para las competencias de la "Copa RTK".



Introducción


En las competiciones "Copa RTK" hay un bloque de tareas que deben completarse sin la intervención del operador. Creo que muchos participantes están evitando injustamente estas tareas, porque la aparente complejidad de crear un diseño de robot y escribir un programa oculta tareas en gran parte simplificadas de otras disciplinas competitivas, combinadas en un campo de entrenamiento. Por mi proyecto quiero mostrar posibles soluciones a tales problemas, considerando como ejemplo lo siguiente a lo largo de la línea.

Para lograr el objetivo del proyecto, se formularon las siguientes tareas intermedias:

  • Análisis de las bases del concurso "Copa RTK"
  • Análisis de algoritmos existentes para la orientación autónoma de un robot móvil.
  • Creación de software

Análisis de las bases del concurso "Copa RTK"


En las competencias de la "Copa RTK", se presenta a los participantes un campo de entrenamiento en el que se modelan secciones de diversa complejidad. La competencia tiene como objetivo estimular la robótica joven para crear dispositivos que puedan funcionar en condiciones extremas, superar obstáculos, bajo control humano o de forma autónoma.



brevemente sobre los elementos que componen el polígono
«» , . , , (), , (), ..

:



:



– , «» ( ) , . , , , , .

. , , , , , , .

Las competiciones se dividen en dos nominaciones fundamentalmente diferentes entre sí: "Buscador" y "Extremo". Esto es para asegurar que la competencia se llevara a cabo entre participantes con una diferencia mínima en edad y experiencia en el desarrollo de sistemas robóticos: Buscador para el nivel más joven y Extremo para participantes a partir de 14 años de edad. En la nominación del Buscador, el operador puede moverse libremente alrededor del rango y tener contacto visual directo con la máquina, mientras que la nominación Extreme asume que el robot tiene sistemas de comunicación por video y / o visión por computadora, ya que el operador debe navegar en el laberinto, confiando solo en el laberinto La cámara y los sensores integrados en el robot, mientras está detrás de una pantalla especial.

Para calificar en las competiciones, el robot debe pasar la tarea de control remoto del manipulador o realizar uno de los elementos de autonomía. En el marco del proyecto, la tarea se estableció para cumplir con las tareas de autonomía, ya que otorgan la mayor cantidad de puntos al menor costo del operador. Los elementos de autonomía incluyen:

  • Conducir a lo largo de una línea con un sensor de luz ambiental o un sistema de línea de visión
  • Captura de baliza independiente mediante sensores de distancia o sistemas de visión
  • Movimiento a lo largo de una trayectoria compleja (por ejemplo, ascenso / descenso de escaleras) a lo largo de una línea usando una brújula, giroscopio, acelerómetro, sistema de visión o métodos combinados

Además, los puntos para superar obstáculos se duplican si el robot los supera de forma autónoma.

En el marco de este proyecto, se considerará la solución a la primera de las tareas: movimiento a lo largo de la línea. Los métodos más comunes utilizados al moverse a lo largo de la línea son sensores de luz y una cámara. Las ventajas de los sensores incluyen la simplicidad de crear un programa: muchos de ellos están equipados con una resistencia de sintonización, de modo que al configurar el sensor para la iluminación de fondo, dará 0 o 1, dependiendo de si está en la línea o no. Por la misma razón, los sensores de luz no exigen la potencia de procesamiento del controlador utilizado. Además, debido a esto, resolver el problema con la ayuda de sensores de luz es el menos costoso: el costo del sensor más simple es de 35 rublos, y para un viaje relativamente estable a lo largo de la línea, tres sensores son suficientes (uno está instalado en la línea y dos en los costados). Sin embargo,Una de las principales desventajas de tales sensores son las restricciones de instalación. Idealmente, el sensor debe instalarse exactamente en el centro, a una pequeña distancia del piso, de lo contrario dará valores incorrectos. Esto no es un problema en competiciones especializadas donde el robot debe conducir lo más rápido posible a lo largo de la pista, pero, en las condiciones de la competencia "Copa RTK", todos los defectos del sensor mencionados anteriormente pueden ser críticos: su instalación requiere principalmente la presencia de piezas mecánicas adicionales en el robot que elevan y bajando los sensores, y esto requiere espacio adicional en el robot, un motor separado que mueve los sensores, y también es un lugar de daño potencial y aumenta la masa del robot.de lo contrario, dará valores incorrectos. Esto no es un problema en competiciones especializadas donde el robot debe conducir lo más rápido posible a lo largo de la pista, pero, en las condiciones de la competencia "Copa RTK", todos los defectos del sensor mencionados anteriormente pueden ser críticos: su instalación requiere principalmente la presencia de piezas mecánicas adicionales en el robot que elevan y bajando los sensores, y esto requiere espacio adicional en el robot, un motor separado que mueve los sensores, y también es un lugar de daño potencial y aumenta la masa del robot.de lo contrario, dará valores incorrectos. Esto no es un problema en competiciones especializadas donde el robot debe conducir lo más rápido posible a lo largo de la pista, pero, en las condiciones de la competencia "Copa RTK", todos los defectos del sensor mencionados anteriormente pueden ser críticos: su instalación requiere principalmente la presencia de piezas mecánicas adicionales en el robot que elevan y bajando los sensores, y esto requiere espacio adicional en el robot, un motor separado que mueve los sensores, y también es un lugar de daño potencial y aumenta la masa del robot.Todos los defectos del sensor mencionados anteriormente pueden ser críticos: su instalación requiere principalmente la presencia de piezas mecánicas adicionales en el robot que elevan y bajan los sensores, y esto requiere espacio adicional en el robot, un motor separado que mueve los sensores, y también es un lugar de daño potencial y aumenta la masa del robot. .Todos los defectos del sensor mencionados anteriormente pueden ser críticos: su instalación requiere principalmente la presencia de piezas mecánicas adicionales en el robot que elevan y bajan los sensores, y esto requiere espacio adicional en el robot, un motor separado que mueve los sensores, y también es un lugar de daño potencial y aumenta la masa del robot. .



La cámara, a su vez, tiene las siguientes ventajas: tiene un radio de medición prácticamente ilimitado (en comparación con los sensores), es decir, solo un módulo de cámara puede ver simultáneamente la línea, tanto directamente debajo del robot como a una distancia suficiente, lo que permite, por ejemplo, evaluar su curvatura y seleccionar una acción de control proporcional. Al mismo tiempo, el módulo de la cámara no interfiere con el avance del robot en otras partes del vertedero que no requieren autonomía, ya que la cámara se fija a una distancia del piso. El principal inconveniente de la cámara es que el procesamiento de video requiere un potente complejo informático a bordo del robot, y el software que se está desarrollando necesita más ajustes, porque la cámara recibe un orden de magnitud más información del mundo exterior que tres sensores de luz, mientras que la cámara y la computadoracapaces de procesar la información recibida son muchas veces más de tres sensores y "arduins".

Para mí personalmente, la respuesta es obvia para mí: en la nominación "extrema", el robot debe tener una cámara direccional, con la cual el operador navegará. Si utiliza soluciones FPV listas para usar, el costo total de los "sensores" puede ser aún mayor, al tiempo que requiere la instalación de dispositivos adicionales. Además, un robot con raspberry pi y una cámara tiene un mayor potencial para el desarrollo del movimiento autónomo, ya que la cámara puede resolver una amplia gama de problemas y puede usarse no solo en el movimiento de línea, sin complicar mucho el diseño.

Análisis de algoritmos de visión por computadora existentes


La visión por computadora es la teoría de crear dispositivos que pueden recibir imágenes de objetos del mundo real, procesar y usar los datos obtenidos para resolver varios tipos de problemas aplicados sin intervención humana.

Los sistemas de visión por computadora consisten en:

  • una o mas camaras
  • complejo informático
  • Software que proporciona herramientas de procesamiento de imágenes.
  • Canales de comunicación para transmitir información de objetivos y telemetría.

Como se escribió anteriormente, hay muchas formas de identificar objetos de interés para nosotros. En el caso de conducir a lo largo de una línea, es necesario separar la línea misma del fondo de contraste (una línea negra sobre un fondo blanco o una línea blanca sobre un fondo negro para una línea inversa). Los algoritmos que utilizan un sistema de visión por computadora se pueden dividir en varios "pasos" para procesar la imagen original:

Adquisición de imágenes: las imágenes digitales se obtienen directamente de la cámara, de una transmisión de video transmitida al dispositivo o como imágenes separadas. Los valores de píxeles generalmente corresponden a la intensidad de la luz (imágenes en color o en escala de grises), pero pueden asociarse con varias mediciones físicas, como, por ejemplo, la temperatura de una cámara termográfica.

Procesamiento preliminar: Antes de que los métodos de visión por computadora se puedan aplicar a los datos de video, es necesario el preprocesamiento para introducir ciertas condiciones, dependiendo del método utilizado. Ejemplos son:

  • Eliminar el ruido o la distorsión causada por el sensor usado
  • El desenfoque de la imagen se usa para eliminar pequeños artefactos que ocurren durante el funcionamiento de la cámara, elementos de descompresión, ruido, etc.
  • Mejorar el contraste para que la información correcta se pueda detectar con mayor probabilidad
  • Cambiar la exposición a sombras o reflejos de recorte
  • Escalado o recorte para distinguir mejor entre las estructuras de la imagen.
  • Convertir una imagen a monocromo o cambiar su resolución para un rendimiento más rápido del sistema

Detalles destacados : los detalles de la imagen de varios niveles de dificultad se extraen de los datos del video. Ejemplos típicos de tales detalles son líneas, bordes, bordes, puntos individuales, áreas que son características de cualquier entidad.
Detección : en una determinada etapa del trabajo del programa, la información relevante para el programa se separa del resto de la imagen. Ejemplos son:

  • La selección de un determinado conjunto de puntos de interés en color, el número de píxeles aislados que son similares de alguna manera (curvatura de la figura, color, brillo, etc.)
  • Segmentación de una o más secciones de imagen que contienen un objeto característico.

Procesamiento de alto nivel : en este paso, la abundancia de información de la imagen se reduce a un tamaño que puede procesarse fácilmente, por ejemplo, un conjunto de ciertos píxeles o las coordenadas de la parte de la imagen en la que supuestamente se encuentra el objeto de interés. Ejemplos son:

  • Filtrar valores por cualquier criterio
  • Evaluación de parámetros tales como las dimensiones físicas del objeto, la forma, su ubicación en el marco o en relación con otros objetos característicos.
  • Clasificación

A continuación, fue necesario elegir la biblioteca en función de la cual se creará el programa. Los factores clave en mi elección fueron:

  • El soporte de la biblioteca para la interfaz de Python debido a la relativa facilidad para que un principiante aprenda este idioma es una sintaxis simple, que tiene un efecto beneficioso sobre la legibilidad del programa.
  • Portabilidad, es decir la capacidad de ejecutar un programa usando esta biblioteca en raspberry pi3.
  • La prevalencia de la biblioteca, que garantiza una comunidad bien desarrollada de programadores que pueden haber encontrado problemas que pueden surgir durante su trabajo.

Entre las opciones que examiné, destaqué la biblioteca de visión por computadora abierta OpenCV, ya que es compatible con Python, tiene una extensa documentación en línea. Hay muchos artículos e instrucciones en Internet que describen todas las sutilezas de trabajar con esta biblioteca. Hay un foro oficial de desarrolladores donde cualquiera puede hacer una pregunta al respecto. Además, esta biblioteca se implementa en lenguajes C / C ++, lo que garantiza el rendimiento del sistema, y ​​su estructura admite varios módulos que se pueden deshabilitar para aumentar el rendimiento.

Desarrollo de software


Después de instalar el sistema operativo y la configuración inicial de Raspberry pi, pero antes de comenzar a crear el programa, debe instalar todos los paquetes necesarios para ello. La mayoría de estos paquetes, a su vez, se instalan utilizando el administrador de paquetes pip (en el caso de Python 3, pip3)

$ sudo apt install python3-pip

Se instalan las siguientes bibliotecas, como:

  • picamera - biblioteca para trabajar con cámara raspberry pi
  • numpy: una biblioteca para trabajar con matrices de datos multidimensionales, como imágenes

$ sudo pip3 install picamera
$ sudo pip3 install numpy

cmake - Utilidad para construir automáticamente un programa desde el código fuente
cmake-curses-gui - paquete GUI (interfaz gráfica) para cmake

$ sudo apt-get install cmake cmake-curses-gui libgtk2.0-dev
$ sudo apt-get install cmake cmake-curses-gui libgtk2.0-dev

bibliotecas para trabajar con diferentes formatos de imagen y video y más

$ sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libx264-dev libxvidcore-dev
$ sudo apt-get install libjpeg-dev libpng12-dev libtiff5-dev libjasper-dev
$ sudo apt-get install gfortran libatlas-base-dev

Para transmitir datos de video del robot a la computadora, se utilizará GStreamer, un marco diseñado para recibir, procesar y transmitir datos multimedia:

$ sudo apt install libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

El siguiente paso es instalar la propia biblioteca openCV desde las fuentes, configurarla y compilarla. Para hacer esto, se crea una carpeta de trabajo opencv.

$ mkdir opencv
$ cd opencv

Para descargar las últimas versiones de la biblioteca, se utiliza wget, un programa de consola para descargar archivos de la red. En el momento de la creación del programa, la última versión estable de openCV es 4.1.0, así que descargue y desempaquete las fuentes:

$ wget https://github.com/opencv/opencv/archive/4.1.0.zip -O opencv_source.zip
$ unzip opencv_source.zip
$ wget https://github.com/opencv/opencv_contrib/archive/4.1.0.zip -O opencv_contrib.zip
$ unzip opencv_contrib.zip

Una vez que se completa el proceso de desempaquetado, se pueden eliminar los archivos fuente.

$ rm opencv_source.zip
$ rm opencv_contrib.zip

Se crea un directorio para el ensamblaje y la configuración.

$ cd /home/pi/opencv/opencv-4.1.0
$ mkdir build
$ cd build

Los parámetros de compilación se configuran utilizando la utilidad cmake. Para hacer esto, todos los parámetros significativos se pasan como variables de utilidad, junto con los valores asignados:

cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_PYTHON_EXAMPLES=ON -D INSTALL_C_EXAMPLES=OFF -D BUILD_opencv_python2=OFF -D WITH_GSTREAMER=ON -D BUILD_EXAMPLES=ON -DENABLE_VFPV3=ON -DENABLE_NEON=ON -DCPU_BASELINE=NEON ..

Después de configurar la configuración, la utilidad mostrará todos los parámetros. A continuación, debe compilar la biblioteca. Para hacer esto, use el comando de consola make –jN, donde N es el número de núcleos que estarán involucrados en el proceso de compilación. Para raspberry pi 3, el número de núcleos es 4, pero definitivamente puede encontrar este número escribiendo el comando nproc en la consola.

$ make –j4

Debido a los recursos limitados de la frambuesa, la compilación puede llevar bastante tiempo. En algunos casos, las frambuesas pueden incluso congelarse, pero si luego ingresa a la carpeta de compilación y vuelve a registrar make, el trabajo continuará. Si esto sucede, vale la pena reducir la cantidad de núcleos involucrados, sin embargo, mi compilación se realizó sin problemas. Además, en esta etapa, vale la pena pensar en el enfriamiento activo de la frambuesa, porque incluso con ella, la temperatura del procesador alcanzó unos 75 grados.

Cuando la compilación fue exitosa, la biblioteca necesita ser instalada. Esto también se hace usando la utilidad make. Luego formaremos todas las conexiones necesarias con la utilidad ldconfig:

$ sudo make install
$ sudo ldconfig

Verificamos la instalación escribiendo los siguientes comandos en modo interactivo python:

import cv2
print(cv2.getBuildInformation())

La siguiente conclusión del programa será evidencia de la instalación correcta.



Debe tenerse en cuenta que el procedimiento anterior para compilar la biblioteca se debe realizar tanto en el robot como en la PC desde la que se planea controlar el robot y en la que se recibirá la transmisión del robot.

Crear un esquema de distribución de video

Antes de comenzar a escribir código, debe desarrollar un esquema según el cual funcionará el algoritmo. En el caso considerado de desarrollo de software para un robot creado para participar en las competiciones de la Copa RTK en la nominación Extreme, el programa completo se dividirá en dos partes: un robot y un control remoto, que será jugado por una computadora con Linux instalado. Una de las tareas más importantes aquí es crear un esquema aproximado de cómo se transmitirán los datos de video entre diferentes partes del algoritmo. Wi-Fi se utilizará como un canal de comunicación entre los dos dispositivos. Los paquetes de datos que proporcionan el control del robot y los datos de retroalimentación se transmitirán de un dispositivo a otro utilizando el protocolo UDP implementado en la biblioteca de sockets. Datos de videodebido a limitaciones en el tamaño del paquete UDP se transmitirá usando GStreamer. Para la conveniencia de la depuración, se implementarán dos transmisiones de video:

  • transmisión de video principal: transfiere datos de video directamente desde la cámara del robot a una computadora para garantizar un retraso de control mínimo.
  • transmisión de video auxiliar: transfiere los datos de video procesados ​​por el robot, necesarios para configurar y depurar un programa que implementa la visión por computadora.

Dos transmisiones de video estarán activas simultáneamente en el robot, y la computadora mostrará la imagen deseada dependiendo del modo de manejo habilitado. El robot, a su vez, dependiendo de si el modo de autonomía está activado o desactivado, utilizará los datos de control recibidos de una computadora o generados por un procesador de imágenes.



El control remoto del robot se realizará debido al trabajo de dos flujos paralelos en el robot y en la computadora:

  • La "consola" en un ciclo sondea todos los dispositivos de entrada disponibles y forma un paquete de datos de control que consta de los datos en sí y la suma de verificación (al momento de hacer los cambios finales al artículo, me negué a crear sumas de verificación para reducir el retraso, pero en la fuente, que establecido al final de este fragmento de código) - de un cierto valor calculado a partir de un conjunto de datos mediante la operación de algún algoritmo utilizado para determinar la integridad de los datos durante la transmisión
  • Robot: espera el acceso a datos desde la computadora. Desempaqueta los datos, recalcula la suma de verificación y la compara con la enviada y calculada en el lado de la computadora. Si las sumas de verificación coinciden, los datos se transfieren al programa principal.

Antes de analizar el algoritmo de detección de línea, le sugiero que se familiarice con las características de diseño del robot:

sobre el robot
.

— . (3 ) . , . 6 , . . . . , - . «» rasberry pi 3 b — .

, , , , Solidworks petg . , raspberry .

ubiquiti bullet M5 hp. ( ) , . , «» .


: «» thingiverse. , , , , .


, , . , . , , , , . , , , .





- ( - 200 ) , , 90 70 ( ), , « ». , VL53L0X , .


«» , , (rds3115). — , , , , .


, , , :


- , , , . . raspberry, , . , .

, USB. , , .




Creación de un algoritmo de detección de línea utilizando métodos de biblioteca OpenCV.


I. Obtención de datos

Debido al hecho de que el procesador de imagen no recibe datos de video directamente de la cámara, sino de la transmisión principal, es necesario transferirlos del formato utilizado para la traducción al formato utilizado para el procesamiento de imágenes, es decir, una matriz numpy que consiste en valores rojos. , verde y azul para cada uno de los píxeles. Para hacer esto, necesita los datos iniciales: un marco recibido del módulo de cámara raspberry pi.

La forma más fácil de obtener cuadros de la cámara c para su posterior procesamiento es utilizar la biblioteca picamera. Antes de comenzar, debe permitir el acceso a la cámara a través de raspi-config -> cámara de opciones de interfaz -> seleccione sí.

sudo raspi-config

la siguiente sección de código está conectada a la cámara de frambuesa y en un ciclo con una frecuencia dada recibe fotogramas en forma de matriz listos para su uso por la biblioteca opencv.

from picamera.array import PiRGBArray
from picamera import PiCamera
import cv2
#   
camera = PiCamera()
camera.resolution = (640, 480) 
camera.framerate = 30
cap = PiRGBArray(camera, size=(640, 480))

for frame in camera.capture_continuous(cap , format="bgr", use_video_port=True):
	new_frame = frame.array
	cap.truncate(0)
	if False: #   -   
		break

También vale la pena señalar que este método de captura de cuadros, aunque es el más simple, tiene un serio inconveniente: no es muy efectivo si necesita transmitir cuadros a través de GStreamer, ya que esto requiere varias veces para volver a codificar el video, lo que reduce la velocidad del programa. Una forma mucho más rápida de obtener imágenes será generar cuadros del flujo de video a pedido del procesador de imágenes, sin embargo, las etapas posteriores del procesamiento de imágenes no dependerán del método utilizado.

Un ejemplo de una imagen de una cámara de rumbo de robot sin procesamiento:


II Preprocesamiento

Al conducir en una línea, será más sencillo separar el área de puntos que más contraste con el color de fondo. Este método es ideal para la competencia de la Copa RTK, ya que utiliza una línea negra sobre un fondo blanco (o una línea blanca sobre un fondo negro para las secciones inversas). Para reducir la cantidad de información que debe procesarse, puede aplicarle un algoritmo de binarización, es decir, convertir la imagen a un formato monocromo, donde solo hay dos tipos de píxeles: oscuro y claro. Antes de esto, la imagen debe traducirse a escala de grises, y también desenfocarla para cortar pequeños defectos y ruidos que inevitablemente aparecen durante el funcionamiento de la cámara. Para desenfocar la imagen, se utiliza un filtro gaussiano.

gray = cv2.cvtColor(self._frame, cv2.COLOR_RGB2GRAY)
blur = cv2.GaussianBlur(gray, (ksize, ksize), 0)

donde ksize es el tamaño del núcleo gaussiano, lo que aumenta el grado de desenfoque.

Imagen de ejemplo después de la traducción en escala de grises y desenfoque:


III. Selección de detalles

Después de traducir la imagen en escala de grises, es necesario binarizarla en un umbral determinado. Esta acción le permite reducir aún más la cantidad de datos. Este valor umbral se ajustará antes de cada partida del robot en un nuevo lugar, o cuando cambien las condiciones de iluminación. Idealmente, la tarea de calibración es asegurarse de que el contorno de la línea esté definido en la imagen, pero al mismo tiempo, no debe haber otros detalles en la imagen que no sean una línea:

thresh = cv2.threshold(blur, self._limit, 255, cv2.THRESH_BINARY_INV)[1]

Aquí, todos los píxeles más oscuros que el valor umbral (self._limit) se reemplazan por 0 (negro), más claro - por 255 (blanco).

Después del procesamiento, la imagen se ve de la siguiente manera:


Como puede ver, el programa ha identificado varias de las partes más oscuras de la imagen. Sin embargo, después de calibrar el valor umbral para "atrapar" completamente los auriculares, aparecen otros elementos blancos en la pantalla además de ellos. Por supuesto, puede ajustar el umbral, y la cámara mirará hacia el campo de entrenamiento competitivo, sin permitir elementos innecesarios en el marco, pero considero que es necesario que separe la línea de todo lo demás.

IV.Detección

En la imagen binarizada, apliqué un algoritmo de búsqueda de bordes. Es necesario para determinar puntos independientes y convertirlos en una conveniente matriz de valores de coordenadas de puntos que conforman el borde. En el caso de opencv, como está escrito en la documentación, el algoritmo estándar para encontrar bucles usa el algoritmo Suzuki85 (no pude encontrar referencias al algoritmo con este nombre en ninguna parte excepto la documentación de opencv, pero supondré que este es el algoritmo Suzuki-Abe ).

contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]

Y aquí está el marco obtenido en esta etapa:


V. Procesamiento de alto nivel

Después de encontrar todos los contornos en el marco, se selecciona el contorno con el área más grande y se toma como el contorno de la línea. Conociendo las coordenadas de todos los puntos de este contorno, se encuentra la coordenada de su centro. Para esto, se utilizan los llamados "momentos de imagen". El momento es la característica total del contorno, calculada sumando las coordenadas de todos los píxeles del contorno. Hay varios tipos de momentos, hasta el tercer orden. Para este problema, solo se necesita el momento de orden cero (m00): el número de todos los puntos que forman el contorno (perímetro del contorno), el momento de primer orden (m10), que es la suma de las coordenadas X de todos los puntos, y m01 es la suma de las coordenadas Y de todos los puntos. Al dividir la suma de las coordenadas de los puntos a lo largo de uno de los ejes por su número, se obtiene la media aritmética, la coordenada aproximada del centro del contorno. A continuación, se calcula la desviación del robot del curso:el curso "directamente" corresponde a la coordenada del punto central a lo largo de X cerca del ancho del marco dividido por dos. Si la coordenada del centro de la línea está cerca del centro del cuadro, la acción de control es mínima y, en consecuencia, el robot conserva su curso actual. Si el robot se desvía de uno de los lados, se introducirá una acción de control proporcional a la desviación hasta que regrese.

mainContour = max(contours, key = cv2.contourArea)
M = cv2.moments(mainContour)
if M['m00'] != 0:#     (..   -  )
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])

A continuación se muestra un dibujo esquemático de la posición del robot con respecto a la línea y los marcos, con los resultados del programa superpuestos en ellos: el contorno "principal", las líneas que pasan por el centro del contorno, así como el punto ubicado en el centro para estimar la desviación. Estos elementos se agregan usando el siguiente código:

cv2.line(frame, (cx, 0), (cx, self.height), (255, 0, 0), 1)    #    
cv2.line(frame, (0, cy), (self.width, cy), (255, 0, 0), 1)                  
cv2.circle(frame, (self.width//2, self.height//2), 3, (0, 0, 255), -1) #  
cv2.drawContours(frame, mainContour, -1, (0, 255, 0), 2, cv2.FILLED) #   

Para la conveniencia de la depuración, todos los elementos descritos anteriormente se agregan al cuadro sin procesar:





Por lo tanto, después de conducir el cuadro a través del algoritmo de procesamiento, obtuvimos las coordenadas X e Y del centro del objeto de interés para nosotros, así como la imagen de depuración. A continuación, se muestra esquemáticamente la posición del robot con respecto a la línea, así como la imagen que ha pasado el algoritmo de procesamiento.


El siguiente paso en el programa es convertir la información obtenida en el paso anterior en los valores de potencia de dos motores.

La forma más fácil de convertir la diferencia entre el desplazamiento del centro de la mancha de color con respecto al centro del cuadro es el regulador proporcional (también hay un regulador de relé, pero, debido a las características de su funcionamiento, no es muy adecuado para conducir a lo largo de la línea). El principio de funcionamiento de dicho algoritmo es que el controlador genera una acción de control sobre el objeto en proporción a la magnitud del error. Además del controlador proporcional, también hay uno integral, donde con el tiempo el componente integral "acumula" el error y el diferencial, cuyo principio se basa en la aplicación de la acción reguladora solo con un cambio suficiente en la variable controlada. En la práctica, estos controladores P, I, D más simples se combinan en controladores del tipo PI, PD, PID.

Vale la pena mencionar que en mi robot traté de "iniciar" el controlador PID, pero su uso no dio ninguna ventaja seria sobre el controlador proporcional habitual. Admito que no pude ajustar correctamente el regulador, pero también es posible que sus ventajas no sean tan claramente visibles en el caso de un robot pesado que no puede desarrollar físicamente altas velocidades. En la última versión del programa al momento de escribir, se usa un regulador proporcional simple, pero con una pequeña característica que le permite usar más información de la cámara: al generar el valor de error, no solo se tuvo en cuenta la posición horizontal del centro del punto, sino también la vertical, que permitió diferentes responder a elementos de líneaubicado "en la distancia" e inmediatamente enfrente o debajo del robot (la cámara de rumbo del robot tiene un ángulo de visión enorme, por lo que al girarla solo 45 grados hacia abajo, ya puede ver una parte significativa del campo debajo del robot).

error= cx / (self.width/2) - 1  
#  ( 0   )  [-1; 1]
error*= cy / self.height + self.gain #

Muy a menudo, en las condiciones de la competencia "Copa RTK", los participantes usan el llamado "circuito de tanque": uno o más motores controlan un lado del robot y funciona tanto con orugas como con ruedas. El uso de este esquema le permite deshacerse de elementos de transmisión complejos que aumentan la posibilidad de rotura (diferenciales o ejes cardán), obtener el radio de giro más pequeño posible, lo que le da una ventaja en un polígono confinado. Este esquema implica el control paralelo de dos "lados" para el movimiento a lo largo de un camino complejo. Para hacer esto, el programa utiliza dos variables: la potencia del motor derecho e izquierdo. Esta potencia depende de la velocidad base (BASE_SPEED), que varía en el rango de 0 a 100.Errores (error): la diferencia entre el centro del marco y la coordenada del medio de la línea y el coeficiente de efecto proporcional (self._koof), que es calibrado por el operador. Su valor absoluto afecta la rapidez con la que el robot intentará alinearse con la línea. Debido al hecho de que en un motor la acción de control se resta de la velocidad base, y en el otro, se agrega, se realiza un giro cuando se desvía del curso. La dirección en la que se realizará la inversión se puede ajustar cambiando el signo de la variable self._koof. Además, puede observar que como resultado de la siguiente sección de código, puede aparecer un valor de potencia que es más de 100, pero en mi programa, estos casos se procesan adicionalmente más adelante.Su valor absoluto afecta la rapidez con la que el robot intentará alinearse con la línea. Debido al hecho de que en un motor la acción de control se resta de la velocidad base, y en el otro, se agrega, se realiza un giro cuando se desvía del curso. La dirección en la que se realizará la inversión se puede ajustar cambiando el signo de la variable self._koof. Además, puede observar que como resultado de la siguiente sección de código, puede aparecer un valor de potencia que es más de 100, pero en mi programa, estos casos se procesan adicionalmente más adelante.Su valor absoluto afecta la rapidez con la que el robot intentará alinearse con la línea. Debido al hecho de que en un motor la acción de control se resta de la velocidad base, y en el otro, se agrega, se realiza un giro cuando se desvía del curso. La dirección en la que se realizará la inversión se puede ajustar cambiando el signo de la variable self._koof. Además, puede observar que como resultado de la siguiente sección de código, puede aparecer un valor de potencia que es más de 100, pero en mi programa, estos casos se procesan adicionalmente más adelante.en el que se realizará la inversión, puede ajustar cambiando el signo de la variable self._koof. Además, puede observar que como resultado de la siguiente sección de código, puede aparecer un valor de potencia que es más de 100, pero en mi programa, estos casos se procesan adicionalmente más adelante.en el que se realizará la inversión, puede ajustar cambiando el signo de la variable self._koof. Además, puede observar que como resultado de la siguiente sección de código, puede aparecer un valor de potencia que es más de 100, pero en mi programa, estos casos se procesan adicionalmente más adelante.

#if lineFound:
leftSpeed = round(self.base_speed + error*self.koof)
rightSpeed = round(self.base_speed - error*self.koof)

Conclusión


Después de probar el programa resultante, puedo decir que el momento difícil principal en la configuración del programa es la calibración del algoritmo a las características de iluminación. Como la etapa de creación del artículo coincidió con el autoaislamiento declarado, tuve que crear un video con una demostración de trabajo en una habitación pequeña. Esto me puso las siguientes dificultades:

  • -, , ( , ), . , , , . , , , ,
  • -, — , ,

A pesar de que ambos problemas están ausentes en las condiciones de las competencias reales, tomaré medidas para asegurar que el trabajo del programa dependa mínimamente de factores externos.
Además, en el futuro se planea continuar trabajando en la implementación de algoritmos utilizando métodos de visión por computadora, creando software capaz de pasar por los elementos restantes de autonomía descritos en la primera parte del artículo (captura de balizas autónomas, movimiento a lo largo de un camino complejo). Se planea expandir la funcionalidad del robot agregando sensores adicionales: telémetro, giroscopio-acelerómetro, brújula. A pesar de que la publicación de este artículo terminará mi trabajo en el proyecto como una asignatura obligatoria, planeo continuar describiendo aquí las etapas posteriores de desarrollo. Por lo tanto, me gustaría recibir comentarios sobre este trabajo.

Después de implementar todos los pasos destinados a resolver los problemas del proyecto, es seguro decir que el uso de algoritmos de visión por computadora, con toda su relativa complejidad en la programación y depuración, brinda la mayor ganancia en la etapa de las competiciones. Con las pequeñas dimensiones de la cámara, tiene un enorme potencial en términos de desarrollo de software, porque la cámara le permite reemplazar varios sensores "tradicionales" a la vez, mientras recibe increíblemente más información del mundo exterior. Fue posible alcanzar el objetivo del proyecto: crear un programa que utilice la visión por computadora para resolver el problema de la navegación autónoma del robot en las condiciones de la competencia "Copa RTK", así como describir el proceso de creación del programa y las etapas principales en el procesamiento de imágenes.

Como dije antes, sin embargo, no fue posible recrear la compleja trayectoria de la línea de la casa, y este ejemplo muestra cómo el algoritmo cumple con los giros. El grosor de la línea aquí corresponde al de acuerdo con las regulaciones, y la mayor curva de las curvas refleja aproximadamente la curvatura de rotación en 90 grados en el polígono:


Puede ver el código del programa, así como monitorear el trabajo adicional en el proyecto, en mi github o aquí, si continúo.

All Articles