Intro: Polarimeter With RaspberryPi
A polarimeter is an instrument often used in chemistry to determine the concentration or purity of specific substances, like sugars or organic acids.
Basically a light beam is -linearly- polarized by passing through a polarizer (filter sheet, crystal, mirror) after which the light propagates in one plane. When this light passes through an optically active substance, this plane rotates depending on the substance, it's concentration, the temperature, the wavelength and length of the path through the medium.
The amount of rotation can be measured with the analyzer, which is just another polarizer that can be rotated to return to a maximum or minimum intensity of light, as shown in image 2 ("analysis with polarized light").
The capability to turn the plane of polarized light is characteristic for each substance and is listed as "specific rotation". Image 3 ("specific rotation and usage") shows the related formulas and gives an example.
You can find much more information on polarimetry, polarimeter and optical activity in the internet.
In this project I built a polarimeter with a reproducible precision of +-0.03° (please see the last step for a comment on this). Image 1 shows the schematic design.
I used many things commonly available. The 3D printout of the analyzer gear was the most expensive part of the project (I had to order it for ~40€). I will point out the issues I encountered and how I approached them. If you find better ways, please let me know.
Step 1: Analyzer Unit
The analyzer unit is the mechanical heart of the project. The accuracy of rotating the analyzer decides about the correctness and the reproducibility of measurements.
The analyzer gear has a hollow axis that goes into a ball bearing in the central panel for easy rotation. On the sample chamber side is a cavity to hold the analyzer filter sheet.
I constructed this version of the analyzer gear and motor gear with 0.5 modulus and ratio of 1:4.5 to be able to set 3600 0.1 ° steps per complete circle. From earlier experiments I learned that the sensor can detect differences in polarizer plane orientation that small.
It was printed in Nylon at 75µm layer height in SLS technology. (I cannot recommend FDM here, as these gears have small structures and the FDM prints I ordered always needed additional work to clear the teeth of the gears.)
The analyzer sheet (10mm x 10mm) was fixed with water based glue with great caution not to stain the parts where light needs to pass through.
The central panel provides overall stability to the construction and has to withstand some (small) mechanical strain. I cut out the hole for the ball bearing with a fret saw and laid it flat on a table, the analyzer side up. Then I fixed the bearing with hot glue, repeating on the other side after cooling. Make sure the inner ring can move freely.
After that, I fixed the panel to the middle of the wood chip board with 2 angle joints. I screwed them to the board, but used hot melt glue for vertical fixture, assuring it is perpendicular to the ground plate (images 4-7).
Push the analyzer gear carefully into the ball bearing. If it is loose, apply some glue.
The bipolar stepper motor I used here has 0.9° full step width, which means 400 steps per revolution. The half step mode runs smoother (which is good) and doubles the number of steps per cycle (even better). In the further course steps are the smallest rotational units, that can be performed and held with the device, no matter if they are full or half steps.
Put the motor gear over the shaft and once you are certain about the position fix it with some glue, even though it appears to stick by its own. Not doing so will give you a headache, when you think your analyze program is not working :/
Unfortunately the analyzer gear turned out to be not completely even. That is why the motor cannot be fixed firmly against the analyzer, but requires some play. So I used two angle joints and rubber bands to hold the motor in place.
This works for both directions of rotation when you don't move too fast and too strong.
Here the materials list:
- 1 wood chipboard 100mm x 300mm x 15mm
- 1 plywood panel 100mm x 70mm x 5mm
- 5 angle joints 50mm x 50mm x 15mm
- 10 wood screws 4mm x 16mm
- ball bearing, 15mm inner diameter
- linear polarizer filter sheet 10mm x 10mm
- stepper motor, bipolar, 0.9° stepwidth
- motor gear and analyzer gear - 3D printouts of iges files
- Hot melt glue
- water based glue
- rubber bands
Step 2: Light Sensor
The light sensor must fit the light source, especially with respect to sensitivity to the wavelength used. CdS-photoresitors show maximum sensitivity to light around 600 nm. The resistance changes with intensity of incident light. A dimensionless value can then be read from an AD-converter in a voltage divider setting. Also check the electronics section.
While very sensitive to light and differences in intensity, they show a memory effect such that the response on incoming light depends on the previous exposure to light. To alleviate this circumstance we run a warmup cycle to achieve comparable values for light of same intensity.
Finally the black plastic pipe and black felt reduce stray light from reaching the sensor to a large degree.
- 5mm CdS photoresistor
- a piece of breadboard with solder points (about 3x1cm )
- 1-1.5 cm black plastic pipe (5mm lumen, 1mm wall thickness)
- some black felt
- 1 angle joint (I had to cut away some bit to place it before the analyzer)
- double-sided tape for assembly
I soldered the photoresistor at about 15mm distance from the board, so that the top reaches into the analyzer. Verify the resistor wasn't scorched during soldering. The plastic pipe was glued to the board, with the photoresistor in the center. After drying, a piece of black felt was cut and applied with glue to cover the holes of the board and the gap between the black pipe and the analyzer channel. Screw the angle joint to the main board centered to the analyzer channel and use the double-sided tape to position the sensor in the center of the channel. The sensor surface should show completely and not tilted through the analyzer window.
Step 3: Sample Chamber
Professional polarimeters have special containers for exposing the sample to the light beam, with a passage of 10 cm or 20 cm, allowing for easy calculation of concentrations with the formula shown in the introduction.
I was looking for a cheap replacement which should have a long light path and should not distort light too much. Finally I found a glass jar for olives. It shows quite parallel walls and has around 6 cm (= 0.6 dm) light path.
For reproducibility I'm using the same jar for calibration and measurements. To make sure the jar is always positioned the same way within the light path, I glued a wedge of wood to its bottom and the counterpart to the ground plate. These were covered with black felt, in order to absorb stray light.
I cut the shape of the box from a piece of stable cardboard (~6mm), pasted black felt on the interior part and opened 2 holes of about 1cm for light and sensor. It's good to lap the felt over the edges, so that you can cover all gaps at the lid and corners of the chamber later. Finally I assembled the chamber with large amounts of hot melt glue. I had to insert some extra stripes of cardboard, because the interior came out too small for the jar after folding. This is taken into account now in the given dimensions.
For this step you need:
- your sample container, e.g. a glass jar, no plastic, as it polarized light itself
- plywood 80mm x 60mm
- cardboard 360mm x 190mm
- black felt
- melt glue
- water based/regular glue
Step 4: Light Source: LED
As explained before, specific rotations of substances are often recorded for the sodium D wavelength (589.3nm) at 20° Celsius.
In order to achieve comparable data to recorded values, I selected wired LEDs with maximum intensity around 590nm wave length (amber, yellow), a narrow radiation angle (max. 20°) and high intensity(> 7000mcd), because they have to pass through 2 polarization filters and the sample with around 10 cm distance between LED and sensor.
For easier handling solder long wires to them and attach it to the top of a plastic tube with hot melt glue.
After the sample chamber is ready, screw the angle joints to the chipboard a little set off the light axis. Hot glue the straw with the inserted LED tube to the joints, so that the light passes through the chamber and illuminates the sensor in the analyzer gear. The straw holds the LED in place firmly, while the latter still can be moved or replaced, if necessary.
Hint: Run PolTest and hit 'l' to turn light on. With 'm 1' you can measure the incoming light and arrange the light accordingly.
For the polarizer unit I cut two circles of 20mm diameter from cardboard, one with a 10mm, the other one with a 5mm side length rectangle. Paste them together and use a tooth pick to apply some glue to the edges of the window and fix the polarizer. Be careful not to spread glue over the polarizer!
After drying, glue the polarizer unit to the sample chamber. Again, make sure that the light reaches the sensor undisturbed.
- 5 mm LED yellow/amber, e.g. this one
- 4 cm plastic tube (5 mm, 1mm wall) from the hardware store
- 2 wires (30 cm each)
- 2 angle joints 50mm x 50mm x 15mm
- 4 cm of plastic straw that just fits over the plastic tube
- cardboard, min. 20mm x 40mm, 5mm thick
- polarizer sheet, 10mm x 10mm
- hot melt glue
- water based glue
Step 5: Electronics
You can definitely start with this part of the project and play around with low level functionality of the sensor, motor and light in conjunction with the PolTest program, found in the programming section.
There should be no big surprises as all circuits are taken from other sources and just put together here.
Still there are some pitfalls to avoid:
Keep the motor circuit completely separated from the measurement circuit, not even connect to the same ground. I started to see jumps between odd and even steps of 2-3 units when I tried to "unite" all parts. This broke my search algorithm under certain circumstances. Now the motor chip is driven by the Pi alone, while the motor is powered through an extra battery pack. The light and AD converter chip are run by the lab power supply.
The small capacitor in the sensor circuit came in just recently, the first impression is good but I still need to investigate all consequences (e.g. the well defined steps in the regions with low values).
I would love to use the power supply for all parts. May be you have a good idea how to do that?
The schema is also available on easyeda where you can also view and work with it.
Now grab the parts and go for it:
- ceramic capacitor 0.1 µF
- ceramic capacitor 10 nF
- resistor 5.6k
- 2 x resistor 100
- BC547 (transistor as a switch)
- SN754410NE (stepper motor driver)
- MCP3208 (AD converter)
- 6V Battery Box (for the stepper)
- 5V-5.3V Lab power supply
- RaspberryPi (2 and up)
- stepper motor, bipolar, 6V, 0.9° , e.g. NANOTEC SH4009M0806
- connector cables
Step 6: Programming
To work with the device 3 Python3 modules are added here. You can run PolTest and PolarimeterMain from the command line, either as
or make them executable and run either
Both programs are based on pythons cmd package and display a usage information at the start, which should be quite self explaining.
Polarimeter is the interface module to the hardware and needed by both PolTest and PolarimeterMain. It provides simple elementary functions, like light on/off, stepping forward/backward and most important, measuring. On top of these, it exposes advanced functions for automated calibration and analysis.
PolTest is the developer main program for interaction with the device through Polarimeter. It allows to run all device functions separately, which is helpful for hard- and software development. Data can be recorded for later analysis, that is how the raw data for the present graphs was achieved.
PolarimeterMain is a user main program reduced to the basic functions necessary for working with a polarimeter. It has an additional warmup functionality, which assures that the sensor was exposed long enough to light to show a constant behavior.
While all implementation details can be taken from the comments in the files themselves, I want to present the reasoning for some decisions here. Some more in-depth information, I want to put on my homepage smartypies.com .
Why use maximum values, which correspond to least intensity of light and 90° angle between polarizer planes and not minimum values?
The first graph shows values acquired over 2 complete rounds. A closer look reveals that the peaks with high values are much better defined within a region of 2° (= 20 steps) than the valleys with a range of about 20° meaning 200 steps with many similar values. Graphs 2 and 3 show this in details, with corresponding regression functions and coefficient of determination.
How are search ranges for peak approximation defined?
Depending on the current value, a helper function returns viable step width to continue the search for the peak from the current position. At first it calculates the percentage of the current value with respect to the range of values (highest value - lowest value). A low percentage means we are in the valley and are far off, so we need take big steps in direction to the peak. A high percentage shows we get closer to the top and need to make smaller steps in order not to pass by the peak. Since our peak region is about 2° degrees (=20 steps) the smallest steps must be much less than 20 steps. Graph 4 shows the (absolute) slopes and the percentages for positions 0 to 900. For example at position 100 we have approximated slopes around 10 and the corresponding value is at 70% of the value range. At position 500 we are just around the lowest 10% and the slope is only slightly bigger than 1. The current implementation assigns the given value to a percentage range and returns a step width that proved working. A future version could employ a fuzzy logic approach or make use of the apparent similarity of percentage and slope curves.
How is the maximum found?
The approachpeak function returns a position close to the peak, which is handed over to the findmax function. It simply collects many values (currently 31) around the peak, performs a 2nd degree polynomial regression and solves for the maximum, where the derivative is zero. When there are several equal maximum values, the shape will not be symmetric, as shown in Graph 5. The resulting polynomial still appears well defined.
What could be next?
- The current versions are not fool proof, wrong user input can break the code.
- It would be nice to separate the Polarimeter class into a general device class and specialized classes.
- create a test class that mimics a polarimeter by returning measure values from real data (see run-7200.csv).
- write a web interface.
- make it a two button box, "calibrate", "analyze" and display result on a small display
Step 7: Running the Show ...
Testing the device capabilities with PolTest
Check the insulation of the sample chamber against light, press 'm 1', for one measurement every second. You should see values close to 4095. Press 's' to stop the automatic measurement.
Turn on the light with 'l', followed by 'm 1' afterwards to see how long it takes until the the values do not change significantly any more (the warm up time).
Run the motor with 'r' one step forward, with 'r -30', thirty steps backward and with 'r 1 7200', 7200 single steps forward. The last command can be interrupted with 's'. After every given step widths (1, -30) number of steps the measured value is printed.
Open a record file with 'rec filename.csv' and run 'r 1 7200'. After about 14 min it should be finished. Close the file with 'rec off' and start the mathematical analysis.
You can also use the 'z' command for calibration (setting to zero) or analysis, with 'ana' . Just keep in mind that you have to turn on light explicitly in PolTest (with 'l').
Running an analysis with PolarimeterMain and PolTest
After start run 'w' for a warmup cycle. This may have to be repeated several times until values have settled.
Fill water into the sample holder and press 'z'. Take care that there are no air bubbles or dirt in the sample. The analyzer wheel will turn to half a circle to scan the landscape of values and return to the point where the maximum has been found so far. After some further search the peak is scanned and the maximum position is determined and set to zero.
Now fill in the sample with an optically active substance. Funny enough, regular household sugar is a very potent "light twister" with a specific rotation of 66.5°, which means that 1g sugar per 100ml solution can rotate the light passing through 1dm by 0.665°.
I dissolved 25 g sugar in water to yield ~300 ml of solution and ran analysis with 'ana 66.5 0.6', where 66.5 is the specific rotation and 0.6 is for 6cm = 0.6 dm, in order to calculate concentration directly.
Interestingly enough there is quite a variance in the results, running repeated analysis.They range from 3.29°-3.47° which is not good. I ran the same sample with PolTest, but let the analyzer rotate a bit before (30, -30, 50, 100). After that, analysis values range from 3.41°-3.42°, resulting to 8.55g-8.57g/100ml, which is pretty close to our expected 8.33g/100ml. See the logfiles of the runs. Since the values are well reproducible in PolTest, I dare to claim a precision of 0.03°. Probably adding a rotation before analysis alleviates the problem even though I'd rather fix the original issue. For thorough tests we need exact concentrations (prepared with laboratory scale and glass ware) and also control the temperature. This can be the incentive to integrate thermal sensors and Peltier elements to the device in future.
This project combines several aspects like motion, sensors and feedback control. There appear to be many options to improve precision and reproducibility, like better gears, a higher gearing ratio, higher bit resolution in the AD converter, improved polarization filters/devices, better light source and sensor or real sample cells. With the programs provided, these modifications can be easily integrated and tested, so that we finally end up with a helpful yet affordable instrument.
This work would not have been possible without the work of many others. Just to mention a few, which were particularly helpful to get started in some areas:
Tom Igoe, stepper motors
Stephen Caudle, spidev
Special thanks to Franz-Erich Schmitz, for his introduction to gears and STL files for another prototype, which can be found on my website.