Progreso febrero 2025 - RoboticsURJC/tfg-dcampoamor GitHub Wiki
SEMANA 76 (03/02/2025-09/02/2025)
SEMANA 77 (10/02/2025-16/02/2025)
SEMANA 78 (17/02/2025-23/02/2025)
SEMANA 79 (24/02/2025-02/03/2025)
SEMANA 76 (03/02/2025-09/02/2025)
-
Actualización de memoria
Dada la extensión de la memoria, se procedió a revisarla con tal de poder detectar apartados e información que en un principio de habían incluido en esta pero que realmente no eran imprescindibles para la explicación del proyecto, reduciendo de esta manera el número de páginas presentado.
-
Aplicación de correciones propuestos en la memoria
Paralelamente a la actualización de la memoria, se procedió a aplicar las correciones propuestas de la misma en el Capítulo 1: Introducción, Capítulo 2: Estado del arte y Capítulo 3: Objetivos.
SEMANA 77 (10/02/2025-16/02/2025)
-
Actualización de memoria
Se continuó buscando la manera de resumir la memoria manteniendo la información necesaria para la correcta explicación y entendimiento del proyecto. Una vez realizadas estas modificaciones, se volvió a enviar al tutor del TFG para su verificación y corrección.
-
Aplicación de correciones propuestos en la memoria
Una vez recibidas estas correcciones tras la actualización de la memoria, se procedió a aplicarlas, incluyendo esta vez el Capítulo 4: Plataforma de desarrollo.
SEMANA 78 (17/02/2025-23/02/2025)
-
Actualización de la Wiki de Github
A pesar de haber continuado realizando trabajos relacionados con el avance y desarrollo del proyecto, y de haber subido archivos y documentos períodicamente al repositorio de GitHub, dejando constancia de esto, la Wiki no había sido actualizada desde el Progreso enero 2025, por lo que se actualizó esta Wiki con los detalles de los avances que se habían realizado de manera semanal.
-
Modificación del código en python para corregir errores
Debido a que cuando se mostraba por pantalla la distancia a la que se encontraba la deteccion, esta siempre era 200mm, siendo esto imposible puesto que se variaba la imagen de la fresa para comprobar si este valor cambiaba o no, se decidió modificar el código xmlrpc_deteccionfresas.py para poder solucionar esto.
Este problema podía deberse a cómo se estaba obteniendo la posición de la cámara y cómo se estaba calculando la proyección de los puntos 2D a 3D, pudiendo tener varias posibilidades el origen de este. Por este motivo se verificó el valor de las coordenadas X,Y,Z con mensajes de depuración para ver si cambiaban, y de no ser así, y ser siempre los mismos, determinar que el problema podría estar en
backproject
.print(f"[DEBUG] Coordenadas de detección 3D: X={x_punto:.2f}, Y={y_punto:.2f}, Z={z_punto:.2f}") print(f"[DEBUG] Coordenadas de la cámara: X={x_cam:.2f}, Y={y_cam:.2f}, Z={z_cam:.2f}")
De igual manera se imprimió por pantalla los valores de entrada de la función que calculaba esta distancia
calcular_distancia_3d
para asegurar que los cálculos eran correctos.print(f"[DEBUG] Calculando distancia entre ({x_cam}, {y_cam}, {z_cam}) y ({x_punto}, {y_punto}, {z_punto})")
Y por último se modificó la función
backproject
para que se preservase la profundidad.Con todo esto la distancia variaba pero esta variación era ínfima e irreal, por ejemplo, para una distancia real de 550 mm ponía por pantalla que la deteccion estaba a 201.02 mm, y cuando se movía la imagen de la fresa a una distancia totalmente alejada, la distancia calculada y mostrada no correspondía para nada con el cambio de distancia real, estando igualmente en el rango de los 200-205 mm.
Por este motivo se decidió verificar la proyección inversa en la función
backproject
, ya que si el cálculo de la proyección de la imagen a coordenadas 3D no estaba bien escalado, los valores podrían ser incorrectos. Para esto, se modificó el fragmento del código:inv_K = np.linalg.inv(camera.k[:, :3]) inv_RT = np.linalg.inv(camera.rt[:3, :3]) p3d_h = np.dot(inv_K, p2d_h) p3d_h = np.dot(inv_RT, p3d_h) p3d[:2] = p3d_h[:2] / p3d_h[2] p3d[2] = 0
Por esto:
if p3d_h[2] != 0: p3d = p3d_h / np.abs(p3d_h[2]) # Evitar errores de profundidad negativa else: p3d = p3d_h # Mantener los valores originales si Z=0
Ya que si
p3d_h[2]
era incorrecta o se normalizaba mal, la distancia también lo estaría, por lo que se dejó de forzarp3d[2] = 0
al modificar este fragmento de la función.También se revisó la función
getIntersectionZ
, modificándola para que sip3d_h[2]
era muy pequeño, se evitasen amplificar errores, escalando bien la proyección con las siguientes líneas:if np.abs(p3d_h[2]) > 1e-6: # Evita problemas con valores muy cercanos a 0 escala = -myCamera.position[2] / p3d_h[2] # Ajustar la escala de profundidad p3d_h *= escala else: p3d_h *= 1 # Mantener la escala actual si Z es casi 0
Paralelamente a estos cambios, se imprimió la matriz de los parámetros de la cámara para corroborar si el error pudiera estar aquí.
print(f"[DEBUG] Matriz RT de la cámara:\n{myCamera.rt}")
Al intentar ejecutar el programa se obtuvo el siguiente error, ya que a la hora de realizar los cambios en
getIntersectionZ
en lugar dep3d_h
, se había escritop3h_d
, provocando esto.Tras modificar esto y volver a ejecutar el programa, la distancia volvió a seguir estando fija, sin embargo, la distancia real a la detección era de 400 mm y el cálculo mostraba 600 mm. Además se obtuvo que la matriz RT de la cámara era incorrecta ya que todas sus componentes eran 0, lo que significaba que la transaformada de la cámara no se había configurado correctamente en
loadCamera()
y que la matriz de calibración (K) no se estaba aplicando correctamente enbackproject()
.Tras revisar el código, se observó que solo se invertía la parte de rotación de RT en
getIntersectionZ()
pero que esto no incluía la traslación de la cámara, y que enbackproject()
tampoco se usaba la traslación de RT correctamente, por lo que engetIntersectionZ()
se cambió el fragmento de código:inv_RT = np.linalg.inv(myCamera.rt[:3, :3]) p3d_h = np.dot(inv_RT, p3d_h)
Por esto:
inv_RT = np.linalg.inv(myCamera.rt[:3, :3]) trans = myCamera.rt[:3, 3] # Extraer la traslación p3d_h = np.dot(inv_RT, (p3d_h - trans)) # Restar la traslación antes de aplicar RT
Y en la función backproject() se cambió:
p3d_h = np.dot(inv_RT, p3d_h)
Por esto:
trans = camera.rt[:3, 3] # Extraer traslación p3d_h = np.dot(inv_RT, (p3d_h - trans)) # Aplicar correctamente la transformada
Al realizar todas estas modificaciones, se comprobó que la distancia de las detecciones a la cámara se había fijado en 400 mm, a pesar que ahora el sistema estaba proyectando los puntos correctamente con la matriz de transformación RT. Esto sugería que la proyección y el escalado han sido corregidos y estaban en línea con la posición de la cámara, sin embargo, que la distancia fuera fija sin importar la posición de esta detección indicaba que volvía a haber un error en la conversión de coordenadas 2D a 3D. Por este motivo, se volvieron a revisar las funcionas
getIntersectionZ()
ycalcular_distancia_3d()
. EngetIntersectionZ()
se cambió el fragmento de código siguiente:if np.abs(p3d_h[2]) > 1e-6: # Evita problemas con valores muy cercanos a 0 escala = -myCamera.position[2] / p3d_h[2] p3d_h *= escala else: p3d_h *= 1 # Mantener la escala actual si Z es casi 0
Por este:
if np.abs(p3d_h[2]) > 1e-6: escala = myCamera.position[2] / p3d_h[2] p3d_h[:2] *= escala # Solo escalamos X e Y, no Z
Esto fue así ya que, la operación del fragmento anterior escalaba todos los valores de
p3d_h
en función de un valor fijo demyCamera.position[2]
, lo que podía causar que z_punto fuera constante. Modificando esto se pretendía quex_punto
yy_punto
variasen correctamente dependiendo de su posición en la imagen, consiguiéndose finalmente tras estos cambios. -
Introducción en el código en python de la distinción por pantalla de las detecciones
Tras solucionar aparentemente el problema con el cálculo de la distancia de la cámara a las detecciones, el programa xmlrpc_deteccionfresas.py mostraba por pantalla las distintas detecciones que se realizaban, sin embargo, de darse varias detecciones no se diferenciaban, por lo que no se sabía identificar cada una de ellas por separado. Para identificar cada detección con su respectivo número (P1, P2, etc.) en la ventana donde se muestran los recuadros en las fresas detectadas, se modificó la parte del código que dibuja los recuadros y agrega el texto, añadiendo un índice
i
al recorrerdetection
, que representará el número de cada detección, y modificando la etiqueta que se dibuja en la imagen para que en lugar de solo decir"Fresa"
, muestre P1, P2, P3... según la detección correspondiente. De esta forma, cuando se muestren los recuadros en la ventana de la cámara, cada uno tendrá su respectivo número, y podrás relacionarlo con la información impresa en la terminal.Al intentar ejecutarlo la primera vez tras estos cambios, se obtuvo el error "ValueError: too many values to unpack (expected 2)" que indicaba que el código estaba intentando desempaquetar más valores de los que se esperaba en esa línea.
Para evitar el error y depurar lo que estaba ocurriendo, se modificó esa parte del código imprimiendo cada detección (
det
) para ver su estructura antes de desempaquetarla, verificando quedet
tenga exactamente 7 valores antes de intentar asignarlos ax1, y1, ...
y evitando que el programa falle sidetection
tiene un formato inesperado.Sin embargo, a pesar de comprobar que con estos cambios si que se distinguían los diferentes puntos detectados, el porcentaje de la confianza de la deteccion siempre era 1.0. Para intentar corregir esto,se modificó el código para que
cls_conf
se tomase directamente desdedet
sin reasignaciones erróneas. mostrándose el valor real de confianza en la imagen y no 1.0 siempre.
SEMANA 79 (24/02/2025-02/03/2025)
-
Solución del porcentaje de confianza de las detecciones mostradas por pantalla
Dado que la confianza (
cls_conf
) seguía apareciendo como 1.00 en todas las detecciones, lo cual indicaba que el valor que se estaba obteniendo no es el correcto o que estaba siendo modificado en alguna parte del código, se modificó la sección donde se muestra la confianza en la imagen para asegurar que se está usando el valor correcto:for i, det in enumerate(detection): if len(det) == 7: # Asegurar que la detección tiene los valores esperados x1, y1, x2, y2, conf, cls_conf, cls_pred = det # Obtener correctamente la confianza # Verificar qué valores tiene cls_conf print(f"[DEBUG] Confianza detectada en P{i+1}: {cls_conf}") # Dibujar el recuadro de detección en el frame color = (255, 0, 0) # Azul para la caja delimitadora cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), color, 2) # Etiqueta con "Fresa" y número de detección (P1, P2, ...) label = f"Fresa - P{i+1}" cv2.putText(frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # Mostrar confianza de la detección correctamente confidence_text = f"{float(cls_conf):.2f}" # Asegurar que se muestra correctamente cv2.putText(frame, confidence_text, (int(x2) + 10, int(y1)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
Sin embargo, el mensaje de [DEBUG] seguía mostrando que la confianza era 1.0. Esto ocurría ya que, al introducir la numeración de las detecciones (P1, P2, ...), probablemente cambió la forma en que se extraía el valor de confianza (
cls_conf
), algo en la forma en que se itera sobre las detecciones podría estar causando que la confianza siempre sea 1.0.Por este motivo se intentó volver a modificar el mismo fragmento de código, pero se seguía obteniendo el mismo resultado:
El hecho de que
cls_conf
siempre fuera 1.0 sugería que en alguna parte del código, el valor de confianza se estaba asignando incorrectamente o que la funciónnon_max_suppression()
estaba devolviendo un valor máximo en lugar del real. La funciónnon_max_suppression()
está normalizando la confianza de detección, lo que puede estar haciendo que se devuelva 1.0 en todas las detecciones porque el modelo fuera cargados con valores procesados de forma errónea. Otra de las posibilidades de que esto estuviera ocurriendo era que, en algunas versiones de YOLO, el valor correcto de confianza esconf
y nocls_conf
, siendo la diferencia entre ambas que conf representa la confianza de detección antes de aplicar la clasificación de la clase.Por todo esto, se procedió a modificar la línea siguiente:
x1, y1, x2, y2, conf, cls_conf, cls_pred = det
Por esta:
x1, y1, x2, y2, cls_conf, conf, cls_pred = det
Con lo que se consiguió obtener finalmente valores razonables y lógicos sobre la confianza de la detección. Esto puede observarse en el vídeo Pruebas confianza deteccion y threshold.mp4.
-
Corrección de la tolerancia en las detecciones
Actualmente, la distancia configurada en el código xmlrpc_deteccionfresas.py para la distancia threshold era de 0.1 mm, distancia usada para comparar detecciones entre frames y evitar que el sistema registre la misma detección repetidamente, ya que si una nueva detección está a menos de este umbral de distancia de una anterior, se considera la misma detección y no se vuelve a registrar. Se modificó este umbral hasta dejarlo en 0.5 mm, ya que 0.1 mm era un valor muy pequeño como para prevenir y/o evitar esta duplicación de las detecciones.
En este código inicializa una lista
previous_positions
para almacenar las posiciones de detecciones anteriores mientras que se establece el umbral o margenthreshold_distance = 5
, que define qué tan cerca pueden estar dos detecciones antes de considerarlas la misma. Se recorren todas las detecciones enpositions
, y para cada posiciónpos
se comparan con las posiciones anterioresprev_pos
usando la funcióncalcular_distancia_3d()
, y si la distancia es menor quethreshold_distance
se considera la misma fresa y no se agrega como nueva, pero si es mayor que este valor, se guarda como nueva enfiltered_positions
y enprevious_positions
para futuras comparaciones.De igual manera, en el vídeo Pruebas confianza deteccion y threshold.mp4 puede observarse como aún moviendo en este caso el móvil con las imágenes de las fresas, a no ser que deje de detectar la fresa, si la imagen sufre movimientos leves no detecta como si fuera otra detección imprimiéndolo por pantalla. Lo que sí se ha detectado es que, si se deja de detectar momentaneamente una fresa y se detecta a la vez otra, esta nueva fresa pasa a tomar la referencia de la anterior, es decir, si se está detectando Fresa - P1, y se deja de detectar por cualquier factor, pero se detecta la fresa que estaba al lado, esta nueva fresa puede pasar a denominarse Fresa - P1, lo que si únicamente se mira la terminal podría llevar a confusión, pero que, con la ventana destinada a mostrar lo que ve la cámara y a marcar las detecciones se ve claramente.
-
Elaboración del Resumen de la memoria
De manera paralela a la consecución de pruebas, se elaboró la primera versión del Resumen de la memoria, a la espera de las posibles modificaciones a posteriori.
-
Pruebas de la distancia de las detecciones a la cámara
Para comprobar que definitivamente el problema con las distancias a las detecciones estaba solucionado, y antes de volver a pasar a realizar pruebas conjuntas con robot real, se llevaron a cabo pruebas de detección conociendo las coordenadas y distancias reales respecto a la cámara. En primer lugar, utilizando una fresa real, y conociendo la altura a la que se situó la cámara en el soporte y habiendo medido su inclinación, se ejecutó por primera vez el programa para comprobar estas distancias y detecciones.
Para estas pruebas se utilizó una altura de 151 mm desde la mesa a la cámara y una inclinación de 62º, obteniendo los siguientes resultados:
Con estos resultados, se observó primeramente que la distancia obtenida por pantalla en comparación de la distancia teórica no coincidía, es decir, que la distancia que se mostraba por pantalla y el cálculo de la distancia que se obtenía con las coordenadas de la detección no eran iguales, por lo que se revisó el código para ver por qué motivo sucedía esto, solucionándose al tomar la posición de la cámara como origen de coordenadas, es decir, como punto [0,0,0].
Para verificar que estos cambios que se habían realizado se volvió a ejecutar el programa, obteniendo los siguientes resultados con solamente 5 posiciones para verificar que las distancias que se mostraban por pantalla coincidían con el cálculo, pero se volvió a medir la altura de la cámara, observando que en lugar de 151 mm se encontraba a 145 mm.
De las tablas de resultados anteriores (los obtenidos con la altura de cámara de 151 mm), tomando la distancia calculada de las coordenadas mostradas por terminal, es decir, la distancia teoríca ya que la distancia obtenida por terminal era errónea, se obtuvieron las siguientes gráficas para poder comparar las coordenadas de los ejes X e Y y las distancias obtenidas por pantalla de las detecciones y las reales que habían sido medidas previa ejecución del programa.
A partir del análisis de estos resultados obtenidos se observa que las coordenadas proyectadas de las fresas situadas en los extremos del campo de visión (FoV) de la cámara presentan valores anómalos o significativamente desviados respecto a los reales, es decir, aquellas fresas detectadas en cualquiera de los extremos del campo de visión de la cámara, ya sea el límite superior, inferior o cualquiera de los dos límites laterales, reportan grandes desviaciones respecto a las coordenadas y distancias reales, siendo la zona central del campo de visión la que más exacta llega a ser tanto en las coordenadas del eje X e Y como en distancias, llegando a tener para P5 una desviación de 1,92 mm en las coordenadas del Eje x, 32,93 mm para el Eje Y y 3,65 mm de desviación en la distancia, siendo este el punto obtenido con mayor precisión de la serie. Esta desviación se debe principalmente a efectos geométricos del modelo de proyección pinhole, que es el utilizado para este proyecto, donde pequeñas variaciones en la posición de los píxeles en los bordes del sensor implican grandes errores en las coordenadas proyectadas, especialmente si se trabaja con una proyección a un plano con profundidad o altura fija. Además, al acercarse al límite angular del FoV, los rayos proyectados se vuelven casi tangenciales al plano de referencia, lo que genera coordenadas extremadamente grandes o incluso tiende a valores infinitos.