Skip to content

Commit

Permalink
- Optimization algorithm for minimizing rapid motions
Browse files Browse the repository at this point in the history
  • Loading branch information
vlachoudis committed Sep 8, 2015
1 parent 23b32de commit 9153315
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 144 deletions.
104 changes: 67 additions & 37 deletions CNC.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
from bpath import Path, Segment
from bmath import *

import Genetic
from tsp import TSPIndividual

IDPAT = re.compile(r".*\bid:\s*(.*?)\)")
PARENPAT = re.compile(r"(.*)(\(.*?\))(.*)")
OPPAT = re.compile(r"(.*)\[(.*)\]")
Expand Down Expand Up @@ -1047,7 +1044,7 @@ def __init__(self, name=None):
self.enable = True # Enabled/Visible in drawing
self.expand = False # Expand in editor
self._path = [] # canvas drawing paths
self.sx = self.sy = self.sz = 0 # start coordinates
self.sx = self.sy = self.sz = 0 # start coordinates (entry point first non rapid motion)
self.ex = self.ey = self.ez = 0 # ending coordinates

#----------------------------------------------------------------------
Expand Down Expand Up @@ -1155,8 +1152,11 @@ def path(self, i):
return self._path[i]

#----------------------------------------------------------------------
def resetPath(self, x, y, z):
def resetPath(self):
del self._path[:]

#----------------------------------------------------------------------
def startPath(self, x, y, z):
self.sx = x
self.sy = y
self.sz = z
Expand Down Expand Up @@ -1691,6 +1691,18 @@ def swapBlockUndo(self, a, b):
self.blocks[b] = tmp
return undoinfo

#----------------------------------------------------------------------
# Move block from location src to location dst
#----------------------------------------------------------------------
def moveBlockUndo(self, src, dst):
if src == dst: return undo.NullUndo
undoinfo = (self.moveBlockUndo, dst, src)
if dst > src:
self.blocks.insert(dst-1, self.blocks.pop(src))
else:
self.blocks.insert(dst, self.blocks.pop(src))
return undoinfo

#----------------------------------------------------------------------
# Invert selected blocks
#----------------------------------------------------------------------
Expand Down Expand Up @@ -1918,8 +1930,10 @@ def initPath(self, bid):
if bid == 0:
self.cnc.initPath()
else:
block = self.blocks[bid]
self.cnc.initPath(block.sx, block.sy, block.sz)
# Use the ending point of the previous block
# since the starting (sxyz is after the rapid motion)
block = self.blocks[bid-1]
self.cnc.initPath(block.ex, block.ey, block.ez)

#----------------------------------------------------------------------
# Move blocks/lines up
Expand Down Expand Up @@ -2403,43 +2417,59 @@ def removeNlines(self, items):
# rapid movements.
#----------------------------------------------------------------------
def optimize(self, items):
# create a list of blocks and their entry and exit coordinates
n = len(items)

matrix = []
for i in range(n):
matrix.append([0.0] * n)

# Find distances between blocks (end to start)
for i in range(n):
block = self.blocks[items[i]]
x1 = block.ex
y1 = block.ey
for j in range(n):
if i==j: continue
block = self.blocks[items[j]]
x2 = block.sx
y2 = block.sy
dx = x1-x2
dy = y1-y2
matrix[i][j] = sqrt(dx*dx + dy*dy)
#from pprint import pprint
#pprint(matrix)

best = [0]
unvisited = range(1,n)
while unvisited:
last = best[-1]
row = matrix[last]
# from all the unvisited places search the closest one
mindist = 1e30
for i,u in enumerate(unvisited):
d = row[u]
if d < mindist:
mindist = d
si = i
best.append(unvisited.pop(si))
#print "best=",best

blocks = []
del TSPIndividual.coords[:]

for bid in items:
blocks.append(bid)
block = self.blocks[bid]
TSPIndividual.coords.append(((block.sx,block.sy), (block.ex,block.ey)))

