One major problem that was actually signalling the end to the project as a whole was the pulley material. I was originally using rubber bands but they were so elastic that it resulted in a very inaccurate system. So inaccurate that there was no way it could store and retrieve items. However, after searching I managed to find a timing belt kit which worked perfectly. With this, the system drives to any point with ease and consistency. However, in using this, I had to 3D print two new wheel hubs and cable tie the timing belt to the X and Y axises.
The other thing affecting the consistency was the guiding rails. These were originally out of coat hanger but have now been replaced with hollow brass rods. These are a closer fit to the lego holes and hence give a good guide. It did have a bit of friction at the ends which was an issue but the timing belt solution overcame it.
Still, there wasn't enough resolution with my old 2 tab encoder wheels. So I have made 2 new encoder wheels with 8 tabs. This will provide much greater resolution for the coordinates but is yet to be attached. These are below:
I also finally cleaned up the wires on the robot. Originally there were wires everywhere and they'd get caught when it ran. However, they now have been trimmed, fed through containment units and guiders to the wires can now move the whole length of the X and Y without getting tangled or caught.
The Z axis is now fully functioning. It is run by a mini servo which is connected a long lego piece with cable ties and string to the servo arm. This is then attached to another long lego piece making it retract and extend. From Testing I have got a range of position 60-230 for this servo so that it doesn't lock on the Y-axis or go out of its range.
The circuit board has also been attached to the base using hex stands. This way the board can be easily screwed off at any time.
The code has also been changed now neater (separated into tabs) and more efficient in its function. This is below:
Automated Gantry: This
contains all the integers (data type for number storage) to tell the program
which pins to reference on the eleven for each variable. It also outlines
whether each variable is an INPUT or OUTPUT.
It also contains general routines such as that to print out the location
of the gantry, to move back to (0,0).
#include <Wire.h>
#include <Servo.h>
int Xdir=6; // drive pin to X direction relay
int Ydir=4; // drive pin to Y direction relay
int Xenable=5; // drive pin to X enable relay
int Yenable=10; // drive pin to Y enable relay
int Xpulse=3; // input from X encoder
int Ypulse=2; // input from Y encoder
int Solenoid = 9;
boolean
Xdirection=false; // true if motor
forward / false if reverse
boolean
Ydirection=false; // true if motor forward
/ false if reverse
Servo Lift;
int Xposition=0; // encoder count for X
int Yposition=0; // encoder count for Y
boolean Xtravel = false;
boolean Ytravel = false;
int OldXstate,
OldYstate; // input state of encoder
pins (HIGH,LOW)
String inputString =
"";
boolean
stringComplete=false;
String cmd = "";
int arg [4] = {0,0,0,0};
int ArgIndex;
void setup()
{
Serial.begin(9600);
Serial.setTimeout(2);
inputString.reserve(200);
Serial.println(" Automated Gantry
Warehouse");
pinMode(Xdir, OUTPUT);
pinMode(Ydir, OUTPUT);
pinMode(Xenable, OUTPUT);
pinMode(Yenable, OUTPUT);
pinMode(Solenoid,OUTPUT);
pinMode(Xpulse,INPUT);
pinMode(Ypulse,INPUT);
digitalWrite(Solenoid,LOW);
Lift.attach(7);
Lift.write(128);
OldXstate = digitalRead(Xpulse); // get state of encoder inputs
OldYstate = digitalRead(Ypulse); // ...
}
void loop(){
// serialEvent();
if (stringComplete){
HandleCommand();
inputString="";
stringComplete=false;
}
}
void Home(){
MoveTo(0,0);
}
void MoveTo(int x, int y){
// if ((x < 0) || (y < 0) || (x > 40)
|| (y > 40)){
// Serial.println("XY commands out of
bounds");
// return;
// }
Serial.println(y);
if (x !=Xposition){
if (x > Xposition) XForward();
if (x < Xposition) XReverse();
}
while (Xposition !=x){
CheckEncoders();
}
XStop();
ShowEncoders();
delay (1000);
// ShowEncoders();
// delay (2000);
if (y != Yposition){
if (y > Yposition) YForward();
if (y < Yposition) YReverse();
}
ShowEncoders();
while (y !=Yposition){
CheckEncoders();
}
YStop();
ShowEncoders();
}
void PickUp(){
MoveTo(arg[0], arg[1]);
}
Commands: This is the code for manual control of the robot. By
inputting a command from the code in the serial monitor, it will reference the
array of commands, and run the routine set to that command; movement of the x,
movement of the y, on and off of electromagnet, driving the servo, going to a
certain coordinate, resetting and reading the encoders to see the coordinate.
void HandleCommand(){
ParseInput();
if (cmd == "xf") XForward();
if (cmd == "xs") XStop();
if (cmd == "xr") XReverse();
if (cmd == "yf") YForward();
if (cmd == "ys") YStop();
if (cmd == "yr") YReverse();
if (cmd == "se") ShowEncoders();
if (cmd == "xy") PickUp();
if (cmd == "re") ResetEncoders();
if (cmd == "servo") DriveServo();
if (cmd == "solon") SolenoidOn();
if (cmd == "soloff") SolenoidOff();
if (cmd == "home") Home();
}
// from the serial input
separate all arguments
void ParseInput(){
Serial.println(inputString);
int space, slash;
for(int x = 0; x < 4; x++){ // flush the last args
arg[x] = 0;
}
ArgIndex = 0; // reset
number of args to zero
space=inputString.indexOf(' ');
slash = inputString.indexOf('\n');
inputString = inputString.substring(0,slash);
cmd = inputString.substring(0,space);
inputString =
inputString.substring(space+1,slash);
while (inputString.indexOf(' ') > 0){
space = inputString.indexOf(' ');
arg[ArgIndex] =
inputString.substring(0,space).toInt();
inputString =
inputString.substring(space+1,inputString.length());
ArgIndex++;
}
arg[ArgIndex] = inputString.toInt();
}
void serialEvent(){
// Serial.println("l");
while(Serial.available()){
// Serial.println("d");
char inChar=(char)Serial.read();
inputString += inChar;
// Serial.println(inputString);
if(inChar=='\n'){
stringComplete=true;
// Serial.println("complete");
}
}
}
Encoders: This
contains the code for reading the photo interrupters and then updating the
coordinates in accordance. This essentially runs the function of the location
system of the robot knows where it is. On top of this it contains the routines
for resetting the coordinates back to (0,0) and printing out the current
position.
The arduino reads the photo interrupter which depending on
the starting position of the encoder wheel attached to the motor (gap or
filled) will read the starting position HIGH or LOW. It will then continue to
read the photo interrupter until it detects a change. Once this change is
detected it must update the position of the axis. To determine whether to
decrement or increment the value, it will check the variable Xdirection (in
Boolean is false). It equals Xdirection (false) then the value of the X
coordinate will increase by 1. Otherwise if it does not equal Xdirection, it
will decrease by 1. It will then update the OldXstate so it knows where it is now.
This process continuously repeats at an extremely fast rate for the X axis and
Y axis to keep an update of the location of the gantry.
void CheckEncoders(){
int X,Y;
X = digitalRead(Xpulse);
Y = digitalRead(Ypulse);
// Serial.print(Xposition);
// Serial.print(" ");
// Serial.println(Yposition);
delay(200);
if (OldXstate == LOW){
if(X ==HIGH){
if (Xdirection) Xposition++;
if (!Xdirection) Xposition--;
OldXstate = X;
goto
CheckY;
}
}
if
(OldXstate == HIGH){
if (X == LOW){
if (Xdirection) Xposition++;
if (!Xdirection) Xposition--;
OldXstate = LOW;
}
}
CheckY:
if (OldYstate == LOW){
if(Y ==HIGH){
if (Ydirection) Yposition++;
if (!Ydirection) Yposition--;
OldYstate = Y;
//
Serial.println(Yposition);
return;
}
}
if (OldYstate == HIGH){
if (Y == LOW){
if (Ydirection) Yposition++;
if (!Ydirection) Yposition--;
OldYstate = LOW;
//
Serial.println(Yposition);
}
}
}
// used in serial to show
encoder counts
void ShowEncoders(){
Serial.print("X= ");
Serial.print(Xposition);
Serial.print( " Y= ");
Serial.println(Yposition);
}
void PrintPosition(){
Serial.print(Xposition);
Serial.print(" ");
Serial.println(Yposition);
}
void ResetEncoders(){
Xposition = 0;
Yposition = 0;
Serial.println("Encoders reset");
}
Motor Control:
This contains the routines for the driving of the x-axis and y-axis motors.
This includes forward movement, reverse movement, stopping of the motors. These
are referenced in an array and are initiated by the commands outlined in the
“commands” tab. When the motor is set at HIGH, it moves in a forward direction.
When LOW, it moves in reverse. When the enable is LOW, the motor is off. When
the enable is HIGH, the motor is on.
void SetXForward(){
digitalWrite(Xdir,HIGH);
Xdirection= true;
}
void SetXReverse(){
digitalWrite(Xdir,LOW);
Xdirection = false;
}
void XForward(){
Xdirection=true;
digitalWrite(Xenable,LOW);
delay(30);
digitalWrite(Xdir,HIGH);
digitalWrite(Xenable,HIGH);
}
void YForward(){
Ydirection=true;
digitalWrite(Yenable,LOW);
delay(30);
digitalWrite(Ydir,HIGH);
digitalWrite(Yenable,HIGH);
}
void XReverse(){
Xdirection=false;
digitalWrite(Xenable,LOW);
delay(30);
digitalWrite(Xdir,LOW);
digitalWrite(Xenable,HIGH);
}
void YReverse(){
Ydirection=false;
digitalWrite(Yenable,LOW);
delay(30);
digitalWrite(Ydir,LOW);
digitalWrite(Yenable,HIGH);
}
void XStop(){
digitalWrite(Xenable,LOW);
digitalWrite(Xdir,LOW);
Xtravel = false;
}
void YStop(){
digitalWrite(Yenable,LOW);
digitalWrite(Ydir,LOW);
Ytravel = false;
}
void Test(){
XForward();
delay(1250);
XStop();
delay(1000);
XReverse();
delay(1250);
XStop();
delay(1000);
YForward();
delay(500);
YStop();
delay(1000);
YReverse();
delay(500);
YStop();
delay(1000);
}
Servo: This
contains the routine for driving the servo to a specific angle. By doing so, it
will lower a string with the electromagnet attached to it. This will bring it low
enough to pick up an item. This code can function manually from the serial
monitor. When the command for the servo is called, and followed by a value, the
servo will drive to that angle.
void DriveServo(){
Lift.write(arg[0]);
}
Solenoid: This
contains the code for the on and off routines of the electromagnet. When it is
written HIGH, it is on. When it is written LOW, it is off.
void SolenoidOn(){
digitalWrite(Solenoid,HIGH);
}
void SolenoidOff(){
digitalWrite(Solenoid,LOW);
}
In this code new commands for manual control have also been added. They are now:
Command
|
Routine
|
Function
|
xf
|
XForward()
|
Move X-axis
forward
|
xs
|
XStop()
|
Stop X-axis
movement
|
xr
|
XReverse()
|
Move X-axis
backwards
|
yf
|
YForward()
|
Move Y-axis
forward
|
ys
|
YStop()
|
Stop Y-axis
movement
|
yr
|
YReverse()
|
Move Y-axis
backwards
|
se
|
ShowEncoders()
|
Print the
current coordinates (in the form “x” “y”)
|
re
|
ResetEncoders()
|
Reset the
coordinate value to 0,0
|
xy “ “ “ “
|
PickUp()
|
Move to the
written x (input value in first “ “) and y coordinate (input value in second “
“)
|
servo “ “
|
DriveServo()
|
Move servo to
position “ “. This lowers and raises the electromagnet (The Z-Axis)
IMPORTANT:
Only values between 60 (highest point) and 230 (lowest point) should be inputted.
|
solon
|
SolenoidOn()
|
Turn
electromagnet on.
|
soloff
|
SolenoidOff()
|
Turn
electromagnet off.
|
home
|
Home()
|
Move x and y
axis back to starting position.
|
All that is left now is the code for the item inventory, putting on a new electromagnet (as my other one broke), adding from limit switches and drilling some new holes to connect the robot to the base (it was slightly out of alignment before). The new electromagnet, resistors (10K) for the limit circuit circuit, and limit switches are below:
This should be done on wednesday and I'll upload a video sometime after.