sabato 31 agosto 2024

Aruco Tag con Opencv (nuova versione)

Ho riscritto per la nuova versione di OpenCV il programma di estrazione dei dati degli Aruco Tag

Il programma e' completamente parametrizzato (con valori di default) ed ha un output in csv in cui sono inserite le coordinate 2D immagine, coordinate 3D nel sistema di riferimento della camera, angoli di roll,pitch ed yaw

 

from os import listdir
from os.path import isfile, join

import numpy as np
import argparse
import cv2
import sys
import math
import os

ARUCO_DICT = {
"DICT_4X4_50": cv2.aruco.DICT_4X4_50,
"DICT_4X4_100": cv2.aruco.DICT_4X4_100,
"DICT_4X4_250": cv2.aruco.DICT_4X4_250,
"DICT_4X4_1000": cv2.aruco.DICT_4X4_1000,
"DICT_5X5_50": cv2.aruco.DICT_5X5_50,
"DICT_5X5_100": cv2.aruco.DICT_5X5_100,
"DICT_5X5_250": cv2.aruco.DICT_5X5_250,
"DICT_5X5_1000": cv2.aruco.DICT_5X5_1000,
"DICT_6X6_50": cv2.aruco.DICT_6X6_50,
"DICT_6X6_100": cv2.aruco.DICT_6X6_100,
"DICT_6X6_250": cv2.aruco.DICT_6X6_250,
"DICT_6X6_1000": cv2.aruco.DICT_6X6_1000,
"DICT_7X7_50": cv2.aruco.DICT_7X7_50,
"DICT_7X7_100": cv2.aruco.DICT_7X7_100,
"DICT_7X7_250": cv2.aruco.DICT_7X7_250,
"DICT_7X7_1000": cv2.aruco.DICT_7X7_1000,
"DICT_ARUCO_ORIGINAL": cv2.aruco.DICT_ARUCO_ORIGINAL,
"DICT_APRILTAG_16h5": cv2.aruco.DICT_APRILTAG_16h5,
"DICT_APRILTAG_25h9": cv2.aruco.DICT_APRILTAG_25h9,
"DICT_APRILTAG_36h10": cv2.aruco.DICT_APRILTAG_36h10,
"DICT_APRILTAG_36h11": cv2.aruco.DICT_APRILTAG_36h11
}

def isRotationMatrix(R):
Rt = np.transpose(R)
shouldBeIdentity = np.dot(Rt, R)
I = np.identity(3, dtype=R.dtype)
n = np.linalg.norm(I - shouldBeIdentity)
return n < 1e-6

def rotationMatrixToEulerAngles(R):
assert (isRotationMatrix(R))
sy = math.sqrt(R[0, 0] * R[0, 0] + R[1, 0] * R[1, 0])
singular = sy < 1e-6
if not singular:
x = math.atan2(R[2, 1], R[2, 2])
y = math.atan2(-R[2, 0], sy)
z = math.atan2(R[1, 0], R[0, 0])
else:
x = math.atan2(-R[1, 2], R[1, 1])
y = math.atan2(-R[2, 0], sy)
z = 0
return np.array([x, y, z])


def estrai_parametri(img):
# inserire qui il ciclo per le img
image = cv2.imread(img)
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

ArucoParams = cv2.aruco.DetectorParameters()
ArucoParams.cornerRefinementMethod = 0

R_flip = np.zeros((3, 3), dtype=np.float32)
R_flip[0, 0] = 1.0
R_flip[1, 1] = -1.0
R_flip[2, 2] = -1.0

Adict = cv2.aruco.getPredefinedDictionary(ARUCO_DICT[args["type"]])
detector = cv2.aruco.ArucoDetector(Adict, ArucoParams)
corners, ids, _ = cv2.aruco.detectMarkers(image, Adict, parameters=ArucoParams)

if len(corners) > 0:
x_sum = corners[0][0][0][0] + corners[0][0][1][0] + corners[0][0][2][0] + corners[0][0][3][0]
y_sum = corners[0][0][0][1] + corners[0][0][1][1] + corners[0][0][2][1] + corners[0][0][3][1]
x_centerPixel = x_sum / 4
y_centerPixel = y_sum / 4

# print(corners) #posizione degli angoli del marker
for i in range(0, len(ids)):
rvec, tvec, markerPoints = cv2.aruco.estimatePoseSingleMarkers(corners[i], float(args["aruco_dim"]), k, d)
# distanza dalla camera
dist = math.sqrt(
(tvec[0][0][0] * tvec[0][0][0]) + (tvec[0][0][1] * tvec[0][0][1]) + (tvec[0][0][2] * tvec[0][0][2]))
str_dist = "{:4.2f}".format(dist)