sys.stdout.write("optimize=%s\n"%(str(blocks)))
sys.stdout.write("coords=%s\n"%(str(TSPIndividual.coords)))

TSPIndividual.prepareMatrix()

# FIXME Do I need the same random seed all the time?
random.seed(1234) # ???

env = Genetic.Environment(TSPIndividual,
size=80,
maxgenerations=100,
parallel=0,
optimum=0.,
crossover_rate=1.0,
mutation_rate=0.03)
env.random = 1
env.reportEvery(100)
env.run()
sys.stdout.write("Best=%s\n"%(str(env.best())))
#write_tour_to_img(TSPIndividual.coords, env.best(), "tsp_result.png")
undoinfo = []
for i in range(len(best)):
b = best[i]
if i==b: continue
ptr = best.index(i)
# swap i,b in items
undoinfo.append(self.swapBlockUndo(items[i], items[b]))
# swap i,ptr in best
best[i], best[ptr] = best[ptr], best[i]
self.addUndo(undoinfo, "Optimize")

#----------------------------------------------------------------------
# Use probe information to modify the g-code to autolevel
#----------------------------------------------------------------------
def compile(self):
autolevel = not self.probe.isEmpty()

lines = []
paths = []
for i,block in enumerate(self.blocks):
Expand Down
7 changes: 6 additions & 1 deletion CNCCanvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,8 @@ def draw(self, view=None): #, lines):
self.initPosition()
drawG = self.draw_rapid or self.draw_paths or self.draw_margin
for i,block in enumerate(self.gcode.blocks):
block.resetPath(self.cnc.x, self.cnc.y, self.cnc.z)
block.resetPath()
start = True # start location found
for j,line in enumerate(block):
#cmd = self.cnc.parseLine(line)
try:
Expand All @@ -777,6 +778,10 @@ def draw(self, view=None): #, lines):
path = self.drawPath(cmd, block.enable)
self._items[path] = i,j
block.addPath(path)
if start and self.cnc.gcode in (1,2,3):
# Mark as start the first non-rapid motion
block.startPath(self.cnc.x, self.cnc.y, self.cnc.z)
start = False
block.endPath(self.cnc.x, self.cnc.y, self.cnc.z)

self.drawGrid()
Expand Down
44 changes: 0 additions & 44 deletions ControlPage.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,50 +98,6 @@ def __init__(self, master, app):
tkExtra.Balloon.set(b, "Software reset of controller [ctrl-x]")
self.addWidget(b)

##===============================================================================
## Serial Group
##===============================================================================
#class SerialGroup(CNCRibbon.ButtonGroup):
# def __init__(self, master, app):
# CNCRibbon.ButtonGroup.__init__(self, master, "Serial", app)
# self.grid3rows()
#
# col,row=0,0
# b = Label(self.frame, text="Port:", background=Ribbon._BACKGROUND)
# b.grid(row=row,column=col,sticky=E)
# self.addWidget(b)
#
# app.portCombo = tkExtra.Combobox(self.frame, False, background="White", width=16)
# app.portCombo.grid(row=row, column=col+1, sticky=EW)
# devices = sorted([x[0] for x in comports()])
# app.portCombo.fill(devices)
# app.portCombo.set(Utils.config.get("Connection","port"))
# self.addWidget(app.portCombo)
#
# row += 1
# b = Label(self.frame, text="Baud:", background=Ribbon._BACKGROUND)
# b.grid(row=row,column=col,sticky=E)
#
# app.baudCombo = tkExtra.Combobox(self.frame, True, background="White")
# app.baudCombo.grid(row=row, column=col+1, sticky=EW)
# app.baudCombo.fill(BAUDS)
# app.baudCombo.set(Utils.config.get("Connection","baud"))
# self.addWidget(app.baudCombo)
#
# # ---
# col += 2
# row = 0
#
# app.connectBtn = Ribbon.LabelButton(self.frame,
# image=Utils.icons["serial32"],
# text="Open",
# compound=TOP,
# command=app.openClose,
# background=Ribbon._BACKGROUND)
# app.connectBtn.grid(row=row, column=col, rowspan=2, padx=0, pady=0, sticky=NSEW)
# tkExtra.Balloon.set(app.connectBtn, "Open/Close serial port")
# self.addWidget(app.connectBtn)

#===============================================================================
# User Group
#===============================================================================
Expand Down
37 changes: 18 additions & 19 deletions FilePage.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,25 +223,25 @@ def __init__(self, master, app):
b.grid(row=row,column=col,sticky=E)
self.addWidget(b)

app.portCombo = tkExtra.Combobox(self, False, background="White", width=16)
app.portCombo.grid(row=row, column=col+1, sticky=EW)
tkExtra.Balloon.set(app.portCombo, "Select (or manual enter) port to connect")
self.portCombo = tkExtra.Combobox(self, False, background="White", width=16)
self.portCombo.grid(row=row, column=col+1, sticky=EW)
tkExtra.Balloon.set(self.portCombo, "Select (or manual enter) port to connect")
devices = sorted([x[0] for x in comports()])
app.portCombo.fill(devices)
app.portCombo.set(Utils.getStr("Connection","port"))
self.addWidget(app.portCombo)
self.portCombo.fill(devices)
self.portCombo.set(Utils.getStr("Connection","port"))
self.addWidget(self.portCombo)

# ---
row += 1
b = Label(self, text="Baud:", background=Ribbon._BACKGROUND)
b.grid(row=row,column=col,sticky=E)

app.baudCombo = tkExtra.Combobox(self, True, background="White")
app.baudCombo.grid(row=row, column=col+1, sticky=EW)
tkExtra.Balloon.set(app.baudCombo, "Select connection baud rate")
app.baudCombo.fill(BAUDS)
app.baudCombo.set(Utils.getStr("Connection","baud","115200"))
self.addWidget(app.baudCombo)
self.baudCombo = tkExtra.Combobox(self, True, background="White")
self.baudCombo.grid(row=row, column=col+1, sticky=EW)
tkExtra.Balloon.set(self.baudCombo, "Select connection baud rate")
self.baudCombo.fill(BAUDS)
self.baudCombo.set(Utils.getStr("Connection","baud","115200"))
self.addWidget(self.baudCombo)

# ---
row += 1
Expand All @@ -256,22 +256,21 @@ def __init__(self, master, app):
col += 2
row = 0

app.connectBtn = Ribbon.LabelButton(self,
self.connectBtn = Ribbon.LabelButton(self,
image=Utils.icons["serial32"],
text="Open",
compound=TOP,
command=app.openClose,
command=lambda s=self : s.event_generate("<<Connect>>"),
background=Ribbon._BACKGROUND)
app.connectBtn.grid(row=row, column=col, rowspan=2, padx=0, pady=0, sticky=NSEW)
tkExtra.Balloon.set(app.connectBtn, "Open/Close serial port")

self.connectBtn.grid(row=row, column=col, rowspan=2, padx=0, pady=0, sticky=NSEW)
tkExtra.Balloon.set(self.connectBtn, "Open/Close serial port")
self.grid_columnconfigure(1, weight=1)

#-----------------------------------------------------------------------
def saveConfig(self):
# Connection
Utils.setStr("Connection", "port", self.app.portCombo.get())
Utils.setStr("Connection", "baud", self.app.baudCombo.get())
Utils.setStr("Connection", "port", self.portCombo.get())
Utils.setStr("Connection", "baud", self.baudCombo.get())
Utils.setBool("Connection", "openserial", self.autostart.get())

#===============================================================================
Expand Down
Loading

0 comments on commit 9153315

Please sign in to comment.