SparkFun Forums 

Where electronics enthusiasts find answers.

For the discussion of Arduino related topics.
By t3rmintor
#126128
Hey yall
i have a seniors project which im currently working on
Im making a robotic hand which mimics the hand of its user
I'm using 5 flex sensors which control five servos via an arduino uno microcontroller

5V----------
|
Pin 0----- 100K ------ One end of flex sensor---------
| |
| Flex Sensor
| |
Ground-------------------other end of flex sensor-----

this is how i've connected the flex sensors to the arduino, now ive got five of theses in different analog pins,
and the servos connected to the digital
my problem is that, when i go to bend the flex sensor the servo rotates the full 180 degrees, but if i want to
stop it at a sertain point in that rotation, say half way, the servo kinda stutters and jolts around and dosnt stay in the place i want it to.
heres the code:
#include <Servo.h>

Servo myservo; // create servo object to control a servo
Servo myservo2;
Servo myservo3;
Servo myservo4;
Servo myservo5;

int potpin = 0; // analog pin used to connect the potentiometer
int potpin2 = 1;
int potpin3 = 2;
int potpin4 = 3;
int potpin5 = 4;
int val; // variable to read the value from the analog pin

void setup()
{
myservo.attach(8); // attaches the servo on pin 9 to the servo object
myservo2.attach(9);
myservo3.attach(10);
myservo4.attach(11);
myservo5.attach(12);
}
void loop()
{
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
val= analogRead(potpin2);
val= analogRead(potpin3);
val= analogRead(potpin4);
val= analogRead(potpin5);
val = map(val, 150, 179, 90, 179); // scale it to use it with the servo (value between 0 and 180)
myservo.write(val); // sets the servo position according to the scaled value
myservo2.write(val);
myservo3.write(val);
myservo4.write(val);
myservo5.write(val);
delay(5); // waits for the servo to get there
}

i've cut and pasted from codes i've found o n the net, and made my own, also the code line val=map( val, 150,179,90,179), i tried to use the smae values as the ones on the example in the arduino libary, val = map(val, 0, 1023, 0, 179)and nothing happened so i messed around with the values and this combo seems to work
can some one please help me,
have i wrote the code wrong?
are my servos or sensors stuffed?
please help
thz
p.s be nice this is my first try at this stuff =D
Last edited by t3rmintor on Thu Apr 28, 2011 12:24 am, edited 1 time in total.
By n1ist
#126134
I can't quite figure out your schematic... The resistor should connect between +5 and the analog input. The flex sensor between the analog input and ground. You are making a voltage divider here.

As for the code, there are some issues - you are overwriting the values that you read from the ADC, and write the last one to all servos. You will need 5 variables and 5 map() calls (or code it as read_ADC/map/write_servo repeated 5 times).

I would try to split the problem in half to make it easier to debug.

Start by reading the sensor and printing the value to your PC. That will tell you if the wiring and ADC code works.

Then, try driving the servos with fixed values (or a sweep of values using a for() loop) to see
what their ranges are and to make sure you have them wired correctly and your power supply is sufficient to move all at once.

Finally, you can figure out the scaling needed to go from ADC readings to servo positions.
/mike
By t3rmintor
#126200
sorry
for some reason the drwaing of how the flex sensor is connected6 didnt come out
too well, its just one end of the flex sensor connected to ground, and the other connectd to an anaglog
pin and.5v
if u still dont under stand i got it from here
C:\Users\Surath\Desktop\Add a flex sensor.mht
what i still dont get is what u mean by this:
"you are overwriting the values that you read from the ADC, and write the last one to all servos. You will need 5 variables and 5 map() calls (or code it as read_ADC/map/write_servo repeated 5 times)."
what do u mean by ADC?
can u please explain
i'm new to this
cheers
By fll-freak
#126216
ADC = Analog Digital Converter.
It is the magic piece of hardware that allows AnalogRead to work.

Suggest this:
Code: Select all
val1 = analogRead(potpin1);
val1 = map(val1, 150, 179, 90, 179);
val2= analogRead(potpin2);
val2 = map(val2, 150, 179, 90, 179);
val3= analogRead(potpin3);
val3 = map(val3, 150, 179, 90, 179);
val4= analogRead(potpin4);
val4 = map(val4, 150, 179, 90, 179);
val5= analogRead(potpin5);
val5 = map(val5, 150, 179, 90, 179);
myservo1.write(val1);
myservo2.write(val2);
myservo3.write(val3);
myservo4.write(val4);
myservo5.write(val5);
Also suggest powering a large number of servos from a beefier power supply.
By n1ist
#126218
Connect the 100k fixed resistor between +5 and the analog input. Also, connect the flex sensor between the same analog input and ground. That will make a voltage divider.
if u still dont under stand i got it from here
C:\Users\Surath\Desktop\Add a flex sensor.mht
No, that's a file on your computer. Nobody else can see it. You would need to either attach the file to the post, or host it on a web server and link to it.

