Setting the Coordinates for the XYZ Robot¶

  • We want to store the coordinates for the various positions in Ice Box 1 and Ice Box 2.
  • This notebook will help you set those positions and store them in a config file.
  • We will the following:
    • 50ml Tubes
    • 15ml Tubes
    • 1/16" ID 0.5mm ID PTFE tubing {CAT NO. JR-T-4183-M25}
      • Email precision@vici.com for a Quote. Tell them your credit card details for payment.
  • Cut a 30cm Piece of PTFE Tubing using a Stanley Razor Blade.
  • Put the 15ml and 50ml Tubes in the Ice Boxes as shown.
  • Push the cut tubing to the position shown through the Pipette Tip on the Linear Rail.
  • Execute the cells below to Intialize your device and define some Classes for our Robots.
In [2]:
# coding : utf8
%matplotlib inline
import os
import sys
sys.path.append(os.getcwd());
import ivPID.PID as PID_controller

print(os.getcwd())
os.chdir(".\config")

#Adding required directories to sys.path. Make sure you are in the Directory with the Git Repo when you start Jupyter Notebook
sys.path.append(os.getcwd());
sys.path.append(os.getcwd()+"\\DLL64");

import time
import Elveflow64
os.chdir("..")
from ctypes import *
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import serial
import threading
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.schedulers import SchedulerNotRunningError
from datetime import datetime,timedelta
import seaborn as sns
import pygame
C:\Users\kanis\Documents\ChronoSeq
pygame 2.5.2 (SDL 2.28.3, Python 3.7.16)
Hello from the pygame community. https://www.pygame.org/contribute.html
In [3]:
%%javascript
IPython.OutputArea.prototype._should_scroll = function(lines) {
    return false;
}

Note on Ipython Magics¶

  • We will store device specific configuration in the config folder using Ipython Magics
  • We modified the %%writefile magic as follows.
  • Execute the cell below
In [4]:
from IPython.core.magic import register_cell_magic

@register_cell_magic
def write_and_run(line, cell):
    argz = line.split()
    file = argz[-1]
    mode = 'w'
    if len(argz) == 2 and argz[0] == '-a':
        mode = 'a'
    with open(file, mode) as f:
        f.write(cell)
    get_ipython().run_cell(cell)
