# Control loop for a pipetting machine and camera

I am new to the Python language and was playing with nested loops. All I am trying to do in my code is to get a value closer or equal to 1.50mm (which is a liquid drop pixel value changed to mm).

Starting steps: 1800, which will form a drop of a particular volume from my machine epmotion. I call my image function from the camera - get_cameraimage() andget_finalimage() provides my pixel value (converted to mm).

I check it every time with my desired value of 1.50mm and enter into the loop. Accordingly, I keep on increasing or decreasing my steps (for example, 1.48 > 1.50, increase last steps by 50 or 100 and vice versa). I finally print my last value when my steps are too small as it is.

What I want: decrease the code as I am quit sure somewhere I am lagging. Good thing: the code is perfectly running. I need help with handling such big nested loops.

print("Starting steps: 1800")
epMotion.DosingMotor.GotoPos(currentPos + 1800)

from together import *
get_cameraimage()
get_finalimage()
i=0
if get_finalimage.c==1.50:
print("Desired diameter of drop obtained: ", get_finalimage.c)
#elif():
#while get_finalimage.c<float(1.50):
#i=i+100
#print("Increasing the step size..", "i= ",100, "Steps: ", (2000+i))
#epMotion.DosingMotor.GotoPos(currentPos + (2000+i))
#time.sleep(3)
#get_camera()
#get_finalimage()
#if get_finalimage.c == float(1.50):
#print("The desired diamter of the drop obtained")
else:
while get_finalimage.c > 1.50:
i = i + 200
#print("After incrementation of 100 i become: ", i)
print("Decreasing step size..", "i= ", 200, "Steps: ", (1800 - i))
epMotion.DosingMotor.GotoPos(currentPos + (1800 - i))
time.sleep(1)
get_cameraimage()
get_finalimage()
if get_finalimage.c == 1.50:
print("Desired diameter of drop obtained: ", get_finalimage.c)
break
else:
i=1800-i
print("******************* The value of steps from last case *****************************:", i)
while get_finalimage.c < 1.50:
i = i+100
#print("After incrementation of 50 i become: ", i)
print("Increasing step size..", "i= ", 100, "New steps: ", i)
j = i
#print("2000-i: ", j)
epMotion.DosingMotor.GotoPos(currentPos + j )
time.sleep(1)
get_cameraimage()
get_finalimage()
if get_finalimage.c == 1.50:
print("Desired diameter of drop obtained: ", get_finalimage.c)
break
else:
i=j
print("***************** The value of steps from last case ******************************:", i)
while get_finalimage.c > 1.50:
i = i-50
#print("After incrementation of 50 i become: ", i)
print("Decreasing step size..", "i= ", 50, "New steps: ",i)
j = i
#print("2000-i: ", j)
epMotion.DosingMotor.GotoPos(currentPos + j )
time.sleep(1)
get_cameraimage()
get_finalimage()
if get_finalimage.c == 1.50:
print("Desired diameter of drop obtained: ", get_finalimage.c)
break
#elif get_finalimage.c == 1.51:
#print("Desired approximate diameter of drop obtained: ", get_finalimage.c)
#break
else:
i=j
print("************** The value of steps from last case ******************************:", i)
while get_finalimage.c < 1.50:
i = i + 20
#print("After incrementation of 10 i become: ", i)
print("Increasing step size..", "i= ", 20, "New steps: ", i)
j = i
#print("2000-i: ", j)
epMotion.DosingMotor.GotoPos(currentPos + j )
time.sleep(1)
get_cameraimage()
get_finalimage()
if get_finalimage.c == 1.50:
print("Desired diameter of drop obtained: ", get_finalimage.c)
break
else:
#print("Desired approximate diameter of drop obtained: ", get_finalimage.c)
i = j
print("************** The value of steps from last case ******************************:", i)
while get_finalimage.c > 1.50:
i = i - 5
#print("After decrementation of 10 i become: ", i)
print("Decreasing step size..", "i= ", 5, "New steps: ", i)
j = i
# print("2000-i: ", j)
epMotion.DosingMotor.GotoPos(currentPos + j)
time.sleep(1)
get_cameraimage()
get_finalimage()
if get_finalimage.c == 1.50:
print("Desired diameter of drop obtained: ", get_finalimage.c)
break
else:
i = j
print("************** The value of steps from last case ******************************:",i)
while get_finalimage.c < 1.50:
i = i + 2
# print("After decrementation of 10 i become: ", i)
print("Increasing step size..", "i= ", 2, "New steps: ", i)
j = i
# print("2000-i: ", j)
epMotion.DosingMotor.GotoPos(currentPos + j)
time.sleep(1)
get_cameraimage()
get_finalimage()
if get_finalimage.c == 1.50:
print("Desired diameter of drop obtained: ", get_finalimage.c)
break
else:
print("Desired approximate diameter of drop obtained: ", get_finalimage.c)
#break
epMotion.ComfortApi.EjectTipIntoWaste()
count = count + 1
print("the value of count is: ", count)
print("loop finished")
exit(1)

