Make Servo motors move more smoothly using a joystick? - arduino

So i'm making a "turret" type thing using two servo motors, controlled by a joystick. The code i'm running works, however it's very jerky and doesn't move that well, especially in diagonal lines. My code is as follows:
#include <Servo.h>
#define LASER 11
int x = 0;
Servo servo_1; // create servo object to control a servo
Servo servo_2;
// Arduino pin numbers
const int SW_pin = 2; // digital pin connected to switch output
const int X_pin = 0; // analog pin connected to X output
const int Y_pin = 1; // analog pin connected to Y output
int butt;
int joy_val;
void setup() {
pinMode(SW_pin, INPUT);
digitalWrite(SW_pin, HIGH);
servo_1.attach(9);// attaches the servo on pin 9 to the servo object
servo_2.attach(10);
pinMode(LASER, OUTPUT);
digitalWrite(LASER, HIGH);
Serial.begin(9600);
}
void loop() {
joy_val = analogRead(X_pin); // reads the value of joystick (between 0-1023)
joy_val = map(joy_val, 0, 1023, 0, 180); // servo value between 0-180
servo_1.write(joy_val); // sets the servo position according to the joystick value
delay(150);
joy_val = analogRead(Y_pin); // reads the value of joystick (between 0-1023)
joy_val = map(joy_val, 0, 1023, 0, 180); // servo value between 0-180
servo_2.write(joy_val); // sets the servo position according to the joystick
value
delay(150);
delay(15);
butt = digitalRead(SW_pin);
if (butt == LOW){
x = true;
}
if (x == true){
digitalWrite(LASER, LOW);
Serial.print(x);
}
}
I would really appreciate any advice or help, I'm fairly new to arduino :)

Servos are small and light and attempt to move to the position you tell them as quickly as they can. Joysticks can also change values very quickly, and they can also be glitchy. As a result, your servos are constantly making lots of small, quick movements, which can make the turret seem jerky.
I can think of two options, and you might want to do both:
Smooth out the joystick inputs with some low-pass filtering. This generally just means using a weighted average of the current and previous values. The idea is to eliminate a bad reading or two that might happen because of dirty contacts in the potentiometer.
Smooth out the motion. Instead of instantly trying to move the servos directly to the joysticks' current positions, move them toward the target position. On each iteration of the loop, they'll get closer to the target position instead of trying to jump there almost instantaneously.
For #2, there are a couple approaches I like to use.
One is to simply use a weighted average of the servo's current position and the target position. If you move the joystick a fair distance, the turret will swivel quickly but slow down as it approaches the target position.
The other is to use a physical model. Imagine creating a force vector that points from the servos' current position to the joysticks' target position and is proportional to the distance between them. Apply that force to the current point. Also apply a "friction" force that resists the current point's velocity. Numerically integrate the velocity and the position in the loop. If you move the joystick suddenly to a new position, then the turret will accelerate toward it and then slow down as it approaches it. Adjusting the constants used to compute the forces will let you control how "heavy" the mechanism appears to be.

I put a delay proportional to the speed of the servo. Try this (taken from my tutorial: Arduino Servo Motor Basics and Control):
#include <Servo.h>
#include <math.h>
Servo servo_1; // servo controller (multiple can exist)
int servo_pin = 3; // PWM pin for servo control
int joy_pin_x = A0; // pin for x-dir joystick
int joy_pin_y = A1; // pin for y-dir joystick
int offset_x = 0; // subtracting the initial joystick x-location
int offset_y = 0; // subtracting the initial joystick y-location
int pos = 90; // servo starting position aligned with joystick
int prev_deg = 0; // bound for reducing jitter
int x_prev = 0; // bound for reducing jitter
int y_prev = 0; // reducing jitter
void setup() {
servo_1.attach(servo_pin); // start servo control
Serial.begin(9600);
servo_1.write(pos); // move to center (joystick neutral)
Serial.println("Positioned at 90 Degrees");
offset_x = analogRead(joy_pin_x); // initial joystick x-val
offset_y = analogRead(joy_pin_y); // initial joystick y-val
}
void loop() {
int x_val = analogRead(joy_pin_x)-offset_x; // relative joystick x
int y_val = analogRead(joy_pin_y)-offset_y; // relative joystick y
if (abs(x_prev-x_val)<10 and abs(y_prev-y_val)<10){
// reduce jitter
} else {
x_prev = x_val;
y_prev = y_val;
float deg = 180-(int(atan2(x_val,y_val)*(180.0/PI))+90); // angle calc
if (abs(deg-prev_deg)>2 and deg>0 and deg<180){
servo_1.write(deg); // move servo to joystick location
delay(abs(deg-prev_deg)*(10.0/6.0));
prev_deg = deg;
Serial.println(deg); // print out degree
}
}
}
Notice the delay that is functionally dependent on the angle it's moving to - this will 'smooth' the servo and reduce jitter (though not completely remove it).

