Commit 655afdea authored by Chetan Sharma's avatar Chetan Sharma
Browse files

Polishing repo

parent eec2d671
"""
Intended to be the brain box of this project.
"""
FAKE = False
import numpy as np
import time
import shelve
import logging
from optimize import Optimizer
from ml import UnifiedLinearModel
from models import T_lin, F_lin
from objects import EndMill, Conditions, MachineChar
import os
import logging
import shelve
import time
import numpy as np
FAKE = True
# hacky fix for issues with serial library
if not FAKE:
if FAKE:
from fake_cut import ReplayCut
else:
from cut import Cut
from objects import EndMill, Conditions, MachineChar
from fake_cut import ReplayCut
from models import T_lin, F_lin, T_lin_full, F_lin_full
from ml import UnifiedLinearModel, UnifiedLinearModelFull
from optimize import Optimizer #, OptimizerFull, OptimizerPSO, OptimizerPSOFull
logging.basicConfig(level="INFO")
MACHINE_PORT = '/dev/ttyS25'
SPINDLE_PORT = '/dev/ttyS33'
TFD_PORT = '/dev/ttyS36'
# input variables
D = 1e-3 # depth of cut (always unchanging...)
W = 1e-3 # initial width of cut for bootstrap
f_r = 0.001 # initial feedrate for bootstrap
f_r_clearing = 0.003 # feedrate for facing and cutting start groove
w = 200 # spindle speed
START_DEPTH = 0.0e-3
START_FACE_D = 0.2e-3
ENDMILL = EndMill(3, 3.175e-3, 3.175e-3, 12e-3, 5e-3)
# ENDMILL = EndMill(3, 9.525e-3/2, 9.525e-3/2, 12.7e-3, 5e-3)
D_A = 0.1e-3 # maximum allowable deflection
N = 50 # total number of cuts to take, including bootstrap cuts
CONFIDENCE_RATE = np.linspace(0.2, 1, 5) # confidence progression during bootstrap cuts
USE_OLD_DATA = False
NAME = "ammp-lcs-1_4" # name to save to / name to draw data from if doing a fake cut
MODEL = UnifiedLinearModel
EQUATIONS = (T_lin, F_lin)
OPTIMIZER = Optimizer # optimizer to use
# taig machine
MACHINE = MachineChar(
r_e = 1,
K_T = 0.10281,
R_w = 0.188,
V_max = 48,
I_max = 10,
T_nom = 0.12,
f_r_max = 0.01,
K_machine = 1.25e6,
D_a = D_A
)
FIXED_CONDITIONS = Conditions(
D = D,
W = W,
f_r = f_r,
w = w,
endmill = ENDMILL
)
logging.info("Initializing all structures")
cut = None
if FAKE:
cut = ReplayCut(NAME, MODEL(), *EQUATIONS, [0,0], [0.1,2])
else:
cut = Cut(MACHINE_PORT, SPINDLE_PORT, TFD_PORT, ENDMILL, 80e-3, 50.8e-3, f_r_clearing, w, START_DEPTH, NAME)
model = MODEL()
optimizer = OPTIMIZER(model, MACHINE, D_A, FIXED_CONDITIONS)
logging.info("Beginning facing operation, creating starting groove")
if START_FACE_D:
cut.face_layer(START_FACE_D)
cut.begin_layer(D)
logging.info("First bootstrap cut to obtain a basic characterization")
conditions_conservative = Conditions(D, W, f_r, w, ENDMILL)
datum = cut.cut(conditions_conservative, save=True, auto_layer=True)
model.ingest_datum(datum)
logging.info("After bootstrap cut, model params are actually at: " + ", ".join(["{:.5e}".format(p) for p in model.params]))
if USE_OLD_DATA and not FAKE:
with shelve.open(os.path.join("saved_cuts", "db")) as db:
model.ingest_data(db[USE_OLD_DATA])
logging.info("Partially optimized bootstrap cuts starting now")
# start optimizing, but only slowly start accepting new datums
confidences = list(CONFIDENCE_RATE) + [1] * (N - len(CONFIDENCE_RATE))
for confidence in confidences:
logging.info("Confidence at : " + str(confidence))
conditions_optimized = optimizer.optimize(verbose = True)
logging.info("Optimized : " + str(conditions_optimized))
conditions_compromise = conditions_conservative.compromise(conditions_optimized, confidence)
logging.info("Compromised : " + str(conditions_compromise))
logging.info("Model guesses : " + str(model.predict_one(conditions_compromise)))
datum = cut.cut(conditions_compromise, save = True, auto_layer=True)
logging.info("Datum obtained : " + str(datum))
def ammp(MACHINE_PORT, SPINDLE_PORT, TFD_PORT, D, W, f_r, f_r_clearing, w, START_DEPTH, START_FACE_D, ENDMILL, D_A, N, X_TRAVEL, CONFIDENCE_RATE, USE_OLD_DATA, NAME, MODEL, EQUATIONS, OPTIMIZER, MACHINE):
"""
Runs the optimizing system.
System starts out by facing the stock flat (since we need to make sure the endmill bottom perfectly corresponds to the start of the stock).
Then does a bootstrap cut to initialize the model.
Then starts optimizing process itself.
Args:
MACHINE_PORT: port name for machine
SPINDLE_PORT: port name for spindle
TFD_PORT: port name for tool force dyno
D: depth of cut (always unchanging...)
W: initial width of cut for bootstrap
f_r: initial feedrate for bootstrap
f_r_clearing: feedrate for facing and cutting start groove
w: spindle speed
START_DEPTH: offset depth to start cutting at
START_FACE_D: how deep to face stock before starting ammp runs
ENDMILL: endmill parameters
D_A: maximum allowable deflection
N: total number of cuts to take, including bootstrap cuts
X_TRAVEL: travel in the x direction
CONFIDENCE_RATE: confidence progression during bootstrap cuts
USE_OLD_DATA: whether or not to use datapoints from an older run. change this to run name to use this feature
NAME: name to save to / name to draw data from if doing a fake cut
MODEL: model class to use
EQUATIONS: equations to use in optimizer
OPTIMIZER: optimizer to use
MACHINE: machine characteristics
"""
FIXED_CONDITIONS = Conditions(
D=D,
W=W,
f_r=f_r,
w=w,
endmill=ENDMILL
)
logging.info("Initializing all structures")
cut = None
if FAKE:
cut = ReplayCut(NAME, MODEL(), *EQUATIONS, [0, 0], [0.1, 2])
else:
cut = Cut(MACHINE_PORT, SPINDLE_PORT, TFD_PORT, ENDMILL, X_TRAVEL,
50.8e-3, f_r_clearing, w, START_DEPTH, NAME, graceful_shutdown=True)
model = MODEL()
optimizer = OPTIMIZER(model, MACHINE, D_A, FIXED_CONDITIONS)
logging.info("Beginning facing operation, creating starting groove")
if START_FACE_D:
cut.face_layer(START_FACE_D)
cut.begin_layer(D)
logging.info("First bootstrap cut to obtain a basic characterization")
conditions_conservative = Conditions(D, W, f_r, w, ENDMILL)
datum = cut.cut(conditions_conservative, save=True, auto_layer=True)
model.ingest_datum(datum)
logging.info("Params updated to: " + ", ".join(["{:.5e}".format(p) for p in model.params]))
if FAKE:
logging.info("Actual cut params: " + ", ".join(["{:.5e}".format(p) for p in cut.params]))
\ No newline at end of file
logging.info("After bootstrap cut, model params are actually at: " +
", ".join(["{:.5e}".format(p) for p in model.params]))
if USE_OLD_DATA and not FAKE:
with shelve.open(os.path.join("saved_cuts", "db")) as db:
model.ingest_data(db[USE_OLD_DATA])
logging.info("Partially optimized bootstrap cuts starting now")
# start optimizing, but only slowly start accepting new datums
confidences = list(CONFIDENCE_RATE) + [1] * (N - len(CONFIDENCE_RATE))
for i, confidence in enumerate(confidences):
logging.info("------------------ Run #" +
str(i+1) + " -----------------------")
logging.info("Confidence at : " + str(confidence))
conditions_optimized = optimizer.optimize(verbose=True)
logging.info("Optimized : " + str(conditions_optimized))
conditions_compromise = conditions_conservative.compromise(
conditions_optimized, confidence)
logging.info("Compromised : " + str(conditions_compromise))
logging.info("Model guesses : " +
str(model.predict_one(conditions_compromise)))
datum = cut.cut(conditions_compromise, save=True, auto_layer=True)
logging.info("Datum obtained : " + str(datum))
model.ingest_datum(datum)
logging.info("Params updated to: " +
", ".join(["{:.5e}".format(p) for p in model.params]))
if FAKE:
logging.info("Actual cut params: " +
", ".join(["{:.5e}".format(p) for p in cut.params]))
if __name__ == "__main__":
# input variables
MACHINE_PORT = '/dev/ttyS25'
SPINDLE_PORT = '/dev/ttyS33'
TFD_PORT = '/dev/ttyS36'
D = 1.5e-3
W = 1e-3
f_r = 0.001
f_r_clearing = 0.005
w = 200
START_DEPTH = 0e-3
START_FACE_D = 0.1e-3
ENDMILL = EndMill(3, 3.175e-3 / 2, 3.175e-3 / 2, 10e-3, 5e-3)
# ENDMILL = EndMill(3, 3.175e-3, 3.175e-3, 12e-3, 5e-3)
# ENDMILL = EndMill(3, 9.525e-3/2, 9.525e-3/2, 20e-3, 5e-3)
D_A = 0.1e-3
N = 10
X_TRAVEL = 30e-3
CONFIDENCE_RATE = np.linspace(0.2, 1, 5)
USE_OLD_DATA = False
NAME = "ammp-alu-1_8_coolant"
MODEL = UnifiedLinearModel
EQUATIONS = (T_lin, F_lin)
OPTIMIZER = Optimizer
# taig machine
MACHINE = MachineChar(
r_e=1,
K_T=0.10281,
R_w=0.188,
V_max=48,
I_max=10,
T_nom=0.12,
f_r_max=0.01,
K_machine=1.25e6,
D_a=D_A
)
ammp(MACHINE_PORT, SPINDLE_PORT, TFD_PORT, D, W, f_r, f_r_clearing, w, START_DEPTH, START_FACE_D,
ENDMILL, D_A, N, X_TRAVEL, CONFIDENCE_RATE, USE_OLD_DATA, NAME, MODEL, EQUATIONS, OPTIMIZER, MACHINE)
"""
Intended to be the brain box of this project; for now, it just runs a test sweep to collect initial data.
A tiny utility script that just collects sweep data.
"""
import numpy as np
import time
......@@ -10,49 +10,41 @@ from ml import LinearModel
from optimize import Optimizer
import logging
logging.basicConfig(level="INFO")
MACHINE_PORT = '/dev/ttyS25'
SPINDLE_PORT = '/dev/ttyS33'
TFD_PORT = '/dev/ttyS36'
IGNORE_FIRST = 0
machine = MachineChar(
r_e = 1,
K_T = 0.10281,
R_w = 0.188,
V_max = 48,
I_max = 10,
T_nom = 0.12,
f_r_max = 0.017,
K_machine = 5e6,
D_a = 1
)
endmill = EndMill(3, 3.175e-3, 3.175e-3, 9.5e-3, 3e-3)
MACHINE_PORT = "/dev/ttyS25"
SPINDLE_PORT = "/dev/ttyS33"
TFD_PORT = "/dev/ttyS36"
endmill = EndMill(3, 3.175e-3, 3.175e-3, 12e-3, 5e-3)
fixed_conditions = Conditions(
D = 1e-3,
W = 1e-3,
f_r = 0.001,
w = 300,
endmill = endmill
D=1e-3, W=1e-3, f_r=0.001, w=300, endmill=endmill)
cut = Cut(
MACHINE_PORT,
SPINDLE_PORT,
TFD_PORT,
endmill,
60e-3,
50.8e-3,
5e-3,
300,
initial_z=0.5e-3,
save_as="sweep-alu-1_4-v2",
)
cut = Cut(MACHINE_PORT, SPINDLE_PORT, TFD_PORT, endmill, 80e-3, 50.8e-3, 5e-3, 300, save_as = "6061-sweep-speed")
f_r_range = np.linspace(2e-3, 0.01, 6)
W_range = np.linspace(1e-3, 3.175e-3 * 1.8, 6)
f_r_range = np.linspace(2e-3, 0.011, 4)
W_range = np.linspace(1e-3, 3.175e-3 * 1.8, 5)
w_range = np.linspace(100, 300, 5)
cut.face_layer(D=0.3e-3)
cut.begin_layer(D=1e-3)
cut.face_layer(D = 0.3e-3)
cut.begin_layer(D = 1e-3)
for f_r in f_r_range:
for f_r in f_r_range:
for W in W_range:
for w in w_range:
conditions = Conditions(1e-3, W, f_r, w, endmill)
cut.cut(conditions, save = True, auto_layer=True)
cut.close()
\ No newline at end of file
conditions = Conditions(1e-3, W, f_r, 200, endmill)
cut.cut(conditions, save=True, auto_layer=True)
cut.close()
......@@ -16,8 +16,9 @@ class MachineCrash(Exception):
class Cut:
def __init__(self, machine_port, spindle_port, tfd_port, endmill, x_max, y_max, f_r_clearing, w_clearing, initial_z=0, save_as=None, graceful_shutdown = False):
self.machine = Machine(machine_port, graceful_shutdown = graceful_shutdown)
def __init__(self, machine_port, spindle_port, tfd_port, endmill, x_max, y_max, f_r_clearing, w_clearing, initial_z=0, save_as=None, graceful_shutdown=False):
self.machine = Machine(
machine_port, graceful_shutdown=graceful_shutdown)
self.machine.unlock()
self.machine.zero()
self.spindle = Spindle_Applied(spindle_port)
......@@ -29,7 +30,7 @@ class Cut:
self.x_max = x_max
self.y_max = y_max
self.cut_x = 0
self.cut_z = -initial_z # initial z in positive units
self.cut_z = -initial_z # initial z in positive units
self.D = 0
self.f_r_clearing = f_r_clearing
self.w_clearing = w_clearing
......@@ -47,6 +48,9 @@ class Cut:
self.spindle.set_w(0)
def warmup_spindle(self):
"""
Just spins the spindle for a little while to get the bearings warmed up.
"""
log.info("Warming up spindle")
self.spindle.set_w(300)
time.sleep(30)
......@@ -60,16 +64,16 @@ class Cut:
D: Depth of cut for this layer.
f_r_clearing: feedrate used for this clearing pass.
w_clearing: spindle speed used for this clearing pass.
"""
log.info("Preparing to face layer to depth " + str(D) +
" at feedrate " + str(self.f_r_clearing) + " with speed " + str(self.w_clearing))
" at feedrate " + str(self.f_r_clearing) + " with speed " + str(self.w_clearing))
# define next cut
self.D = D
self.cut_z -= D
cuts = np.append(np.arange(self.X_START, self.X_END, 1.8 * self.endmill.r_c), self.X_END)
cuts = np.append(np.arange(self.X_START, self.X_END,
1.8 * self.endmill.r_c), self.X_END)
# perform cut
self.spindle.set_w(self.w_clearing)
self.machine.rapid({'x': self.X_START, 'y': self.Y_START})
......@@ -100,8 +104,6 @@ class Cut:
f_r_clearing: feedrate used for this clearing pass.
w_clearing: spindle speed used for this clearing pass.
Returns:
A data blob from this operation.
"""
log.info("Preparing to clear layer to depth " + str(D) +
" at feedrate " + str(self.f_r_clearing) + " with speed " + str(self.w_clearing))
......@@ -122,12 +124,21 @@ class Cut:
log.info("Layer prepared for clearing")
def cut(self, conditions, save = True, auto_layer = True):
"""
Performs a stroke of facing. Returns a data blob.
def cut(self, conditions, save=True, auto_layer=True):
"""
Performes a new cut using the conditions provided.
Args:
conditions (Conditions): The conditions for this cut
save (bool, optional): Whether or not to save this cut. Defaults to True.
auto_layer (bool, optional): Whether or not to advance to the next layer if you hit X bounds. Defaults to True.
Raises:
MachineCrash: If auto_layer is false and you reach the end travel
Returns:
Data: A data blob for this cut
"""
_, W, f_r, w, _ = conditions.unpack()
self.spindle.set_w(w)
......@@ -142,13 +153,13 @@ class Cut:
self.machine.cut({'y': self.Y_END}, self.f_r_clearing)
self.machine.rapid({'z': self.cut_z + self.D + 1e-3})
self.machine.hold_until_still()
# start next layer
self.begin_layer(self.D)
log.info("Actually performing cut now.")
# try again, return result
return self.cut(conditions, save = save, auto_layer=False)
return self.cut(conditions, save=save, auto_layer=False)
else:
raise MachineCrash(
"Cutting too far in X direction: X = " + str(X_START))
......@@ -204,7 +215,7 @@ class Cut:
db[self.save_as] = existing
else:
db[self.save_as] = [data]
db.sync()
log.info("Data saved under name " + self.save_as)
......@@ -231,4 +242,3 @@ class Cut:
if __name__ == "__main__":
log.info("Hi")
This diff is collapsed.
......@@ -8,6 +8,7 @@ from ml import UnifiedLinearModel
import logging
log = logging.getLogger(__name__)
class Fake_Cut:
"""
Fake cutting process. Returns results using prebaked parameters and specified noise levels.
......@@ -37,6 +38,14 @@ class Fake_Cut:
pass
def cut(self, conditions: Conditions, *args, **kwargs):
"""Performs a simulated cut with noise
Args:
conditions (Conditions): Conditions for this fake cut
Returns:
Data: A fake data blob with noise
"""
# use prediction as output
T = self.T_func(conditions, *self.params)
_, Fy = self.F_func(conditions, *self.params)
......@@ -49,7 +58,8 @@ class Fake_Cut:
# generate fake times
t = np.linspace(0, 1, 100)
# return fake reading
data = Data(*conditions.unpack(), np.array([t, T_noisy]).T, np.array([t, Fy_noisy]).T)
data = Data(*conditions.unpack(),
np.array([t, T_noisy]).T, np.array([t, Fy_noisy]).T)
return data
def scale_coefs(self, scale):
......@@ -62,4 +72,4 @@ class ReplayCut(Fake_Cut):
with shelve.open(os.path.join("saved_cuts", "db")) as db:
data = db[replay_data]
self.model.ingest_data(data)
super().__init__(self.model.params, T_func, F_func, error, noise)
\ No newline at end of file
super().__init__(self.model.params, T_func, F_func, error, noise)
......@@ -9,12 +9,15 @@ from models import T_lin, F_lin, T_lin_full, F_lin_full, T_x_vector, T_x_vector_
from objects import Data, Conditions, EndMill, Prediction
# https://stackoverflow.com/questions/11686720
def mean_no_outliers(data, m=2):
d = np.abs(data - np.median(data))
mdev = np.median(d)
s = d / (mdev if mdev else 1.)
return np.mean(data[s < m])
class Model(abc.ABC):
"""
Represents a model that can be trained with one training datum at a time.
......@@ -36,6 +39,11 @@ class Model(abc.ABC):
def predict_one(self, conditions):
"""
Predicts milling forces using the model as it currently is.
Args:
conditions: a single condition object
Return:
A Prediction using the model in its current state
"""
pass
......@@ -43,61 +51,14 @@ class Model(abc.ABC):
return list(map(self.predict_one, conditions))
class LinearModel(Model):
def __init__(self, initial_params = [0, 0, 0, 0]):
self.training_T_x = list()
self.training_T_y = list()
self.training_Fy_x = list()
self.training_Fy_y = list()
self.regressor_T = linear_model.LinearRegression(fit_intercept=False)
self.regressor_Fy = linear_model.LinearRegression(fit_intercept=False)
self.params = initial_params
def ingest_datum(self, datum):
# decompose
_, _, _, _, _, Ts, Fys = datum.unpack()
T, Fy = mean_no_outliers(Ts[:, 1]), mean_no_outliers(Fys[:, 1])
# get linear coefficients
T_x = T_x_vector(datum.conditions())
Fy_x = Fy_x_vector(datum.conditions())
# add to training set
self.training_T_x.append(T_x)
self.training_T_y.append(T)
self.training_Fy_x.append(Fy_x)
self.training_Fy_y.append(Fy)
self.update()
def update(self):
# convert force into numpy arrays for convenience
training_Fy_x = np.array(self.training_Fy_x)
training_Fy_y = np.array(self.training_Fy_y)
# calculate best fit from data
self.regressor_T.fit(self.training_T_x, self.training_T_y)
K_tc, K_te = self.regressor_T.coef_
self.params[0], self.params[1] = K_tc, K_te
# transform Fy into a smaller linear problem and fit
intercepts = training_Fy_x @ np.array([K_tc, K_te, 0, 0])[np.newaxis].T
training_Fy_y_no_intercepts = np.reshape(
training_Fy_y - intercepts.T, (-1))
self.regressor_Fy.fit(
training_Fy_x[:, 2:], training_Fy_y_no_intercepts)
K_rc, K_re = self.regressor_Fy.coef_
self.params[2], self.params[3] = K_rc, K_re
def predict_one(self, conditions):
# evaluate
T = T_lin(conditions, *self.params)
F = F_lin(conditions, *self.params)
class UnifiedLinearModel(Model):
"""A model that uses a simple linear regressor.
# repack and return
return Prediction(*conditions.unpack(), T, F)
Args:
initial_params: Initial parameters to use with this model. Helps with bootstrapping.
"""
class UnifiedLinearModel(Model):
def __init__(self, initial_params = [0,0,0,0]):
def __init__(self, initial_params=[0, 0, 0, 0]):
self.training_Tx = list()
self.training_Ty = list()
self.training_Fyx = list()
......@@ -136,7 +97,8 @@ class UnifiedLinearModel(Model):
def update(self):