Introduction: DIY Laser Steering Module for Arduino

About: I am a hobbyist with an interest in open-source software, 3D printing, science and electronics. Please visit my store or Patreon page to help support my work!

In this Instructable, I will demonstrate the construction of a dual-axis, single-mirror laser beam steering module using 3D printed parts and inexpensive components from eBay.

This project has similarities to Arduino Laser Show with Full XY Control and Arduino Laser Show With Real Galvos but I believe is the first to use a 3D printed design with inexpensive solenoids. I am putting all the design files under the GPLv3 so that the design can be enhanced and improved upon.

Although at present I have only assembled the module and written some very basic test code, my hope is that one day I can take it to the next level by incorporating the vector graphics code from my earlier Instructable, Super Fast Analog Voltages from Arduino.

Step 1: Gather the Non-3D Printed Parts

The laser assembly consists of the following parts:

The particular solenoids I used were purchased on eBay for $1.45 each. The round mirror was found in crafts aisle at HobbyLobby -- a pack of 25 cost me less than $3 dollars. You can also find mirrors on eBay.

You will also need an inexpensive laser pointer, again, from eBay. A violet laser along with a glow-in-the-dark sheet of vinyl is an excellent combo for this project!

A set of helping hands is not necessary, but will be very useful for holding and positioning the laser pointer. A large binder clip can be used to hold down the power button.

You will need an Arduino (I used an Arduino Nano) and a way to drive the solenoids. As VajkF has stated in the comments, you could use pre-made H-bridge such as those based on the L298 or the L9110. These are readily available on eBay for a few bucks and can also be used for driving motors and robotics projects.

Since I did not have an H-bridge, I built my own driver out of discrete components:

  • Four NPN bipolar transistors (I used a MPS3704)
  • Four resistors (I used 1.2k ohm resistor)
  • Four diodes (I used an 1N4004)
  • A 9V battery and battery connector

The electronic components were from my lab, so I do not have an exact cost for them, but unless you already have the parts or can scavenge them, it's probably more cost effective to use a pre-build H-bridge. Nonetheless, I will provide the schematics for building your own.

Step 2: 3D Print the Mirror Steering Module

The laser steering module consists of two 3D printed parts: a base for mounting four solenoids and an articulated platform for the mirror.

I have attached the two STL files for you to 3D print, as well as FreeCAD files in case you need to modify the design. All content is under the GPLv3, so you are free to make and share your improvements!

Step 3: Assemble the Laser Module

  • Use hot glue to affix the four solenoids to the lower piece.
  • Use hot glue to affix the mirror on the center of the upper piece.
  • Insert the metal pistons into the solenoids and then position the upper piece on the posts (but do not screw it down). Rotate the top piece slightly and using a small screw driver, lift each piston into position. The lip of the disc should slide into the groove on the piston. Be careful, as the 3D printed hinges are very fragile. With patience and possibly a few failed attempts, you should be able to position all four pistons without twisting or putting pressure on the hinges.
  • Once all pistons are positioned, partially insert the M3 screws, but prior to tightening them down, push down on each piston gently and make sure the mirror tilts freely. If it does not move freely or catches, it may be necessary to remove the top plate, pry off one or more solenoids loose and re-attach it at a slight outward angle (putting spacers between it and the central post may help with this).

Step 4: Print the Laser Pointer Collar

The laser pointer collar fits on the head of the laser pointer. You can then use a set of helping hands to grasp the collar and allow you position the laser precisely on your bench.

Step 5: Assemble the Driving Circuit

The drive circuit is shown in the schematic. As stated earlier, my version is built out of discrete components, but you could also use a readily available H-bridge. If you choose to build your own, you will need to build four copies of this circuit, one for each of the four solenoids.

Each circuit will connect to an Arduino pin, two for controlling the left and right solenoid, and two for the up and down solenoids. These will need to be connected to PWM capable pins, like so:

  • Pin 9: Up Solenoid
  • Pin 3: Down Solenoid
  • Pin 11: Left Solenoid
  • Pin 10: Right Solenoid

A single 9V battery can be used for driving all four solenoid driver circuits or you could use a benchtop power supply. The Arduino will run off USB power and should not be connected to the positive side of the 9V battery. However, the negative side of the battery is used as ground-reference and should be wired to the GND pin on the Arduino as well as to the emitter pins on the transistors.

Step 6: Upload the Sample Code

The sample code has been updated with the following features:

  • Adjusts the PWM frequency such that the mechanism is nearly silent at low speeds. The buzzing in Motion Test 1 is entirely gone!
  • Adds as voltage equations based on the paper by Schimpf in order to "linearize" the non-linear response of the solenoids.

I've also included an implementation of a Lorenz Attractor based on the code from this blog.

The fidelity of the results leave quite a bit to be desired, but I am still working on it! :)

The subsequent steps illustrate some of the techniques used in the code.

Step 7: Turning Down the Volume

In my Motion Test 1, you can hear a loud buzzing, in particular during up and down movement. It turns out that this was caused by the default PWM chopping frequency of the Arduino being within the audible range. The rapid switching of the coil voltage on and off would cause them to vibrate at that frequency, making them into tiny little loudspeakers.