In [5]:
%run -i config/instrument_initialization.py
Pressure Controller Connection Started
Pressure Controller ID: 0
True
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 0, 'inter_byte_timeout': None}
True
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 0, 'inter_byte_timeout': None}
True
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 0, 'inter_byte_timeout': None}
COM12
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 0, 'inter_byte_timeout': None}
True
Cell Channel Flow Meter ID: 3000
error 0 :
Oil Channel Flow Meter ID: 3001
error 0 :
Cell Channel Density:
0.37821754813194275
Cell Channel Flow rate:
-2805.2143764712837
Oil Channel Density:
1.2706427574157715
Oil Channel Flow rate:
-1659.781160837623
COM15
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 1, 'inter_byte_timeout': None}
True
COM9
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 1, 'inter_byte_timeout': None}
True
COM7
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 1, 'inter_byte_timeout': None}
True
COM10
{'baudrate': 9600, 'bytesize': 8, 'parity': 'N', 'stopbits': 1, 'xonxoff': False, 'dsrdtr': False, 'rtscts': False, 'timeout': 1, 'write_timeout': 0, 'inter_byte_timeout': None}
True
In [6]:
class robot_action():
    
    all_time_points={}
    in_tube=False
    previous_yoffset=0
    previous_xoffset=0
    previous_y=0
    previous_x=0
    
    def __init__(self,xpos,ypos,final_xoffset,final_yoffset,timepoint=None):
        self.xpos=xpos
        self.ypos=ypos
        self.xoffset=final_xoffset
        self.yoffset=final_yoffset
        self.timepoint=timepoint
        if (self.timepoint is not None):
            robot_action.all_time_points[self.timepoint]=self
    
    def goToCoordinates(self):
        if robot_action.in_tube is True:
            plotter.write(("Y"+str(robot_action.previous_yoffset*-1)+"|X"+str(robot_action.previous_xoffset*-1)+"\n").encode())
        plotter.write(("G"+str(self.xpos)+","+str(self.ypos)+"|Y"+str(self.yoffset)+"|X"+str(self.xoffset)+"\n").encode())
        robot_action.in_tube=True
        robot_action.previous_yoffset=self.yoffset
        robot_action.previous_xoffset=self.xoffset
        robot_action.previous_y=self.ypos
        robot_action.previous_x=self.xpos
    
    def goToCoordinatesFast(self):
        if robot_action.in_tube is True:
            plotter.write(("Y"+str(robot_action.previous_yoffset*-1)+"|X"+str(robot_action.previous_xoffset*-1)+"\n").encode())
        plotter.write(("G"+str(self.xpos)+","+str(self.ypos)+"|Y"+str(self.yoffset)+"|X"+str(self.xoffset)+"\n").encode())
        robot_action.in_tube=True
        robot_action.previous_yoffset=self.yoffset
        robot_action.previous_xoffset=self.xoffset
        robot_action.previous_y=self.ypos
        robot_action.previous_x=self.xpos
    
    
    def goToCoordinatesSmoothly(self):
        assert robot_action.previous_x==self.xpos , "Please only change the Y coordinate for this movement. Changing the X coordinate is not currently supported. "
        assert self.yoffset==0 and self.xoffset==0 and robot_action.previous_xoffset==0 and robot_action.previous_yoffset==0 , "No X and Y offsets are supported for this movement both for Previous and Current Position. Please remove all offsets for starting and ending postion."
        assert abs(self.ypos-robot_action.previous_y)>1000,"The final Y positions have to differ by more than 1000 steps."
        if robot_action.in_tube is True:
            plotter.write(("Y"+str(robot_action.previous_yoffset*-1)+"|X"+str(robot_action.previous_xoffset*-1)+"\n").encode())
        plotter.write(("S"+str(self.xpos)+","+str(self.ypos)+"|Y"+str(self.yoffset)+"|X"+str(self.xoffset)+"\n").encode())
        robot_action.in_tube=True
        robot_action.previous_yoffset=self.yoffset
        robot_action.previous_xoffset=self.xoffset
        robot_action.previous_y=self.ypos
        robot_action.previous_x=self.xpos

    
    def goToCoordinatesKeepTubeUp(self):
        if robot_action.in_tube is True:
            plotter.write(("Y"+str(robot_action.previous_yoffset*-1)+"|X"+str(robot_action.previous_xoffset*-1)+"\n").encode())
        plotter.write(("U"+str(self.xpos)+","+str(self.ypos)+"|Y"+str(self.yoffset)+"|X"+str(self.xoffset)+"\n").encode())
        robot_action.in_tube=True
        robot_action.previous_yoffset=self.yoffset
        robot_action.previous_xoffset=self.xoffset
        robot_action.previous_y=self.ypos
        robot_action.previous_x=self.xpos
        
    def getCoordinatesFromPosition():
        buffer=plotter.read(plotter.inWaiting())
        plotter.write("C\n".encode())
        time.sleep(7.0)
        buffer=plotter.read(plotter.inWaiting()).decode()
        lines=buffer.split("\n")
        print(lines[2])

    def goHome(self):
        robot_action.robotGoHome()

    def robotGoHome():
        if robot_action.in_tube is True:
            plotter.write(("Y"+str(robot_action.previous_yoffset*-1)+"|X"+str(robot_action.previous_xoffset*-1)+"\n").encode())
        plotter.write("H\n".encode())
        robot_action.in_tube=False
        droplet_collection.droplet_collector_go_home()
        
    def enableRobot():
        plotter.write("E\n".encode())

    def disableRobot():
        plotter.write("D\n".encode())

    def tubeUp():
        plotter.write("T1\n".encode())

    def tubeDown():
        plotter.write("T0\n".encode())
    
    def manuallyMove():
        plotter.write("D|T1\n".encode())

