ROMIO: a Remotely-Operated MIcrOscope

by DaniloR29 in Teachers > University+

830 Views, 4 Favorites, 0 Comments

ROMIO: a Remotely-Operated MIcrOscope

Slide17.jpeg
By love, that first did prompt me to enquire;
He lent me counsel, and I lent him eyes. 

William Shakespeare. Romeo and Juliet. Act II.


We could not help but continue exploring our favourite hobby: microscopy. The interest received in our previous microscopy-related projects has encouraged us to propose other ideas and designs based on further elaborations of both the ROTAMI and the roto-microscope projects. For the latter, Arduino was the microcontroller of choice. In this new device, we decided to take a step further to venture into the use of the fabulous Raspberry PI computer. The results paid back all the efforts. The ROMIO (we hope Shakespeare's lover will forgive us for using the illustrious character's name association) is a remotely controlled translational platform for USB digital microscopes. In addition to the translation movements, it is possible to adjust the focus of the digital microscope with an additional servo motor. The device is operated remotely by a RaspPI zero 2 W. We have developed a simple Python program to control the microscope and automatically collect images for stitching reconstruction.

Moreover, using an additional RaspCamera, you can monitor the device's correct functioning. The project requires a 3D printer, an inexpensive USB microscope and a Raspberry PI zero W (or higher models) version 2. The project could be used as a STEM project or an affordable replacement for more expensive school equipment for biology projects. 

I hope you find this project helpful and enjoyable, too!

Downloads

Supplies

For this project, you need the following parts:

HARDWARE

  1. 3D printer. All the parts have been printed using PLA filaments. We have used a resolution of up to 0.3 mm to speed up the printing of the main body and the microscope table support. We have used a Creality Ender 5 Pro for this project.
  2. One Raspberry Pi Zero 2 W board or higher models.
  3. Two SG90-9G Micro servo motors. One was used for moving the Y-axis of the platform, and one for the focusing mechanism of the microscope.
  4. One SG90-9G Micro servo continuous motor. This model was used to control the X-axis.
  5. A USB MIcroscope. We have used an inexpensive Chinese one with a claimed magnification range of 50-1000x and a camera resolution of 3M pixels (640x480). However, better-quality USB microscopes might work as well.
  6. Connecting cables.
  7. A support platform for electronics. See the one in our previous The Sand slicer project.
  8. A power bank battery.
  9. Microscope slides (62x20 mm).
  10. Grease or just vaseline for lubricating the moving part (optional).
  11. A flat rubber band (4 mm x ~100 mm). Just cut a rubber band for office stationery.
  12. SCREWS:
  13. Six + three screws to fix the servo motors to the platform and the gears (usually come with the servos).
  14. Four M2 15 mm screws to attach the servo holders to the translating platform.
  15. Two small M4 4mm screws (we have used the one taken from a Mechano kit)

3D PRINTED PARTS

The files in STL format of all parts are provided.

Build the X-Axis Translation Mechanism

Slide1.jpeg
  • First, print the Base (Part A) as shown in the picture.
  • Then print gear one and servo1 support (Part B). Now attach the servo, support and the first gear according to the Step 2B picture above using screws.
  • Finally, attach this to the base where the two small holes are and the large opening using screws.

Build the Microscope Holder

Slide2.jpeg
Slide8.jpeg


  • For the second part of ROMIO, print parts C1 and C2, shown in the image above. The USB microscope you have might be different from the one we used. Therefore, we provide below the original program for the software OpenScad that you can freely modify to adapt to the size and shape of your microscope.
  • Attach these two parts, as shown in the picture, using screws.
  • Then print part C3 (the collar) and attach it to part C2 as shown in the image. This is the completed microscope Holder (MH)
  • Finally, attach the MH to the X-axis translation part, as shown in Step 3D above. Remember not to tighten the screws too much as you connect the MH, as it will reduce the X-axis movement.



//
// This file contains the modules for the USB microscope holder (PartC1-3)
// of ROMIO
// (c) 2022 Danilo Roccatano 

