An absolute position encoder for the Openflexure Microscope

In an earlier posting (Home/limit switch) I described a system to provide limit switches (or end-stops) to the microscope mechanism. It was based on a pair of Hall-effect sensors and a small magnet embedded in either end of the actuator column. This then could be used as a home signal to reset the position scale after a power-down. This requires moving to the end of travel and setting the postion to zero. Given the fairly slow speed of the motion, that can be a time-consuming operation. Responses to that posting suggested that a preferable solution would be an absolute encoder, which did not lose its position on power-down. By chance, I came across a TI application note[https://www.ti.com/lit/pdf/slya051] for its Hall sensors which described just such a system. I have used the test setup I built for the end-stops measurements to explore this possibility. That setup uses the same motors as the microscope, so the Sangaboard firmware works. For this setup I get 8192 steps per mm. The data look really promising to me.

The system uses a TI sensor which provides an analog output voltage proportional to the applied magnetic field component perpendicular to its plane. The sensor is placed near a bar magnet such that it senses the field component perpendicular to the long axis of the magnet. In this geometry, when the sensor is at the mid-point of the magnet, this component is zero; the field lines are parallel to the magnet. Moving in either direction along the magnet length causes the field to increase either positive or negative, depending on the direction. Thus, the sensor output is a measure of position. The shape of the output voltage curve which results depends on the proximity of the sensor to the magnet. When it is close, the output is quite nonlinear, but if it is moved away a few millimeters, the central part becomes quite linear. In the following I will show the results of my measurements.

The first test was to look at the shape of the output curve versus distance from the magnet, to try and choose the optimum placement. The curve below shows the measurements.
The magnets I used were ½” long x 1/16” diameter [MAGCRAFT® NSN0578 - Rare-Earth Rod Magnets][ K&J Magnetics: D18]. The general rule is that close in, the field is stronger but the curve is non-linear for all the travel. Further away and the curve starts to show a linear region in the center of travel. This is the situation we want. It is then essentially a compromise between linearity and signal strength. I decided that around 6 mm separation was good.

The collection of the data was done using a Python script to move the steppers on my XYZ translation stage, with the output of the sensor being recorded by an ADS1115 analog-digital converter. I first tried using the Arduino’s on-board 12-bit ADC, but the resolution was marginal. The ADS1115 is a 4-channel 16-bit device, which gives much better data. It is readily available mounted on a small PCB; just google ADS1115 and a host of vendors will pop up. It is already supported by the Sangaboard firmware, masquerading as the light sensor. The sangaboard program needs to be configured for single-ended inputs. By default it assumes a differential input on channels 0 and 1. I also modified it to allow all 4 ADC channels to be read, by using the parameter already passed to the module, but not previously used. The chip is readily available on a small PCB which is easily hooked up to the Arduino [Assembly and Wiring | Adafruit 4-Channel ADC Breakouts | Adafruit Learning System]. The distance was changed in 1 mm steps, and the linearity scan consisted of 600 points, each 200 steps apart, covering a range of 14 mm.
The python script writes a series of files in Octave’s ASCII format. I use Octave (an open Matlab clone) for most of my computational work and find it very easy to use, particularly when dealing with large arrays of data. All of the plots here were generated using Octave.
The next step is to fit this thing to an actuator. I knew there would be trial and error involved, and I didn’t want to have to wait 24 hrs for a new microscope to print, so I hacked at the Openflexure’s SCAD code and generated a single actuator on a simple stand, so it could operate free-standing. That object only takes 4hrs to print.


The magnet is embedded in a hole in an addition to the actuator column. The housing had to be extended a couple of millimeters to accommodate that. The sensor PCB was glued to the outside of that extension. Again, a Python script was used to measure the response over the travel range of the axis. The result is shown below.

The graph shows the superposition of five consecutive scans. The system is very reproducible, including the oscillations at the lower end of the scan. I suspected that they may be the result of uncontrolled lateral translations of the column. A discussion with Richard suggested the addition of a stabilizing link from the microscope base to the top of the actuator column. This should be stiff for lateral motion but allow vertical motion. The photo below shows the stage with this addition. It adds a triangular strut with flexures at the base and tip such that it provides the required degrees of freedom.

The following graph shows the same scan as the previous graph. The reduction of ripples is obvious. The slope of the graph is reversed because I accidentally put the magnet into the column in the opposite polarity. The graph shows 10 consecutive scans plotted together and it is hard to see the differences; a good sign!

To bring out the differences, I fit one of them (#5) to a 5-segment spline and plotted the differences of all ten data sets against this fit. The result is in the plot below.

The peak deviations are around +/- 20 ADUs. It turns out that one ADU is approximately 1 um. If we could eliminate the once-per-turn errors, the remainder would be closer to +/- 5 um. I’ll keep looking for the source of this periodic error. Again, the reproducibility of the data is amazingly good.

So, the real test of all this is how useful will it be in a microscope? First, there will be no need to home the axes anymore. As soon as the instrument is turned on, you know exactly (well, +/- 20 um) where you are. It is difficult to see how any economically viable home switch could do better than that, and you don’t have to wait. I’m not a microscopist so I don’t have an intuition as to what makes a good instrument. I would hope that this helps.

In order to do the acid test I need to put my stage additions into the full microscope base. I struggled with SCAD to extract my little test piece from the full code, but putting it back is beyond me :slight_smile: I have attached my SCAD code which builds the bones of my test stand minus the additional support blocks. Is there a SCAD guru out there who would be interested in doing this?

Below is the SCAD code for the mods to the leg and actuator, ready to be put back into the microscope base. I would have attached a file, but the web site didn’t want me to do that::

use <…/libs/utilities.scad>
use <…/libs/compact_nut_seat.scad>
use <…/libs/logo.scad>
use <…/libs/z_axis.scad>
use <…/libs/wall.scad>
use <…/libs/main_body_transforms.scad>
use <…/libs/main_body_structure.scad>
//use <…/reflection_illuminator.scad>
use <…/libs/libdict.scad>
use <…/libs/microscope_parameters.scad> //All the geometric variables are now in here.
$fn = 32;

module prism(l, w, h){
polyhedron(
points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]
);
}

module housing(){
// front wall
difference(){
translate([-5,10,0]){
cube([10,2,30]);
}
//sensor slot
translate([-2.5,11,0]){
cube([5.0,1.5,30]);
}
}
//right wall
translate([-5,7,0]){
cube([2.5,4,30]);
}
//left wall
translate([2.5,7,0]){
cube([2.5,4,30]);
}
// top
translate([-5,8,30]){
cube([10,4,2]);
}
}

module housing_with_nut_hole(){
difference(){
housing();
translate([-4,6,18]){
cube([8,9,3]);
}
translate([-2,47,0]){
cube([4,1,30]);
}
}
}
module x_only_actuator(){
params = default_params();
ties = key_lookup(“print_ties”,params);
actuator_h = key_lookup(“actuator_h”, params);
translate([0,34,0]){
housing_with_nut_hole();
}
actuator(params);
translate_y(actuating_nut_r(params)){

            actuator_column(actuator_h, join_to_casing=ties);
    }

//actuator_column(actuator_h, join_to_casing=ties);
translate_y(actuating_nut_r(params)){
difference(){
xy_screw_seat(params, label="");
translate([-3,6,0]){
cube([6,2.5,30]);
}
}
}

flex_len = flex_dims().y + microscope_wall_t()/2;
w=flex_dims().x;

reflect_x(){
translate([leg_middle_w()/2-w, 0, lower_xy_flex_z()+0.5]){
//Each flexure is the hull of two offset cuboids.
hull(){
repeat([flex_len,-flex_len,0],2){
cube([w, tiny(), flex_dims().z]);
}
}
}
}

// magnet holder block
translate([-2,38,2.5]){
cube([4,5,15]);
}
}

module magnet_hole(){
difference(){
x_only_actuator();
translate([0,40,4]){
cylinder(h=38,d=3.5); //hole for magnet
}
translate([-3,25,16]){
cube([6,3,14]); // cutout for second actuator arm
}
}
}

magnet_hole();

CubePoints = [
[ -3, 0, 0 ], //0
[ 30, 4, 0 ], //1
[ 30, 6, 0 ], //2
[ -3, 10, 0 ], //3
[ -3, 0, 4 ], //4
[ 30, 4, 4 ], //5
[ 30, 6, 4 ], //6
[ -3, 10, 4 ]]; //7

CubeFaces = [
[0,1,2,3], // bottom
[4,5,1,0], // front
[7,6,5,4], // top
[5,6,2,1], // right
[6,7,3,2], // back
[7,4,0,3]]; // left

module wedge_arm(){
rotate([0,0,90]){
polyhedron( CubePoints, CubeFaces );
}
}
module wedge_arm_hinges(){
difference(){
translate([5,0,21.4]){
wedge_arm();
}
translate([-6,-3,21.8]){
cube([12,3,3.6]);
}
translate([-6,26,21.8]){
cube([12,3,3.6]);
}
}
}
wedge_arm_hinges();

2 Likes

That is remarkably reproducible. The deviation from fit plot seems to show oscillations at one revolution of the main actuator screw (two revolutions of the motor), and it is stable. This indicates that it is giving really useful information on the fine scale, even though the resolution is not as small as the motor steps it is telling us about the actual actuator movement.
It will be interesting how different the calibration is on different instances - from variation of positioning, variation of the magnets and the sensors. and how much it might change in long timescales. The very nice thing here is that the value at the central position is relatively insensitive to the geometry, so even if the calibration changes the central position will still be well defined.

Yes, zero field is zero field whatever happens :slight_smile: There is still plenty of work to do to fully characterize it, and plenty of potential causes of drift: the ADC, the field dependence on temperature, the mechanical stability of the microscope stage among others. I imagine the actuators will need to be calibrated at least once, depending on all these factors, but that is simply a case of making the scan and doing the fit. If it seems this will become part of the project then I’ll write some scripts to do that.

I really need help putting the modified actuator back into the full model. Any volunteers?

1 Like

Thinking about stability, I have noticed that the motors on my setup do get rather hot. Has anyone thought about implementing a power-reduction when nothing is moving? That is standard on most commercial stepper drivers. It would seem to require additional gates on the stepper outputs, unless it can be achieved within the Arduino. Given the gates, one could control the motor current using the analog output PWM signal to reduce the power arbitrarily while stationary.

Some of the 28BYJ-48 steppers seem to run very hot, others do not. There has been some discussion of dealing with this on another thread, but the overheating does not seem to be a universal problem. On one of my builds one motor was noticeably warmer than the others, but I have not had a really hot one (yet).

The drivers we’re using are just Darlington pairs, so could presumably be PWMed if the Arduino pins we’re using support it. We’d probably want to do some testing to figure out what we need to do to keep a decent holding torque, and whether it causes noise/vibration though. I dimly remember the Arduino PWM wasn’t as fast as I wanted for some other motor controller boards I used, so the motors vibrated the whole microscope when stationary!

Yes, you’re probably right. The ULN2003s don’t have a gate input, only a 3-state input, but given that they have an internal pull-down, pulsing that pin would probably work. Then an external, gateable oscillator could do the trick, say a 555. But if it isn’t really a problem… I’m designing a simple board with a Nano, the drivers and the ADC chip, so I will add the 555 and the passives to make the oscillator, and hook its enable to a Nano pin so we can play with the idea.
Pete.

I didn’t get any offers of help with adding the encoder to a full microscope design, so I guess I’ll try something myself.
I saw that the Delta stage is more symmetrical than the standard microscope, so maybe that’s easier. I cloned the gitlab code and tried to render it from the scad file. It failed, giving this error:

ERROR: CGAL error in CGAL_Nef_polyhedron3(): CGAL ERROR: assertion violation! Expr: e_below != SHalfedge_handle() File: /build/openscad/stage/usr/include/CGAL/Nef_3/SNC_FM_decorator.h Line: 418

Am I doing something wrong?
Pete.

Oops! I apologize. I guess I was just too impatient. I tried again and it came up OK.

1 Like

I like this, especially because today my stage broke :frowning:

2 Likes

Well, no-one else was interested, so I haven’t done anything since making it work on the delta stage. And it was painful because I didn’t know SCAD.

I think it’s pretty essential for the microscope to have an encoder. It’s a huge oversight that it wasn’t designed with one in the first place, or at least a single endstop on each axis. There’s no actual way to tell the position of the stage between reboots, so at best the positioning can only be relative during each session until of course you reach the physical extent of the stage and something breaks.

The motor board should save the position between reboots. If you centre the microscope and reset the steps to zero that same location should be zero need boot?

We are working on software to find the centre using and any flat sample. We do a small scan, look at the z position and move to the top of the estimated sphere cap.

Having an encoder would be great, but we would need to find something globally available and easily integrated which isn’t simple.

Anywhere you can get a Raspberry Pi, a Sangaboard, stepper motors, a tube lens, a microscope objective, a large pile of specialized mechanical fasteners, and 3D printed parts, you can also definitely get a limit switch.

We have spent considerable time working with partners in countries working out specific supply chains for specific components. It is certainly not as simple as its available on Amazon, so its available everywhere. Global supply chains are incredibly complex, so limiting components has been a real focus, some we cannot avoid, for each one we can’t avoid this adds an import headache, which decreases accessibility.

We certainly can get limit switches, I know @filip.ayazi had a design with an endstop in the feet. What I am less certain on (because I don’t understand electronic supply chains) is how standard they are in size and compatibility, and without being a large single company can we design around a specific component that is easy to specify in a BOM, and will remain available. Preferably one that is easy to mount and easy to wire up without soldering.

I’m not saying that this is impossible, just that there is a lot to consider when adding something to the design in terms of assembly complexity, and more BOM components to source.

Using end stops on the OpenFlexure system is not particularly difficult, but it adds a lot of assembly steps for the switches and the wiring. It would also be very slow because it needs to go to the end of the range.

Absolute position, as in this thread, is better because you don’t need to find the switch and then go to the centre. The parts are harder to standardise.

As the position is stored in the Sangaboard, it should only need checking periodically. The zero coordinates button in the software maybe should be less accessible. I set the stage to physical centre by looking in the nut slot holes in the actuators and then use zero coordinates as a step during configuration. I can periodically send the stage to (0,0,0), and check it is central. As Julian says, that periodic centering can be automated in software if we have a flat sample (marker pen on a blank slide).

Hello, I tried the hall sensor with ads1115 to measure the absolute position encode which was built in main body already .
Unfortunately, I didn’t get much linear voltage signal vs motor step. It likes parabolic or hat curve steps per voltage change(it should be horizontal line if much linear). But I think it should be that because with different position the magnetic gradient is different, much smooth in the middle, much tilt at the end.
By the way, I’m using 15mm cylinder magnet with 2mm diameter, distance is about 6mm. my motor moving step is about 1000 per mm, it is wield why yours is 8192?

step vs voltage looks linear, but the difference is hat shape which make sense if you know the magnetic filed for dipole


I changed the way mount magnet and sensor for down to up

The most common 28BYJ-48 motors are 64 steps in the primary motor, with Ă—64 internal gearing, giving ~4000 steps per turn. This is geared further Ă—2 with the standard OpenFlexure gearing to ~8000 steps per turn of the actuator screw. There are some motors sold under the same 28BYJ-48 designation with different internal gearing, Adafruit have some at ~500 steps per turn.

got it,so should I change it to ×64 internal gearing? which one is more stable?

I don’t think the internal gearing can be changed, you would need to buy different motors.

The different gearing gives a different step size, speed, and torque. If your motors are able to drive the actuators without stalling then the torque is enough, and you will have faster motion. However there will be larger individual steps, which is probably not good for focus if you have high magnification, but useful for xy. If you have a problem with stalling or large steps then you will need the larger gear reduction.