class droplet_collection():
    in_tube=False
    current_time_delay_from_home=1000
    at_home=True
    def __init__(self,servo_angle1,servo_angle2,time_taken_from_home=1000):
        self.servo_angle1=servo_angle1
        self.servo_angle2=servo_angle2
        self.time_taken_from_home=time_taken_from_home
    def goToCoordinates(self):
        if droplet_collection.in_tube==False and droplet_collection.at_home==True:
            droplet_collector.write(("A"+str(self.servo_angle1)+"|W"+str(self.time_taken_from_home)+"|B"+str(self.servo_angle2)+"\n").encode())
            droplet_collection.in_tube=True
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
            droplet_collection.at_home=False
        elif droplet_collection.in_tube==False and droplet_collection.at_home==False:
            time_diff=abs(droplet_collection.current_time_delay_from_home-self.time_taken_from_home)
            droplet_collector.write(("A"+str(self.servo_angle1)+"|W"+str(time_diff)+"|B"+str(self.servo_angle2)+"\n").encode())
            droplet_collection.in_tube=True
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
            droplet_collection.at_home=False

        else:
            time_diff=abs(droplet_collection.current_time_delay_from_home-self.time_taken_from_home)
            droplet_collector.write(("B0|W500|A"+str(self.servo_angle1)+"|W"+str(time_diff)+"|B"+str(self.servo_angle2)+"\n").encode())
            droplet_collection.in_tube=True
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
            droplet_collection.at_home=False
    
    def goToCoordinatesFast(self):
        self.goToCoordinates()
        
    def goToCoordinatesSlowly(self):
        if droplet_collection.in_tube==False and droplet_collection.at_home==True:
            droplet_collector.write(("X"+str(self.servo_angle1)+"|Y"+str(self.servo_angle2)+"\n").encode())
            droplet_collection.in_tube=True
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
            droplet_collection.at_home=False
        elif droplet_collection.in_tube==False and droplet_collection.at_home==False:
            droplet_collector.write(("X"+str(self.servo_angle1)+"|Y"+str(self.servo_angle2)+"\n").encode())
            droplet_collection.in_tube=True
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
            droplet_collection.at_home=False

        else:
            #time_diff=abs(droplet_collection.current_time_delay_from_home-self.time_taken_from_home)
            droplet_collector.write(("Y0|X"+str(self.servo_angle1)+"|Y"+str(self.servo_angle2)+"\n").encode())
            droplet_collection.in_tube=True
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
            droplet_collection.at_home=False
        
    def goToCoordinatesKeepTubeUp(self):
        if droplet_collection.in_tube==False:
            droplet_collector.write(("A"+str(self.servo_angle1)+"\n").encode())
            droplet_collection.in_tube=False
            droplet_collection.at_home=False
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
        else:
            droplet_collector.write(("B0|W500|A"+str(self.servo_angle1)+"\n").encode())
            droplet_collection.in_tube=False
            droplet_collection.at_home=False
            droplet_collection.current_time_delay_from_home=self.time_taken_from_home
    def goHome(self):
        droplet_collection.droplet_collector_go_home()
    
    def droplet_collector_go_home():
        if droplet_collection.in_tube==False:
            droplet_collector.write(("H\n").encode())
            droplet_collection.in_tube=False
            droplet_collection.at_home=True
        else:
            droplet_collector.write(("B0|W500|H\n").encode())
            droplet_collection.in_tube=False
            droplet_collection.at_home=True

robot_action.robotGoHome() 
#Defining Various Tube Positions for the XY Robot:
good_collection_tube=droplet_collection(180,180,1000)
bad_collection_tube=droplet_collection(60,180,350)
  • Execute the cell below to watch a video of how to get the coordinates for a Certain Position on the Ice Boxes.
In [7]:
%%HTML
<iframe width="800" height="600" src="https://www.youtube.com/embed/fFkCPMRG7UE?si=EZtgWsnKRZRd1p_b" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
  • Execute the cell below to Manually Move the Device to Position 1 in Ice Box 2.
In [7]:
time.sleep(5)
robot_action.manuallyMove()
  • Manually move to position. Pull up the Tubing if it collides with the 15ml or 50ml Tubes.
  • Now we will put the Tubing in the Down Position.
In [8]:
time.sleep(5)
robot_action.tubeDown()
  • Try to move the XYZ Robot as close to the Center of the Tubes as possible.
  • The robot can still be moved after the Tube has moved to the down position.
  • Execute the cell below to get the coordinates of this position.
In [9]:
robot_action.getCoordinatesFromPosition()
No remaining commands with the following input:

  • We will now save these X and Y coordinates in a List
  • The robot_action object sets the coordinates as follows:

robot_action(<X Coordinates>,<Y Coordinates>,<X offset after the Tube Goes Down>,<Y Offset after the Tube Goes Down>,<Timepoint/Position>)

  • Copy the measured X and Y coodinates in the Object in the list bead_time_point_collection_tubes below.
In [10]:
bead_time_point_collection_tubes=[]
bead_time_point_collection_tubes.append(robot_action(-8510,-8515,0,500,1))
  • We can now check our Coordinate by moving the Robot to that position.
  • The robot will move by the Yoffset of 500 after the Tube goes down.
    • This offset is important to prevent liquid droplets from dripping to other tubes during robot movement.
  • Execute the cell below to move the robot to the first position.
In [11]:
position=1
bead_time_point_collection_tubes[position-1].goToCoordinates()
  • We can use the same principle to find the Coordinates for Waste Tube 1 and Waste Tube 2.
    • However in their case we will use a X Offset to prevent liquid droplets from dripping to other tubes during robot movement.
  • Try to move the XYZ Robot as close to the Center of the Tubes as possible.
  • The robot can still be moved after the Tube has moved to the down position.
  • Execute the cell below to Home the Robot and then manually move to Waste Tube 2.