$fn=100;
module PartC1() {
         difference() {
          translate ([7,8,-30]) cube ([8,7,70]); 
          translate ([10,7.5,-7]) cube ([55,9,14]);
 // hole for the fixing screws
          translate ([0,11.5,-4.5]) rotate ([0,90,0]) cylinder (19,1.4,1.4); 
          translate ([0,11.5,4.5]) rotate ([0,90,0]) cylinder (19,1.4,1.4); 
         }  
         difference(){
          union() {
            translate ([7,8,30]) cube ([8,32,10]); 
            translate ([7,8,-30]) cube ([8,32,10]); 
          } 
            rotate ([0,90,0])    translate([-35,30.5,-28])  cylinder(100,1.5,1.5,center=true); 
            rotate ([0,90,0])     translate([25,30.5,-28])  cylinder(100,1.5,1.5,center=true); 
            rotate ([0,90,0])    translate([-35,26,-28])  cylinder(100,1.5,1.5,center=true); 
            rotate ([0,90,0])     translate([25,26,-28])  cylinder(100,1.5,1.5,center=true);        
        }
 }
 
 module PartC2() {
    difference(){
           translate ([10,8,-15]) cube ([40,7,30]);
              translate ([6,7,7]) cube ([83,10,40]);
            translate ([6,7,-47]) cube ([83,10,40]);
// opening
             translate ([15,7,-2]) cube ([30,10,4]);
             translate ([31,12.5,-10]) cylinder (30,1.4,1.4); 
             translate ([36,12.5,-10]) cylinder (30,1.4,1.4); 
        // hole for the fixing screws
          translate ([0,11.5,-4.5]) rotate ([0,90,0]) cylinder (19,1.4,1.4); 
          translate ([0,11.5,4.5]) rotate ([0,90,0]) cylinder (19,1.4,1.4);     
         }
     }
 
module PartC3 () {
           translate ([-10,0,0])  union(){
           translate ([0,-5,0]) difference(){
           translate ([25,-6.6,0])  rotate( [0,90,0]) cylinder (14,18,21);
           translate ([24,-6.6,0])  rotate( [0,90,0]) cylinder (44,15.1,17.5);
          for (a =[0:45:360])
            translate ([39,-6.6,0])  rotate( [a,0,0]) cylinder (84,0.5,0.5);
// holes for fixing screws
            translate ([33,-56.6,-1])  rotate( [0,90,90]) cylinder (44,2.4,2.4);
          }
          translate ([25,5,-7]) cube ([14,5,14]);
       // slider  
         difference(){
          translate ([25,9,-1.9]) cube ([14,6,3.9]);
          translate ([32,12.5,-10]) cylinder (30,1.4,1.4); 
         }       
      }
   }
PartC3();

Build the XY-Axis Slider

Slide3.jpeg
Slide9.jpeg
  • After completing the first two steps, print out the X-axis sliding platform in the picture (Part D).
  • Then print out the Servo2 support and Gear2 (Part E).
  • Attach Part E with the servo shown in Step 5B (similar to step 2B) using screws.
  • Finally, attach this to the Part D that you printed before, according to Step 5C

The Assembly of ROMIO

Slide4.jpeg
Slide13.jpeg
Slide11.jpeg
  • Mount the XY-Axis Slider to the rest of ROMIO as shown in step 6.
  • Print the microscope slide holder (Part F). For this part, we provide also the OpenScad code as the microscope sides can have different lengths. Therefore, you can adjust the part accordingly. The script generate the rack using the function InvoluteGear_rack from the public library by Leemon Baird, that can be download at https://www.thingiverse.com/thing:1645390.
  • Attach it to XY-Axis Slider as shown in step 8 on the image.
  • Note that under Part F, we placed on the bottom of the squared well a suitably sized white plastic sheet that we made by cutting out a white plastic square container with flat walls (for example, we used an empty container for deionized water). This improved the visibility, and we suggest adding it to your ROMIO.
