Implement fitting to circle using LSC and HyperFit (#45)

* chore: add pylint to devcontainer

Signed-off-by: Dev Singh <dev@devksingh.com>

* feat: init LSC fitting

cuda and cpu-based LSC fitting using cupy and numpy

Signed-off-by: Dev Singh <dev@devksingh.com>

* docs: add changelog entry and module to class list

Signed-off-by: Dev Singh <dev@devksingh.com>

* docs: fix typo in comment

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: only import cupy if cuda available

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: move to own file, abandon cupy

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: remove numba dep

Signed-off-by: Dev Singh <dev@devksingh.com>

* deps: remove cupy dep

Signed-off-by: Dev Singh <dev@devksingh.com>

* feat: add tests

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: correct indentation

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: variable names

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: add self when refering to coords

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: numpy ordering

Signed-off-by: Dev Singh <dev@devksingh.com>

* docs: remove version bump, nomaintain

add notice that module is not actively maintained, may be removed in future release

Signed-off-by: Dev Singh <dev@devksingh.com>

* fix: remove hyperfit as not being impled

Signed-off-by: Dev Singh <dev@devksingh.com>
This commit is contained in:
Dev Singh 2020-09-25 02:06:30 +00:00 committed by GitHub
parent fe4372bd3b
commit 88f68782f7
4 changed files with 98 additions and 7 deletions

View File

@ -24,5 +24,5 @@
"ms-python.python", "ms-python.python",
"waderyan.gitblame" "waderyan.gitblame"
], ],
"postCreateCommand": "apt install vim -y ; pip install -r data-analysis/requirements.txt ; pip install -r analysis-master/requirements.txt ; pip install tra-analysis" "postCreateCommand": "apt install vim -y ; pip install -r data-analysis/requirements.txt ; pip install -r analysis-master/requirements.txt ; pip install pylint ; pip install tra-analysis"
} }

View File

@ -1,8 +1,11 @@
from tra_analysis import analysis as an from tra_analysis import analysis as an
from tra_analysis import metrics from tra_analysis import metrics
from tra_analysis import fits
def test_(): def test_():
test_data_linear = [1, 3, 6, 7, 9] test_data_linear = [1, 3, 6, 7, 9]
x_data_circular = []
y_data_circular = []
y_data_ccu = [1, 3, 7, 14, 21] y_data_ccu = [1, 3, 7, 14, 21]
y_data_ccd = [1, 5, 7, 8.5, 8.66] y_data_ccd = [1, 5, 7, 8.5, 8.66]
test_data_scrambled = [-32, 34, 19, 72, -65, -11, -43, 6, 85, -17, -98, -26, 12, 20, 9, -92, -40, 98, -78, 17, -20, 49, 93, -27, -24, -66, 40, 84, 1, -64, -68, -25, -42, -46, -76, 43, -3, 30, -14, -34, -55, -13, 41, -30, 0, -61, 48, 23, 60, 87, 80, 77, 53, 73, 79, 24, -52, 82, 8, -44, 65, 47, -77, 94, 7, 37, -79, 36, -94, 91, 59, 10, 97, -38, -67, 83, 54, 31, -95, -63, 16, -45, 21, -12, 66, -48, -18, -96, -90, -21, -83, -74, 39, 64, 69, -97, 13, 55, 27, -39] test_data_scrambled = [-32, 34, 19, 72, -65, -11, -43, 6, 85, -17, -98, -26, 12, 20, 9, -92, -40, 98, -78, 17, -20, 49, 93, -27, -24, -66, 40, 84, 1, -64, -68, -25, -42, -46, -76, 43, -3, 30, -14, -34, -55, -13, 41, -30, 0, -61, 48, 23, 60, 87, 80, 77, 53, 73, 79, 24, -52, 82, 8, -44, 65, 47, -77, 94, 7, 37, -79, 36, -94, 91, 59, 10, 97, -38, -67, 83, 54, 31, -95, -63, 16, -45, 21, -12, 66, -48, -18, -96, -90, -21, -83, -74, 39, 64, 69, -97, 13, 55, 27, -39]
@ -29,3 +32,4 @@ def test_():
assert all(a == b for a, b in zip(an.Sort().bubblesort(test_data_scrambled), test_data_sorted)) assert all(a == b for a, b in zip(an.Sort().bubblesort(test_data_scrambled), test_data_sorted))
assert all(a == b for a, b in zip(an.Sort().cyclesort(test_data_scrambled), test_data_sorted)) assert all(a == b for a, b in zip(an.Sort().cyclesort(test_data_scrambled), test_data_sorted))
assert all(a == b for a, b in zip(an.Sort().cocktailsort(test_data_scrambled), test_data_sorted)) assert all(a == b for a, b in zip(an.Sort().cocktailsort(test_data_scrambled), test_data_sorted))
assert fits.CircleFit(x=[0,0,-1,1], y=[1, -1, 0, 0]).LSC() == (0.0, 0.0, 1.0, 0.0)

