21
21
| | __ | | || || |
22
22
o-----> X o-----> Y |__| o-----> X
23
23
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
+
24
31
'''
25
32
26
33
import pyAPT
27
34
import threading
28
35
import time
29
36
import yaml
37
+ import sys
38
+ from math import *
30
39
from runner import runner_serial
40
+ from matplotlib import pyplot as plt
41
+ from mpl_toolkits .mplot3d import Axes3D
31
42
32
43
class LinearStage (object ):
33
44
@@ -53,6 +64,15 @@ def __init__(self):
53
64
self .DOWN = 0
54
65
self .UP = 1
55
66
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
+
56
76
def getInfoAxis (self , axis ):
57
77
con = pyAPT .MTS50 (serial_number = axis )
58
78
ret = con .info ()
@@ -132,7 +152,7 @@ def getPos(self, axis = None):
132
152
return [posX , posY , posZ ]
133
153
134
154
'''
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).
136
156
'''
137
157
def goHome (self ):
138
158
# Move to home position of the stage
@@ -157,24 +177,33 @@ def goHome(self):
157
177
self .moveAbsolute (0 , 0 , 0 )
158
178
159
179
'''
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.
163
185
'''
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
+
165
190
# 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 )
168
194
print ('OK' )
169
195
170
196
# Setting the initial direction of the X (k) and Y(j) axes
171
197
kDir = self .RIGHT
172
198
jDir = self .DOWN
173
199
174
200
# 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()
178
207
179
208
# Looping through the workspace in a raster fashion
180
209
while (i <= self .MAX_DIST ):
@@ -193,7 +222,11 @@ def rasterScan(self, step, delayMs):
193
222
kDir = self .RIGHT
194
223
while (k >= 0 and k <= self .MAX_DIST ):
195
224
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 ...' )
197
230
if kDir == self .RIGHT :
198
231
k += step
199
232
else :
@@ -205,17 +238,94 @@ def rasterScan(self, step, delayMs):
205
238
i += step
206
239
207
240
'''
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.
213
252
'''
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
216
325
217
326
'''
218
- TODO
327
+ @brief Moving X axis of the stage to the position x (mm)
328
+ @param[in] x Goal position in mm.
219
329
'''
220
330
def moveAbsoluteX (self , x ):
221
331
x = float (self .MAX_DIST ) - x
@@ -228,7 +338,8 @@ def moveAbsoluteX(self, x):
228
338
con .close ()
229
339
230
340
'''
231
- TODO
341
+ @brief Moving Y axis of the stage to the position y (mm)
342
+ @param[in] y Goal position in mm.
232
343
'''
233
344
def moveAbsoluteY (self , y ):
234
345
con = pyAPT .MTS50 (serial_number = self .Y_AXIS_SN )
@@ -240,7 +351,8 @@ def moveAbsoluteY(self, y):
240
351
con .close ()
241
352
242
353
'''
243
- TODO
354
+ @brief Moving Z axis of the stage to the position z (mm)
355
+ @param[in] z Goal position in mm.
244
356
'''
245
357
def moveAbsoluteZ (self , z ):
246
358
z = float (self .MAX_DIST ) - z
@@ -253,11 +365,11 @@ def moveAbsoluteZ(self, z):
253
365
con .close ()
254
366
255
367
'''
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.
261
373
'''
262
374
def moveAbsolute (self , x , y , z ):
263
375
#tx = threading.Thread(target = self.moveAbsoluteX(x))
@@ -274,11 +386,11 @@ def moveAbsolute(self, x, y, z):
274
386
self .moveAbsoluteZ (z )
275
387
276
388
'''
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
282
394
'''
283
395
def moveRelative (self , x , y , z ):
284
396
return 0
0 commit comments