R_ct = np.matrix(cv2.Rodrigues(rvec)[0])
R_ct = R_ct.T
roll_marker, pitch_marker, yaw_marker = rotationMatrixToEulerAngles(R_flip * R_ct)
str_roll = "%4.2f" % (math.degrees(roll_marker))
str_pitch = "%4.2f" % (math.degrees(pitch_marker))
str_yaw = "%4.2f" % (math.degrees(yaw_marker))

if (int(args["tag"]) > 0):
if (int(ids[i]) == int(args["tag"])):
f.write(str(ids[i]) + ";" + str(x_centerPixel) + ";" + str(y_centerPixel) + ";" + str(
tvec[0][0][0]) + ";" + str(tvec[0][0][1]) + ";" + str(tvec[0][0][2]) + ";" + os.path.basename(img) + ";" + str_roll + ";" + str_pitch + ";" + str_yaw+"\n")
else:
f.write(str(ids[i]) + ";" + str(x_centerPixel) + ";" + str(y_centerPixel) + ";" + str(
tvec[0][0][0]) + ";" + str(tvec[0][0][1]) + ";" + str(tvec[0][0][2]) + ";" + os.path.basename(img) + ";" + str_roll + ";" + str_pitch + ";" + str_yaw+"\n")



ap = argparse.ArgumentParser()
#mettere rem sulla successiva per utilizzare il ciclo sulle img
#ap.add_argument("-i", "--image", required=True, help="path to input image containing ArUCo tag")
ap.add_argument("-t", "--type", type=str, default="DICT_4X4_250", help="type of ArUCo tag to detect")
ap.add_argument("-k", "--K_Matrix", type=str,default='./calibration_matrix.npy',help="Path to calibration matrix (numpy file)")
ap.add_argument("-d", "--D_Coeff", type=str,default='./distortion_coefficients.npy',help="Path to distortion coefficients (numpy file)")
ap.add_argument("-a", "--aruco_dim", default=25,type=int, help="ArUco tag dimension")
ap.add_argument("-g", "--tag", default=0, type=str, help="Select only one Id")
ap.add_argument("-p", "--path", default="./", help="Path folder immagini")


#la dimensione del tag e' quella dello spigolo esterno del quadrato nero esterno, non i singoli quadrati interni

args = vars(ap.parse_args())
if ARUCO_DICT.get(args["type"], None) is None:
print(f"ArUCo tag type '{args['type']}' is not supported")
sys.exit(0)


calibration_matrix_path = args["K_Matrix"]
distortion_coefficients_path = args["D_Coeff"]
k = np.load(calibration_matrix_path)
d = np.load(distortion_coefficients_path)



immagini = [f for f in listdir(args["path"]) if isfile(join(args["path"], f))]

with open('aruco'+str(args["tag"])+'.csv', 'w') as f:
f.write("Id;Xpix;Ypix;X;Y;Z;Filename;Roll;Pitch;Roll\n")
for i in immagini:
print(args["path"]+i)
estrai_parametri(args["path"]+i)
f.close()

 

domenica 25 agosto 2024

Passato ferrarista 156/85

Ho ritrovato una foto in cui sono "alla guida" di una F1 Ferrari 156/85 (Alboreto)

In un tempo in cui era possibile farsi una foto senza spendere un patrimonio

 

Qui la macchina con il suo leggittimo proprietario


ed un disegno che avevo fattto
 


 

 

venerdì 23 agosto 2024

Freecad e Windows

 Mi sono messo a studiare Freecad per fare una scatolina per LC29H da stampare in 3D ma una volta installato su windows 11 non ne voleva sapere di partire



La soluzione e' stata rinominare il file openglsw32.dll in opengl32.dll

