Created
December 19, 2017 09:42
-
-
Save serser/a3e0b71eb447c9260cfb722d3618f5d6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The program incrementally apply constraints into matrix omega and vector xi. | |
# The final estimation of robot position is thus given by | |
# mu = omega.inverse() * xi | |
# notice the steps | |
# | |
# [1, 0, 0] -> [init] | |
# | |
# [1, -1, 0] -> [-move1] | |
# [-1, 1, 0] -> [move1] | |
# so on and so forth. | |
# https://classroom.udacity.com/courses/cs373/lessons/48696626/concepts/487372220923 | |
# | |
# ----------- | |
# User Instructions | |
# | |
# Write a function, doit, that takes as its input an | |
# initial robot position, move1, and move2. This | |
# function should compute the Omega and Xi matrices | |
# discussed in lecture and should RETURN the mu vector | |
# (which is the product of Omega.inverse() and Xi). | |
# | |
# Please enter your code at the bottom. | |
from math import * | |
import random | |
#=============================================================== | |
# | |
# SLAM in a rectolinear world (we avoid non-linearities) | |
# | |
# | |
#=============================================================== | |
# ------------------------------------------------ | |
# | |
# this is the matrix class | |
# we use it because it makes it easier to collect constraints in GraphSLAM | |
# and to calculate solutions (albeit inefficiently) | |
# | |
class matrix: | |
# implements basic operations of a matrix class | |
# ------------ | |
# | |
# initialization - can be called with an initial matrix | |
# | |
def __init__(self, value = [[]]): | |
self.value = value | |
self.dimx = len(value) | |
self.dimy = len(value[0]) | |
if value == [[]]: | |
self.dimx = 0 | |
# ------------ | |
# | |
# makes matrix of a certain size and sets each element to zero | |
# | |
def zero(self, dimx, dimy = 0): | |
if dimy == 0: | |
dimy = dimx | |
# check if valid dimensions | |
if dimx < 1 or dimy < 1: | |
raise ValueError, "Invalid size of matrix" | |
else: | |
self.dimx = dimx | |
self.dimy = dimy | |
self.value = [[0.0 for row in range(dimy)] for col in range(dimx)] | |
# ------------ | |
# | |
# makes matrix of a certain (square) size and turns matrix into identity matrix | |
# | |
def identity(self, dim): | |
# check if valid dimension | |
if dim < 1: | |
raise ValueError, "Invalid size of matrix" | |
else: | |
self.dimx = dim | |
self.dimy = dim | |
self.value = [[0.0 for row in range(dim)] for col in range(dim)] | |
for i in range(dim): | |
self.value[i][i] = 1.0 | |
# ------------ | |
# | |
# prints out values of matrix | |
# | |
def show(self, txt = ''): | |
for i in range(len(self.value)): | |
print txt + '['+ ', '.join('%.3f'%x for x in self.value[i]) + ']' | |
print ' ' | |
# ------------ | |
# | |
# defines elmement-wise matrix addition. Both matrices must be of equal dimensions | |
# | |
def __add__(self, other): | |
# check if correct dimensions | |
if self.dimx != other.dimx or self.dimx != other.dimx: | |
raise ValueError, "Matrices must be of equal dimension to add" | |
else: | |
# add if correct dimensions | |
res = matrix() | |
res.zero(self.dimx, self.dimy) | |
for i in range(self.dimx): | |
for j in range(self.dimy): | |
res.value[i][j] = self.value[i][j] + other.value[i][j] | |
return res | |
# ------------ | |
# | |
# defines elmement-wise matrix subtraction. Both matrices must be of equal dimensions | |
# | |
def __sub__(self, other): | |
# check if correct dimensions | |
if self.dimx != other.dimx or self.dimx != other.dimx: | |
raise ValueError, "Matrices must be of equal dimension to subtract" | |
else: | |
# subtract if correct dimensions | |
res = matrix() | |
res.zero(self.dimx, self.dimy) | |
for i in range(self.dimx): | |
for j in range(self.dimy): | |
res.value[i][j] = self.value[i][j] - other.value[i][j] | |
return res | |
# ------------ | |
# | |
# defines multiplication. Both matrices must be of fitting dimensions | |
# | |
def __mul__(self, other): | |
# check if correct dimensions | |
if self.dimy != other.dimx: | |
raise ValueError, "Matrices must be m*n and n*p to multiply" | |
else: | |
# multiply if correct dimensions | |
res = matrix() | |
res.zero(self.dimx, other.dimy) | |
for i in range(self.dimx): | |
for j in range(other.dimy): | |
for k in range(self.dimy): | |
res.value[i][j] += self.value[i][k] * other.value[k][j] | |
return res | |
# ------------ | |
# | |
# returns a matrix transpose | |
# | |
def transpose(self): | |
# compute transpose | |
res = matrix() | |
res.zero(self.dimy, self.dimx) | |
for i in range(self.dimx): | |
for j in range(self.dimy): | |
res.value[j][i] = self.value[i][j] | |
return res | |
# ------------ | |
# | |
# creates a new matrix from the existing matrix elements. | |
# | |
# Example: | |
# l = matrix([[ 1, 2, 3, 4, 5], | |
# [ 6, 7, 8, 9, 10], | |
# [11, 12, 13, 14, 15]]) | |
# | |
# l.take([0, 2], [0, 2, 3]) | |
# | |
# results in: | |
# | |
# [[1, 3, 4], | |
# [11, 13, 14]] | |
# | |
# | |
# take is used to remove rows and columns from existing matrices | |
# list1/list2 define a sequence of rows/columns that shall be taken | |
# is no list2 is provided, then list2 is set to list1 (good for symmetric matrices) | |
# | |
def take(self, list1, list2 = []): | |
if list2 == []: | |
list2 = list1 | |
if len(list1) > self.dimx or len(list2) > self.dimy: | |
raise ValueError, "list invalid in take()" | |
res = matrix() | |
res.zero(len(list1), len(list2)) | |
for i in range(len(list1)): | |
for j in range(len(list2)): | |
res.value[i][j] = self.value[list1[i]][list2[j]] | |
return res | |
# ------------ | |
# | |
# creates a new matrix from the existing matrix elements. | |
# | |
# Example: | |
# l = matrix([[1, 2, 3], | |
# [4, 5, 6]]) | |
# | |
# l.expand(3, 5, [0, 2], [0, 2, 3]) | |
# | |
# results in: | |
# | |
# [[1, 0, 2, 3, 0], | |
# [0, 0, 0, 0, 0], | |
# [4, 0, 5, 6, 0]] | |
# | |
# expand is used to introduce new rows and columns into an existing matrix | |
# list1/list2 are the new indexes of row/columns in which the matrix | |
# elements are being mapped. Elements for rows and columns | |
# that are not listed in list1/list2 | |
# will be initialized by 0.0. | |
# | |
def expand(self, dimx, dimy, list1, list2 = []): | |
if list2 == []: | |
list2 = list1 | |
if len(list1) > self.dimx or len(list2) > self.dimy: | |
raise ValueError, "list invalid in expand()" | |
res = matrix() | |
res.zero(dimx, dimy) | |
for i in range(len(list1)): | |
for j in range(len(list2)): | |
res.value[list1[i]][list2[j]] = self.value[i][j] | |
return res | |
# ------------ | |
# | |
# Computes the upper triangular Cholesky factorization of | |
# a positive definite matrix. | |
# This code is based on http://adorio-research.org/wordpress/?p=4560 | |
def Cholesky(self, ztol= 1.0e-5): | |
res = matrix() | |
res.zero(self.dimx, self.dimx) | |
for i in range(self.dimx): | |
S = sum([(res.value[k][i])**2 for k in range(i)]) | |
d = self.value[i][i] - S | |
if abs(d) < ztol: | |
res.value[i][i] = 0.0 | |
else: | |
if d < 0.0: | |
raise ValueError, "Matrix not positive-definite" | |
res.value[i][i] = sqrt(d) | |
for j in range(i+1, self.dimx): | |
S = sum([res.value[k][i] * res.value[k][j] for k in range(i)]) | |
if abs(S) < ztol: | |
S = 0.0 | |
res.value[i][j] = (self.value[i][j] - S)/res.value[i][i] | |
return res | |
# ------------ | |
# | |
# Computes inverse of matrix given its Cholesky upper Triangular | |
# decomposition of matrix. | |
# This code is based on http://adorio-research.org/wordpress/?p=4560 | |
def CholeskyInverse(self): | |
res = matrix() | |
res.zero(self.dimx, self.dimx) | |
# Backward step for inverse. | |
for j in reversed(range(self.dimx)): | |
tjj = self.value[j][j] | |
S = sum([self.value[j][k]*res.value[j][k] for k in range(j+1, self.dimx)]) | |
res.value[j][j] = 1.0/ tjj**2 - S/ tjj | |
for i in reversed(range(j)): | |
res.value[j][i] = res.value[i][j] = \ | |
-sum([self.value[i][k]*res.value[k][j] for k in \ | |
range(i+1,self.dimx)])/self.value[i][i] | |
return res | |
# ------------ | |
# | |
# comutes and returns the inverse of a square matrix | |
# | |
def inverse(self): | |
aux = self.Cholesky() | |
res = aux.CholeskyInverse() | |
return res | |
# ------------ | |
# | |
# prints matrix (needs work!) | |
# | |
def __repr__(self): | |
return repr(self.value) | |
# ###################################################################### | |
# ###################################################################### | |
# ###################################################################### | |
""" | |
For the following example, you would call doit(-3, 5, 3): | |
3 robot positions | |
initially: -3 | |
moves by 5 | |
moves by 3 | |
which should return a mu of: | |
[[-3.0], | |
[2.0], | |
[5.0]] | |
""" | |
def doit(initial_pos, move1, move2): | |
# | |
# | |
# Add your code here. | |
# | |
# | |
omega = matrix() | |
omega.zero(3,3) | |
xi = matrix() | |
xi.zero(3,1) | |
omega.value[0][0] = 1 | |
xi.value[0][0] = initial_pos | |
omega.value[0][0] += 1 | |
omega.value[0][1] = -1 | |
xi.value[0][0] += -move1 | |
omega.value[1][0] = -1 | |
omega.value[1][1] = 1 | |
xi.value[1][0] = move1 | |
omega.value[1][1] += 1 | |
omega.value[1][2] = -1 | |
xi.value[1][0] += -move2 | |
omega.value[2][1] = -1 | |
omega.value[2][2] = 1 | |
xi.value[2][0] = move2 | |
mu = omega.inverse() * xi | |
return mu | |
doit(-3, 5, 3) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment