Skip to content

Commit ffa4da1

Browse files
author
Luis Carlos Garcia-Peraza Herrera
committed
Raster scan and cylindrical scan working. Just some minor fixes needed.
1 parent 31c6f82 commit ffa4da1

File tree

2 files changed

+144
-32
lines changed

2 files changed

+144
-32
lines changed

configfile.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ Y_AXIS_SN: '83854474'
44
Z_AXIS_SN: '83853018'
55

66
# Scanning Distance Parameters
7-
MAX_DIST: 50 # mm
7+
MAX_DIST: 50 # mm
88
ENCODER_SCALE: 24576

linearstage.py

+143-31
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,24 @@
2121
| | __ | | || || |
2222
o-----> X o-----> Y |__| o-----> X
2323
24+
Pre-requisites:
25+
26+
1) pip install --upgrade matplotlib
27+
2) pip install --upgrade mpl_toolkits
28+
3) Run this command before executing this script:
29+
export PYTHONPATH=/Library/Python/2.7/site-packages
30+
2431
'''
2532

2633
import pyAPT
2734
import threading
2835
import time
2936
import yaml
37+
import sys
38+
from math import *
3039
from runner import runner_serial
40+
from matplotlib import pyplot as plt
41+
from mpl_toolkits.mplot3d import Axes3D
3142

3243
class LinearStage(object):
3344

@@ -53,6 +64,15 @@ def __init__(self):
5364
self.DOWN = 0
5465
self.UP = 1
5566

67+
# Plotting stuff
68+
self.fig = plt.figure()
69+
plt.ion()
70+
self.ax = self.fig.gca(projection = '3d')
71+
self.ax.set_xlim3d(0, self.MAX_DIST)
72+
self.ax.set_ylim3d(0, self.MAX_DIST)
73+
self.ax.set_zlim3d(0, self.MAX_DIST)
74+
# self.ax.view_init(elev = 45, azim = 90)
75+
5676
def getInfoAxis(self, axis):
5777
con = pyAPT.MTS50(serial_number = axis)
5878
ret = con.info()
@@ -132,7 +152,7 @@ def getPos(self, axis = None):
132152
return [posX, posY, posZ]
133153

134154
'''
135-
@brief Sends the 3D linear stage to the position (0, 0, 0).
155+
@brief Sends the 3D linear stage to the position (0, 0, 0).
136156
'''
137157
def goHome(self):
138158
# Move to home position of the stage
@@ -157,24 +177,33 @@ def goHome(self):
157177
self.moveAbsolute(0, 0, 0)
158178

159179
'''
160-
@brief This method performs a 3D raster scan.
161-
@param[in] step Increment in microns from point to point.
162-
@param[in] delayMs Time delay after each position has been reached. It stabilises the movement.
180+
@brief This method performs a 3D raster scan.
181+
@param[in] step Increment in microns from point to point.
182+
@param[in] delay Seconds of delay after each position has been reached.
183+
FIXME: show points in graph at the same time that the stage is moving.
184+
FIXME: rotate the 3D view so that it is equivalent to the real coordinate frame.
163185
'''
164-
def rasterScan(self, step, delayMs):
186+
def rasterScan(self, step, delay):
187+
# FIXME: reset plot if it was already opened
188+
# plt.close()
189+
165190
# Going home to reset the encoders
166-
print('Homing... ', end = '', flush = True)
167-
self.goHome()
191+
sys.stdout.write('Homing... ')
192+
sys.stdout.flush()
193+
self.moveAbsolute(0, 0, 0)
168194
print('OK')
169195

170196
# Setting the initial direction of the X (k) and Y(j) axes
171197
kDir = self.RIGHT
172198
jDir = self.DOWN
173199

174200
# Initialising iterators
175-
i = 0
176-
j = 0
177-
k = 0
201+
i = 0.0
202+
j = 0.0
203+
k = 0.0
204+
205+
# Showing the window with the plot of the points
206+
# plt.show()
178207

179208
# Looping through the workspace in a raster fashion
180209
while (i <= self.MAX_DIST):
@@ -193,7 +222,11 @@ def rasterScan(self, step, delayMs):
193222
kDir = self.RIGHT
194223
while (k >= 0 and k <= self.MAX_DIST):
195224
self.moveAbsolute(k, j, i)
196-
time.sleep(delayMs / 1000)
225+
print('Current position: %6.3f %6.3f %6.3f' % (k, j, i))
226+
# self.ax.scatter(k, j, i, zdir = 'z', c = 'red')
227+
# plt.draw()
228+
time.sleep(delay)
229+
print('Moving to next position ...')
197230
if kDir == self.RIGHT:
198231
k += step
199232
else:
@@ -205,17 +238,94 @@ def rasterScan(self, step, delayMs):
205238
i += step
206239

207240
'''
208-
@brief TODO
209-
@param[in] stepX TODO
210-
@param[in] stepY TODO
211-
@param[in] stepZ TODO
212-
@param[in] delayMs TODO
241+
@brief Cylindrical scan starting from the floor and going up. For each height level it
242+
performs (self.MAX_DIST / step) circles. The scanning is clockwise. The spacing
243+
between the points of each circle is maintained the same regardless the distance
244+
to the centre of the cylinder. That is, the external circles have more points
245+
than the internal ones to maintain the same spacing.
246+
@param[in] stepAngle Initial angle of separation between points. It is a ratio of pi.
247+
@param[in] step Increment of the radius of the circle for each step of scanning.
248+
It is also used for the increment in the z axis. It is a ratio of
249+
the maximum distance.
250+
@param[in] delay Delay in seconds after a position has been reached.
251+
FIXME: rotate the 3D view so that it is equivalent to the real coordinate frame.
213252
'''
214-
def spiralScan(self, stepX, stepY, stepZ, delayMs):
215-
return 0
253+
def cylindricalScan(self, stepAngle, step, delay):
254+
# Checking that the parameters are ratios
255+
if (stepAngle > 1 or step > 1):
256+
print('The step angle and the step must be lower than one because they are ratios.')
257+
258+
IN = 0
259+
OUT = 1
260+
stepAngle *= pi
261+
initialStepAngle = stepAngle
262+
phi = pi
263+
step *= self.MAX_DIST
264+
r = step
265+
z = 0
266+
rDir = OUT
267+
epsilon = 0.001
268+
269+
# FIXME: reset plot if it was already opened
270+
# plt.close()
271+
272+
# Going home to reset the encoders
273+
sys.stdout.write('Homing... ')
274+
sys.stdout.flush()
275+
# self.moveAbsolute(0, 0, 0)
276+
print('OK')
277+
278+
# Showing the window with the plot of the points
279+
plt.show()
280+
281+
# Looping around all the points of the cylinder
282+
while (z <= self.MAX_DIST):
283+
if r > self.MAX_DIST / 2:
284+
stepAngle = (stepAngle * r) / (r - step)
285+
r -= step
286+
rDir = IN
287+
else:
288+
stepAngle = initialStepAngle
289+
r = step
290+
rDir = OUT
291+
x = self.MAX_DIST / 2
292+
y = self.MAX_DIST / 2
293+
self.moveAbsolute(x, y, z)
294+
print('Current position: %6.3f %6.3f %6.3f' % (x, y, z))
295+
self.ax.scatter(x, y, z, zdir = 'z', c = 'red')
296+
plt.draw()
297+
time.sleep(delay)
298+
while ((r > step or abs(r - step) < epsilon) and (r < self.MAX_DIST / 2 or abs(r - self.MAX_DIST / 2) < epsilon)):
299+
while (phi > -pi):
300+
x = r * cos(phi) + self.MAX_DIST / 2
301+
y = r * sin(phi) + self.MAX_DIST / 2
302+
self.moveAbsolute(x, y, z)
303+
print('Current position: %6.3f %6.3f %6.3f' % (x, y, z))
304+
self.ax.scatter(x, y, z, zdir = 'z', c = 'red')
305+
plt.draw()
306+
time.sleep(delay)
307+
phi -= stepAngle
308+
if (rDir == IN):
309+
if (abs(r - step) > epsilon):
310+
stepAngle = (stepAngle * r) / (r - step)
311+
r -= step
312+
else:
313+
stepAngle = (stepAngle * r) / (r + step)
314+
r += step
315+
phi = pi
316+
if (rDir == IN):
317+
x = self.MAX_DIST / 2
318+
y = self.MAX_DIST / 2
319+
self.moveAbsolute(x, y, z)
320+
print('Current position: %6.3f %6.3f %6.3f' % (x, y, z))
321+
self.ax.scatter(x, y, z, zdir = 'z', c = 'red')
322+
plt.draw()
323+
time.sleep(delay)
324+
z += step
216325

217326
'''
218-
TODO
327+
@brief Moving X axis of the stage to the position x (mm)
328+
@param[in] x Goal position in mm.
219329
'''
220330
def moveAbsoluteX(self, x):
221331
x = float(self.MAX_DIST) - x
@@ -228,7 +338,8 @@ def moveAbsoluteX(self, x):
228338
con.close()
229339

230340
'''
231-
TODO
341+
@brief Moving Y axis of the stage to the position y (mm)
342+
@param[in] y Goal position in mm.
232343
'''
233344
def moveAbsoluteY(self, y):
234345
con = pyAPT.MTS50(serial_number = self.Y_AXIS_SN)
@@ -240,7 +351,8 @@ def moveAbsoluteY(self, y):
240351
con.close()
241352

242353
'''
243-
TODO
354+
@brief Moving Z axis of the stage to the position z (mm)
355+
@param[in] z Goal position in mm.
244356
'''
245357
def moveAbsoluteZ(self, z):
246358
z = float(self.MAX_DIST) - z
@@ -253,11 +365,11 @@ def moveAbsoluteZ(self, z):
253365
con.close()
254366

255367
'''
256-
@brief TODO
257-
@param[in] x TODO
258-
@param[in] y TODO
259-
@param[in] z TODO
260-
@param[in] delayMs TODO
368+
@brief Move the stage to the position x, y, z.
369+
@param[in] x Position of the x axis in mm.
370+
@param[in] y Position of the y axis in mm.
371+
@param[in] z Position of the z axis in mm.
372+
@param[in] delay Delay (in seconds) after each position has been reached.
261373
'''
262374
def moveAbsolute(self, x, y, z):
263375
#tx = threading.Thread(target = self.moveAbsoluteX(x))
@@ -274,11 +386,11 @@ def moveAbsolute(self, x, y, z):
274386
self.moveAbsoluteZ(z)
275387

276388
'''
277-
@brief TODO
278-
@param[in] x TODO
279-
@param[in] y TODO
280-
@param[in] z TODO
281-
@param[in] delayMs TODO
389+
@brief TODO
390+
@param[in] x TODO
391+
@param[in] y TODO
392+
@param[in] z TODO
393+
@param[in] delayMs TODO
282394
'''
283395
def moveRelative(self, x, y, z):
284396
return 0

0 commit comments

Comments
 (0)