Having constructed the small 4 degree of freedom (4 DOF) plastic robot arm, similar to the MeArm, ( A Small Simple Robot Arm : Part 1 Mechanical Construction ) I have now added a Nano controller and some simple software.
For this initial software I have not used the Servo library but instead written my own PWM generation for the micro servo motors. I did this for two reasons; first I wanted to have complete control over the servo motors rather than relying on someone else's library and second to reduce the drain on the battery. Using the Servo library results in the servo motors being driven all the time, which is good for position control but bad for power consumption. As I generally use AA batteries to power most of my projects then reducing battery drain is important, especially during the initial getting going and testing periods. I may well change to using a servo library in later software once I am happy with everything else.
RC servo motors, including micro servo motors, use much the same PWM structure to operate. Every 20 ms there is a pulse of at least one ms but no longer than 2 ms. Altering the pulse width from 1 ms (equivalent to 0 degrees on the servo motor shaft) to 2 ms (equivalent to 180 degrees on the servo motor shaft) enables the angle of the servo motor to be controlled very easily from a microcontroller such as the Nano.
Below is the function I have written to produce this waveform, without using any timers or interrupts.
void myservo(int select_servo, int servo_value)
// servo_value must be between 0 and 2000
{
int index;
if (servo_value < 0)
servo_value = 0;
if (servo_value > 2000)
servo_value = 2000;
for (index = 0; index < 30; index++)
{
digitalWrite(select_servo, HIGH); // Start the servo pulse
delayMicroseconds(500); // Basic pulse period part
delayMicroseconds(servo_value); // Additional delay for servo position
digitalWrite(select_servo, LOW); // End the servo pulse.
delayMicroseconds(2000 - servo_value);
delay(18); // wait for the pulse space
} /* for */
}
I create a pulse of at least 1000 us which is then extended by up to a further 1000 us to provide accurate control of the pulse and hence (hopefully) the servo motor angle, with a 18 ms delay, the space delay, at the end. It is not particularly clever but it does work.
I decided to use a serial text user interface as this provides the options of using the Serial Monitor built into the Arduino IDE or using a Bluetooth serial communications link to a mobile phone, using an App called BlueTerm. I have used this App before, it is not the best as it can be a bit unreliable with the Bluetooth link, but it is simple. At the moment I am using the Serial Monitor within the Arduino IDE.
Arduino does not provide a comprehensive ASCII character input function, just the Serial.read() function, which is for individual characters so I have modelled my User Interface on single character entry. To avoid crashing the input I treat all inputs as ASCII characters instead of integers and then use a switch statement to convert to integers. It is a bit clunky but it it is simple and it does work. Below is an example of selecting the Base rotation. I allow angles to be multiples of 10 degrees so the selection is from 0 degrees to 180 degrees (the number 0 to the letter I).
void Base(void)
{
int base_select;
int base_angle;
base_angle = 0;
base_select = 0;
Serial.println("Base Servo Selected ");
Serial.println("Select Base Angle ");
Serial.println();
Serial.println("0 degrees : 0 ");
Serial.println("10 degrees : 1 ");
Serial.println("20 degrees : 2 ");
Serial.println("30 degrees : 3 ");
Serial.println("40 degrees : 4 ");
Serial.println("50 degrees : 5 ");
Serial.println("60 degrees : 6 ");
Serial.println("70 degrees : 7 ");
Serial.println("80 degrees : 8 ");
Serial.println("90 degrees : 9 ");
Serial.println("100 degrees : a ");
Serial.println("110 degrees : b ");
Serial.println("120 degrees : c ");
Serial.println("130 degrees : d ");
Serial.println("140 degrees : e ");
Serial.println("150 degrees : f ");
Serial.println("160 degrees : g ");
Serial.println("170 degrees : h ");
Serial.println("180 degrees : i ");
Serial.println();
base_select = ' ';
while (Serial.available() < 1)
{
delay(10);
} /* while */
base_select = Serial.read();
switch (base_select)
{
case '0' : base_angle = 0; break;
case '1' : base_angle = 10; break;
case '2' : base_angle = 20; break;
case '3' : base_angle = 30; break;
case '4' : base_angle = 40; break;
case '5' : base_angle = 50; break;
case '6' : base_angle = 60; break;
case '7' : base_angle = 70; break;
case '8' : base_angle = 80; break;
case '9' : base_angle = 90; break;
case 'a' : base_angle = 100; break;
case 'b' : base_angle = 110; break;
case 'c' : base_angle = 120; break;
case 'd' : base_angle = 130; break;
case 'e' : base_angle = 140; break;
case 'f' : base_angle = 150; break;
case 'g' : base_angle = 160; break;
case 'h' : base_angle = 170; break;
case 'i' : base_angle = 180; break;
default : Serial.println("Unknown Command ");
base_angle = 90;
delay(1000);
} /* switch */
There are similar functions for the Shoulder(), Arm() and Grip() servos, although the gripper only has three positions; open, middle, closed. The main loop then just cycles through to select which servo motor is to be changed (Base, Shoulder, Arm, Gripper) and uses another switch statement to activate the appropriate joint function, see below.
while (1)
{
Serial.println("Small robot Arm " );
Serial.println("Dubbie Dubbie : Just for Fun " );
Serial.println(" 21st Jan'20 ");
Serial.println();
delay(500);
Serial.println("Select Servo ");
Serial.println("Base : 1 ");
Serial.println("Shoulder : 2 ");
Serial.println("Arm : 3 ");
Serial.println("Grip : 4 ");
Serial.println();
int value = ' ';
while (Serial.available() < 1)
{
delay(10);
} /* while */
value = Serial.read();
switch (value)
{
case '1' : Base();
break;
case '2' : Shoulder();
break;
case '3' : Arm();
break;
case '4' : Grip();
break;
default : Serial.println("Unknown Command "); delay(1000);
} /* switch */
delay(1000);
} /* while */
I am quite happy with what I have produced so far but I am thinking of making it better, if I can. The servo motor movements are fairly jerky as they try to move from the current position to the next position as quickly as possible, without any acceleration or deacceleration. I would like to see if I can make these movements much smoother which I believe may be called servo easing. I'm not sure what the trigonometric function is, some sort of sine or cosine I think, which I could put into a lookup table and then somehow combine with the distance to be moved.
Dubbie
Top Comments