Laser projector with 3 lasers (RGB) incident on an x-cube(dichroic) with galvanomirror
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
Aligning the beam took a bit of time as aligning all three horizontally and vertically first required that the beams were straight. tested each laser beams vertical height and horizontal shift with a ruler.
Then incident that beam on to the galvanomirror and tried to centre the beams again by making sure that all the beams were almost incident on the same point at one of the mirrors of the galvo.
This alignment would have been way easier if done with individual dichroic mirrors.
I then proceeded to implement the algorithm of raster scanning with a single beam at constant voltage, and then with three beams at constant voltage (not PWM) I got a bright white light that had a bit of a blueish hue to it because of the much higher intensity of the blue and green laser compared to the red which was almost hidden in the resultant light.
This is how my setup looked with everything working.. I chose to put a aperture to limit the beam width right in front of galvo which was a bad idea.. i then moved it post galvo for better results.
with the algorithm working i projected a rectangle and a circle on the screen and these were the results.
rectangle with RGB white light (blueish hue)
and a circle.
We can see a clipping at the bottom i'm not sure why that is.. will test more..
The idea that we can project any shape to it is a proof that we can project anything onto it using the bitmap methodology. and with this i conclude the demonstration part of my project. I'll still be working on making it better and will try to project a complete image or a 5 second video on it, but that will take some time.
This was all done as part of my final year project at "CMND: centre for micro and nano devices" facility at COMSATS, Islamabad by me (Shoaib Mustafa) under the supervision of my supervisor: Dr. Mujtaba Hussain, mujtaba.hussain@comsats.edu.pk
feel free to reach out at shoaib.mustafa7@hotmail.com
follow my youtube:
https://www.youtube.com/@capistor1 for more details and following videos.
here is the github where i'll be uploading all the material. https://github.com/manhoosbilli1/Laser_Projector
I hope you had fun!
I've been experiment with the design of the laser holder, since there was no open source design of it so i've decided to make it myself.
We started with this holder as inspiration. Though it would have been better to make it with aluminium for finer control but acrylic sheet is all we had available.
First started with a basic vertical alignment with having a plate that was curved from the bottom edge and added two screws such that they were opposite to each other with a spring for providing tension. When one of the screws is tightened, it pushes the vertical plate a bit forward curving the laser beam up or down.
Here is the auto cad design of it which was laser cut. Note this is only for the top and bottom plate but not the vertical.
Before fitting the screws here's how it looked.
For the bottom plate added two plates on top of each other where was one attached to the holder and was fixed, the other one on top is movable. it also has two screws opposite to each other providing tension and works on the same principle as the vertical one.
This gave us the ability to align the beam and to correct its inherent bending of the beam due to the structural drawback of the laser.
This holder was integral in aligning the beam on the xcube.
There are a few things that could have been better, instead of having a hex nut embedded in the plastic a brass insert would have been better. Instead of having the plates lying directly on to the other plate it would be nice if they were free to provide finer control.
I've been experimenting with the setup trying to make the galvo movement as smooth as possible and for this i've been successful in implementing a triangle wave generator on the DAC with DMA, i've gotten the results and compared with the interrupt based output which was limited to 300 Hz, with DMA however i've tested the wave output to 200KHz, This creates many different opportunities for me. First i can rest assured that the microcontroller is not being the bottleneck and concurrent processing in the stm32 while the SCAN mechanism is running.
I've discovered that the DMA has callbacks which i can use such as half-complete or full-complete. This allows us to change the one half of the array while the other half is being output. this way we can keep the lookup table updated. but i don't think we will need this as we just need a constant scanning mechanism.
I've also gotten a bit better in the stm32cubeide, now i can use the debugging features such as live variables and use the debugger better. I also understand that an easy way to look for available functions is to look in the header file and use that. not everything is defined in reference manual or data sheet.
I faced a problem while generating the triangle wave with DMA on the DAC; the wave had some weird peaks showing
I tried to fix it by doing several things such as adding a bypass capacitor, low pass filter but it couldn't be fixed. in addition to the wrong waveform, the frequency it was showing was also wrong, almost triple of the calculated frequency. I then fixed it by changing the word size of the DMA from word to half word. this fixed the issue.
I've also gotten hold of some better lasers which i've connected the power supply to by directly connecting to it's + and - also i'm working on developing a holder for the laser. before we use kinematic mirror mounts which allowed to make minute adjustment in x and y direction. but since this new laser is larger we can't fit it in there.
The laser looks heavy stuff but it's just a slightly better laser than the usual pointer. I wouldn't be surprised if the same diode is used here as well.
Right now I'm looking into somehow triggering the 2nd dac used for Y axis steering when the X axis DAC triangle wave is completed by using the complete callback.
After implementing the code on hardware the following were the results:
I created a lookup table of around 100-256 samples with values increasing from 0-4095 to represent an incremental increase in the voltage of around (50-124px representing a pixel). Then that lookup table was fed to the DAC via Interrupt routine in stm32cubeide and also implemented on stm32duino with just a normal for loop to test the difference. However the results were almost the same.
The scanning done horizontally was going quite good it showed a straight line and i confirmed that it is working by slowing the scan speed of the galvo so i could see it jumping to each different pixel. The problem was that it took quite a bit of time coming from top to bottom as expected because the y axis dac was supposed to increment after the horizontal (x-axis) scan was complete. I deleted all delays but still results were the same.
It is my understanding that no matter what i do doing it through the CPU might be slow for whatever reason, maybe the DAC is being notified late or value executed late. I have my clock speed of the peripheral and system clock speed at 72Mhz (maximum available) but this is still the case. the following are my assumptions without looking too deep into it.
To estimate the time it takes to output a single value from RAM to the DAC using a for
loop on the STM32 F3 Discovery board, we need to consider several factors:
for
loop, including loading the value from RAM, and writing it to the DAC.But that is assuming that the galvo's can follow the speed at which signal is given. So i'm sure now that the microcontroller is not the issue. It has to be the bulky mirrors slowing everything down. I wouldn't be surprised if it skips a couple of values as it is reaching the specified position.
My galvo's are the generic ones sold in amazon but i got it from aliexpress, Looking at the specification it has the rating of 15kpps so that should give us around 66.67 microseconds for each step. now that's still not surprising, our whole sweep which was running at full speed completely taking around 11.1 microseconds, the galvo is being a bottleneck which is around 6 times slower.
In addition to that according to specifications my galvo can sweep an angle of +-20 degrees with an input signal range of +-5V, since my signal is only 0 - 3.3V that gives me an angle range of ≈6.6 degrees for the whole range. This is good because the smaller range it is the lesser momentum it will build and easier for it to put a stop to it's motion.
There seems to be a problem in my understanding because if the galvo's response rate is truly 6 times slower that means the galvo should not even jump to the next point, instead...
Read more »Well never a better feeling than what you thought might work actually works. After reading up more on some computer graphics turns out the mechanism with which i want to project and which was implemented on CRT tvs is called "Raster scanning" but i couldn't find any documentation of it or any work that had implemented this on galvos. after searching a bit more it turned out that this is a pretty common method and is usually hidden from public because i mean who even thinks about how characters are printed to the screen. I found some people utilizing windows.h api to print to screen from scratch and found some old commodore_64 era programs that used terminal as a screen. Most of all Javid from youtube with his game engine videos on command line helped alot.
Here is the code.
#include <stdio.h>
#include <unistd.h> // for usleep()
// Define the Pixel struct
typedef struct {
int x; // Position of the pixel in x
int y; // Position of the pixel in y
int dac; // Voltage value to be fed to DAC
int intensity; // Brightness level
int color[3]; // RGB color options
} Pixel;
// Define the Settings struct
typedef struct {
int pixelDelay; // Delay between drawing each pixel in microseconds
int gridSize; // Size of the grid (number of pixels per side)
} Settings;
// Hardcoded bitmap for the 8x8 grid with a 4x4 square in the center
// 1 represents pixels to be drawn (green), 0 represents non-pixels (red)
const int bitmap[8][8] = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0}
};
// Function to initialize the grid from the bitmap
void initializeGrid(Pixel grid[], int gridSize, const int bitmap[8][8]) {
for (int y = 0; y < gridSize; y++) {
for (int x = 0; x < gridSize; x++) {
int index = y * gridSize + x;
grid[index].x = x;
grid[index].y = y;
if (bitmap[y][x] == 1) {
// Pixel value
grid[index].dac = 99;
grid[index].color[0] = 0; // Green
grid[index].color[1] = 255; // Green
grid[index].color[2] = 0; // Green
} else {
// Non-pixel value
grid[index].dac = 0;
grid[index].color[0] = 255; // Red
grid[index].color[1] = 0; // Red
grid[index].color[2] = 0; // Red
}
}
}
}
// Function to print the grid with colors and simulate delay per pixel
void printGrid(const Pixel grid[], int gridSize, int pixelDelay) {
printf("Grid:\n");
for (int y = 0; y < gridSize; y++) {
for (int x = 0; x < gridSize; x++) {
int index = y * gridSize + x;
// ANSI escape codes to set the text color
printf("\033[48;2;%d;%d;%dm", grid[index].color[0], grid[index].color[1], grid[index].color[2]);
printf(" "); // Print a space to represent the pixel
// Reset text color
fflush(stdout);
printf("\033[0m");
// Simulate delay between printing each pixel
usleep(pixelDelay);
}
printf("\n");
}
}
int main() {
Settings settings;
settings.gridSize = 8; // Size of the grid (8x8)
settings.pixelDelay = .2 * 1000000; // Delay between each pixel in microseconds (0.2 seconds)
// Define a grid to hold a single image
Pixel grid[settings.gridSize * settings.gridSize];
initializeGrid(grid, settings.gridSize, bitmap); // Initialize the grid from the bitmap
printGrid(grid, settings.gridSize, settings.pixelDelay); // Display the grid
return 0;
}
The raster projection I should say is working quite well. I've created each pixel to have position in the x,y a value which i'm calling dac value which is the actual voltage level where these pixels will correspond. For now these are just numbers. Then assigning each pixel intensity and colors. When i convert this to real galvo system i'll have to add in delta_v (voltage difference) and hope to calculate next pixel position with this. Then will first try to make it work with intensity. Possibly creating a shade of green laser along a line should be cool. The pixel delay or "Dwelling time" Is not just adding a delay here...
Read more »I'm pausing the experimental setup to plan the next steps:
Initial Approach: I'll begin by researching papers on similar laser scanning mechanisms. My goal is to synchronize laser activation with the galvo mirror's feedback, ensuring precise targeting. I plan to implement functions on a microcontroller (starting with C on a computer) to draw horizontal lines by specifying certain parameters. Greater control over these functions will help me understand and refine the system.
Planned Functionality:
Objective: The function will be integrated into the galvo's horizontal scanning mechanism, allowing it to draw lines while maintaining a fixed vertical angle. After completing a horizontal scan, the system will increment vertically and repeat the process. The idea is to synchronize the horizontal scanning and line drawing mechanisms, enabling the precise rendering of an image, such as a vector or bitmap, in a line-by-line fashion.
Here is how it should look like. say we are given an image which after being processed looks like a vector image or a bitmap to make it simple.
byte customChar[] = { B01110, B10001, B10001, B01110, B10001, B10001, B10001, B01110 }
All I have to do is when I know that the galvo is at the top vertical position i.e, top right (0,0) and scans horizontally towards right, it should go something like this
pixelTobePopulated()? DrawPixel(): skip();
The result should be a pattern like this: FTTTF (T for true, F for false). Initially, I'll hard-code a bitmap or vector image, adjusting it to fit the system's screen size and limits.
While researching, I found a helpful video that demonstrated a similar approach. If this works, I could explore the archives of old CRT TVs or early system source codes to understand their scanning methods.
Once I determine where the dots land, I'll focus on projecting different colors or mixtures using an RGB LED approach. However, that's a later step.
To simulate the process and test code changes in real-time, I'll use the command line as my simulation monitor, controlling the cursor like the galvo's position to replicate dot placement. This approach is reminiscent of early programming exercises, like drawing shapes on the screen.
After completing these steps, I can address issues like refresh rates, FPS, and the time delay for dots to remain in position before moving.
Lastly, I want this project to be portable so I can experiment outside the lab. This means creating a portable optical table, similar to the one in my lab, by using CNC to drill holes and threads to secure the components. Achieving proper beam alignment is challenging, and moving the project to another optical table could complicate things.
I decided to go with this galvo set for a few reasons
After looking into the galvos a bit, the inner construction of each galvo was found to be very simple. Comprised of two coils on opposite ends, the shaft of the mirror having permanent magnet on it. Some feedback ciruitry, usually light detecting diodes that will change it's value depending on the mirror. Providing it information about the direction it's travelling and the current position.
The galvos work with a differential signal of 15V (so -15V to +15V) capable of detecting millivolts fluctuation and there about (I'm not really sure about this claim).
The driver board is there to take care of the nitty-gritty of the galvo, it's feedback, making sure it is where we want it to be. Keeping account of the inertia of the mirrors, sweeping up an angle at different speeds, staying locked in at an angle are all parts of the functions offered by the driver board. It has two identical blocks of circuitry on the PCB, one for X axis control the other for Y. the rest of the connectors are for input of +15 and -15 so that the galvo will receive this signal. And the inputs for the DAC signal.
The board requires an analog signal as per my knowledge. For that, the STM32F3 discovery board offers DAC (Digital to analog) peripheral. This is a 12 bit dual system perfect for controlling both X and Y. capable of driving them simultaneously with the help of DMA (Direct memory access), more on these terms later. This allows us to vary the analog signal between 0 - 4069 values. However, it seems like this is still not a differential signal because it goes from 0 - +3.3V with that many steps. I believe the driver board itself will be responsible for generation a differential signal from this? Or would we have to provide. I still have to figure this out.
The Galvo's seem to be very straight forward. After contacting the manufacturer and doing some testing on my own i couldn't figure out the connectors on the board and what they do, luckily I found multiple projects where people used the same galvo. but amongst them all this detailed https://hackaday.io/project/165977-laser-galvo-clock/log/164901-frame-driver-schematics article proved to be most helpful. I realised the board i had accepted a signal of +-5V max by finding a listing on amazon of the same board.
A problem soon arose because the board needed a bipolar signal (+-5V) instead of the usual (0-5V). I found out that using a combination of 2 opamps in a certain configuration helps you amplify the signal i was providing (0-3.3V) to (0-5V) and it would add a dc offset to the signal; where usually the signal is offset from 0V now i would get the signal offset from -5V this would create a range of 10. The board then takes that voltage and converts it to appropriate +-15V for the galvo, while also adding in feedback and driving mechanism into the signal to make sure that the mirror is oriented according to the signal.
I didn't know much about opamps and even after https://www.ti.com/lit/an/sloa097/sloa097.pdf up on it a bit and copying the circuit provided in the article i couldn't make sense of this application so decided to not use it and postpone it. Had this been successful it would have permitted me to use the full range of the galvo of around 20-25 degrees. But that would also make the calculations difficult and frankly much more time taking.
By not using the bipolar signal and using the galvo's in such a configuration where...
Read more »Let's sit and think about the number of instruments i have to automate and the data that i actually need.
Happy to share that i've made some promising progress towards accessing instruments supporting usb with python. I call the project "Instrument Orchestra", Instrument Orchestra is now hosted on GitHub with its own repository. https://github.com/manhoosbilli1/Instrument_Orchestra I've provided a detailed step by step installation guide for first experiment.
I tried about 20 packages and different softwares to access this. thought why reinvent the wheel but none worked and almost no one was using this simple approach of accessing the instrument through serial. having some experience in electronics it was easy setting it up but installing the drivers were a pain.
My setup is going to be a sweet one with me remotely getting data from the instrument. this will allow for long lasting testing sessions even when the lab is closed. only have to have my remote laptop attached to instruments and powered on.
Next step would be integrating all 3 instruments with this software and creating a gui with it. i think a live graph of each instruments data would be nice.
I think the nearest possible application is of an rgb led and how it's able to generate a certain a color. Since i'm using a laser it will be a bit different but i think the conversion factor can be changed. I'll look into the construction of a laser.. how it's driven and find some papers that have attempted to control color of a laser.
Side note: Just figured out why the IR wavelength was showing in the green laser. turns out the pure green lasers are rare and what we get is an IR laser lasing at 1056nm there about. The frequency is then double within the structure which halves the wavelength so we get around 532nm there about green light.
The green laser is usually made of inGan material. but since my diode is an IR one not so sure what it's made of. Here's a proper true green laser. https://ams-osram.com/products/lasers/color-lasers-eel/osram-metal-can-to56-plt5-520b i found a good paper explaining the effects of PWM with this laser. i'll read it see if it will give me some insight into having proper pwm control with the laser.
Interesting facts gathered from this paper.
i'll collect each lasers wavelength using spectrometer for CIE coordinates gamut. i'll also measure each lasers optical power with change in voltage/pwm.
I've found that most instruments have usbtcm connection which will let us talk to the device and gather data programatically. i'll try to set that up because when the new lasers arrive i have to be able to get the data fast. even right now i have to measure, wavelength, voltage, current, pwm value, intensity and so on.. will be hard to get 1024 (10bit) data
Create an account to leave a comment. Already have an account? Log In.
Become a member to follow this project and never miss any updates