module newStageY() {  
n1 = 16; //red gear number of teeth. Set to zero if you don't want to print a pinion.
n5 = 35; //gray rack number of teeth. Set to 1 if you don't want to print a rack- that's as small as it gets (you'll get 2 teeth if you set it to zero... I haven't tried to determine why that happens)
n6 = 18;  
mm_per_tooth = 2.25;

thickness  = 9; // depth of the rack from side-to-side, including backboard if any. The pinion will be this depth minus the backboard_thickness
hole    = 2; //hole diameter in pinion gear
height   = 4; // distance from bottom of the rack to the tips of the teeth

//rac-Added - Set your values here for a Backboard along one side of the teeth.
backboard_thickness = 0.0; //set to zero for no backboard
  backboard_height = 2; //set to zero for top of backboard to be even with tips of teeth, a positive number will extend above the teeth by the chosen amount. Negative number will place the top of the backboard lower than the teeth.

//rac-Added - Set your values here for a rear Side-Flange along one side of the teeth extending to the rear of the rack (opposite the direction of a backboard).
side_flange_thickness = 0; //set to zero for no rear Side-Flange
  side_flange_height = 10; // A positive number extends the side-flange beyond the bottom of the rack the by specified amount.
  flange_offset = 0; //Set to zero for a flange running coplanar with one side of the teeth. A positive number moves the flange toward other side of the teeth

//rac-Added - Set your values here for Stop Blocks at one or both ends.
stop_height = 0.8; //Stop Blocks will extend this much above the teeth. A positive number will extend above the teeth by the chosen amount. Negative number will place the top of the block lower than the teeth. 
  left_stop_enable = 1; //set to 1 to generate Left Stop Block, set to 0 to disable
  right_stop_enable = 1; //set to 1 to generate Right Stop Block, set to 0 to disable

//rac-Added - Set your values here for rear End-Flanges at one or both ends.  
flange_height = 2.25; //Flanges will extend this much from the bottom of the rack. ****Combine with 0 height Stop Blocks to effectively start flanges at top of tooth instead of backside.****
  left_flange_enable = 0; //set to 1 to generate Left Rear Flange, set to 0 to disable. 
  right_flange_enable = 0; //set to 1 to generate Right Rear Flange, set to 0 to disable.
//end of inputs """"""""""""""""""-   


 difference() {
   color ("purple",1) union() {
   translate([2.5,-6,-10.5]) cube([85,45,6],center=true);
    translate ([45,14,-10.5] ) rotate([0,0,-90]) color([0.75,0.75,0.75]) InvoluteGear_rack(mm_per_tooth,n6,thickness-3,height,backboard_thickness,backboard_height,side_flange_thickness,side_flange_height,flange_offset,stop_height,left_stop_enable,right_stop_enable,flange_height,left_flange_enable,right_flange_enable);  
   translate([7.5,-21,-10.5]) cube([85,15.5,6],center=true);  
   }
   translate([0,-10.7,1]) cube([76,42,21],center=true);
   translate([0,-6.7,-5]) cube([68,30,21],center=true);
}  

}

Build the Adjusting Focus Mechanism

Slide5.jpeg
Slide10.jpeg
Slide14.jpeg



  • The final part of ROMIO is the focusing wheel attachment to focus the microscope. First, print out FocusParts A and B shown in the picture above. Also, in this case, as your USB microscope might be different from the one we used, we provide below the OpenScad program that you can freely modify to adapt to the size and shape of your microscope.
  • As shown in the photo, you need to glue a flat rubber band around the Wheel to provide the grip of your microscope's focusing wheel.
  • Attach these to a servo motor as shown in Step 9C using screws
  • Finally, attach the Focus Mechanism to your USB Microscope focusing wheel so that FocusPart B touches it.
  • In this prototype, the servo provides the control for the adjustment of the focus but does not allow a change of the magnification. You need to rotate the focus to the correct magnification and then fix the focusing mechanism to the microscope.


//
// This OpenScad script contains the modules for the servo attachemnt
// to the USB microscope for controlling the focusing mechanism
//
// (c) 2022 Danilo Roccatano

$fn=100
// Attachment for the USB microscope

module MicroAttach() {
  union() {
    union() {
      difference() {
        union() {
          translate([0, -2, -4]) cube([34, 20, 20], center = true);
          translate ([0, -22, -4]) cylinder(10, 17.7, 17.7);
        }
        scale (1.00) translate ([0, 2.5, 0]) cube([23, 12.5, 22], center = true);

        for ( hole = [14, -14] ) {
          translate([hole, 2.5, 5]) cylinder(r = 1.0, h = 20, $fn = 50, center = true);
        }
        translate([15, 5, -11.5]) cube([90, 40, 15], center = true);
        translate([-5, -21, 2]) cube([80, 0.1, 15], center = true);
        translate ([0, -22, -5]) cylinder(20, 16.7, 16.7);
      }