To solve this problem, I increased the PWM frequency in the code:

#define PWM_FREQ_31372Hz 0x01 // Sets the PWM frequency to 31372.55 Hz<br>#define PWM_FREQ_3921Hz  0x02 // Sets the PWM frequency to 3921.16 Hz
#define PWM_FREQ_980Hz   0x03 // Sets the PWM frequency to  980.39 Hz

void setPWMTimerFrequencies(uint8_t frequency) {
  TCCR1B = (TCCR1B & 0b11111000) | frequency; // Set timer1 (pins 9 & 10) frequency
  TCCR2B = (TCCR2B & 0b11111000) | frequency; // Set timer2 (pins 3 & 11) frequency

Setting the Arduino PWM frequency is a useful trick for quieting down solenoids or motors. Experiment with the different choices of frequencies to see which one give you the best results. Although it involves some more advanced programming, a good resource on how the timers work is here.

Step 8: Tuning the Voltages to Reduce Distortion

My initial motion tests showed that the were significant distortion in the response of the solenoids. In Motion Test 3 (left figure), what was supposed to be a circular spiral instead became a rectangular web with jagged edges.

Solving this problem required a little bit of math, but I was able to locate an amazing paper on the web that helped me understand the problem well enough to solve it in software.

What follows steps you through the process I went through to tune the system and improve the appearance of the resulting traces!

Step 9: Perfecting the Software, With Math

The secret to tuning the system turned out to be an excellent paper called "A Detailed Explanation of Solenoid Force" by Paul H. Schimpf of Eastern Washington University (link). In particular, equation 17 gave me the solenoid force in terms of various terms.

The following terms were easy to measure:

  • R - The resistance of my solenoid
  • l - The length of the solenoid
  • x - The displacement of the piston in the solenoid
  • V - The voltage across the solenoid

I also knew that the force put out by the solenoid had to balance out the force from the 3D printed springs on the dual-axis mirror. The force of a spring is governed by Hooke's law, which is stated as follows:

  • F = -kx

Although I did not know the value of k, I at least knew that the force I got out of equation 17 from Schimpf's paper had to equal the force from Hooke's law.

The value of alpha (α) was a tricky one. Although equations 13 and 14 showed how to compute these values from the area of the solenoid (A), the number of turns (N) and magnetic permeability values (μ), I did not want to have to tear apart a solenoid to count the number of turns, nor did I know the material out of which my solenoid's core was made.

Step 10: An Inexpensive Component Tester Saves the Day!

It turned out however, that equation 15 and 16 gave me what I needed. I had an inexpensive M328 component tester that I had purchased from eBay for $10. It was able to use it to measure the inductance of my solenoid and I found that by pushing the armature in at different depths gave me different induction values.

Measuring it with the armature fully inserted gave me the value of L(0).

The length of my solenoid was 14mm, so I measured the inductance with the armature at five positions and this gave me various values for L(x):

  • L(0.0) = 19.8 mH
  • L(3.5) = 17.7 mH
  • L(7.0) = 11.1 mH
  • L(10.5) = 9.3 mH
  • L(14) = 9.1 mH

I then used a spreadsheet to plot my values vs. the value of equation 15 and 16, for a particular choice of μr and then varied my choice until I found a good match. This happened when μr was 2.9, as shown in the graph.

Step 11: Find the Spring Constant K, Solve the Problem

The only remaining unknown was K, the spring constant. I measured this by applying 9V to one of the solenoids in my dual-axis assembly and measuring the distance in which the mirror was pulled down. With these values, I was able to solve the equations for K, which I found was around 10.41.

I now had the values I needed to compute the pull of the solenoid at various positions along the stroke. By setting F(x) equal to the spring force from Hooke's law, I can solve for the required voltage V.

The graph shows the voltage required for moving the solenoid to any desired position x.

On the right, where the voltage is zero and the position is 3 mm, this corresponds to the neutral resting point of the solenoid when the 3D printed hinges are fully relaxed. Moving left on the graph corresponds to pulling the armature into the solenoid against the pull of the 3D printed hinges—this initially requires more voltage, but as the armature gets deeper into the solenoid, the pull increases and the required driving voltage tapers off.

This relationship is definitely non-linear, but with the equations from Schimpf's paper, I can write my Arduino code to output the correct voltages so the the beam deflection is linear:

float positionToVoltage(float x) {
  // Restoring force exerted by hinges (Hooke's Law) at desired x. 
  const float spring_F = -spring_K * (x - spring_X0);

  // Voltage such that the pulling force of the solenoid matches the
  // restoring force of the hinges
  return sqrt(-2*R*R*(-spring_F)*solenoid_len/(a*L_0*exp(-a*x/solenoid_len)));

This leads to a much more circular spiral than in my original motion test. Mission accomplished!

Step 12: Question and Answers About the Driver Circuit Using Discrete Components

Why can't I hook up the solenoid directly to the Arduino?

It's a matter of how much current the Arduino can provide without sustaining damage. This is about 40mA per pin. Knowing that the Arduino operates at 5V, we can use Ohm's law to compute the required minimum resistance of the load (in this case, the solenoid). Dividing 5 volts by 0.040 amps gives us, 125 ohms. If the load has a greater resistance, we can hook it up directly to the Arduino, otherwise we cannot. A small solenoid typically has a resistance of 50 ohms, so we cannot drive it directly from the Arduino. If we did, it would pull 100mA, which is clearly too much.

Why do you use 9V for the solenoid, but 5V for the Arduino?

The Arduino runs at 5V, but this is a bit too little for a solenoid. Using a transistor allows us to pick a voltage for the solenoid which is independent of the 5V used for the Arduino.

How do I know whether a transistor is suitable for this project?

Just like the Arduino, the major requirement is that the current flowing through the solenoid not exceed the maximum ratings for the transistor (in particular, the collector current). We can easily compute the worst-case scenario by measuring the resistance of the solenoid and then dividing the supply voltage by that. In the case of a 9V supply current for the solenoids, and a solenoid resistance of 50 ohms, the worst-case scenario puts us at 180mA. The MPS3704, for example, is rated for a maximum collector current of 600 mA, which gives us a margin of about 3.

How do I determine the minimum value of the resistance to put between the Arduino's output and the base of the transistor?

The output of the Arduino will connect the base leg of the bipolar transistors through a current limiting resistor. Since the Arduino operates at 5V, we can again use Ohm's law to compute the resistance required to limit the current below 40mA. That is, divide 5 volts by 0.04 amperes to obtain a value of at least 125 ohms. Higher resistor values will decrease the current, thus giving us an even greater safety margin.

Is there a maximum value for that resistance which I should not exceed?

It turns out, yes. A transistor has what is known as a current gain. For example, if the gain is 100, it means that if we put 1mA into the base, then up to 100mA will flow through the load the transistor is controlling. If we put 1.8mA into the base, then up to 180mA will flow through the load. Since we computed earlier that at 9V, 180mA flows through the solenoid, then a base current of 1.8mA is the "sweet spot", and less and our solenoid will not turn on completely.

We know the Arduino puts out 5V and we want 1.8mA of current to flow, so we use Ohm's law (R=V/I) to compute the resistance (R=V/I). 5V divided by 1.8mA gives a resistance of 2777 ohms. So given the assumptions we made, we expect the resistance has to lie between 125 and 2777 -- choosing something like 1000 ohms gives us a fairly good safety margin either way.

Step 13: Analysis of Current Problems and Possible Solutions

The current prototype shows potential, but several problems remain:

  1. Motion along the X and Y axis does not appear to be perpendicular.
  2. There is a jump when the mirror changes direction.
  3. The resolution is quite low and there are visible stair step patterns.
  4. At higher motion speeds, the path of the laser is distorted by vibrations and ringing.

Issue 1) may be caused by the design of the 3D printed flexible hinges which are transmitting motion along one axis to the perpendicular axis.

Issue 2) is due to slack in the coupling between the driving pistons and the mirror platform, this causes the mirror to jerk and skip at transitions between the X and Y axis. This sudden movement leads to a darkened X shaped gap where the laser dot is doing a faster uncontrolled move.