Related

analogRead() output oscillates even though the pin is grounded

I'm using Arduino Micro to read from 5 flex sensors and display the corresponding angles to the Serial monitor. I am currently having quite some problems with the oscillating values I am getting from the analogRead(). It doesn't seem to matter whether the pin is connected to a flex sensor or just grounded - the output is oscillating a lot.
Originally everything was being read and outputted just fine but I wanted to have an exact 100Hz sampling frequency and tried to play a bit with Timer Interrupts. And that's when this oscillating behaviour started. I reversed to my original code, which just uses some delay(), and simplified to only read from two pins, but cannot seem to shake off the oscillations.
I think I may have messed up something about ADC when trying to implement Interrupts, but I don't know how to check it or fix it. Please, help me figure out how to fix this!
This is the raw output of analogRead. The drop in values occurs when I bend the flex sensor
And this is the resulting calculated angle. Also oscillating.
Here is my code minimal working example:
int fin;
const int input[5] = {A0,A1,A2,A3,A4}; // the analog pins
int flex[5]; // analog signal read
float flexV;
float flexR[5]; // resistance on the 47k resistor
int angle[5]; // joint angles
const float VCC = 4.98; // Measured voltage of Arduino 5V line
// Measured resistance of the 47k resistors R1-R5
const float R[5] = {45900.0,45900.0,45900.0,45900.0,45900.0};
// Calibration values of resistance measured during straight phase and 90 deg bend phase
const float R_STRAIGHT[5] = {37651.0,37651.0,37651.0,37651.0,37651.0};
const float R_BEND[5] = {71783.0,71783.0,71783.0,71783.0,71783.0};
void setup() {
}
void loop() {
for(fin = 0; fin <= 4; fin++) {
flex[fin] = analogRead(input[fin]);
flexV = flex[fin]*VCC/1023.0;
flexR[fin] = R[fin] * (VCC/flexV - 1.0);
angle[fin] = map(flexR[fin],R_STRAIGHT[fin],R_BEND[fin],0,90.0);
delay(1);
}
Serial.print(angle[0]);
Serial.print(" ");
Serial.print(angle[1]);
Serial.print(" ");
Serial.print(angle[2]);
Serial.print(" ");
Serial.print(angle[3]);
Serial.print(" ");
Serial.print(angle[4]);
Serial.print(" ");
Serial.println(millis());
delay(6);
}
ok, analogReads normally do have a little oscillation, its normal! they are measuring voltage values and depending on the sensor you are using they will oscillate, same idea of measuring voltage using a multi meter. if you want to learn a bit more about this, ADC's conversor is a good way to start.
What you need to do in order to prevent those oscillations is to develop a filter. this could be done on hardware or software. Obviously the software is the easiest way to go.
My tip for you would be to a average filter! it's a simple concept, you will get X readings at the same time of that sensors (values would go up and down on variation) and you would get the avarage value out of it.
Here is a simple example using your code:
int fin;
const int input[5] = {A0,A1,A2,A3,A4}; // the analog pins
int flex[5]; // analog signal read
float flexV;
float flexR[5]; // resistance on the 47k resistor
float average; //Variable to store the sum of measurements
int nSamples = 4; //Number of reading you are going to use
int angle[5]; // joint angles
const float VCC = 4.98; // Measured voltage of Arduino 5V line
// Measured resistance of the 47k resistors R1-R5
const float R[5] = {45900.0,45900.0,45900.0,45900.0,45900.0};
// Calibration values of resistance measured during straight phase and 90 deg bend phase
const float R_STRAIGHT[5] = {37651.0,37651.0,37651.0,37651.0,37651.0};
const float R_BEND[5] = {71783.0,71783.0,71783.0,71783.0,71783.0};
void setup() {
}
void loop() {
for(fin = 0; fin <= 4; fin++) {
/* A new for here to make readings and store them on the average variable */
for(int x = 0; x <= nSamples; x++){
flex[fin] = analogRead(input[fin]);
average = average + flex[fin];
}
/*Do de avarage and clear the value on this variable*/
flex[fin] = average/nSamples;
avarage = 0;
flexV = flex[fin]*VCC/1023.0;
flexR[fin] = R[fin] * (VCC/flexV - 1.0);
angle[fin] = map(flexR[fin],R_STRAIGHT[fin],R_BEND[fin],0,90.0);
delay(1);
}
Serial.print(angle[0]);
Serial.print(" ");
Serial.print(angle[1]);
Serial.print(" ");
Serial.print(angle[2]);
Serial.print(" ");
Serial.print(angle[3]);
Serial.print(" ");
Serial.print(angle[4]);
Serial.print(" ");
Serial.println(millis());
delay(6);
}
The idea here is simple, to smooth the values by doing this average, leading to more consistent values. Obviously, Higher number of samples improve the results.
It's simple math, if you are getting 4 values like: 45, 50, 55, 50, your average would be 50 (45+50+55+50 = 200/nSamples = 50)

