Seguimiento de l铆nea basado en OpenCV

Ahora cursos muy populares sobre la creaci贸n de pilotos autom谩ticos para autom贸viles. Este nanogrado de Udacity es probablemente la opci贸n m谩s conocida.

Muchas personas aprenden de esto y publican sus decisiones. Yo tampoco pod铆a pasar y dejarme llevar.

La diferencia es que el curso implica el desarrollo de un algoritmo basado en los datos proporcionados, e hice todo por mi robot .

La primera tarea a la que se enfrentan los estudiantes del curso cuando estudian visi贸n artificial es seguir la l铆nea en el camino. Se han escrito muchos art铆culos sobre este tema, estos son algunos de los m谩s detallados:


Todos ellos son bastante simples y el esquema de trabajo se reduce a varios puntos:


Pegu茅 cinta blanca al piso y me puse manos a la obra.



En los trabajos mencionados anteriormente, su tarea era encontrar la l铆nea amarilla, por lo que trabajaron con colores HLS y HSV. Como mi l铆nea era solo blanca, decid铆 no molestarme con esto y limitarme a un filtro blanco y negro.

Geometr铆a


Los problemas con la geometr铆a comenzaron de inmediato. Para los estudiantes en las im谩genes, la franja con la flecha va al horizonte. Y a煤n as铆, se detectan muchas l铆neas en 茅l, que los autores tuvieron que combinar. Sin embargo, sus l铆neas estaban bien dirigidas y no hab铆a restos en las im谩genes.

Tengo una imagen completamente diferente. La geometr铆a de la tira de cinta aislante distaba mucho de ser recta. El resplandor en el suelo generaba ruido.

Despu茅s de aplicar Canny, esto es lo que sucedi贸:



Y las l铆neas de Hough eran as铆:

imagen

Al fortalecer los criterios, fue posible excluir la basura, pero casi todas las l铆neas encontradas en la tira desaparecieron. Confiar en segmentos tan peque帽os ser铆a una tonter铆a.



En general, los resultados fueron extremadamente inestables, y se me ocurri贸 intentar un enfoque diferente.

En lugar de l铆neas, comenc茅 a buscar contornos. Habiendo asumido que el circuito m谩s grande es la cinta aislante, logramos deshacernos de la basura. (Luego result贸 que el gran z贸calo blanco ocupaba m谩s espacio en el marco que la cinta aislante. Tuve que cubrirlo con un coj铆n de sof谩).
Si tomamos el rect谩ngulo m铆nimo que limita el contorno, entonces la l铆nea longitudinal media es muy adecuada para el papel del vector de movimiento.



La luz


El segundo problema fue la iluminaci贸n. Con mucho 茅xito puse un lado de la pista a la sombra del sof谩 y era completamente imposible procesar fotos de toda la pista con la misma configuraci贸n. Como resultado, tuve que implementar un corte din谩mico en un filtro blanco y negro. El algoritmo es el siguiente: si despu茅s de aplicar el filtro en la imagen hay demasiado blanco (m谩s del 10%), entonces el umbral deber铆a elevarse. Si es muy poco (menos del 3%) - omita. La pr谩ctica ha demostrado que en promedio para 3-4 iteraciones es posible encontrar el l铆mite 贸ptimo.

Los n煤meros m谩gicos se colocan en una configuraci贸n separada (ver m谩s abajo), puedes jugar con ellos en busca del 贸ptimo.

def balance_pic(image): global T ret = None direction = 0 for i in range(0, tconf.th_iterations): rc, gray = cv.threshold(image, T, 255, 0) crop = Roi.crop_roi(gray) nwh = cv.countNonZero(crop) perc = int(100 * nwh / Roi.get_area()) logging.debug(("balance attempt", i, T, perc)) if perc > tconf.white_max: if T > tconf.threshold_max: break if direction == -1: ret = crop break T += 10 direction = 1 elif perc < tconf.white_min: if T < tconf.threshold_min: break if direction == 1: ret = crop break T -= 10 direction = -1 else: ret = crop break return ret 

Una vez ajustada la visi贸n de la m谩quina, fue posible pasar al movimiento real. El algoritmo fue el siguiente:

  • 0.5 segundos conducen derecho
  • toma una foto
  • encuentra el vector
  • Si el comienzo del vector se desplaza en relaci贸n con el centro de la imagen, rodaremos ligeramente en la direcci贸n correcta
  • Si el 谩ngulo de inclinaci贸n del vector se desv铆a de la vertical m谩s de lo necesario, giramos en la direcci贸n correcta
  • Si de repente sucedi贸 que la tira desapareci贸 del marco, asumimos que condujimos un giro y comenzamos a girar en la direcci贸n de la 煤ltima direcci贸n o inclinaci贸n del vector en el paso anterior.

Versi贸n acortada del c贸digo (Completo - en Github ):

 def check_shift_turn(angle, shift): turn_state = 0 if angle < tconf.turn_angle or angle > 180 - tconf.turn_angle: turn_state = np.sign(90 - angle) shift_state = 0 if abs(shift) > tconf.shift_max: shift_state = np.sign(shift) return turn_state, shift_state def get_turn(turn_state, shift_state): turn_dir = 0 turn_val = 0 if shift_state != 0: turn_dir = shift_state turn_val = tconf.shift_step if shift_state != turn_state else tconf.turn_step elif turn_state != 0: turn_dir = turn_state turn_val = tconf.turn_step return turn_dir, turn_val def follow(iterations): tanq.set_motors("ff") try: last_turn = 0 last_angle = 0 for i in range(0, iterations): a, shift = get_vector() if a is None: if last_turn != 0: a, shift = find_line(last_turn) if a is None: break elif last_angle != 0: logging.debug(("Looking for line by angle", last_angle)) turn(np.sign(90 - last_angle), tconf.turn_step) continue else: break turn_state, shift_state = check_shift_turn(a, shift) turn_dir, turn_val = get_turn(turn_state, shift_state) if turn_dir != 0: turn(turn_dir, turn_val) last_turn = turn_dir else: time.sleep(tconf.straight_run) last_turn = 0 last_angle = a finally: tanq.set_motors("ss") 

Resultados


De manera desigual, pero con confianza, el tanque se arrastra a lo largo de la trayectoria:



Y aqu铆 hay un GIF de los gr谩ficos de depuraci贸n:



Configuraciones de algoritmo


 ## Picture settings # initial grayscale threshold threshold = 120 # max grayscale threshold threshold_max = 180 #min grayscale threshold threshold_min = 40 # iterations to find balanced threshold th_iterations = 10 # min % of white in roi white_min=3 # max % of white in roi white_max=12 ## Driving settings # line angle to make a turn turn_angle = 45 # line shift to make an adjustment shift_max = 20 # turning time of shift adjustment shift_step = 0.125 # turning time of turn turn_step = 0.25 # time of straight run straight_run = 0.5 # attempts to find the line if lost find_turn_attempts = 5 # turn step to find the line if lost find_turn_step = 0.2 # max # of iterations of the whole tracking max_steps = 100 

C贸digo Github

Source: https://habr.com/ru/post/es426675/


All Articles