ADC = analog to digital converter (the part of the processor that reads analog values and converts them to digital values)
Code: Select all
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023) 
val= analogRead(potpin2);
val= analogRead(potpin3);
val= analogRead(potpin4);
val= analogRead(potpin5); 
This will read potpin, and store the reading in val. Then, it will read potpin2, and overwrite the value in val with the new reading. It will then to the same with potpin3-5. So val will have the reading of potpin5 at the end, which you send to all the servos.

There are two better ways to do this.
Code: Select all
val1 = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023) 
val2= analogRead(potpin2);
val3= analogRead(potpin3);
val4= analogRead(potpin4);
val5= analogRead(potpin5); 
followed by the appropriate five map() and servo.write() calls
or
Code: Select all
val = analogRead(potpin); 
val = map(val, 150, 179, 90, 179); // scale it to use it with the servo (value between 0 and 180) 
myservo.write(val); 
repeated for each pot/servo.

/mike
By t3rmintor
#126271
thx to all who replied
but my problems arent over unfortunatley :(
this is my code curently
#include <Servo.h>

Servo myservo; // create servo object to control a servo
Servo myservo2;
Servo myservo3;
Servo myservo4;
Servo myservo5;

int potpin = 0; // analog pin used to connect the potentiometer
int potpin2 = 1;
int potpin3 = 2;
int potpin4 = 3;
int potpin5 = 4;
int val; // variable to read the value from the analog pin
int val2;
int val3;
int val4 ;
int val5;

void setup()
{
myservo.attach(8); // attaches the servo on pin 9 to the servo object
myservo2.attach(9);
myservo3.attach(10);
myservo4.attach(11);
myservo5.attach(12);
}
void loop()
{
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
val = map(val,150,179,90,179);
val2= analogRead(potpin2);
val2 = map(val,150,179,90,179);
val3= analogRead(potpin3);
val3 = map(val,150,179,90,179);
val4= analogRead(potpin4);
val4 = map(val,150,179,90,179);
val5= analogRead(potpin5);
val5 = map(val, 150, 179, 90, 179); // scale it to use it with the servo (value between 0 and 180)
myservo.write(val); // sets the servo position according to the scaled value
myservo2.write(val);
myservo3.write(val);
myservo4.write(val);
myservo5.write(val);
delay(5); // waits for the servo to get there
}
Is this wat u ment or is t still wrong?
the servo works with this code but stilkl stutters at positions within that 180 arc
is the servo stuffed?
what i've also noticed is when it stutteres when i try to stop it is a certain position
the on ligh of the arduino flicker constantly, do i need a seperate power source for the servo
remember with this code, i only hooked up one servo to try ount not all 5

cheers
p.s sorry about the message errors, i thought i pasted the page url...woops ;)
and is there anyway i can upload a video to this site?, then i can realy get my message across
By n1ist
#126281
Closer...
Try this (I just modified your code; I don't have an Arduino to try this out on...)
Code: Select all
#include <Servo.h> 

Servo myservo1; // create servo object to control a servo 
Servo myservo2; 
Servo myservo3;
Servo myservo4;
Servo myservo5;

int potpin1 = 0; // analog pin used to connect the potentiometer
int potpin2 = 1;
int potpin3 = 2;
int potpin4 = 3;
int potpin5 = 4;
int val1; // variable to read the value from the analog pin 
int val2; 
int val3;
int val4 ; 
int val5;

void setup() 
{ 
    myservo1.attach(8); // attaches the servo on pin 9 to the servo object 
    myservo2.attach(9);
    myservo3.attach(10);
    myservo4.attach(11);
    myservo5.attach(12);
}

void loop() 
{ 
    val1 = analogRead(potpin1); // reads the value of the potentiometer (value between 0 and 1023) 
    val1 = map(val1,150,179,90,179);
    val2 = analogRead(potpin2);
    val2 = map(val2,150,179,90,179);
    val3 = analogRead(potpin3);
    val3 = map(val3,150,179,90,179);
    val4 = analogRead(potpin4);
    val4 = map(val4,150,179,90,179);
    val5 = analogRead(potpin5); 
    val5 = map(val5, 150, 179, 90, 179); // scale it to use it with the servo (value between 0 and 180) 
    myservo1.write(val1); // sets the servo position according to the scaled value 
    myservo2.write(val2); 
    myservo3.write(val3);
    myservo4.write(val4);
    myservo5.write(val5);
    delay(5); // waits for the servo to get there 
} 
The stuttering and blinking power light tells me that your power supply can't supply enough current. Are you trying to power the servos from the 5V regulator on the Arduino? I doubt that will be able to supply enough current. You will need a separate supply for the servos (4 D-cells in series will work for testing if you don't have a bench supply handy). Connect the ground or negative of the new supply to the ground of the Arduino, and the positive of the supply to the servo power pins.
/mike
By fll-freak
#126282
Anticipating the next problem... Once you solve the power supply issue and the power LEDs stop blinking, you will find that the servos will always likely be hunting. They may not move a lot, but they will always be buzzing even if your hand is still. The issue will be that the ADCs will be returning different values just from tiny changes as your hand moves even when 'still'. The hunting over time will lower the life span of the servos. For the short term it will not be a problem unless you find the noise objectionable. One possible solution is to add some hysteresis or a simple time lag filter.
By t3rmintor
#126361
Wooooot
fil-freak, i realised that the servo will be hunting even with the hand at rest, but thats just some thing i'm gonna have to live with
i'm gonna try and solve the power issure now
n1ist
OMG i LOVE UUU!!!
thx for the mod code, works a treat
owe u one ;)
just gotta sovle the power problem now
probs been a paint in the rear end, casue i'm such a noob at this
but thax to evey one who replied
cheers
T3rminator
By HappyBomberGuy
#127087
I've thought of trying a similar project and thought of the same problem. What is happening is as mentioned in a previous post... the ADC will be returning the minute differences with each reading... this is from minute muscle twitch and electrical noise. My suggestion is creating a "Dead Band" how you would do this is in your code.
What this will do is compare the current position or value to a new one, by doing this the code can determine how much you've moved your finger... if its alot then it will move the servo, if its minute due to muscle twitch or electrical noise then it will stay at it's last position without jitter. The outline below is for one servo, but all you need to do is get one servo working and then expand the code to do the same for all 5.