Arduino to generate a rising waveform (sine or triangle)

I would like to apply an increasing voltage and hold to my output from my arduino UNO. I realize that the arduino does not allow me to output analog values, and thus I decided to use an R2R ladder (with R- 22kohms and 2R- 47kohms). This would allow me to convert to an analog voltage. I made use of the eight digital pins on the arduino, to set up an 8 bit R2R ladder. I am able to output a sine wave, with my current setup, but a little bit unsure on how to output a wave which goes up to the maximum value and stops. (i.e. a wave like given in the picture below).
This wave is basically a triangle wave or even a sine wave which goes up to a max value and stays there (with 200 micro second pulse duration).
I have created a visual of my circuit to better demonstrate my problem:
I also attempted my problem, by outputting a sine wave. My code is as follows:
void setup() {
//set pins 0-7 as outputs
for (int i=0; i<8; i++){
pinMode(i, OUTPUT);
}
}
void loop() {
double value =0;
int check=0; int t=0;
while(check==0){
if (value<254){
value = 127+127*sin(2*3.14*t/100);
//this sends a sine wave centered around (127/255 * 5)=2.5V
//max will reach when t=25
PORTD=value;
delayMicroseconds(4); //wait 4 micro seconds
//this means that the max value will reach at ~25*6 =150 microseconds
}
else{
value =255;
PORTD=value; //just output the max of the sine wave (i.e. 255)
delayMicroseconds(50); //delay to ensure total duration is 150+50=200 microseconds
PORTD=0; //output back a 0
check=1; //condition to exit the loop
}
t=t+1;
}
}
For some reason, the pulse generated is not exactly what I am looking for. Is there something I am doing wrong? Or is there a better implementation for something like this? Additionally, if there is something I am missing in my question, please let me know.
I realize that the arduino does not allow me to output analog values
In order to output analog values use one of the analog outputs of the Arduino.
They are marked with a ~
Here' an example from the Arduino reference:
int ledPin = 9; // LED connected to digital pin 9
int analogPin = 3; // potentiometer connected to analog pin 3
int val = 0; // variable to store the read value
void setup() {
pinMode(ledPin, OUTPUT); // sets the pin as output
}
void loop() {
val = analogRead(analogPin); // read the input pin
analogWrite(ledPin, val / 4); // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
}

Creating an oscillating tone using Arduino, ATTiny85 and a simple buzzer

