Table of contents
Our solar tracker uses sunlight to engrave a substrate with a low melting temperature [wax]. It is sensed by four LDR; it uses stepper motors and a servo to control the build plates positioning towards maximum light intensity. A fresnel lens will bundle the incoming solar rays which are reflected by a mirror perpendicularly towards the machine bed. A second lens will allow final focusing of the beam. The bed runs on two axes, enabling the 2D solar engraving of the substrate.
By all
It is a machine composed of two mini-systems:
As the sketch show, the solar tracker will follow the sun in order to keep the lens oriented in the right direction. It will be driven by servo or stepper motor. The machine be will use the Gestalt nodes for x and y movements. These movement will be controlled with a mouse to enable drawing. You will basically draw with the mouse and the light beam on a piece of wax.
Initial experiments with lenses proved the feasibility of the concept. We used a lens-focussed sunbeam to cut paper and to engrave wax.
What are you testing
By Cesar, Konstantin, Ahmed
By Konstantin.
A rough sketch of a 2-axis bed was completed in Rhino. The design uses the motors of the MTM kit. Measurements are taken carefully from the motor threads and rods available in the fablab.
Glide bearings are added too to the design for 3d printing.
By Konstantin.
Design is transferred to Fusion360 where joints are tested.
Design is ready for laser cutting:
Bed is assembled.
By Cesar
Gestalt library is used to test the x-y movement of the bed. X-Y Plotter example is used to test the machine movement:
Code below interfaces with gestald nodes using serial and moves the machine in a square like pattern.
# Two stage example Virtual Machine file # moves get set in Main # usb port needs to be set in initInterfaces # Nadya Peek Dec 2014 #------IMPORTS------- from gestalt import nodes from gestalt import interfaces from gestalt import machines from gestalt import functions from gestalt.machines import elements from gestalt.machines import kinematics from gestalt.machines import state from gestalt.utilities import notice from gestalt.publish import rpc #remote procedure call dispatcher import time import io #------VIRTUAL MACHINE------ class virtualMachine(machines.virtualMachine): def initInterfaces(self): if self.providedInterface: self.fabnet = self.providedInterface #providedInterface is defined in the virtualMachine class. else: self.fabnet = interfaces.gestaltInterface('FABNET', interfaces.serialInterface(baudRate = 115200, interfaceType = 'ftdi', portName = '/dev/ttyUSB0')) def initControllers(self): self.xAxisNode = nodes.networkedGestaltNode('X Axis', self.fabnet, filename = '086-005a.py', persistence = self.persistence) self.yAxisNode = nodes.networkedGestaltNode('Y Axis', self.fabnet, filename = '086-005a.py', persistence = self.persistence) self.xyNode = nodes.compoundNode(self.xAxisNode, self.yAxisNode) def initCoordinates(self): self.position = state.coordinate(['mm', 'mm']) def initKinematics(self): self.xAxis = elements.elementChain.forward([elements.microstep.forward(4), elements.stepper.forward(1.8), elements.leadscrew.forward(8), elements.invert.forward(True)]) self.yAxis = elements.elementChain.forward([elements.microstep.forward(4), elements.stepper.forward(1.8), elements.leadscrew.forward(8), elements.invert.forward(False)]) self.stageKinematics = kinematics.direct(2) #direct drive on all axes def initFunctions(self): self.move = functions.move(virtualMachine = self, virtualNode = self.xyNode, axes = [self.xAxis, self.yAxis], kinematics = self.stageKinematics, machinePosition = self.position,planner = 'null') self.jog = functions.jog(self.move) #an incremental wrapper for the move function pass def initLast(self): #self.machineControl.setMotorCurrents(aCurrent = 0.8, bCurrent = 0.8, cCurrent = 0.8) #self.xNode.setVelocityRequest(0) #clear velocity on nodes. Eventually this will be put in the motion planner on initialization to match state. pass def publish(self): #self.publisher.addNodes(self.machineControl) pass def getPosition(self): return {'position':self.position.future()} def setPosition(self, position = [None]): self.position.future.set(position) def setSpindleSpeed(self, speedFraction): #self.machineControl.pwmRequest(speedFraction) pass #------IF RUN DIRECTLY FROM TERMINAL------ if __name__ == '__main__': # The persistence file remembers the node you set. It'll generate the first time you run the # file. If you are hooking up a new node, delete the previous persistence file. stages = virtualMachine(persistenceFile = "examples.vmp") # You can load a new program onto the nodes if you are so inclined. This is currently set to # the path to the 086-005 repository on Nadya's machine. #stages.xyNode.loadProgram('../../../086-005/086-005a.hex') # This is a widget for setting the potentiometer to set the motor current limit on the nodes. # The A4982 has max 2A of current, running the widget will interactively help you set. #stages.xyNode.setMotorCurrent(0.7) # This is for how fast the stages.xyNode.setVelocityRequest(2) # Some random moves to examples with #moves = [[x,0],[x,y],[0,y],[x,0]] moves = [ [0,0], [10,0], [10,10], [0,10], [0,0], [0,0], [10,0], [10,10], [0,10], [0,0], [0,0], [10,0], [10,10], [0,10], [0,0], ] # Move! for move in moves: stages.move(move, 0) status = stages.xAxisNode.spinStatusRequest() # This checks to see if the move is done. while status['stepsRemaining'] > 0: time.sleep(0.001) status = stages.xAxisNode.spinStatusRequest()
The bed test ran successfully.
By Cesar
Code is made from scratch for cursor-control functionality. Tkinter library is used; as well as some scaling conversions.
Once run, code launches an interface that represents the bed of the machine. If the mouse is moved inside the interface, the movement is translated to the machine real-time.
Further details can be found here
# Created on APRIL 2017 # @author: J.C. Mariscal # LICENSE: GNU GPL V3.0 from gestalt import interfaces from gestalt import machines from gestalt import functions from gestalt.machines import elements from gestalt.machines import kinematics from gestalt.machines import state from gestalt.utilities import notice from gestalt.publish import rpc #remote procedure call dispatcher import time import io import Tkinter as tk size_bed_machine = 220.0 size_window = 500.0 scale_ratio = size_window/size_bed_machine x_old = 0 y_old = 0 threshold = 0.1 machine_speed = 10 def motion(event): global x_old global y_old global threshold x, y = event.x, event.y x_machine = x/scale_ratio y_machine = y/scale_ratio print('window{}, {}'.format(x, y)) print('machine:{}, {}'.format(x_machine, y_machine)) if abs(x_machine - x_old) > threshold or abs(y_machine - y_old) > threshold: print('go') x_old = x_machine y_old = y_machine move = [-x_old, y_old] stages.move(move, 0) status = stages.xAxisNode.spinStatusRequest() # This checks to see if the move is done. #while status['stepsRemaining'] > 0: #time.sleep(0.001) #status = stages.xAxisNode.spinStatusRequest() # Two stage example Virtual Machine file # moves get set in Main # usb port needs to be set in initInterfaces # Nadya Peek Dec 2014 #----------------------------------------------------- class virtualMachine(machines.virtualMachine): def initInterfaces(self): if self.providedInterface: self.fabnet = self.providedInterface #providedInterface is defined in the virtualMachine class. else: self.fabnet = interfaces.gestaltInterface('FABNET', interfaces.serialInterface(baudRate = 115200, interfaceType = 'ftdi', portName = '/dev/ttyUSB0')) def initControllers(self): self.xAxisNode = nodes.networkedGestaltNode('X Axis', self.fabnet, filename = '086-005a.py', persistence = self.persistence) self.yAxisNode = nodes.networkedGestaltNode('Y Axis', self.fabnet, filename = '086-005a.py', persistence = self.persistence) self.xyNode = nodes.compoundNode(self.xAxisNode, self.yAxisNode) def initCoordinates(self): self.position = state.coordinate(['mm', 'mm']) def initKinematics(self): self.xAxis = elements.elementChain.forward([elements.microstep.forward(4), elements.stepper.forward(1.8), elements.leadscrew.forward(8), elements.invert.forward(True)]) self.yAxis = elements.elementChain.forward([elements.microstep.forward(4), elements.stepper.forward(1.8), elements.leadscrew.forward(8), elements.invert.forward(False)]) self.stageKinematics = kinematics.direct(2) #direct drive on all axes def initFunctions(self): self.move = functions.move(virtualMachine = self, virtualNode = self.xyNode, axes = [self.xAxis, self.yAxis], kinematics = self.stageKinematics, machinePosition = self.position,planner = 'null') self.jog = functions.jog(self.move) #an incremental wrapper for the move function pass def initLast(self): #self.machineControl.setMotorCurrents(aCurrent = 0.8, bCurrent = 0.8, cCurrent = 0.8) #self.xNode.setVelocityRequest(0) #clear velocity on nodes. Eventually this will be put in the motion planner on initialization to match state. pass def publish(self): #self.publisher.addNodes(self.machineControl) pass def getPosition(self): return {'position':self.position.future()} def setPosition(self, position = [None]): self.position.future.set(position) def setSpindleSpeed(self, speedFraction): #self.machineControl.pwmRequest(speedFraction) pass #--------------------------------------------------------------------------- if __name__ == '__main__': stages = virtualMachine(persistenceFile = "examples.vmp") stages.xyNode.setVelocityRequest(machine_speed) #move machine to initialize it stages.move([0.1, 0.1], 0) status = stages.xAxisNode.spinStatusRequest() # This checks to see if the move is done. while status['stepsRemaining'] > 0: time.sleep(0.001) status = stages.xAxisNode.spinStatusRequest() #global scale_ratio #global movement print('scale:{}',format(scale_ratio)) root = tk.Tk() root.resizable(width=False, height=False) root.geometry('{}x{}'.format(500, 500)) root.bind('> Motion>', motion) root.mainloop() #always end your script by calling the mainloop method of the root window.
Code worked as expected.
There were no problems at this stage and everything fit snuggly.
By Dimaw, Ahmed, Konstantin & Joern
By Dima
By Joern
This part of the machine design did not make it into the final machine, as it was to error prone and complicated to integrate it in the race against the clock. The idea was to make the beam hit the bed perpendicularly at any time by reflecting it by a small mirror after crossing the Fresnel lens. To create a sharp focus, a second converging lens was supposed to concentrate the beam again on the bed.
The complication however was the exact movement of mirror and lens. As we did not want to add more motor and servos. Belts were supposed to drive the correct orientation (not displayed here).
In the end, I went for a simpler design, which looked like this:
The parts where laser cut from 6 mm acrylics, using 4 % for speed and 100 % for frequency and power. The magnifying lens is fixed with nuts and bolts, so that its position in z can be adjust to the focul length of the fresnel lens. In order to fix the orientation of the lens parallel to the fresnel lens, adapter from PLA were 3D printed.
By Ketlli
The solar tracker is necessary for this system in order to orient the payload towards the sun. The payloads in our system are the fresnel reflector together with the bed of our system. The solar tracker will be helpful for the fresnel reflector to get the maximum beam of light and concentrate it to the bed.
In order for the LDRs to be connected and work properly, they have to receive the same amount of sunlight, no less no more. To achieve that, something that would be able to give the system that ability had to be designed. So, a structure that when the sun is not perfectly perpendicular to the LDRs would give a shadow to them as a result of getting less sunlight and making them adjusting accordingly was designed and laser cut using a 3mm MDF. Two of the parts of the design were designed to work as a press fit and then were super glued to the base of the design were slots for the placement of the LDRs were also put.
The PCB design of the solar tracker is a modified version of the satshakit created by Daniele Ingrassia. This pcb board differs from the original board because of its additional resistors needed for the functionality of the LDRs and its additional pins for VCC and GND for the requirements of the two stepper motors to be used.
Stepper.h library was used for the movement of the stepper motors and mathematical equations for the movement of the whole structure according to the LDR readings. The code was written in an Arduino language.
#include < Stepper.h > #define motorstep 100 //horizontal stepper #define hstep 13 #define hdir 12 int step_hdivision = 10; Stepper stepperh(motorstep, hstep, hdir); //vertical stepper #define vstep 10 #define vdir 9 int step_vdivision = 10; Stepper stepperv(motorstep, vstep, vdir); int ldrLT = A0; // LDR Left Top int ldrRT = A1; // LDR Right Top int ldrLD = A2; // LDR Left Down int ldrRD = A3; // LDR Right Down void setup(){ Serial.begin(9600); stepperh.setSpeed(200); stepperv.setSpeed(40); } void loop(){ int lt = analogRead(ldrLT); //Serial.println (lt); int rt = analogRead(ldrRT); //Serial.println (rt); int ld = analogRead(ldrLD); //Serial.println (ld); int rd = analogRead(ldrRD); //Serial.println (rd); int dtime = 1; int tol = 50; int avt = (lt + rt)/2; // average value top int avd = (ld + rd)/2; // average value down int avl = (lt + ld)/2; // average value left int avr = (rt + rd)/2; // average value right int dvert = avt - avd; // difference of up and down int dhoriz = avl - avr; //difference of left and right // horizontal stepper motor if(-1*tol > dhoriz || dhoriz > tol){ if(avl > avr){ stepperh.step(step_hdivision); delay(10); } else if(avl < avr){ stepperh.step(-step_hdivision); delay(10); } else{ stepperh.step(0); delay(10); } } // vertical servo motor if(-1*tol > dvert || dvert > tol){ if (avt > avd){ stepperv.step(step_vdivision); delay(10); } else if (avt < avd){ stepperv.step(-step_vdivision); delay(10); } else if (avl == avr){ //nothing } } delay(dtime); }
First the 2 axis bed is put together snuggly after laser cutting.
Picture below illustrates the parts needed for assembly.
Two stepper motors are tightly fit onto each axis. 4 rods are also placed to hold each bed.
Next, the bed is fit onto the wooden main frame. Same is done onto the other side of the bed.
The main frame upper pillars are press-fit onto the circular base.
In the upper part of each of the frame pillars, the 3d printer support, in green, is tightly placed with a rod through held by nuts; this provides the rotation of the main fresnel lense.
Next, on the other side of the main frame, a stepper motor is tightly fitted. This will provide the power to rotate the circular base of the main frame. This can be clearly seen in picture below, the black belts will precisely move the circular frame as needed.
Next, the beam concentrator is fitted to the rectangular fresnel lense. It will move together with it.
At the bottom of the circular base of the main frame, 4 support structures are placed with bearings to support the base and allow for the rotation of the whole system.