# Openflexure stage program in microscope

Hello,

I’ve successfully built confocal microscope. Here is the picture of 1 euro star from this microscope: Now I’m trying to optimize my program to make it work with smaller steps (link to old documentation):

``````def micronToStep(v):
#approximately 62nm/step
return round((v * 1000)/62)

def mz(self, position, absolute=True):
"""Move the stage to a given position.

WARNING! If you specify zeros, the stage might move a long way, as
the default is absolute moves.  Position should be a dictionary
with keys called "x", "y", and "z", although we will (for now) also
accept an iterable of three numbers.
"""
try:
pos = {k: int(position[k]) for k in ["x", "y", "z", "z2"]}
except:
pos = {k: int(position[i]) for i, k in enumerate(["x", "y", "z", "z2"][:len(position)])}
pos['absolute'] = absolute
response = self.post_json("/extensions/com.openflexure.stage-mapping/actions/stage/move-measure/MeasureZAPI", pos)
return response

#microscope = ofm_client.find_first_microscope()
microscope = ofm_client.MicroscopeClient("192.168.0.100")
microscope.measureZ = mz

# homing routine
pos = microscope.position
print("Current position: " + json.dumps(pos))
print("Homing")
pos['x'] = 0
pos['y'] = 0
pos['z'] = 0
#microscope.move(pos)

#zStep = 20
#maxValue = -1
#maxPosition = -1
#for z in range(micronToStep(-2000), micronToStep(2000), micronToStep(zStep)):
#    pos = microscope.position
#    pos['x'] = 0
#    pos['y'] = 0
#    pos['z'] = z
#    values = microscope.moveMeasure(microscope, pos, 1)
#    #values = [a for a in values if a >= 0 ]
#    values = np.asarray(values)
#    value = np.median(values)
#    if (value > maxValue):
#        maxValue = value
#        maxPosition = z
#    print(value, maxValue, maxPosition)

#print("Final values:", maxValue, maxPosition)

# units in microns
xyStep = 5
xRange = 800
yRange = 800
zRange = 400
#zBase = maxPosition - micronToStep(zRange/2)
#zBase = -((30000 - 15000)/2)
zBase = 0

saveData = {
"xyStep": xyStep,
"xRange": xRange,
"yRange": yRange,
"zRange": zRange,
"zBase": zBase,
"data": [ * ceil(xRange/xyStep) for i in range(ceil(yRange/xyStep))],
}

measurements = 1
pos = microscope.position
print("Current position: " + json.dumps(pos))

starting_pos = microscope.position
#figure(figsize=(20,5))

ix = 0
iy = 0

totalElapsed = 0
stepsTaken = 1
maxSteps = (xRange/xyStep) * (yRange/xyStep)
up = True

ix = 0
for x in range(-micronToStep(xRange/2), micronToStep(xRange/2) - micronToStep(xyStep), micronToStep(xyStep)):
iy = 0
for y in range(-micronToStep(yRange/2), micronToStep(yRange/2) - micronToStep(xyStep), micronToStep(xyStep)):
start = timer()
values = []
if (up):
pos = {}
pos['x'] = x
pos['y'] = y
pos['z'] = zBase - micronToStep(zRange/2)
pos['z2'] = zBase + micronToStep(zRange/2)
print("Scanning up to position:", pos)

values = microscope.measureZ(microscope, pos)
values = np.asarray(values)
thisTime =  timer() - start
totalElapsed += thisTime
up = False
else:
pos = {}
pos['x'] = x
pos['y'] = y
pos['z'] = zBase + micronToStep(zRange/2)
pos['z2'] = zBase - micronToStep(zRange/2)
print("Scanning to position:", pos)

values = microscope.measureZ(microscope, pos)
values = np.asarray(values)
values = np.flip(values)
thisTime =  timer() - start
totalElapsed += thisTime
up = True

#xs = np.linspace(zBase - micronToStep(zRange/2), zBase + micronToStep(zRange/2), values.shape)
#coef = np.polyfit(xs, values, 1)
#poly1d_fn = np.poly1d(coef)
#plt.plot(xs, values, '-ok')
#plt.axhline(y=np.median(values), color='r', linestyle='-')
#plt.xlim(0, values.shape)
#annot_max(xs,values)
#plt.show()

print(pos)
saveData['data'][ix][iy] = values

iy += 1
timeLeft = (maxSteps - stepsTaken) * (totalElapsed / stepsTaken) / 60 / 60
print(">>>>>>>>>>>> Estimated time left: [" + str(timeLeft) + "hr], Per Scan: [" + str(totalElapsed / stepsTaken) + "s][" + str(thisTime) + "], Total Elapsed: [" + str(totalElapsed) + "s], Scans taken: [" + str(stepsTaken) + "] out of: [" + str(maxSteps) + "]")
stepsTaken += 1
pickle.dump(saveData, open('saveData.bin', 'wb'))