      difference() {

        translate ([0, -22, -1]) cylinder(3.35, 16.7, 16.7);
        translate ([0, -22, -3.0]) cylinder(10, 15, 15);

        translate([-5, -21, 2]) cube([80, 0.1, 15], center = true);
      }

      difference() {
        translate([-21, -19.95, 1]) cube([8, 2, 10], center = true);
        translate ([-22, -15, 1]) rotate ([90, 0, 0]) cylinder(10, 1.9, 1.9);
      }
      difference() {
        translate([-21, -22.05, 1]) cube([8, 2, 10], center = true);
        translate ([-22, -15, 1]) rotate ([90, 0, 0]) cylinder(10, 1.9, 1.9);
      }
      difference() {
        translate([21, -19.95, 1]) cube([8, 2, 10], center = true);
        translate ([22, -15, 1]) rotate ([90, 0, 0]) cylinder(10, 1.9, 1.9);
      }
      difference() {
        translate([21, -22.05, 1]) cube([8, 2, 10], center = true);
        translate ([22, -15, 1]) rotate ([90, 0, 0]) cylinder(10, 1.9, 1.9);
      }

    }
  }
}


module Wheel() {
  // Attachment to the USB microscope


  //translate ([0,-22,-1])     difference(){
  translate ([5, 0, 18])  difference() {
    union() {

      translate ([0, 0, 1]) cylinder(4, 12.67, 12.67);
      cylinder(4, 6, 6);
      translate ([0, 0, -2])      cylinder(6, 3, 3);
    }
    translate ([0, 0, -4]) cylinder(5, 2.46, 2.46);
    translate ([0, 0, -1.8]) cylinder(10, 1.25, 1.25);
    translate ([0, 0, 2]) cylinder(5, 2.44, 2.44);
  }
}

Wheel();
MicroAttach();

Connect to Rasberry PI Zero 2 W

Slide7.jpeg
Slide15.jpeg

The ROMio is controlled remotely by a Raspberry Pi 2 W connected to the three servos. The Figure shows all the connections with the RaspPi pins. Note that the servos are powered by the RasPi board, which is achieved by building a T connector to supply 5V Vcc from pin 2 to two servos.

We have used a 3D printed case for RaspPI and the Pi camera (STL are provided). The Picamera case can be vertically mounted on top of the RaspPI case. The camera can be used to check the working condition of the device remotely.

The X-axis servo used in this prototype is a continuous rotation type model. It allows extensive motion along the longer side of the microscope slide. For the Y-axis movement, we used a standard servo motor having a rotation span of 180 degrees. The total linear motion along the Y-axis of ~9 mm.

We have also used the RasPicamera to monitor the working condition of the device. We provide the case for the Raspberry PI (pizeroW.stl, pizeroTop.stl) and the Picamera (PicameraCase.stl). The latter one can be attached to the RasPi case lid as shown in the Figure. You can orient the PiCamera+RasPi to the device as shown in the figure. However to get the camera to a so close distance, you need to change its focus. A good practical guide can be found in this link

https://projects.raspberrypi.org/en/projects/infrared-bird-box/6


Finally, do not power ROMio using the USB of your laptop, but use an external power supply. We have used a power bank.

RasPi Software: the PythonTk GUI

Slide19.jpeg

We setup the Raspberry Pi Zero 2 to use a VNC connection with a laptop to access the Raspberry Pi. To control ROMIO from the RaspPi, we have written a Python GUI interface using the library Tinker, and we called it RasPyliet. You can find below the current beta version of this code that still might need improvements. Therefore, suggestions are welcomed as usual.

