In [1]:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from IPython.display import display
import ipywidgets as widgets
from __future__ import division
In [2]:
%matplotlib notebook
Lin Engineering
http://www.linengineering.com/wp-content/uploads/downloads/Silverpak_17C/documentation/Lin_Command_Manual.pdf
In [ ]:
import serial as s
import time
import yaml
# TODO: get current position for relative move
class Motor:
def __init__(self, config_file, init=True):
self.serial = s.Serial() # placeholder
f = open(config_file, 'r')
self.config = yaml.load(f)
f.close()
if init:
self.initialize()
def initialize(self):
self.serial = s.Serial(**self.config['serial']) # open serial connection
# TODO set moving current
# TODO set holding current
self.set_velocity(self.config['velocity_limit']) # set velocity
self.home() # move motor to home
def cmd(self, cmd_string, block=True):
full_string = self.config['prefix'] + cmd_string + self.config['terminator']
self.serial.write(full_string)
time.sleep(0.15) # TODO: monitor for response?
response = self.serial.read(self.serial.inWaiting()).decode('utf8', 'ignore')
while block and self.is_busy():
pass
return response
def is_busy(self):
cmd_string = 'Q'
time.sleep(0.05)
response = self.cmd(cmd_string, False)
return response.rfind('`') == -1
# velocity: (usteps/sec)
def set_velocity(self, velocity):
if velocity > self.config['velocity_limit']:
velocity = self.config['velocity_limit']
print 'ERR: Desired velocity exceeds velocity_limit; velocity now set to velocity_limit'
cmd_string = 'V{}R'.format(velocity)
return self.cmd(cmd_string)
def halt(self):
cmd_string = 'T'
self.cmd(cmd_string)
def home(self):
cmd_string = 'Z{}R'.format(self.config['ustep_max'])
return self.cmd(cmd_string)
def move(self, mm, block=True):
ustep = int(self.config['conv']*mm)
if ustep > self.config['ustep_max']:
ustep = self.config['ustep_max']
print 'ERR: Desired move to {} mm exceeds max of {} mm; moving to max instead'.format(mm, self.config['ustep_max']/self.config['conv'])
if ustep < self.config['ustep_min']:
ustep = self.config['ustep_min']
print 'ERR: Desired move to {} mm exceeds min of {} mm; moving to min instead'.format(mm, self.config['ustep_min']/self.config['conv'])
cmd_string = 'A{}R'.format(ustep)
return self.cmd(cmd_string, block)
def move_relative(self, mm):
ustep = int(self.config['conv']*mm)
ustep_current = int(self.config['ustep_max']/2) # TODO: limit movement (+ and -)
if mm >= 0:
if (ustep_current + ustep) > self.config['ustep_max']:
ustep = self.config['ustep_max'] - ustep_current
print 'ERR: Desired move of +{} mm exceeds max of {} mm; moving to max instead'.format(mm, self.config['ustep_max']/self.config['conv'])
cmd_string = 'P{}R'.format(ustep)
else:
if (ustep_current + ustep) < self.config['ustep_min']:
ustep = self.config['ustep_min'] - ustep_current
print 'ERR: Desired move of {} mm exceeds min of {} mm; moving to min instead'.format(mm, self.config['ustep_min']/self.config['conv'])
ustep = -1*ustep
cmd_string = 'D{}R'.format(ustep)
return self.cmd(cmd_string)
def where(self):
cmd_string = '?0'
ustep = self.cmd(cmd_string)
retrun float(ustep)/self.config['conv']
def exit(self):
self.serial.close()
In [ ]:
m = Motor('config/le_motor.yaml')
In [ ]:
m.serial.write('/1Q\r')
time.sleep(0.5)
m.serial.read(m.serial.inWaiting())
In [ ]:
m.cmd('Z1000R')
In [ ]:
print m.move(32)
time.sleep(1)
print m.move(20)
In [ ]:
print m.cmd('P100000D100000P100000D100000P100000D100000P100000D100000R')
print m.cmd('/1?0')
In [ ]:
m.exit()
Applied Scientific Instrumentation
http://www.asiimaging.com/downloads/manuals/Operations_and_Programming_Manual.pdf
In [ ]:
import serial as s
import time
import yaml
# TODO: Fix serial.read encoding
class ASI_Controller:
def __init__(self, config_file, init=True):
self.serial = s.Serial() # placeholder
f = open(config_file, 'r')
self.config = yaml.load(f)
f.close()
if init:
self.initialize()
def initialize(self):
self.serial = s.Serial(**self.config['serial']) # open serial connection
self.cmd_xy('mc x+ y+') # enable motor control for xy
self.cmd_z('mc z+') # enable motor control for z
print "Initializing stage..."
self.move_xy(2000, -2000) # move to switch limits (bottom right)
self.r_xy(-0.5, 0.5) # move from switch limits 0.5 mm
def cmd(self, cmd_string):
full_string = self.config['prefix'] + cmd_string + self.config['terminator']
self.serial.write(full_string)
time.sleep(0.05)
response = self.serial.read(self.serial.inWaiting())
return response
def halt(self):
self.halt_xy()
self.halt_z()
# XY ----------------------------------------------
def cmd_xy(self, cmd_string, block=True):
full_string = '2h ' + cmd_string
response = self.cmd(full_string)
while block and self.is_busy_xy():
time.sleep(0.05)
pass
return response
def is_busy_xy(self):
status = self.cmd('2h STATUS')[0]
return status == 'B'
def halt_xy(self):
self.cmd_xy('HALT', False)
def where_xy(self):
response = self.cmd_xy('WHERE X Y')
if response.find('A'):
pos_xy = response.split()[1:3]
pos_x = float(pos_xy[0])
pos_y = float(pos_xy[1])
return pos_x, pos_y
else:
return None, None
def move_xy(self, x_mm, y_mm):
conv = self.config['conv']
xStr = 'x=' + str(float(x_mm) * conv)
yStr = 'y=' + str(float(y_mm) * conv)
return self.cmd_xy(' '.join(['m', xStr, yStr]))
def r_xy(self, x_mm, y_mm):
conv = self.config['conv']
xStr = 'x=' + str(float(x_mm) * conv)
yStr = 'y=' + str(float(y_mm) * conv)
return self.cmd_xy(' '.join(['r', xStr, yStr]))
# Z -----------------------------------------------
def cmd_z(self, cmd_string, block=True):
while block and self.is_busy_z():
time.sleep(0.3)
full_string = '1h ' + cmd_string
return self.cmd(full_string)
def is_busy_z(self):
status = self.cmd('1h STATUS')
return status[0] == 'B'
def halt_z(self):
self.cmd_z('HALT', False)
def where_z(self):
response = self.cmd_z('WHERE Z')
if response.find('A'):
pos_z = float(response.split()[1:2])
return pos_z
else:
return None
def move_z(self, z_mm):
conv = self.config['conv']
zStr = 'z=' + str(float(z_mm) * conv)
return self.cmd_z(' '.join(['m', zStr]))
def r_z(self, z_mm):
conv = self.config['conv']
zStr = 'z=' + str(float(z_mm) * conv)
return self.cmd_z(' '.join(['r', zStr]))
def exit(self):
self.serial.close()
In [ ]:
a = ASI_Controller('config/asi_controller.yaml')
In [ ]:
a.exit()
In [ ]:
from utils import lookup, read_delim_pd
import numpy as np
class Autosipper:
def __init__(self, z, xy):
self.Z = z # must be initialized first!
self.XY = xy
while True:
fp = raw_input('Type in plate map file:')
try:
self.load_platemap(fp) # load platemap
break
except IOError:
print 'No file', fp
raw_input('Place dropper above reference (press enter when done)')
self.XY.cmd_xy('here x y') # establish current position as 0,0
def load_platemap(self, filepath):
self.platemap = read_delim_pd(filepath)
def go_to(self, columns, values):
x1,y1,z1 = np.array(lookup(self.platemap, columns, values)[['x','y','z']])[0]
self.Z.home() # move needle to travel height (blocking)
self.XY.move_xy(x1,y1) # move stage (blocking)
self.Z.move(z1) # move needle to bottom of well (blocking)
def where(self):
pos_x, pos_y = XY.where_xy()
pos_z = Z.where()
return pos_x, pos_y, pos_z
def exit(self):
self.XY.exit()
self.Z.exit()
In [ ]:
d = Autosipper(Motor('config/le_motor.yaml'), ASI_Controller('config/asi_controller.yaml'))
In [ ]:
d.platemap
In [ ]:
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(d.platemap['x'], d.platemap['y'], d.platemap['z'], s=5)
plt.show()
In [ ]:
d.Z.home
In [ ]:
d.XY.r_xy(0,5)
In [ ]:
d.go_to(['name'],'A12')
In [ ]:
d.exit()
Install NI-VISA: https://pyvisa.readthedocs.io/en/stable/getting_nivisa.html
In [ ]:
import visa
In [ ]:
rm = visa.ResourceManager()
In [ ]:
rm.list_resources()
In [ ]:
rm.list_resources_info()