Reference coordinate system: hardware
Deck and plates should share scaling

  • Scale: direction (+/-); unit conversion
  • Rotate: axes mapping
  • Translate: offset (references, plate positions)

Where store?
Scale-Translate table:

$\begin{array}{r|rrr} \textbf{coord} & \bf{(A,S,T)_x} & \bf{(A,S,T)_y} & \bf{(A,S,T)_z} \\ \hline \text{hardware} & x,1,0 & y,1,0 & z,1,0 \\ \hline \text{deck} & x,-1,0 & y,1,0 & z,-1,-90 \\ \text{plate1} & x,-1,ref_x & y,1,ref_y & z,-1,ref_z \\ \end{array}$

Affine transform example


In [9]:
import numpy as np

In [71]:
plate = np.array([0,0,0,1])

In [507]:
W = np.array([[0,1,0],
              [1,0,0],
              [0,0,1],
              [0,0,0],
             ])

# Wi=np.linalg.inv(W)

In [75]:
hardware = np.matmul(plate,W)
hardware


Out[75]:
array([0, 2, 0])

In [31]:
np.matmul(y,Wi)


Out[31]:
array([  5.,  10.,  15.,   2.])

Platemapping


In [485]:
# I:
# num_rc: (ct, ct) 2-tuple of 
# space_rc: (mm, mm)
# O: 
def generate_platemap(num_rc, space_rc, z):
    temp = list()
    headers = ['n','s','r','c','name','x','y','z']
    
    for r in range(num_rc[0]):
        for c in range(num_rc[1]):
            n = c + (r)*num_rc[1]
            s = ((r+1)%2)*(c + r*num_rc[1]) + (r%2)*((r+1)*num_rc[1] - (c+1))
            name = chr(64+r+1) + '{:02d}'.format(c+1)
            x = c*space_rc[1]
            y = r*space_rc[0]
            z = z
            temp.append([n, s, r, c, name, x, y, z])
    df = pd.DataFrame(temp, columns=headers)
#     df.to_clipboard()
    return df

num_rc = 16,24
space_rc = 5,5
z=10
generate_platemap(num_rc, space_rc, z)


Out[485]:
n s r c name x y z
0 0 0 0 0 A01 0 0 10
1 1 1 0 1 A02 5 0 10
2 2 2 0 2 A03 10 0 10
3 3 3 0 3 A04 15 0 10
4 4 4 0 4 A05 20 0 10
5 5 5 0 5 A06 25 0 10
6 6 6 0 6 A07 30 0 10
7 7 7 0 7 A08 35 0 10
8 8 8 0 8 A09 40 0 10
9 9 9 0 9 A10 45 0 10
10 10 10 0 10 A11 50 0 10
11 11 11 0 11 A12 55 0 10
12 12 12 0 12 A13 60 0 10
13 13 13 0 13 A14 65 0 10
14 14 14 0 14 A15 70 0 10
15 15 15 0 15 A16 75 0 10
16 16 16 0 16 A17 80 0 10
17 17 17 0 17 A18 85 0 10
18 18 18 0 18 A19 90 0 10
19 19 19 0 19 A20 95 0 10
20 20 20 0 20 A21 100 0 10
21 21 21 0 21 A22 105 0 10
22 22 22 0 22 A23 110 0 10
23 23 23 0 23 A24 115 0 10
24 24 47 1 0 B01 0 5 10
25 25 46 1 1 B02 5 5 10
26 26 45 1 2 B03 10 5 10
27 27 44 1 3 B04 15 5 10
28 28 43 1 4 B05 20 5 10
29 29 42 1 5 B06 25 5 10
... ... ... ... ... ... ... ... ...
354 354 354 14 18 O19 90 70 10
355 355 355 14 19 O20 95 70 10
356 356 356 14 20 O21 100 70 10
357 357 357 14 21 O22 105 70 10
358 358 358 14 22 O23 110 70 10
359 359 359 14 23 O24 115 70 10
360 360 383 15 0 P01 0 75 10
361 361 382 15 1 P02 5 75 10
362 362 381 15 2 P03 10 75 10
363 363 380 15 3 P04 15 75 10
364 364 379 15 4 P05 20 75 10
365 365 378 15 5 P06 25 75 10
366 366 377 15 6 P07 30 75 10
367 367 376 15 7 P08 35 75 10
368 368 375 15 8 P09 40 75 10
369 369 374 15 9 P10 45 75 10
370 370 373 15 10 P11 50 75 10
371 371 372 15 11 P12 55 75 10
372 372 371 15 12 P13 60 75 10
373 373 370 15 13 P14 65 75 10
374 374 369 15 14 P15 70 75 10
375 375 368 15 15 P16 75 75 10
376 376 367 15 16 P17 80 75 10
377 377 366 15 17 P18 85 75 10
378 378 365 15 18 P19 90 75 10
379 379 364 15 19 P20 95 75 10
380 380 363 15 20 P21 100 75 10
381 381 362 15 21 P22 105 75 10
382 382 361 15 22 P23 110 75 10
383 383 360 15 23 P24 115 75 10