Step 1: Initialize and Read ADC value and then position servo accordingly.

Step 2: On all subsequent reads of the ADC compare the new value of the ADC to the old and use a if statement to decide if there is a significant change in position... a general block might look something like this:

Read ADC // Get ADC reading
ADC = newValue // save value
move_Servo(newValue); // Servo control function
oldValue = newValue // save current value into oldValue for comparison for next read
Read ADC // Get next ADC reading

if((newValue - oldValue) > deadBand) OR (newValue - oldValue) < deadBand)) // if newValue has a difference greater than or less than the dead band then move
{
move_Servo(newValue):
}

Remember this is a general outline... you'll need to find the actual syntax commands to apply... such as the OR in the if statement... I believe it's "^" but check to make sure... let me know how this works.
By HappyBomberGuy
#127106
Correction...

Considerations:
1. Deadband
2. Drift
3. Updating servo with new value

Thoughts:
1. What we want to do is eliminate the electrical noise and minute muscle twitch. As the ADC will pick both of these up we must compensate in software. This is where our deadband comes in play. It allows us to dictate the amount of change that must be registered before reacting. In this way electrical noise and muscle twitching will be ignored.

2. Drift will occur if our reads continually compare the new value to the old value. Think of it like this. Is it possible to move your fingures ever so slightly and slowly that the difference between each read of the ADC is less than our deadband, but still drifting slightly away from the current servo position, so that overtime our servo position no longer reflects our finger position?
Example....
Deadband = 20
newValue = 150
oldValue = 140

according to the if statement there is no signigicant change... however if we constantly update the oldValue based on the newValue you will then get this next time

oldValue = 150... and say the newValue now equals 160
newValue = 160

Still no significant change... again if we continue to update the oldValue based on the newValue we may again get the same situation
oldValue = 160
newValue = 170

Still no significant change... so our servo has not budged a single bit... however compare the current position to the new value and what do you get
170(newValue) - 140(value where we started and where the servo is holding)
that difference is 30... well above our deadband, but the value has been allowed to accumulate drift, so our servo is still in the initial position.

To get rid of this we need update the oldValue ONLY when the deadband range is exceeded. This way ALL new reads of the ADC are compared to the current position of the servo.

3. Unless we exceed the deadBand range we update the servo control function(running in an isr called every 20ms, 16 bit timer would be ideal) with the last value each time through the program. When the range is exceeded we feed it the new value.

So perhaps a better outline would be:

const char DeadBand = 20; // Dead band range constant
int newValue = 0; // value of the most recent ADC read
int currentValue = 0; // Value of the current ADC value used to hold current servo position

void isr routine(void)
{
// insert code to give desired pulse width based on currentValue variable (timer0 would be ideal here)
}

void main()
{
//setup hardware

Read ADC // Get initial position of flex sensor
currentValue = ADC // save to currentValue variable

// Configure TMR1 to call interrupt isr every 20ms

while(1) // Do forever
{
Read ADC
newValue = ADC // save ADC read to new variable

if ( ((newValue - currentValue) > deadBand) ^ ((currentValue - newValue) > deadBand))
{
currentValue = newValue // update servo position value
}
}
}

Again this is a very rough outline, but I hope it gives you the general gist...