First a bit of background. I am attempting to make an LED glow and a buzzer produce a tone that sweeps smoothly up and down in frequency, like an air raid siren. I am using an Arduino Uno, connected to an ATTiny85 chip operating at 8hz clock speed. An SPDN contact switch is used to provide input on 4, while 0 and 1 go out to the positive legs of the buzzer and LED respectively. Suitable resistors are being used to limit current, which is 5v from the Arduino board.
Now, my problem. I can produce a constant tone at any frequency I like. I can produce a tone that goes back and forth between two tones like a UK police siren (Dee-Daa-Dee-Daa etc) but I am unable to generate a smooth transition between two tones. The LED works as expected.
What I actually observe is a single tone that does not vary. Once or twice I've managed to produce a tone that varies, but randomly within the given range rather than smoothly.
I am not using the tone() Arduino command and would prefer not to, as it is not best suited for what I am trying to accomplish.
Here is my code:
const float pi2 = 6.28318530717;
const int buzzer = 0;
const int light = 1;
const int button = 4;
// Set up the pins as input and output
void setup() {
pinMode(buzzer, OUTPUT);
pinMode(light, OUTPUT);
pinMode(button, INPUT);
}
bool buzzerState = LOW;
float nextFlip = 0;
// Generates a sine wave for the given uptime, with a period and offset (in milliseconds).
float sineWave(float uptime, float period, float offset, float minimum, float maximum) {
float s = sin(((uptime + offset) * pi2) / period);
// Normalise the result between minimum and maximum
return (s + 1) / 2 * (maximum - minimum) + minimum;
}
// Returns the time between buzzer inversions based on a given system uptime.
float frequency(float uptime) {
return sineWave(uptime, 5000, 0, 1, 10);
}
// Main loop
void loop() {
// Check button state and turn the light on or off
bool buttonDown = digitalRead(button);
digitalWrite(light, buttonDown);
// Check to see if it's time for the next buzzer inversion
float m = micros();
if (!buttonDown || m < nextFlip) return;
// Get the inverse of the current buzzer state
if (buzzerState == HIGH) {
buzzerState = LOW;
} else {
buzzerState = HIGH;
}
// Write the new buzzer state
digitalWrite(buzzer, buzzerState);
// Decide when the next inversion will occur
nextFlip = m + frequency(m);
}
Silly mistake! I finally noticed: I'm reading micros() where I meant to read millis() - in other words, it was oscillating, just a thousand times faster than I intended it to! Multiplying all values up by a factor of 1000 in the sine wave function produced a lovely oscillation.

Arduino Project - LDR and ServoMotor Sync and Rotate

Im doing a home project just for fun and Im pretty new to the Arduino, but I do know the basics.
Im creating a automatic venetian blind where it will open when dark and close when bright.
So the slats of the venetian blinds will rotate to a open position when the light sensor detects darkness and rotate to a closed position when the light sensor detects light.
Im using:
arduino uno r3/
continuous servo motor /
led/
LDR (light sensor)/
10k resistor/
This code works for a standard servo motor. I wanted it to work with a continuous servo motor because you can "control it better" and its the one I have.
I guess I will need a if statement something like this:
if light sensor detects dark then rotate the servomotor a to a certain degree and then stop
and if light sensor detects light then rotate the servomotor to a certain degree and then stop.
#include <Servo.h>
Servo servo1;
int sensorPin = A0; // select the input pin for the ldr
int ledPin = 13;
unsigned int sensorValue = 0;
int servoPin = 9;
int pos = 0;
void setup()
{
//Start Serial port
Serial.begin(9600); // start serial for output - for testing
servo1.attach(9);
pinMode(ledPin, OUTPUT);
}
void loop()
{
// For DEBUGGING - Print out our data, uncomment the lines below
Serial.print("Cell = "); // print the value (0 to 1024)
Serial.println(analogRead(sensorPin)); // print carriage return
pos = analogRead(sensorPin);
pos = constrain (pos, 0, 1023);
int servoPos = map(pos, 0, 1023, 255, 0);
int servoDegree = map(servoPos, 255, 0, 0, 179);
servo1.write(servoDegree);
Serial.print("Servo Degree = ");
Serial.println(servoDegree);
int val = analogRead(sensorPin);
val = constrain (val, 0, 1023);
int ledLevel = map(val, 0, 1023, 255, 0);
analogWrite (ledPin, ledLevel);
delay(50);
}
With continuous servo motors, you give up position information. (See this Polou page for details.) This means you won't know when the blinds have reached their open/closed positions unless you also add limit switches. If you go with the switches, then a continuous servo would work. A better solution might be a small gearhead stepper motor like this one from AdaFruit. They have torque but they are much slower than continuous servos.
Key thing is that you don't want to be energizing the motor continuously (which is how standard servos maintain position). That is wasteful and will burn out the motor in something like a blinds application, day in, day out. You want it to do the task and then in loop() wait until the state (light level in your app) has changed. So you would need to keep track of the last light level, then in loop() check if the current light level is different (and greater than some threshold you will have to determine through testing), then change the state of the blinds and store that last level.