In addition to the library Tinker, you must install the library gpiozero (see information at the link https://lawsie.github.io/guizero/) to drive the servos.

You also need to install OpenCV libraries. The instructable by Nishantk06

https://www.instructables.com/Install-OpenCV-on-Raspberry-Pi-in-Less-Than-10-Min/

give considerable help to speed up this task.

Finally, you need the library Pillow that can be installed using the command:

pip3 install pillow

To reduce the servo jitter, we used pigpio pin factory. Therefore, you need to activate the library using the command:

sudo pigpiod

Before running RasPyliet, you also need to check which video device is connected to the USB microscope camera in PI Camera. You can check by running the command.

v4l2-ctl --list-devices

On our RasPi, we have got the following output

bcm2835-codec-decode (platform:bcm2835-codec):
/dev/video10
/dev/video11
/dev/video12
/dev/video18

bcm2835-isp (platform:bcm2835-isp):
/dev/video13
/dev/video14
/dev/video15
/dev/video16

mmal service 16.1 (platform:bcm2835-v4l2-0):
/dev/video2

USB2.0 UVC PC Camera: USB2.0 UV (usb-3f980000.usb-1):
/dev/video0
/dev/video1


In this case, the video device for the USB microscope is video0 e for the camera video2. If different, then change the numbers accordingly in the cv2.VideoCapture command lines

cam = cv2.VideoCapture(0) # USB microscope
cam1 = cv2.VideoCapture(2) # Raspberry Camera

in the RasPyliet program.

In the first Figure, you can see the two view mode of the program that can be switched using the green button on the bottom of the interface. The default mode is the USB camera, and the second mode is the Pi Camera.

You can move the slide along the Y direction using the scale widget on the right side of the video image. The Y-servo is a standard servo, and the value of steps represents the degree of rotation that varies from ~-90 to 90. As mentioned before, the standard servo gives accurate positional information as it contains a potentiometer, but given the radius of the gear, the linear excursion is limited to ~9 mm.

You must use the Move Left/Right buttons to move the stage along the X-direction. For the X movement, a continuous server is used, and in this case, you can control the speed, but you do not have information about the current rotation of the motor shaft. Therefore, each movement step is performed for a fixed number of rotation steps (50) at a constant speed of 0.25. You can experiment with these two numbers changing their values in the code.

Speed=0.25
FStepX=25


The X-steps are the number of relative movement steps performed with respect to a reference position that you can set using the "Set Zero X" button.

You can save snapshots of the images using the SavePic button. The picture is saved in png format in the same directory where you launched the program using the root name specified in the input box at the bottom of the GUI.

For making a photo scanning along an XY range, you can set the value of the max/min values for the movement along the two axes by clicking the buttons or by setting the value directly. By pushing the scanning button, the scanning sequence is initiated by moving the X-axis first for the number of steps specified as |max-min|/increment. The figures are saved using the root name as in the File Name box, adding two numbers that specify the sampling step along X and Y in the format "rootnamexx-yy.png". We suggest running the program from an empty directory to avoid mixing the generated picture files with other files.


Downloads

Scanning and Stitching

IMG_5712trim.mov.gif
Slide20.jpeg

As an example of application, we have scanned a large specimen (the fragment of the elytra tip of a locusta). This specimen was collected by the author more than 35 years ago. We collected a grid of images from the specimen and then used the program Fiji's plugin grid/collection stitching (https://imagej.net/software/fiji/) to stitch all the photos together.

The translation motion of our first ROMIO prototype is not yet utterly smooth. Therefore, before using the images, we manually removed the unfocused ones and those without the elytra to avoid ghost effects or the lack of convergence of the stitching program. For this reason, we also run the stitching program with type selection: unknown positions.

You can find a detailed description of the plugin at this link: https://imagej.net/plugins/image-stitching

By looking at the result in the Figure, you can judge this first experiment.

Conclusions

Slide21.jpeg

We have presented a prototype for ROMIO, a remotely controlled sliding platform for USB microscopes. The device provides XY translation movements for microscope slides and the possibility to adjust the focus of a USB microscope mounted on it. A Raspberry Pi Zero 2 W is used to operate the device remotely using the Python interface RasPyliet.


The story of ROMIO and RasPyiet did not end up in a tragedy, but as with every prototype, it still has some known problems and limitations, and there is space for improvement. So we hope you like it, and constructive comments and suggestions are always welcome!