Update to use findExampleDataFile and Rot3 hat and Vee
parent
769784a0e6
commit
d52f73cf77
|
@ -16,7 +16,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 7,
|
"execution_count": 13,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
|
@ -33,13 +33,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": 14,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"import numpy as np\n",
|
"import numpy as np\n",
|
||||||
"import pandas as pd\n",
|
"import pandas as pd\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"from gtsam import findExampleDataFile\n",
|
||||||
|
"\n",
|
||||||
"from EqF import formatCSV, blockDiag, repBlock, sim\n"
|
"from EqF import formatCSV, blockDiag, repBlock, sim\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -60,7 +62,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 9,
|
"execution_count": 15,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
|
@ -115,7 +117,7 @@
|
||||||
"\n",
|
"\n",
|
||||||
"# Load dataset\n",
|
"# Load dataset\n",
|
||||||
"print(f\"Loading dataset:\\n\")\n",
|
"print(f\"Loading dataset:\\n\")\n",
|
||||||
"df = pd.read_csv('EqFdata.csv', encoding = 'utf-8')\n",
|
"df = pd.read_csv(findExampleDataFile(\"EqFdata.csv\"))\n",
|
||||||
"data = formatCSV(df) \n",
|
"data = formatCSV(df) \n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Define initial covariance\n",
|
"# Define initial covariance\n",
|
||||||
|
|
|
@ -22,26 +22,6 @@ from typing import List
|
||||||
|
|
||||||
coordinate = "EXPONENTIAL"
|
coordinate = "EXPONENTIAL"
|
||||||
|
|
||||||
def wedge(vec : np.ndarray) -> np.ndarray:
|
|
||||||
if not isinstance(vec, np.ndarray):
|
|
||||||
raise TypeError
|
|
||||||
if not vec.size == 3:
|
|
||||||
raise ValueError
|
|
||||||
mat = np.array([[ 0.0, -vec.item(2), vec.item(1)],
|
|
||||||
[ vec.item(2), 0.0, -vec.item(0)],
|
|
||||||
[-vec.item(1), vec.item(0), 0.0]])
|
|
||||||
return mat
|
|
||||||
|
|
||||||
def vee(mat : np.ndarray) -> np.ndarray:
|
|
||||||
if not isinstance(mat, np.ndarray):
|
|
||||||
raise TypeError
|
|
||||||
if not mat.shape == (3,3):
|
|
||||||
raise ValueError
|
|
||||||
vec = np.array([mat[2,1],
|
|
||||||
mat[0,2],
|
|
||||||
mat[1,0]])
|
|
||||||
return vec
|
|
||||||
|
|
||||||
def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
||||||
"""Return the SO(3) Left Jacobian
|
"""Return the SO(3) Left Jacobian
|
||||||
|
|
||||||
|
@ -56,7 +36,7 @@ def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
||||||
|
|
||||||
# Near |phi|==0, use first order Taylor expansion
|
# Near |phi|==0, use first order Taylor expansion
|
||||||
if np.isclose(angle, 0.):
|
if np.isclose(angle, 0.):
|
||||||
return np.eye(3) + 0.5 * wedge(arr)
|
return np.eye(3) + 0.5 * Rot3.Hat(arr)
|
||||||
|
|
||||||
axis = arr / angle
|
axis = arr / angle
|
||||||
s = np.sin(angle)
|
s = np.sin(angle)
|
||||||
|
@ -64,7 +44,7 @@ def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
||||||
|
|
||||||
return (s / angle) * np.eye(3) + \
|
return (s / angle) * np.eye(3) + \
|
||||||
(1 - (s / angle)) * np.outer(axis, axis) + \
|
(1 - (s / angle)) * np.outer(axis, axis) + \
|
||||||
((1 - c) / angle) * wedge(axis)
|
((1 - c) / angle) * Rot3.Hat(axis)
|
||||||
|
|
||||||
def checkNorm(x: np.ndarray, tol: float = 1e-3):
|
def checkNorm(x: np.ndarray, tol: float = 1e-3):
|
||||||
"""Check norm of a vector being 1 or nan
|
"""Check norm of a vector being 1 or nan
|
||||||
|
@ -183,7 +163,7 @@ class Input:
|
||||||
:return: self.w as a skew-symmetric matrix
|
:return: self.w as a skew-symmetric matrix
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return wedge(self.w)
|
return Rot3.Hat(self.w)
|
||||||
|
|
||||||
class G:
|
class G:
|
||||||
"""Symmetry group (SO(3) |x so(3)) x SO(3) x ... x SO(3)
|
"""Symmetry group (SO(3) |x so(3)) x SO(3) x ... x SO(3)
|
||||||
|
@ -233,7 +213,7 @@ class G:
|
||||||
assert (isinstance(other, G))
|
assert (isinstance(other, G))
|
||||||
assert (len(self.B) == len(other.B))
|
assert (len(self.B) == len(other.B))
|
||||||
return G(self.A * other.A,
|
return G(self.A * other.A,
|
||||||
self.a + wedge(self.A.matrix() @ vee(other.a)),
|
self.a + Rot3.Hat(self.A.matrix() @ Rot3.Vee(other.a)),
|
||||||
[self.B[i]*other.B[i] for i in range(len(self.B))])
|
[self.B[i]*other.B[i] for i in range(len(self.B))])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -260,7 +240,7 @@ class G:
|
||||||
|
|
||||||
n = int((x.size - 6) / 3)
|
n = int((x.size - 6) / 3)
|
||||||
A = Rot3.Expmap(x[0:3])
|
A = Rot3.Expmap(x[0:3])
|
||||||
a = wedge(Rot3LeftJacobian(x[0:3]) @ x[3:6])
|
a = Rot3.Hat(Rot3LeftJacobian(x[0:3]) @ x[3:6])
|
||||||
B = [Rot3.Expmap(x[(6 + 3 * i):(9 + 3 * i)]) for i in range(n)]
|
B = [Rot3.Expmap(x[(6 + 3 * i):(9 + 3 * i)]) for i in range(n)]
|
||||||
|
|
||||||
return G(A, a, B)
|
return G(A, a, B)
|
||||||
|
@ -271,7 +251,7 @@ class G:
|
||||||
:return: A element of the group G given by the inverse of self
|
:return: A element of the group G given by the inverse of self
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return G(self.A.inverse(), -wedge(self.A.inverse().matrix() @ vee(self.a)), [B.inverse() for B in self.B])
|
return G(self.A.inverse(), -Rot3.Hat(self.A.inverse().matrix() @ Rot3.Vee(self.a)), [B.inverse() for B in self.B])
|
||||||
|
|
||||||
class Direction:
|
class Direction:
|
||||||
"""Define a direction as a S2 element"""
|
"""Define a direction as a S2 element"""
|
||||||
|
@ -317,25 +297,6 @@ def repBlock(A : np.ndarray, n: int) -> np.ndarray:
|
||||||
res = blockDiag(res, A)
|
res = blockDiag(res, A)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def wedge(vec : np.ndarray) -> np.ndarray:
|
|
||||||
if not isinstance(vec, np.ndarray):
|
|
||||||
raise TypeError
|
|
||||||
if not vec.size == 3:
|
|
||||||
raise ValueError
|
|
||||||
mat = np.array([[ 0.0, -vec.item(2), vec.item(1)],
|
|
||||||
[ vec.item(2), 0.0, -vec.item(0)],
|
|
||||||
[-vec.item(1), vec.item(0), 0.0]])
|
|
||||||
return mat
|
|
||||||
|
|
||||||
def vee(mat : np.ndarray) -> np.ndarray:
|
|
||||||
if not isinstance(mat, np.ndarray):
|
|
||||||
raise TypeError
|
|
||||||
if not mat.shape == (3,3):
|
|
||||||
raise ValueError
|
|
||||||
vec = np.array([mat[2,1],
|
|
||||||
mat[0,2],
|
|
||||||
mat[1,0]])
|
|
||||||
return vec
|
|
||||||
|
|
||||||
def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
||||||
"""Return the SO(3) Left Jacobian
|
"""Return the SO(3) Left Jacobian
|
||||||
|
@ -351,7 +312,7 @@ def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
||||||
|
|
||||||
# Near |phi|==0, use first order Taylor expansion
|
# Near |phi|==0, use first order Taylor expansion
|
||||||
if np.isclose(angle, 0.):
|
if np.isclose(angle, 0.):
|
||||||
return np.eye(3) + 0.5 * wedge(arr)
|
return np.eye(3) + 0.5 * Rot3.Hat(arr)
|
||||||
|
|
||||||
axis = arr / angle
|
axis = arr / angle
|
||||||
s = np.sin(angle)
|
s = np.sin(angle)
|
||||||
|
@ -359,7 +320,7 @@ def Rot3LeftJacobian(arr: np.ndarray) -> np.ndarray:
|
||||||
|
|
||||||
return (s / angle) * np.eye(3) + \
|
return (s / angle) * np.eye(3) + \
|
||||||
(1 - (s / angle)) * np.outer(axis, axis) + \
|
(1 - (s / angle)) * np.outer(axis, axis) + \
|
||||||
((1 - c) / angle) * wedge(axis)
|
((1 - c) / angle) * Rot3.Hat(axis)
|
||||||
|
|
||||||
def numericalDifferential(f, x) -> np.ndarray:
|
def numericalDifferential(f, x) -> np.ndarray:
|
||||||
"""Compute the numerical derivative via central difference"""
|
"""Compute the numerical derivative via central difference"""
|
||||||
|
@ -415,7 +376,7 @@ def stateAction(X: G, xi: State) -> State:
|
||||||
raise ValueError("the number of calibration states and B elements of the symmetry group has to match")
|
raise ValueError("the number of calibration states and B elements of the symmetry group has to match")
|
||||||
|
|
||||||
return State(xi.R * X.A,
|
return State(xi.R * X.A,
|
||||||
X.A.inverse().matrix() @ (xi.b - vee(X.a)),
|
X.A.inverse().matrix() @ (xi.b - Rot3.Vee(X.a)),
|
||||||
[(X.A.inverse() * xi.S[i] * X.B[i]) for i in range(len(X.B))])
|
[(X.A.inverse() * xi.S[i] * X.B[i]) for i in range(len(X.B))])
|
||||||
|
|
||||||
|
|
||||||
|
@ -427,7 +388,7 @@ def velocityAction(X: G, u: Input) -> Input:
|
||||||
:return: A new element of the Input given by the action of psi of G in the Input space
|
:return: A new element of the Input given by the action of psi of G in the Input space
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return Input(X.A.inverse().matrix() @ (u.w - vee(X.a)), u.Sigma)
|
return Input(X.A.inverse().matrix() @ (u.w - Rot3.Vee(X.a)), u.Sigma)
|
||||||
|
|
||||||
|
|
||||||
def outputAction(X: G, y: Direction, idx: int = -1) -> np.ndarray:
|
def outputAction(X: G, y: Direction, idx: int = -1) -> np.ndarray:
|
||||||
|
@ -457,7 +418,7 @@ def local_coords(e: State) -> np.ndarray:
|
||||||
eps = np.concatenate((Rot3.Logmap(e.R), e.b, np.asarray(tmp).reshape(3 * len(tmp),)))
|
eps = np.concatenate((Rot3.Logmap(e.R), e.b, np.asarray(tmp).reshape(3 * len(tmp),)))
|
||||||
elif coordinate == "NORMAL":
|
elif coordinate == "NORMAL":
|
||||||
raise ValueError("Normal coordinate representation is not implemented yet")
|
raise ValueError("Normal coordinate representation is not implemented yet")
|
||||||
# X = G(e.R, -SO3.wedge(e.R @ e.b), e.S)
|
# X = G(e.R, -SO3.Rot3.Hat(e.R @ e.b), e.S)
|
||||||
# eps = G.log(X)
|
# eps = G.log(X)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid coordinate representation")
|
raise ValueError("Invalid coordinate representation")
|
||||||
|
@ -630,7 +591,7 @@ class EqF:
|
||||||
assert (y.cal_idx <= self.__n_cal)
|
assert (y.cal_idx <= self.__n_cal)
|
||||||
|
|
||||||
Ct = self.__measurementMatrixC(y.d, y.cal_idx) # Equation 14b
|
Ct = self.__measurementMatrixC(y.d, y.cal_idx) # Equation 14b
|
||||||
delta = wedge(y.d.d.unitVector()) @ outputAction(self.__X_hat.inv(), y.y, y.cal_idx)
|
delta = Rot3.Hat(y.d.d.unitVector()) @ outputAction(self.__X_hat.inv(), y.y, y.cal_idx)
|
||||||
Dt = self.__outputMatrixDt(y.cal_idx)
|
Dt = self.__outputMatrixDt(y.cal_idx)
|
||||||
S = Ct @ self.__Sigma @ Ct.T + Dt @ y.Sigma @ Dt.T # Equation 21
|
S = Ct @ self.__Sigma @ Ct.T + Dt @ y.Sigma @ Dt.T # Equation 21
|
||||||
K = self.__Sigma @ Ct.T @ np.linalg.inv(S) # Equation 22
|
K = self.__Sigma @ Ct.T @ np.linalg.inv(S) # Equation 22
|
||||||
|
@ -714,9 +675,9 @@ class EqF:
|
||||||
|
|
||||||
# If the measurement is related to a sensor that has a calibration state
|
# If the measurement is related to a sensor that has a calibration state
|
||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
Cc[(3 * idx):(3 + 3 * idx), :] = wedge(d.d.unitVector())
|
Cc[(3 * idx):(3 + 3 * idx), :] = Rot3.Hat(d.d.unitVector())
|
||||||
|
|
||||||
return wedge(d.d.unitVector()) @ np.hstack((wedge(d.d.unitVector()), np.zeros((3, 3)), Cc))
|
return Rot3.Hat(d.d.unitVector()) @ np.hstack((Rot3.Hat(d.d.unitVector()), np.zeros((3, 3)), Cc))
|
||||||
|
|
||||||
def __outputMatrixDt(self, idx: int) -> np.ndarray:
|
def __outputMatrixDt(self, idx: int) -> np.ndarray:
|
||||||
"""Return the measurement output matrix Dt
|
"""Return the measurement output matrix Dt
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue