This third project is based around 3D vision. It seemed fun to do this with the third project (However the upcoming fourth project will not go into four dimensions and have a functioning time machine unfortunately).
The aim of this series is to get beginners up-to-speed with software coding to develop custom Pi applications which could consist of software and hardware. This project is the first which integrates software with some simple hardware, in this case home-made 3D glasses/specs. We also switch to a different programming language, but again you don’t need to know how to code in order to copy the code and modify it slightly to do different things.
At the end of this project you’ll know how to control simple external hardware using the Pi.
The 3D glasses/specs described here are very toy-like, they are experimental and function with a lot of flicker. The purpose is just to demonstrate some simple coding and how to connect hardware to the 40-pin connector on the Pi.
How does it Work?
3D specs are quite low cost these days, but for this project it was decided to build some custom ones. The specs work by rapidly blocking the light from ingressing to the left or right eyes alternately. When synchronised to two images that alternate on the LCD screen just as rapidly, the brain is confused into believing that a three-dimensional object is being observed.
For this project, I used two LCD shutters available from ebay or aliexpress (search for ‘LCD valve’) and connected them to a circuit that would drive them. The LCD shutters come with some protective film on each side which is peeled off before use.
LCDs require an AC waveform, and the circuit generates this and switches the waveform on and off under control of the Pi. When the waveform is switched on, the shutter blocks light. With the waveform switched off the shutter is transparent.
The entire circuit consists of three logic integrated circuits (ICs) and a few resistors and capacitors. It was assembled on breadboard.
Building the 3D specs
I mounted the LCD shutters inside the frames of disposable specs from the nearest IMAX cinema. The plastic polarised sheets inside the frame were partially cut out, and the LCD shutters are secured in place with glue, and 4-core wire is used to carry the signals. I used an old USB cable because it has 4 cores, however the wire is quite thick and heavy. Thinner wire would be better.
Building the Driver Circuit
The heart of the circuit is an oscillator that runs at about 600Hz. It uses a Schmitt Trigger input logic gate; the capacitor C1 gets charged up through R1, and at a certain threshold the output inverts and then the capacitor discharges through R1 and the cycle repeats. The output from the inverter consists of pulses (see further below for a trace of the waveform at the input and output of the inverter) which are converted into a square wave using a D-type flip-flop; it divides the frequency by 2 but this has the desired side-effect of forming a 50% duty cycle square wave from the pulses. The two outputs are in opposite phase. The reason for having opposite phase is that a 10V peak-to-peak signal can be created from logic circuitry running at 5V. This large signal level results in higher contrast between the open and closed LCD shutter states.
The other main part of the circuitry is the driver portion. The NAND gates are used to enable or disable the left or the right shutter depending on the input signal which is connected to the Raspberry Pi, GPIO17. When GPIO17 is low, the left eye shutter is open and the right eye shutter has the oscillator signals applied and this causes the shutter to block light. When GPIO17 is high the left eye shutter is energised and it blocks light and the right eye shutter is open and allows light through.
The ICs used in this project are very handy to have for solving many problems. It is recommended to purchase a few 74HCT14 and 74HCT132 chips for future experimentation.
The signals that the oscillator and shutter driver sections generate are shown below. The green trace (channel 1) is the voltage at the input of the Schmitt inverter. It shows the voltage rising and falling as the capacitor is charged and discharged between the input voltage thresholds that cause the output of the inverter to change logic level. The purple trace (channel 3) shows the output of the inverter. The pulses are divided by two and then gated to the LCD shutters depending on the state of the GPIO17 signal. An example output connected to the LCD shutter, pin 6 from the 74HCT132 chip is shown in blue (channel 2) when GPIO17 is low.
The three integrated circuits need power and this is taken from the 5V supply available on the 40-pin connector on the Pi. The 100nF capacitor connected close to each chip is very important for correct functioning of the ICs.
The unused logic circuity needs the input signals connected to 0V (Ground) or the 5V supply.
The entire circuit fitted on a small breadboard, and three wires are used to connect to the Pi. The 5V, 0V and GPIO17 pins connect to the 40-way connector on the Pi, to pins 2, 6 and 11 respectively (refer to the Connections Overview section of the Raspberry Pi GPIO Explained blog post to see a diagram of the connections available on the 40-way connector).
Getting a 3D Image
Dual cameras could be used to capture a 3D scene. I went for the easy option; a 3D model of a cottage was downloaded from https://free3d.com (the author is animatedheaven) and then the free online Autodesk 3D viewer was used to generate an image. A screen capture was obtained, and then the 3D model was moved slightly and another screen capture was obtained. The two files were saved in .png format. The two images that were used are shown here, click to enlarge and then right-click to save them.
What are ‘Objects’?
The code shown below was used to display the two images in rapid sequence such that the glasses would reveal a 3D image. ‘Objects’ are used in this code and it is important to briefly explain what this means.
In the earlier Xmas tree code, algebra-like variables were assigned numeric values. An object is different; for now imagine a factory that makes musical instruments. In a similar vein some computer code may exist that can play sounds. The person who wrote the code to play sounds wanted to make the code flexible and usable by others, so it was nicely documented. If the user wants the code to play a musical note then perhaps the documentation says to type ‘note(‘A’, 500)’ in order to play such the musical note ‘A’ for 500 milliseconds.
In order to provide extra flexibility, the person who wrote the original code may have implemented it slightly differently, such that the user could create multiple instruments. This would allow many notes to be played at the same time like in an orchestra.
So, the user could (say) type:
And then the user could play simultaneous notes by typing:
intrument1.note(‘A’, 500); instrument2.note(‘B’, 500); wait(500);
In such an example, instrument1 and instrument2 are two instances (or objects) of the MusicInstrument code. Another technical term is a ‘class’. The MusicInstrument code is technically known as a class. The instrument1 and instrument2 objects are instances of that class. It all sounds more complicated than it really is.
The code has several objects instantiated from a few classes. On line 5, an object is created from the class called OutputDevice. Someone has written some code and named the class OutputDevice. The user can create an object from that class and give it a name. On line 5, the object was given the name 'eye', since pin GPIO17 of the Raspberry Pi is used to control which LCD eye shutter is activated. We would need to examine the documentation of the OutputDevice class to know what actions we can apply to the object that we have created. Further down in the code it is possible to see that the actions were achieved using the lines eye.on() and eye.off() to set the GPIO signal to a logic high or logic low value respectively.
There are many other objects in the code. For example, on line 8, an object called pygame has an action called ‘init’ applied. In technical terms we say that the init function or ‘method’ was executed.
What the code does is to create a couple of objects of the class pygame.image, one for the left eye image and one for the right eye image. Each object is displayed on the screen one at a time, with a 15 millisecond delay before the image is swapped. The eye object has the on() and off() methods executed in sequence too.
Unlike the previous xmas tree code, there are a lot fewer curly brackets (braces) in this code. Instead, the code has some indentations in places. These are important, and behave as if the code had curly brackets in those areas. This code happens to be written in a language that doesn’t use curly brackets whereas the earlier blog post used a language that did use curly brackets.
import pygame from gpiozero import OutputDevice eye = OutputDevice(17) pygame.init() screen = pygame.display.set_mode((0,0),pygame.FULLSCREEN) done = False image1 = pygame.image.load("./cottage_left.png") image2 = pygame.image.load("./cottage_right.png") back1 = pygame.Surface(screen.get_size()) back1 = back1.convert() back1.blit(image1,(0,0)) back2 = pygame.Surface(screen.get_size()) back2 = back2.convert() back2.blit(image2,(0,0)) while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: done = True screen.blit(back1,(0,0)) eye.off() pygame.display.update() pygame.time.wait(15) screen.blit(back2,(0,0)) eye.on() pygame.display.update() pygame.time.wait(15) pygame.quit()
Running the Code
To try this example, create a folder and then copy and save the code above into a file called (say) 3dglasses.py in that folder. Save the two image files into the same folder. Then type the following to run the code:
You should see the LCD shutters start rapidly flickering as they alternately flip between open and closed states, and the monitor should show the .png file graphics alternating too. Put on the 3D glasses and the monitor display should stabilise to reveal a 3D image. There is still some wobble/flicker for reasons that are related to how code runs on the Linux operating system. Nevertheless, for a third beginner project with simple code to achieve 3D, it is a start. There is plenty of scope for improvement.
|IC1||1||Hex Schmitt Trigger Inverter|
|IC2||1||Dual D-Type Flip-Flop|
|IC3||1||Quad Schmitt Trigger NAND Gate|
|C2, C3, C4||3||axial capacitor|
The code displayed a static 3D image by alternately displaying two images from slightly different perspectives. It could be extended with a larger number of images, for 3D video.
In summary, this blog post showed how to control some custom circuitry using the 40-way connector on the Raspberry Pi. It also briefly explored objects and applied actions (using functions or methods) on them to display images on the screen in (fairly close) synchronisation with the signal on the 40-way connector. To see how to use the 40-way connector further, see the Raspberry Pi GPIO Explained blog post. To explore some classes for games and graphics and to see what functions or methods are available, see the pygame documentation website (scroll down to the Reference section).
In the next blog post, some more games related coding will be explored.