Issue 3) occurs because the default Arduino PWM only has 255 levels and quite a few of those are wasted due to the shape of the voltage curve. This could be improved significantly by the use of timer1, which is 16-bits and would be capable of 65536 unique values.

Issue 4) occurs because the mirror and the solenoid's sliding armature (pistons) constitute a significant amount of moving mass.

As issues 1) and 2) are related to the mechanical design, one possibility may be to remove the metallic pistons and replace them with small rare-earth magnets that are affixed directly to the tilt plate. The solenoids would be an open coil that would attract or repel the magnets without making physical contact. This would lead to smoother motion and eliminate the possibility of jerking, while reducing total mass.

Reducing mass is the primary solution for issue 4), but any remaining problems could be targeted directly in software by implementing a motion control profile in software to accelerate and decelerate the mirror in a controlled manner. This is already widely done in 3D printer firmware and similar methods might work here as well. Here are some resources related to motion control as it applies to 3D printers:

  • "Mathematics of Motion Control Profiles", Chuck Lewin (link)
  • "Jerk Controlled Motion Explained", (link)

I suspect that adding a trapezoidal motion control profile would allow the mirror to be driven at much higher speeds without ringing or vibration artifacts.

Step 14: Future Work and Possible Applications

Although developing solutions to these problems will take a considerable amount of work, I am hopeful that this open-source beam steering module can become an affordable alternative to galvanometer based projects in such applications as:

  • An inexpensive laser shows for DJs and VJs.
  • An electro-mechanical vector display for a vintage arcade game such as the Vectrex.
  • A DIY resin-type SLA 3D printer that in the spirit of the RepRap movement, can print its own laser steering module.
  • Digital panning or optical image stabilization for cameras.
Arduino Contest 2017

Second Prize in the
Arduino Contest 2017

Epilog Challenge 9

Participated in the
Epilog Challenge 9