Source code for classes.xyzstage

# XYZ-Stage Class

import threading
import numpy as np
import logging
import math

# helper functions to add/subtract tuples
[docs]def t_add(tuple1, tuple2): return tuple(np.add(tuple1, tuple2))
[docs]def t_sub(tuple1, tuple2): return tuple(np.subtract(tuple1, tuple2))
##
[docs]class XYZ_Stage(object): def __init__(self, mtr_list,off_angle=0): ''' Constructor ser_list (list): a list with serial objects corresponding to the x, y and z motors respectively ''' # # new threading approach # def init_x_axis(): # self.x_axis = StepMotor(ser_list[0]) # def init_y_axis(): # self.y_axis = StepMotor(ser_list[1]) # def init_z_axis(): # self.z_axis = StepMotor(ser_list[2]) # # t_x = threading.Thread(target=init_x_axis) # t_y = threading.Thread(target=init_y_axis) # t_z = threading.Thread(target=init_z_axis) # # t_x.start(), t_y.start(), t_z.start() # # t_x.join(), t_y.join(), t_z.join() self.x_axis = mtr_list[0] self.y_axis = mtr_list[1] self.z_axis = mtr_list[2] self.x, self.y, self.z = (0, 0, 0) self.zeros = (0, 0, 0) self.stepsize = 0.001 # virtual leveling helpers self.ang_x = None self.ang_y = None # offset angle of x and y stages from optimal axis self.off_angle = float(off_angle) def set_off_angle(self,off_angle): self.off_angle = float(off_angle) def get_off_angle(self): return self.off_angle def get_calibrated(self): ''' Check whether the stage was calibrated to chip by checking whether zeros are unequal 0 ''' if self.zeros == (0,0,0): return False else: return True
[docs] def home(self): '''Sets all of step-motors to home position''' # Create threads for homing x = threading.Thread(self.x_axis.home()) y = threading.Thread(self.y_axis.home()) z = threading.Thread(self.z_axis.home()) # Starts x-direction, then y-direction and z-direction x.start(); y.start(), z.start() # Reset isntance variables self.x, self.y, self.z = (0, 0, 0) self.zeros = (0, 0, 0) self.stepsize = 0.001
[docs] def check_coordinates_2d(self, target_pos): ''' Check whather move to a point in space is possible within actuator range target_pos (2-tuple): (x,y) coordinates of the final point ''' return self.check_coordinates((target_pos[0], target_pos[1], self.z))
[docs] def check_coordinates(self, target_pos): ''' Check whather move to a point in space is possible within actuator range target_pos (3-tuple): (x,y,z) coordinates of the final point ''' act_x, act_y, act_z = t_add(target_pos, self.zeros) if self.x_axis.check_abs_transl(act_x) and self.y_axis.check_abs_transl(act_y) and self.z_axis.check_abs_transl(act_z): return True else: return False
[docs] def set_coordinates(self, target_pos, long=False): ''' Move to a point in the plane target_pos (3-tuple): (x,y,z) coordinates of the final point ''' # actual distances sent to StepMotor act_x, act_y, act_z = t_add(target_pos, self.zeros) if not self.x_axis.moving and not self.y_axis.moving and not self.z_axis.moving and self.x_axis.check_abs_transl(act_x) and self.y_axis.check_abs_transl(act_y) and self.z_axis.check_abs_transl(act_z): x_d = target_pos[0] - self.x y_d = target_pos[1] - self.y self.x, self.y, self.z = target_pos z_extra = 0 if long : if self.leveled(): z_extra += x_d * math.tan(math.radians(self.ang_x)) + y_d * math.tan(math.radians(self.ang_y)) self.z += z_extra act_z += z_extra # threading approach t_x = threading.Thread(target=self.x_axis.abs_transl, args=(act_x,)) t_y = threading.Thread(target=self.y_axis.abs_transl, args=(act_y,)) t_z = threading.Thread(target=self.z_axis.abs_transl, args=(act_z,)) if z_extra > 0: t_x.start(), t_y.start() t_x.join(), t_y.join() t_z.start() t_z.join() elif z_extra < 0: t_z.start() t_z.join() t_x.start(), t_y.start() t_x.join(), t_y.join() else: t_x.start(), t_y.start(), t_z.start() t_x.join(), t_y.join(), t_z.join() else: print 'XYZ_stage::error: Attempted move out of bounds (abs_pos:{},{},{})'.format(act_x, act_y, act_z)
[docs] def set_coor_2d(self, target_pos, long=False): ''' Move to a point in the plane target_pos (2-tuple): (x,y) coordinates of the final point ''' self.set_coordinates((target_pos[0], target_pos[1], self.z), long)
[docs] def set_stepsize(self,stepsize): ''' Updates the stepsize used in step() function ''' self.stepsize = stepsize
[docs] def step(self, direction): ''' - Move Stage in either direction by one step - Step Size defined in set_stepsize() - direction expected to be char: U(p),D(own) L(eft),R(ight) F(orward),B(ackward) ''' if self.x_axis.moving == False: if direction == 'F': if self.x_axis.delta_transl(self.stepsize): self.x += self.stepsize if direction == 'B': if self.x_axis.delta_transl(-self.stepsize): self.x -= self.stepsize if self.y_axis.moving == False: if direction == 'L': if self.y_axis.delta_transl(self.stepsize): self.y += self.stepsize if direction == 'R': if self.y_axis.delta_transl(-self.stepsize): self.y -= self.stepsize if self.z_axis.moving == False: if direction == 'U': if self.z_axis.delta_transl(-self.stepsize): self.z -= self.stepsize if direction == 'D': if self.z_axis.delta_transl(self.stepsize): self.z += self.stepsize
[docs] def get_coordinates(self): ''' Returns the current position of the XYZ-Stage relative to the set origin ''' return (self.x, self.y, self.z)
[docs] def get_coor_2d(self): ''' Returns the current position of the XYZ-Stage relative to the set origin ''' return (self.x, self.y)
[docs] def get_real_coordinates(self): ''' Returns the actual position of the XYZ-Stage (relative to homing) ''' return t_add((self.x, self.y, self.z), self.zeros)
[docs] def set_as_zero(self, new_zero): ''' Change the origin new_zero (3-tuple): (x,y,z) coordinates of the new origin ''' actual = t_add((self.x, self.y, self.z), self.zeros) self.x, self.y, self.z = t_sub(actual, new_zero) self.zeros = new_zero
[docs] def set_cur_as(self, correct_pos): ''' Changes the coordinates of the current position, esentially computing the new origin. Does NOT involve any motor movement correct_pos (3-tuple): (x,y,z) coordinates of the "corect" position ''' zero_trans = t_sub((self.x, self.y, self.z), correct_pos) self.set_as_zero(t_add(self.zeros, zero_trans))
[docs] def set_cur_as_2d(self, correct_pos): ''' Changes the coordinates of the current position, esentially computing the new origin. Does NOT involve any motor movement correct_pos (2-tuple): (x,y) coordinates of the "corect" position ''' correct_pos_3 = (correct_pos[0], correct_pos[1], self.z) self.set_cur_as(correct_pos_3)
[docs] def set_level(self, ang_x, ang_y): ''' set variables for virtual leveling Note: the axes are relative to the stage ang_x (float): angle in x-axis ang_y (float): anfle in y-axis ''' self.ang_x = ang_x self.ang_y = ang_y
[docs] def leveled(self): ''' Check if virtual leveling parameters have been set ''' x = self.ang_x == None y = self.ang_y == None if not x : if y: raise AssertionError('Incorrect virtual level initialization') else: return True else: return False
[docs] def close(self): ''' Closes the underlying motors ''' self.x_axis.close() self.y_axis.close() self.z_axis.close()
''' Copyright (C) 2017 Robert Polster This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. '''