Using arduino analog inputs

I am creating my first Arduino program on the UNO r3. I have played with the Arduino Uno before just with petty example programs, etc. I am using two analog inputs to sense distance using 2 laser sensors with 0-5vdc scaling. These two inputs are 0-5vdc and I have ensured common grounding throughout. The two sensors are named left and right and are input to A0 and A1 respectively. I also have a differential POT which uses a 10K ohm POT wiper voltage as an input on A2. The theory of the program is to take the absolute value of the difference in input voltages between the left and right lasers then determine if the result is greater than or equal to the voltage on pin A2 from the POT wiper. Based on the resulting math, turn on or off a relay interposed to pin D13 via a transistor driver circuit.
The PROBLEM: I cannot achieve accurate changes in voltage on the scale (0-1023) on pins A0, A1, or A2. I have utilized the serial monitor to diagnose this problem. Not sure what the problem is, any help would be great. Also, I cannot achieve a 0 value on any of the above analog pins, even the POT wiper!!!
Here's my code:
const int lf_dist = A0; //names A0
const int rt_dist = A1; //names A1
const int differential = A2; //names A2
const int relay = 13; // select the pin for the relay coil
unsigned int left = 0; // variable to store the value coming from the left sensor
unsigned int right = 0; // variable to store the value coming from the right sensor
unsigned int diff = 0; // variable to store the value coming from the differential POT for maximum distance differential
unsigned int offset = 0; // variable that stores the value between the two laser sensors
void setup() {
Serial.begin(9600);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(relay, OUTPUT); // declare the relay pin as an OUTPUT:
analogReference(DEFAULT);
}
void loop()
{
unsigned int left = 0; // variable to store the value coming from the left sensor
unsigned int right = 0; // variable to store the value coming from the right sensor
unsigned int diff = 0; // variable to store the value coming from the differential POT for maximum distance differential
unsigned int offset = 0; // variable that stores the value between the two laser sensors
left = analogRead(A0); // read the value from the left laser
delay(5);
right = analogRead(A1); // read the value from the right sensor
delay(5);
diff = analogRead(A2); // read the value from the differential POT
delay(5);
offset = abs(left - right);
if(offset >= diff) // does math to check if left and right distances are greater than the value clocked in by the differential POT
{
digitalWrite(relay, LOW); // turns off the relay, opens the stop circuit, and turns on the yellow light
}
else
{
digitalWrite(relay, HIGH); // turns on the relay if all is good, and that keeps the machine running
}
Serial.print("\n left = " );
Serial.print(left);
Serial.print("\n right = " );
Serial.print(right);
Serial.print("\n differential = " );
Serial.print(diff);
delay(1000);
}
afaict, this should really be due to the floating pins surrounding the measuring pins, having erratic values, hence perturbating your measures. You should look at your values using arduinoscope, which will show you the interfering effects of the other floating pins on your measuring pins.
The easy workaround for this is to ground all analogical input pins you're not using, and put as much space as you can between both your inputs, so they don't interfere with each other.
I realize this thread is somewhat old not, but perhaps this will help someone. If you power the Arduino with only 5V, as you say you did with a regulator, you will get very erratic behavior, particularly from the analog pins. This is because you will start to brown out the internal voltage regulators that provide the AREF, 3.3, and 5.0 outputs. I've tested this for a robotics project I'm working on, and right around 6.5 volts, everything begins to go wrong. I suppose if you always provided 5.0 input voltage you could compensate for this effect, but in my case I used a LiPo battery that could range from 8.4 volts down to 6.0 volts, and everything goes crazy at 6.5 volts.
The minimum current that arduino sinks in during the sampling from potentiometer should not disturb the actual open input volts at the wiper.
Initialize the pins in pull up mode to avoid garbage values or 'floating' pins or use your own pull down/up resistors at the pins :)

Resources