• Could you check your code indentation? At the moment it seems some elses are aligned with whiles. Aug 9, 2017 at 16:48
• @toto2 This is valid in Python: a while loop admits an else clause.
– vnp
Aug 9, 2017 at 18:17

It's a small detail, but you should be careful with language conventions. For example, get_camera_image instead of get_cameraimage and there should be spaces around the equal sign in i=0 or get_finalimage.c==1.50.

get_finalimage.c which is a function attribute seems quite unusual programming style. Maybe using object oriented programming would be more appropriate.

The variable i is used everywhere so it would have been worth giving it some proper name such as increment or step_size.

1.50 is hard coded everywhere, so a variable should have been defined instead such as target_diameter. In many places you have something like i = i + 200 and then later you have a print statement which also outputs 200. That should also have been a variable to avoid the case where you change one value, but forget about the other one.

The big problem with your code (and this is why you asked for help) is that there is a lot of copy/paste. This not only makes the code very long, but it is very dangerous. The issue is that when you make a change to one of the copied section, you might forget to make on all other sections. This can be solved by defining a function instead of copying chunks of code.

It's better to create a function for any pattern that is repeated often, not just for the repeated part in each while-block. You can see what I did below.

This code likely has several mistakes.

def get_diameter():
get_cameraimage()
get_finalimage()
return get_finalimage.c

def goto_position(pos):
"""
Moves the motor and gets the diameter.  This a long running operation
which includes a wait of 1 second between the motor command
and the image taking.

:param pos: the target position for the motor
:return: the diameter of the drop
"""
epMotion.DosingMotor.GotoPos(pos)
time.sleep(1)
return get_diameter()

target_diam = 1.5
position = currentPos + 1800

diam = get_diameter(position)

def increment_position(diam, step_size):
"""
If the diameter is larger than the target, it increments repeatedly
the motor position by the given step_size until it reaches
or undershoots the target.  And vice versa when the diameter is initially
too small.

The external position variable is modified by this function.

:param diam: the starting observed diameter
:param step_size: the size of the step that is used to increment the motor
position.  Always a positive value.  The function determines if it should
be an increment or decrement.
:return: the final observed diameter
"""
if diameter == target_diam:
return diam
is_overshoot = diam > target_diam
increment = step_size * (1 if is_overshoot else -1)
while True:
position += increment
diam = goto_position(position)
print("Incrementing position by {} to {}.  Diameter is now {}."
.format(increment, position, diam))
if diam == target_diam:
return diam
is_overshoot_post = diam > target_diam
if is_overshoot_post != is_overshoot: // went too far the other way
return diam

diam = increment_position(diam, 200)
diam = increment_position(diam, 100)
diam = increment_position(diam, 50)
diam = increment_position(diam, 20)
diam = increment_position(diam, 5)
diam = increment_position(diam, 2)

print("Got {} diameter of {:.4f} mm."
.format("exact" if diam == target_diam else "approximate" , diam))
epMotion.ComfortApi.EjectTipIntoWaste()


The repetitive part calling increment_position can be simplified using reduce. But if you are not familiar with functional programming, it might be more confusing.

from functools import reduce
diam = reduce(increment_position,
[200, 100, 50, 20, 5, 2],
initializer=diam)