384 rows × 8 columns


In [499]:
def spacing(num_rc,p1,p2):
    r,c =map(float,num_rc)
    return tuple(abs(np.nan_to_num(np.divide(np.subtract(p2, p1), (c-1,r-1)))))

In [504]:
num_rc = 1,4
p1 = 0.0,0.0
p2 = 115,0.0

spacing(num_rc,p1,p2)


/usr/local/lib/python2.7/site-packages/ipykernel/__main__.py:4: RuntimeWarning: invalid value encountered in divide
Out[504]:
(38.333333333333336, 0.0)

Setup Routine

  1. Determine/adjust X, Y, Z limits
    • 104.35, 100.4, 100.0 mm
  2. Affix deck to stage
  3. Determine axes mapping between deck and stage
    • fill out top left four of matrix
    • keep in mind that movement of stage likely reversed relative to plate position
    • deck will have same axes as plate
  4. Align (physically) deck to stage using deck reference
    • reference is not an origin, but has X,Y
    • zero stage to top left of deck (deck's min X and min Y)
    • move stage to deck's XY ref position
    • physically move stage to bring needle into alignment with deck reference

In [ ]:
# 1a determine XY limits
# only use if hall effect sensors are in place
XY = ASI_Controller()

overload = 999.9

XY.move_xy(-overload, -overload)
xmin, ymin = XY.where_xy()

XY.move_xy(overload, overload)
xmax, ymax = XY.where_xy()

ranges_xy = (xmax - xmin, ymax - ymin)

# ranges_xy = (104.35, 100.4)

In [ ]:
# 1b determine Z limits
# be careful not to exceed limit
ranges_z = 100.0
autosipper.ranges_xyz = ranges_xy + ranges_z

In [ ]:
# 2 Affix *deck* to *stage*

In [439]:
# 3 Determine axes mapping between deck and stage
autosipper.hardware.transform = np.array([[1,0,0],
                                         [0,1,0],
                                         [0,0,1],
                                        ])
autosipper.deck.transform = np.array([[1,0,0],
                                     [0,1,0],
                                     [0,0,1],
                                    ])
assert(np.linalg.norm(autosipper.W['deck'], ord='nuc') == 3) # check for a valid transformation matrix (includes rotations)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-439-2694e2d8626a> in <module>()
      2 autosipper.W['hardware'] = np.array([[1,0,0],
      3                                      [0,1,0],
----> 4                                      [0,0,1],
      5                                     ])
      6 autosipper.W['deck'] = np.array([[1,0,0],

NameError: name 'autosipper' is not defined

In [ ]:
# 4 Align (physically) deck to stage using deck reference
x,y = deck
autosipper.XY.zero
autosipper.XY.move_xy(x,y) # move to reference

Runtime Routine

  1. Load deck coord_frame
  2. For each plate, add plate
    • move to first well in plate (A01)
    • store offset (translation)
    • a) go to final well and generate platemap OR b) add platemap
    • b) detect out of range wells