SETUP and CALIBRATION

Sampler

  • Assemble Z-drive, arm, DL needle

  • Build stand (with desired stage heights)

  • Loosely attach brackets w/ fasteners to stage bottom
  • Attach stage (near end of cantilever)
  • Attach Z-drive assembly
    • linear guide should be perpendicular to stage surface
  • Set up controller and software(?), attach cables
  • Attach deck to stage
    • Be aware that arm could be obstructed by taller vessels in certain orientations, preventing access to shorter vessels
  • HW: adjust hardware limits, alignment with Z axis
  • HW/DECK: set convenient zero/home corner in config
  • DECK: Determine transform to HW

Fraction Collector

  • Assemble slider, arm

  • subset of Sampler, w/ arm, stage attachment difference


In [ ]:
from acqpack import Motor, AsiController, Autosampler, FractionCollector
from acqpack import gui
from acqpack import utils as ut

import yaml
import numpy as np
import pandas as pd

1 HW: set imits; align XY with Z

physical adjustments

  • adjust hall effect stops to maximize range
  • make sure:
    • all relevant points on deck are accessible (check with 384-well plate)
    • the stage does not collide with Z-axis (reorientation of the stage/deck may be necessary)
    • at max Z, needle is at or just below top of deck

In [ ]:
a = Autosampler(Motor('config/motor.yaml'),  AsiController('config/input.yaml'))
gui.stage_control(a)

2 HW/DECK: set convenient XY zero/home corner in config


In [ ]:
# in input.yaml, set desired init_dir (x_dir and y_dir)

3 DECK: Determine transform to hardware

This is one way to do this; it assumes things are generally rectilinear (90deg rotations)
Could also do regression between frame1 and frame2 (generalizable way?)

  • XY rotation / reflection must be re-determined any time:
    • deck is rotated relative to stage
    • zero/home corner is changed in config
  • offset must be re-determined any time:
    • deck is rotated relative to stage
    • zero/home corner is changed in config
    • hall-effect sensors are moved
    • z-drive and stage are shifted (in any direction) relative to one another
    • needle is changed to a different one
a x' y' z' w'
x 1. 0. 0. 0.
y 0. 1. 0. 0.
z 0. 0. -1. .0
w 0. 0. 0. 1.

In [ ]:
a = Autosampler(Motor('config/motor.yaml'),  AsiController('config/input.yaml'))
gui.stage_control(a)

In [ ]:
# DECK is row; HW is col
# determine XY axes mapping (rotations / reflections)

# determine XYZ offset (i.e. when DECK is 0,0,0, what is HW ?)

# save affine in input_deck.txt

4 PLATES: Make frames in deck coordinates

This is one way to do this; it assumes things are generally rectilinear (90deg rotations)
Could also do regression between frame1 and frame2 (generalizable way?)

  • x,y,z of all well positions are in 'DECK coordinates', i.e. relative to fixed point on deck (ref point, coordinate origin)
  • this allows platemaps to be reused so long as:
    • deck is the same
    • position AND orientation of plate on deck is the same
  • could also use sequence of transforms (PLATE -> DECK -> HARDWARE), but this is simpler for now

In [ ]:
a = Autosampler(Motor('config/motor.yaml'),  AsiController('config/input.yaml'))
a.add_frame('deck', 'config/input_deck.txt')
gui.stage_control(a)

In [ ]:
# determine plate 'A01' offset relative to deck origin using above GUI
# i.e. deck coordinates of 'A01'
offset = (2.493, 12.749, 0)

# generate platemap with offset
num_rc = (8,12)   # ct, ct
space_rc = (9,9)  # mm, mm (can do negative if row or column reflected)
platemap = ut.generate_position_table(num_rc, space_rc, offset, True)

# save
filename = '96plate.txt'
platemap

In [ ]:
# determine plate 'A01' offset relative to deck origin using above GUI
# i.e. deck coordinates of 'A01'
offset = (8.192, -18.952, 37.700)

# generate platemap with offset
num_rc = (1,4)   # ct, ct
space_rc = (0,28.375)  # mm, mm (can do negative if row or column reflected)
platemap = ut.generate_position_table(num_rc, space_rc, offset, True)

# save
filename = '4scint.txt'
platemap

In [ ]:
a.add_frame('plate', 'config/input_deck.txt', 'config/96plate.txt')
a.add_frame('wash', 'config/input_deck.txt', 'config/4scint.txt')

In [ ]:
a.goto('plate','name', 'H10')

In [ ]:
a.where()

Runtime: option to check deck transform?

  • be sure to clear deck!!!!!!
  • go to XY (0,0)
  • drop Z manually to check (0,)
  • can:
    • tweak physically to bring back into alignment
    • update deck coordinate frame

In [ ]:
a.goto('deck', 'xyz', (0,0,0))

Generalized prodcedure to determine transform between two coordinate frames?

  • regression between set of points from frame1 and frame2

TODO

  • Determine transforms
  • TEST

    • push version
  • needle

  • yaml np.array load/store format (better to print to save for readability? or use csv?)
    • or use RS matrix + offset vector

In [ ]:

RUN!


In [2]:
from acqpack import Motor, AsiController, Autosampler, FractionCollector
from acqpack import gui
from acqpack import utils as ut

Autosampler


In [3]:
a = Autosampler(Motor('config/motor.yaml'),  AsiController('config/input.yaml'))
gui.stage_control(a)


(-0.000, 0.000, 0.000) hardware
(0.330, -28.698, 99.600) plate
(0.330, -28.698, 99.600) wash

In [33]:
a.add_frame('plate', 'config/input_deck.txt', 'config/96plate.txt')
a.add_frame('wash', 'config/input_deck.txt', 'config/4scint.txt')

In [60]:
a.goto('plate', 'name', 'A01', zh_travel=35)

In [34]:
a.goto('plate', 'xyz', (0,0,0))

FractionCollector


In [55]:
f = FractionCollector(AsiController('config/output.yaml'))
gui.stage_control(f)


(101.493, -75.749) hardware
(101.493, 75.749) plate

In [56]:
f.add_frame('plate', 'config/output_deck.txt', 'config/96plate.txt')

In [58]:
f.goto('plate', 'name', 'H12')

In [62]:
f.where('plate')


Out[62]:
(101.49339999999999, 75.748900000000006)

In [54]:
f.exit()