Statistics
| Branch: | Revision:

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