ix += 1

pickle.dump(saveData, open('saveData.bin', 'wb'))
#pickle.dump(dataMaxPositions, open('dataMaxPositions.bin', 'wb'))

print("Homing")
pos['x'] = 0
pos['y'] = 0
pos['z'] = 0

microscope.move(pos)
``````

The code above works with xyStep > 4 (micron). For smaller steps i. e. xyStep = 0.5 the condition` for x in range(-micronToStep(xRange/2), micronToStep(xRange/2) - micronToStep(xyStep), micronToStep(xyStep))` is not fulfilled.

The question is what and how should I change in the code above to make it work with smaller steps e. x. xyStep = 0.6? I suppose it is connected with `"data": [ * ceil(xRange/xyStep) for i in range(ceil(yRange/xyStep))]`? I also read that the stage move in xy plane with accuracy 70 nanometers and in z height with accuracy 50 nanometers, so with that information should I modify my `micronToStep()` function?

Thank you very much for your help.

2 Likes

Do you mean that the range ends up as an empty range? If that is the case I would try checking the values passed for the three parameters in the `range` statement. It does not look as though a number <1 makes a difference, unless `xystep` is interpreted as integer.

If you want to get real-world distances correct you would do best to calibrate you microscope in steps against a known distance target. In the cartesian microscope the xy motion is geometrically different from the z motion, so you would need different conversion factors.

The problem is the `saveData["data"]` is a 400 x 400 list-of-lists.

For `xyStep = 1`, `xRange = 400`, `yRange = 400` in case of loops:

``````for x in range(-micronToStep(xRange/2), micronToStep(xRange/2) - micronToStep(xyStep), micronToStep(xyStep)):
for y in range(-micronToStep(yRange/2), micronToStep(yRange/2) - micronToStep(xyStep), micronToStep(xyStep)):
``````

after performing the calculations, the actual range conditions on these loops are:

``````range(-3225, 3209, 16)
``````

They produces slightly more than 400 iterations which is in this case 403. I thought I should modify the command:

``````"data": [ * ceil(xRange/xyStep) for i in range(ceil(yRange/xyStep))]
``````

Could you show me exactly code solutions to better understand your idea?

I’m not really sure what it is that’s not working - from your description I thought maybe you were ending up with an empty range - but I guess not, from this post. If you were to copy your code and then remove everything inside the for loops, and just print out the position it moved to each time, what do you see?

For values `xyStep = 10` , `xRange = 400` the condition: `for x in range(-micronToStep(xRange/2), micronToStep(xRange/2) - micronToStep(xyStep), micronToStep(xyStep))` is equal to: `range(-3226, 3065, 161) = 40` which is okay with xRange/xyStep = 400/10 = 40.
But for values `xyStep = 4` , `xRange = 400` the condition: `for x in range(-micronToStep(xRange/2), micronToStep(xRange/2) - micronToStep(xyStep), micronToStep(xyStep))` is equal to: `range(-3226, 3161, 65) = 99` which lower than xRange/xyStep = 400/4 = 100. I think it causes to crash `for` loop.

I figured out that the problem is with `micronToStep()` function.

Well done! Yes perhaps defining the output array and the ranges from the same data could help the, or even defining the list of lists on the fly with `append`.

Could you write down, how the solution should look like in my case?

The `for x in range()` loop commands in my opinion works good but the main problem is with calculations in `micronToStep` function. Is it possible to generate correct values with `micronToStep` function?

Could you also explain me, why there are some extra iterations in case of loop for `xyStep = 1` while for `xyStep = 10` there aren’t?

I noticed that modifying to: `for x in range(-micronToStep(1000*xRange/2), micronToStep(1000*xRange/2) - micronToStep(1000*xyStep), micronToStep(1000*xyStep))` gives as it is known incorrect range values but also it gives correct range conditions.

I’ve not tested this, but in case it helps, you could change the `"data"` line here to start with an empty list:

Then, you would need to add a new list each iteration, i.e.:

Then, each time you want to save a new data point, you would do:

So, you still end up with a list-of-lists of `np.array` objects, but you don’t have to specify in advance what its dimensions are. “single-sourcing” the array size like this should eliminate your error when the dimensions are slightly off.

You might want to print or otherwise save the values of x,y,z,z2 at each point though, just as a sanity check to let you make sure you don’t have strange things happening due to rounding errors.