ArduSimple Ublox F9P e QuecTel LC29H

 Ho provato i modulit Ardusimple F9P e LC29H. Si tratta di due moduli entrambi daul band con il primo in L1-L2 ed il secondo L1-L5. Entrambi sono stati stati usati con RTKNavi di RTKLIB con impostazioni simili (F9P e' stato programmato per inviare messggi UBX mentre LC29H messaggi RTCM3)


In un primo momento ho fatto la prova usando i moduli in contemporanea utilizzando l'atenna in dotazione al LC29H (solo L1-L2) ed una antenna full band per l'Ublox


In questa configurazione pero' LC29H non riusciva ad ottenere il Fix (sempre soluzioni Float). Ho quindi montato l'antenna full band su LC29H ed ho ottenuto il fix in modo corretto

Queste le impostazioni di RTKNavi per LC29H



e queste per Ublox F9P






il client NTrip ha utilizzato Euref come nel precedente post https://debiaonoldcomputers.blogspot.com/2024/08/quectel-lc29hda.html
Le impostazioni generali sono le seguenti






Il risultato finale 

Quectel LC29H antenna full band



Ublox F9P antenna full band

Come si vede dalla scala il posizionamento e' di tipo centimetrico in entrambi i casi con risultati migliori come deviazione per F9P

per confronto metto il grafico dei punti di LC29H con antenna L1-L5




Quectel LC29HDA

Cavalcando l'onda della banda 5 del GPS ho provato il Quectel LC29H DA (rover) e BS (base) che ha la possibilita' di RTK (esiste anche il rover DEA che costa un po' di piu' ed offre un campionamento a 10 Hz)

Piccola nota: per attivare l'uscita sulla UART si deve spostare il microswitch dal lato dove sono posti la porta USB ed i refori dalla porta seriale. L'alimentazione del modulo e' a 3.7 V


Il software di riferimento per questo dispositivo e' QGNSS della Quectel che ha incluso il proprio Ntrip Client. Al contrario di quanto indicato su altri siti la velocita' di default della porta e' 115200 

Come Ntrip server ho impiegato euref-ip.asi.it porta 2101 con la stazione IGMI00ITA


Si ottiene in pochi secondi RTK Fix 

Come si vede il posizionamento di prova e' sostanzialmente nelle specifiche ma si nota che i punti si distribuiscono su nuvole distinte 

La cosa curiosa con questo dispositivo e' che non funziona di default con altri software differenti da QGNSS. Il problema e' il software QGNSS di fatto non effettua nessun calcolo ma trasmette i dati dal server NTRIP verso LC29H che effettua i calcoli sul modulo e poi restituisce indietro una stringa NMEA con i dati gia' corretti

Io volevo trasmettere i dati dell'antenna via Lora verso un computer remoto e questa impostazione non e' applicabile. 

E' possibile modificare la configurazione in modo che LC29H trasmetta solo messaggi RTCM3 e non efffettui calcoli in modo poi da poter passare il calcolo ad RTKNAVI. Per fare cio' 

$PQTMRESTOREPAR*13 
$PQTMCFGRCVRMODE,W,2*29
$PQTMSAVEPAR*5A 
# reset
$PAIR432,1*22 # output RTCM3 MSM7 messages
$PAIR062,0,01*0F # Enable NMEA GGA message
$PQTMSAVEPAR*5A # save PQTM params to flash

Per la trasmissione Lora ho provato dei moduli E32 868T30D che sono compatibili con il voltaggio a 3.3 V in modo da essere alimentati direttamente da LC29H

Per programmarli ho utilizzato il modulo E15- USB-T2 spostando il jumper dell'alimentazione a 3.3 V e rimuovendo i jumper M0 ed M1 con il software RF Settings 





mercoledì 21 agosto 2024

U-Center e Window 11

Ho provato ad installare U-Center su Windows 11 ed sono partite una fila di lamentele su DLL non trovate 


La soluzione e' stato installare tutto il pacchetto con tutti le librerie Visual C++ in blocco da qui

martedì 20 agosto 2024

Oracle Instaclient

Per avere Instant Client Oracle (una versione alleggerita) su Linux si va a questo link e si scaricano i file .zip basic, sdk, tools, sqlplus e si scompattano con 

unzip insta*.zip -d /opt/oracle

si modifica il file bashrc per puntare la path ad instaclient

export PATH=/opt/oracle/instaclient_21_15:$PATH

si deve poi configurare il file tnsnames.ora che si trova qui

 /opt/oracle/instantclient_21_15/network/admin/tnsnames.ora 

con il formato di questo tipo

=================================================

 XXXX.XXXX.TOSCANA.IT =
        (DESCRIPTION =
                (ADDRESS =
                        (PROTOCOL = TCP)
                        (HOST = xxxx.xxxxx.toscana.it)
                        (PORT = 1521)
                        )
                (CONNECT_DATA =
                        (SERVICE_NAME = xxxx.xxxx.toscana.it)
                        )
        )

 

 =================================================

a questo punto si puo' effettuare la connessione con 

sqlplus user/password@xxxx.xxxx.toscana.it

Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...