View File

@ -0,0 +1,85 @@
# Titan Robotics Team 2022: CPU fitting models
# Written by Dev Singh
# Notes:
# this module is cuda-optimized (as appropriate) and vectorized (except for one small part)
# setup:
__version__ = "0.0.1"
# changelog should be viewed using print(analysis.fits.__changelog__)
__changelog__ = """changelog:
0.0.1:
- initial release, add circle fitting with LSC
"""
__author__ = (
"Dev Singh <dev@devksingh.com>"
)
__all__ = [
'CircleFit'
]
import numpy as np
class CircleFit:
"""Class to fit data to a circle using the Least Square Circle (LSC) method"""
# For more information on the LSC method, see:
# http://www.dtcenter.org/sites/default/files/community-code/met/docs/write-ups/circle_fit.pdf
def __init__(self, x, y, xy=None):
self.ournp = np #todo: implement cupy correctly
if type(x) == list:
x = np.array(x)
if type(y) == list:
y = np.array(y)
if type(xy) == list:
xy = np.array(xy)
if xy != None:
self.coords = xy
else:
# following block combines x and y into one array if not already done
self.coords = self.ournp.vstack(([x.T], [y.T])).T
def calc_R(x, y, xc, yc):
"""Returns distance between center and point"""
return self.ournp.sqrt((x-xc)**2 + (y-yc)**2)
def f(c, x, y):
"""Returns distance between point and circle at c"""
Ri = calc_R(x, y, *c)
return Ri - Ri.mean()
def LSC(self):
"""Fits given data to a circle and returns the center, radius, and variance"""
x = self.coords[:, 0]
y = self.coords[:, 1]
# guessing at a center
x_m = self.ournp.mean(x)
y_m = self.ournp.mean(y)
# calculation of the reduced coordinates
u = x - x_m
v = y - y_m
# linear system defining the center (uc, vc) in reduced coordinates:
# Suu * uc + Suv * vc = (Suuu + Suvv)/2
# Suv * uc + Svv * vc = (Suuv + Svvv)/2
Suv = self.ournp.sum(u*v)
Suu = self.ournp.sum(u**2)
Svv = self.ournp.sum(v**2)
Suuv = self.ournp.sum(u**2 * v)
Suvv = self.ournp.sum(u * v**2)
Suuu = self.ournp.sum(u**3)
Svvv = self.ournp.sum(v**3)
# Solving the linear system
A = self.ournp.array([ [ Suu, Suv ], [Suv, Svv]])
B = self.ournp.array([ Suuu + Suvv, Svvv + Suuv ])/2.0
uc, vc = self.ournp.linalg.solve(A, B)
xc_1 = x_m + uc
yc_1 = y_m + vc
# Calculate the distances from center (xc_1, yc_1)
Ri_1 = self.ournp.sqrt((x-xc_1)**2 + (y-yc_1)**2)
R_1 = self.ournp.mean(Ri_1)
# calculate residual error
residu_1 = self.ournp.sum((Ri_1-R_1)**2)
return (xc_1, yc_1, R_1, residu_1)

View File

@ -1,8 +1,9 @@
# Titan Robotics Team 2022: CUDA-based Regressions Module # Titan Robotics Team 2022: CUDA-based Regressions Module
# Not actively maintained, may be removed in future release
# Written by Arthur Lu & Jacob Levine # Written by Arthur Lu & Jacob Levine
# Notes: # Notes:
# this module has been automatically inegrated into analysis.py, and should be callable as a class from the package # this module has been automatically inegrated into analysis.py, and should be callable as a class from the package
# this module is cuda-optimized and vectorized (except for one small part) # this module is cuda-optimized (as appropriate) and vectorized (except for one small part)
# setup: # setup:
__version__ = "0.0.4" __version__ = "0.0.4"
@ -25,7 +26,7 @@ __changelog__ = """
__author__ = ( __author__ = (
"Jacob Levine <jlevine@imsa.edu>", "Jacob Levine <jlevine@imsa.edu>",
"Arthur Lu <learthurgo@gmail.com>" "Arthur Lu <learthurgo@gmail.com>",
) )
__all__ = [ __all__ = [
@ -40,14 +41,15 @@ __all__ = [
'ExpRegKernel', 'ExpRegKernel',
'SigmoidalRegKernelArthur', 'SigmoidalRegKernelArthur',
'SGDTrain', 'SGDTrain',
'CustomTrain' 'CustomTrain',
'CircleFit'
] ]
import torch import torch
global device global device
device = "cuda:0" if torch.torch.cuda.is_available() else "cpu" device = "cuda:0" if torch.cuda.is_available() else "cpu"
#todo: document completely #todo: document completely