In [12]:
robot_action.robotGoHome()
time.sleep(5)
robot_action.manuallyMove()
  • Execute the cell below to move the Tubing to the Down Position.
  • Move the tip of the Tubing as close to the Center Position as possible.
  • You should not trigger the Limit Switch.
  • If the Limit switch is accidentally triggered then you might have to move your XY Robot, Ice Box 1 and Ice Box 2 a little bit further away from the XYZ Robot.
In [13]:
robot_action.tubeDown()
  • Execute the cell below to get the Coordinates for Waste Tube 2.
In [14]:
robot_action.getCoordinatesFromPosition()
D

  • Copy the X and Y Coordinate Values to the Object Below.
In [15]:
waste_tube2=robot_action(-1920,-263,1000,0)
  • We can now check our Coordinates by moving the Robot to that position.
  • The robot will move by the Yoffset of 1000 after the Tube goes down.
  • Execute the cell below to move the robot to Waste Tube 2.
In [16]:
waste_tube2.goToCoordinates()
  • Now we can repeat this method of getting the Coordinates for all the Positions in Ice Box 1 and 2.
  • Complete replace the X and Y Coordinate Values for all the Positions below.
  • These coordinates will be stored in the file config/xyzRobotCoordinateObjects.py
In [8]:
%%write_and_run config/xyzRobotCoordinateObjects.py
waste_tube1=robot_action(-1983,-3202,1000,0)
waste_tube2=robot_action(-1920,-263,1000,0)
bead_time_point_collection_tubes=[]
bead_time_point_collection_tubes.append(robot_action(-8510,-8515,0,500,1))
bead_time_point_collection_tubes.append(robot_action(-6272,-8640,0,500,2))
bead_time_point_collection_tubes.append(robot_action(-3970,-8577,0,500,3))
bead_time_point_collection_tubes.append(robot_action(-1632,-8541,0,500,4))
bead_time_point_collection_tubes.append(robot_action(-8512,-10625,0,500,5))
bead_time_point_collection_tubes.append(robot_action(-6242,-10589,0,500,6))
bead_time_point_collection_tubes.append(robot_action(-3998,-10529,0,500,7))
bead_time_point_collection_tubes.append(robot_action(-1699,-10655,0,500,8))
bead_time_point_collection_tubes.append(robot_action(-8510,-12610,0,500,9))
bead_time_point_collection_tubes.append(robot_action(-6210,-12736,0,500,10))
bead_time_point_collection_tubes.append(robot_action(-4030,-12674,0,500,11))
bead_time_point_collection_tubes.append(robot_action(-1759,-12701,0,500,12))
bead_time_point_collection_tubes.append(robot_action(-8544,-14816,0,500,13))
bead_time_point_collection_tubes.append(robot_action(-6336,-14785,0,500,14))
bead_time_point_collection_tubes.append(robot_action(-3971,-14721,0,500,15))
bead_time_point_collection_tubes.append(robot_action(-1694,-14879,0,500,16))
  • Next we will test all the Coordinates for our XYZ and XY Robots.
    • Make adjustments if necessary.
  • Execute the cell below to test the coordinates.
In [10]:
def test_robot():
    waste_tube1.goToCoordinates()
    time.sleep(5.0)
    good_collection_tube.goToCoordinates()
    for tube in bead_time_point_collection_tubes:
        tube.goToCoordinates()
        time.sleep(5.0)
    waste_tube1.goToCoordinates()
    time.sleep(10.0)
    waste_tube2.goToCoordinates()
    time.sleep(10.0)
    bad_collection_tube.goToCoordinates()
    time.sleep(10.0)
    good_collection_tube.goToCoordinates()
    time.sleep(10.0)
    robot_action.robotGoHome()
    
test_robot()
  • Execute the cell below to see a video of our XYZ and XY Robot coordinate test.
In [11]:
%%HTML
<iframe width="800" height="600" src="https://www.youtube.com/embed/FhH23Al3K14?si=yRKEauGkWJupocM6" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
  • Tape the cables for the XYZ Robot to your bench as shown.
    • Use Labelling Tape.
    • This should prevent unwanted Tangles or accidental disconnection.
  • Execute the cell below to Shutdown the ChronoSeq device and close all the Serial connections.
In [12]:
%run -i config/deviceShutdown.py
Pressure Controller Connection Closed!
Valve Controller 1 Connection Closed!
Valve Controller 2 Connection Closed!
Valve Controller 3 Connection Closed!
Magnetic Stirrer Connection Closed!
Cell Channel Flow meter Connection Closed!
Oil Channel Flow meter Connection Closed!
Connection to the XYZ Robot closed successfully!
Connection to the XY Robot closed successfully!
Bead Channel Flow meter Connection Closed!
Connection to the Vortex Relay closed successfully!