testing-framework / R / fit.r @ 8526ae8c
History | View | Annotate | Download (7.465 KB)
| 1 |
# Dies ist die Datei "fit.r". |
|---|---|
| 2 |
# Sie benutzt einen Levenberg-Marquardt-Algorithmus |
| 3 |
# um Eulerwinkel zu bestimmen, die der gegebenen |
| 4 |
# Rotationsmatrix möglicht gut entsprechen. |
| 5 |
# Dadurch wird der Fehler aller Matritzeneinträge |
| 6 |
# in den Eulerwinkeln abgebildet. |
| 7 |
|
| 8 |
|
| 9 |
# Berechnet die Abweichung dreier angenäherter Winkel an eine Rotationsmatrix. |
| 10 |
# Die Winkel werden in XYZ-Euler angenommen. |
| 11 |
# a ist ein Zeilenvektor der Rotationsmatrix |
| 12 |
# x ist ein Vektor der Rotationswinkel in XYZ-Euler |
| 13 |
# Das Ergebnis ist ein Vektor, der die Differenz der aus den Winkeln berechneten Rotationsmatrix von der vorgegebenen Matrix angibt. |
| 14 |
DiffXYZeulerRotmFIT <- function(x,a){
|
| 15 |
ret <- numeric(10) |
| 16 |
#cat("Diff",x,a,"\n")
|
| 17 |
#Die unten zugrundeliegende Berechnung der Rotationsmatrix entspricht der Darstellung in Weisstein (k.D.), wurde jedoch auf des verwendete Koordinatensystem (rechtshändig) und die Koordinatensysteme der verwendeten Bibliotheken umgerechnet |
| 18 |
#cat(class(as.numeric(x[2]))) |
| 19 |
ret[1]<- (cos(as.numeric(x[2]))*cos(as.numeric(x[3]))-as.numeric(a[3])) |
| 20 |
ret[2]<- ((sin(as.numeric(x[1]))*sin(as.numeric(x[2]))*cos(as.numeric(x[3]))-sin(as.numeric(x[3]))*cos(as.numeric(x[1])))-as.numeric(a[4])) |
| 21 |
ret[3]<- (cos(as.numeric(x[1]))*sin(as.numeric(x[2]))*cos(as.numeric(x[3]))+sin(as.numeric(x[1]))*sin(as.numeric(x[3]))-as.numeric(a[5])) |
| 22 |
ret[4]<- (cos(as.numeric(x[2]))*sin(as.numeric(x[3]))-as.numeric(a[6])) |
| 23 |
ret[5]<- (sin(as.numeric(x[1]))*sin(as.numeric(x[2]))*sin(as.numeric(x[3]))+cos(as.numeric(x[1]))*cos(as.numeric(x[3]))-as.numeric(a[7])) |
| 24 |
ret[6]<- (cos(as.numeric(x[1]))*sin(as.numeric(x[2]))*sin(as.numeric(x[3]))-sin(as.numeric(x[1]))*cos(as.numeric(x[3]))-as.numeric(a[8])) |
| 25 |
ret[7]<- (-sin(as.numeric(x[2])))-as.numeric(a[9]) |
| 26 |
ret[8]<- (cos(as.numeric(x[2]))*sin(as.numeric(x[1]))-a[10]) |
| 27 |
ret[9]<- (cos(as.numeric(x[1]))*cos(as.numeric(x[2]))-a[11]) |
| 28 |
|
| 29 |
#Dieser Eintrag kann für Anpassungen der Gewichtsfunktion verwendet werden |
| 30 |
ret[10]<- 0 |
| 31 |
return(ret) |
| 32 |
} |
| 33 |
|
| 34 |
# Die obenstehende Methode läuft oft in lokale Minima und liefert dann falsche Werte. |
| 35 |
# Um dies zu verhindern, werden die Werte für Blender aus 3 Gleichungen "analytisch" berechnet. (Das ist natürlich nicht wirklich eine analytische Rechnung, weil die trigonometrischen Funktionen nummerisch implementiert sein dürften). |
| 36 |
startXYZeulerFIT <- function(a){
|
| 37 |
|
| 38 |
toleranz = 1e-10 |
| 39 |
|
| 40 |
ret <- c(-1,-1,-1) |
| 41 |
|
| 42 |
#Bester Ansatz nach Slabaugh (2015) (Wesentliche Abweichungen sind angegeben) |
| 43 |
if (abs(abs(a[7])-1) > toleranz) #Betrachte auch Werte sehr nahe an +-1 als +-1 |
| 44 |
{
|
| 45 |
ret[2] <- asin(-a[7]) |
| 46 |
#cat("Y-Euler:",ret[2]," ")
|
| 47 |
temp <- cos(ret[2]) |
| 48 |
ret[1] <- atan2(a[8]/temp,a[9]/temp) |
| 49 |
#cat("X-Euler:",ret[1]," ")
|
| 50 |
ret[3] <- atan2(a[4]/temp,a[1]/temp) |
| 51 |
#cat("Z-Euler:",ret[3]," ",a[4]," ",a[1]," ",temp,"\n")
|
| 52 |
|
| 53 |
} |
| 54 |
else |
| 55 |
{
|
| 56 |
ret[3] <- (pi/2) # Die im Pseudocode von Slabaugh vorgeschlagene 0 ist für uns numerisch ungünstig |
| 57 |
if(abs(a[7]+1) <= toleranz) #Betrachte auch Werte sehr nahe an -1 als -1 |
| 58 |
{
|
| 59 |
ret[2] = pi/2 |
| 60 |
ret[1] = (pi/2)+atan2(a[2],a[3]) |
| 61 |
} |
| 62 |
else |
| 63 |
{
|
| 64 |
ret[2] = 3*pi/2 # Entspricht -pi/2 |
| 65 |
ret[1] = atan2(-a[2],-a[3])-(pi/2) |
| 66 |
} |
| 67 |
} |
| 68 |
|
| 69 |
#Sorgt für positive Winkel |
| 70 |
for(i in 1:3){
|
| 71 |
while(ret[i] < 0){
|
| 72 |
ret[i] <- ret[i]+2*pi |
| 73 |
} |
| 74 |
} |
| 75 |
return(ret) |
| 76 |
} |
| 77 |
|
| 78 |
#Wrapper für acos |
| 79 |
#min, max behebt Probleme mit der Maschienengenauigkeit im Grenzbereich und verhindert Nans, weil Werte die Definitionsgrenze auf hinteren Nachkommastellen überschreiten (vgl. Bolker (2012)). |
| 80 |
myacos <- function(x){
|
| 81 |
return (acos(max(min(x,1.0),-1.0))) |
| 82 |
} |
| 83 |
|
| 84 |
# Diese Funktion führt alle notwendigen Schritte aus. |
| 85 |
# Leider hat lsqnonlin ein Problem damit, wenn der perfekte Wert bereits im ersten Durchgang übergeben wird. Desshalb müssen wir vorher prüfen, ob wir bereits die optimale Lösung haben. |
| 86 |
# a ist wieder die Rotationsmatrix |
| 87 |
# x0 ist der Startwert |
| 88 |
# init_rotm_inv ist die Inverse der für die Bibliothek benötigten Anpassungsmatrix. |
| 89 |
Rot2XYZeulerFIT <- function(a,x0,init_rotm_inv) {
|
| 90 |
|
| 91 |
rotm <- as.numeric(as.character(a)) |
| 92 |
ret <- numeric(5) |
| 93 |
# Setze Frame und Markerid |
| 94 |
ret[1] <- a[1] |
| 95 |
ret[2] <- a[2] |
| 96 |
|
| 97 |
# Korrigiere Eingabematrix |
| 98 |
matA <- matrix(rotm[3:11],ncol=3,byrow=TRUE) |
| 99 |
matA <- matA %*% init_rotm_inv |
| 100 |
for(i in 0:2){
|
| 101 |
rotm[(i*3+3):(i*3+5)] <- unlist(matA[(i+1),]) |
| 102 |
} |
| 103 |
#Berechne aktuellen Fehler |
| 104 |
akterr <- pracma::rmserr(as.double(DiffXYZeulerRotmFIT(x=x0,a=rotm)),rep(0,10)) |
| 105 |
#Sind wir nicht perfekt (mse = gemittelter quadratischer Fehler)? |
| 106 |
if(akterr$mse > 0){
|
| 107 |
#winkel2 <- pracma::gaussNewton(as.double(winkel),DiffXYZeulerRotmFIT,JacobiFIT,maxiter=250,a=as.double(rotm)) |
| 108 |
winkel2 <- pracma::lsqnonlin(DiffXYZeulerRotmFIT,as.double(x0),options = list(tau=1e-9,maxeval=250),a=rotm) |
| 109 |
newerr <- pracma::rmserr(as.double(DiffXYZeulerRotmFIT(x=winkel2$x,rotm)),rep(0,10)) |
| 110 |
} |
| 111 |
if(newerr$mse <= akterr$mse){
|
| 112 |
#print("Verbesserung erfolgreich!")
|
| 113 |
#print(x0) |
| 114 |
#print(winkel2$x) |
| 115 |
ret[3:5] <- winkel2$x |
| 116 |
return(ret) |
| 117 |
} |
| 118 |
else{
|
| 119 |
#Verbesserung war nicht möglich |
| 120 |
print("Warnung: Verbesserung gescheitert!")
|
| 121 |
print(akterr$mse) |
| 122 |
print(newerr$mse) |
| 123 |
print(x0) |
| 124 |
print(winkel2$x) |
| 125 |
ret[3:5] <- x0 |
| 126 |
return(ret) |
| 127 |
} |
| 128 |
} |
| 129 |
|
| 130 |
Rot2XYZeulerBART <- function(a,x0) {
|
| 131 |
#print("BART")
|
| 132 |
x0 <- unlist(x0) |
| 133 |
#Drehrichtung um die X-Achse umdrehen |
| 134 |
x0[1]<- (2*pi)-x0[1] |
| 135 |
init_mat_table <- read.table(file="Bestimmung_Umformungen_Rotm/bart_init_rotm_inv.table") |
| 136 |
init_rotm_inv <- matrix(unlist(init_mat_table), ncol=3, nrow=3, byrow=TRUE) |
| 137 |
ret <- Rot2XYZeulerFIT(a,x0,init_rotm_inv) |
| 138 |
#Drehrichtung um die X-Achse umdrehen |
| 139 |
ret[[3]]<- (2*pi)-ret[[3]] |
| 140 |
return(ret) |
| 141 |
} |
| 142 |
|
| 143 |
Rot2XYZeulerALVAR <- function(a,x0) {
|
| 144 |
#print("ALVAR")
|
| 145 |
x0 <- unlist(x0) |
| 146 |
#Drehrichtung um die X-Achse umdrehen |
| 147 |
x0[1]<- (2*pi)-x0[1] |
| 148 |
init_mat_table <- read.table(file="Bestimmung_Umformungen_Rotm/alvar_init_rotm_inv.table") |
| 149 |
init_rotm_inv <- matrix(unlist(init_mat_table), ncol=3, nrow=3, byrow=TRUE) |
| 150 |
ret <- Rot2XYZeulerFIT(a,x0,init_rotm_inv) |
| 151 |
#Drehrichtung um die X-Achse umdrehen |
| 152 |
ret[[3]]<- (2*pi)-ret[[3]] |
| 153 |
return(ret) |
| 154 |
} |
| 155 |
|
| 156 |
Rot2XYZeulerARUCO <- function(a,x0) {
|
| 157 |
#print("ARUCO")
|
| 158 |
#print(x0) |
| 159 |
#Tausche die ersten beiden Zeilen |
| 160 |
a[3:8] <- a[c(6:8,3:5)] |
| 161 |
|
| 162 |
x0 <- unlist(x0) |
| 163 |
#Drehrichtung um die Z-Achse umdrehen |
| 164 |
x0[3] <- 2*pi-x0[3] |
| 165 |
init_mat_table <- read.table(file="Bestimmung_Umformungen_Rotm/aruco_init_rotm_inv.table") |
| 166 |
init_rotm_inv <- matrix(unlist(init_mat_table), ncol=3, nrow=3, byrow=TRUE) |
| 167 |
ret <- Rot2XYZeulerFIT(a,x0,init_rotm_inv) |
| 168 |
#Drehrichtung um die Z-Achse umdrehen |
| 169 |
ret[[5]]<- (2*pi)-ret[[5]] |
| 170 |
return(ret) |
| 171 |
} |
| 172 |
|
| 173 |
Rot2XYZeulerBLENDER <- function(a) {
|
| 174 |
rotm <- as.numeric(as.character(a)) |
| 175 |
ret <- numeric(5) |
| 176 |
# Setze Frame und Markerid |
| 177 |
ret[1] <- a[1] |
| 178 |
ret[2] <- a[2] |
| 179 |
#Skalierung auf der Z-Achse ausgleichen |
| 180 |
a[9:11] <- a[9:11]*200 |
| 181 |
|
| 182 |
ret[3:5] <- startXYZeulerFIT(unlist(a[3:11])) |
| 183 |
#Drehrichtung um die Y-Achse umdrehen |
| 184 |
ret[4] <- 2*pi-unlist(ret[4]) |
| 185 |
return(ret) |
| 186 |
} |
| 187 |
|
| 188 |
#Quellen: |
| 189 |
#Bolker, B. (2012, 24. Dezember) acos(1) returns NaN for some values, not others Antwort auf http://stackoverflow.com/questions/18806944/about-arccos-function-in-r-nan-produced |
| 190 |
#Slabaugh, G. G. (2015) Computing Euler angles from a rotation matrix http://staff.city.ac.uk/~sbbh653/publications/euler.pdf |