Help with sin waves using 2 pan/tilt servos

Hello, I am working on an animation project for school and I built a pan/tilt rig using MG996r servos that move an LED around in a half sphere of motion. It is almost exacly like the setup in this video Light Painting with an Arduino - YouTube except I am only going to move mine in a quarter sphere of motion. I tried asking the person who uploaded that video, but he never responded. Instead of just taking one picture, I am taking multiple pictures and using for loops to increase the steps every picture on certain values. I am having some trouble controlling the 2 servos simultaneously at different rates.

I am able to do diagonal lines and zigzags with no problems but What I really need help with is doing circles and sin waves. I started off using the provided servo sweep sketch that uses for loops and delay but I could not get any decent results. I tried using the code provided in this link http://forum.arduino.cc/index.php?topic=191472.0 but it looked more like a square than a circle and it was super jittery. The problem with this code is that it gives decimal values to the servos when I think they can only handle round numbers. I then realized that I maybe should be using millis() timer instead of delay to better control the 2 servos simultaneously.

Would anyone be able to help me write up a code that makes the X axis servo move from 0-180 at a normal rate while moving the Y servo up and down from angles 30-60 at a sin rate of speed? Similar to one of the lines at 0:50 seconds of the light painting video.

The X servo travels from 0-180 and the Y servo travels from 0-90 so the X would need to move at twice the rate as the Y.

It doesn't need to be super accurate but I'm pretty much using the same stuff as pierrrecesar and he was able to get some good results.

Here is a link showing what my setup can do: - YouTube

While the sine is pretty simple, you can probably do the same with a series of short lines.

The equation for a circle of radius 1 is cos(x)*cos(x) + sin(y)*sin(y) = 1

HauntedCircuit:
it gives decimal values to the servos when I think they can only handle round numbers.

You can use servo.writeMicroseconds() to get finer control than you get with servo.write(). But it still only works with integers.

The demo several things at a time illustrates the use of millis() for timing.

You need to post your own code to get useful advice.

...R

KeithRB:
While the sine is pretty simple, you can probably do the same with a series of short lines.

The equation for a circle of radius 1 is cos(x)*cos(x) + sin(y)*sin(y) = 1

I'm thinking a series of short lines will be jittery.

Robin2:
You can use servo.writeMicroseconds() to get finer control than you get with servo.write(). But it still only works with integers.

The demo several things at a time illustrates the use of millis() for timing.

You need to post your own code to get useful advice.

...R

I did post the code. It's here http://forum.arduino.cc/index.php?topic=191472.0. That's all I have in my sketch that has to do with sin and cos. Forget what I said about circles, I just want an example of a sin wave so I can move on from there. If you were to start from beginning, how would you move a single servo back and from 0-180 so that it moves faster in the middle and slower at each end (edit: using millis() instead of delay)?

Edit**
This is the millis() sweep code I am using which works great on multiple servos

int position = 0 ;    // state variable
boolean forward = false ;   // state variable

unsigned long ts = millis () ;   // time accounting.
#define DELAY 20


void loop ()
{
  if (millis () - ts >= DELAY)
  {
    ts += DELAY ;   // setup timestamp for next time.
    if (forward)
    {
      servo.write (-- position) ;  // progress the servo
      if (position == 0)  // test for reverse
        forward = false ;
    }
    else
    {
      servo.write (++ position) ;  // progress the servo
      if (position == 180)  // test for reverse
        forward = true ;
    }
  }
  // other stuff
}

HauntedCircuit:
I did post the code.

No you didn't.
None of the Posts in that Thread was by you. And, in any case I have no idea which Post in that Thread you were referring to.

Make it easy for us to help you. Post the code you are using here.

...R

Robin2:
No you didn’t.
None of the Posts in that Thread was by you. And, in any case I have no idea which Post in that Thread you were referring to.

Make it easy for us to help you. Post the code you are using here.

…R

Here’s the sketch. Can you re-read my earlier posts and help me please? The only decent looking circle is void aCircle() but that was my attempt at editing the void circle() code. I used the value 6.3 because that’s the closest I could get to 2pi, and I’m trying to use the for loop to oscillate from 0 to 1 at a rate of sine (or maybe -1 to 1 would be better). It starts looks like a square at smaller values also. I’m also trying to do a similar thing with the void MillisSweep () except I just want one of the servos to oscillate while the other one sweeps at normal rate.

void Horizontal() is 0:02-0:08 of my video

#include <Servo.h> 
#include <math.h> 
#include <multiCameraIrControl.h>

int i1 = 5;
int i2 = 5;


// Camera Type
Canon D5(2); //Canon, CanonWLDC100, Nikon, Pentax, Olympus, Minolta, Sony, 

Servo servoY;
Servo servoX;
 
int posX = 0;    // variable to store the servo position 
int posY = 0;    // variable to store the servo position 

int AnimX = 0;
int AnimY = 0;

int Rval = 255;
int Gval = 255;
int Bval = 255;

#define GREEN 3
#define RED 5
#define BLUE 6



int delayval = 500;

//Random numbers
 long rand1;
 long rand2;
 
//----------------------------------------------------------------------------------------------------------------------------------------

void setup() 
{ 
  Serial.begin(9600);
  pinMode(2,OUTPUT);
  servoY.attach(11);  // attaches the servo on pin 11 to the servo object
  servoX.attach(10);  // attaches the servo on pin 10 to the servo object 
} 

//----------------------------------------------------------------------------------------------------------------------------------------

void loop() 
{
 //Horizontal();
}
//---------------------------------------------------------------
void Horizontal() {
    //All lines upward
  for(int Anim = 0; Anim <= 15; Anim+=1){
    servoY.write(5);
    servoX.write(0);
      analogWrite(BLUE, 255);
      analogWrite(RED, 255);
    delay(500);
    D5.shutterNow();
    delay(1000); 
      for(posX = 0; posX <= 180; posX += 1){  
    int RED1 = map(posX, 0, 180, 255, 0);
    int GREEN1 = map(posX, 0, 180, 0, 255);
    analogWrite(BLUE, RED1);
    analogWrite(RED, GREEN1);  
    servoX.write(posX);     
    delay(4);
  }
  analogWrite(BLUE, 255);
  analogWrite(RED, 255);
  delay(300); 

  for(posX = 180; posX >= 0; posX -= 1){
  int RED1 = map(posX, 0, 180, 255, 0);
    int GREEN1 = map(posX, 0, 180, 0, 255);  

    analogWrite(RED, GREEN1);
    analogWrite(BLUE, RED1);    
    servoX.write(posX);     
    delay(4);
  }
  for(int i = 5 + Anim; i <= 95; i+=15){
    analogWrite(BLUE, 255);
    analogWrite(RED, 255);
  servoX.write(0);
  delay(50);
  servoY.write(i);
  delay(500);

  for(posX = 0; posX <= 180; posX += 1){  
    int RED1 = map(posX, 0, 180, 255, 0);
    int GREEN1 = map(posX, 0, 180, 0, 255);
    analogWrite(BLUE, RED1);
    analogWrite(RED, GREEN1);  
    servoX.write(posX);     
    delay(4);
  }
  analogWrite(BLUE, 255);
  analogWrite(RED, 255);
  delay(300); 

  for(posX = 180; posX >= 0; posX -= 1){
  int RED1 = map(posX, 0, 180, 255, 0);
    int GREEN1 = map(posX, 0, 180, 0, 255);  

    analogWrite(RED, GREEN1);
    analogWrite(BLUE, RED1);    
    servoX.write(posX);     
    delay(4);
  }
   
  analogWrite(RED, 255); 
  analogWrite(BLUE, 255);
  
 }
 delay(100);
  D5.shutterNow();
  delay(2000); 
 }


//---------------------------------------------------------------
 void Circle() {
for(int i=0; i<=359; i++){
servoX.write(20 * cos (i) + 90);
servoY.write(20 * sin(i) + 45);
analogWrite(GREEN, 0); 
delay(100);
}
 }
 //---------------------------------------------------------------
 void aCircle() {
  //for(int ii=0; ii<=10; ii++){
   for(float i=0; i<=6.3; i+=.1){
      float j = round(400 * cos (-i) + 1500);
      float k = round(250 * sin (i) + 1000);
      int jj = j;
      int kk = k;
      analogWrite(GREEN, 0); 
      servoX.writeMicroseconds(jj);
      servoY.writeMicroseconds(kk);
      Serial.print(jj);
      Serial.print(",");
      Serial.println(kk);
      delay(6);
 }
  //}
 }

void MillisSweep () {
 int pposition = 0 ;    // state variable
boolean forward = false ;   // state variable

unsigned long ts = millis () ;   // time accounting.
#define DELAY 5

  if (millis () - ts >= DELAY)
  {
    ts += DELAY ;   // setup timestamp for next time.
    if (forward)
    {
      servoX.write (-- pposition) ;  // progress the servo
      if (pposition == 0)  // test for reverse
        forward = false ;
    }
    else
    {
      servoX.write (++ (180*cos(i)) ;  // progress the servo
      if (pposition == 180)  // test for reverse
        forward = true ;
    }
  }
}
//---------------------------------------------------------------
 void RandomCircle() {
  int CircleXCenter = 90;
  int CircleYCenter = 90;
  int loopDelay = 30;
   
  randomSeed(analogRead(4));
  CircleXCenter = random(80,100);
  randomSeed(analogRead(5));
  CircleYCenter = random(80,100);
  randomSeed(analogRead(6));
  loopDelay = random(15,30);
 
  for (int Rad = 5; Rad < 20; Rad++)
  {
    for (int i = 0; i < 100; i++)
    {
      float angle = i*2*3.14/100;
      int xPos = CircleXCenter + (cos(angle) * Rad);
      int yPos = CircleYCenter + (sin(angle) * Rad);       
      servoX.write(xPos);
      servoY.write(yPos);
      delay(loopDelay);     
    }
  }
  randomSeed(analogRead(4));
  CircleXCenter = random(80,100);
  randomSeed(analogRead(5));
  CircleYCenter = random(80,100);
  randomSeed(analogRead(6));
  loopDelay = random(15,30);
   
  for (int Rad = 20; Rad > 5; Rad--)
  {
    for (int i = 100; i > 0; i--)
    {
      float angle = i*2*3.14/100;
      int xPos = CircleXCenter + (cos(angle) * Rad);
      int yPos = CircleYCenter + (sin(angle) * Rad);
       
      servoX.write(xPos);
      servoY.write(yPos);
      delay(loopDelay);
     
    }
  }
 }

Does your Horizontal() function work as you want?
If so it would help if you can provide a walk-through of how it works.

Do you know how to calculate the successive X and Y positions to describe a sine wave? It may make sense to calculate them on a spreadsheet and put them into an array in your Arduino program. Then all you need to do is read through the array and send the values to the two servos.

...R

Robin2:
Does your Horizontal() function work as you want?
If so it would help if you can provide a walk-through of how it works.

Do you know how to calculate the successive X and Y positions to describe a sine wave? It may make sense to calculate them on a spreadsheet and put them into an array in your Arduino program. Then all you need to do is read through the array and send the values to the two servos.

...R

Not really sure how to do that. That's why I'm asking for help. The horizontal code causes servoX to move from 0-180 and then from 180-0 and then moving up 15 degrees and repeats every for loop on servoY (this is one frame of animation). Then a for loop progresses the starting Y position up 1 degree every new frame of animation. The Horizontal code is just a modification of the Servo.h Sweep code and doesn't have anything to do with what I'm trying to do.

I'm using degrees from 0-180 on servoX and 0-100(or 0-90) on servoY, and not X/Y coordinates. So, the 0,0 point is really 90°,45°

Will an array work if I'm trying to progress the size of circle or move the sin wave over in space by 1 unit every for loop. The idea is to be able to morph each shape over time automatically using +=1 every loop.

How do I sweep a servo back and from from 0-180 degrees at a rate of sin so that it moves faster in the middle and slower at the edges? I'm using a MG996r servo.

KeithRB:
While the sine is pretty simple, you can probably do the same with a series of short lines.

The equation for a circle of radius 1 is cos(x)*cos(x) + sin(y)*sin(y) = 1

LOL, no!! That's one convoluted way of saying the angle x = the angle y

Unit circle equation: xx + yy = 1

Or parametrically in the angle w:

x = cos(w)
y = sin(w).

Use a sin function? Use millis as your timebase, don't attempt to do it with delays.

const float Timescale 5000;
const float Distance 200;
void loop() {
  float Radians = millis() / Timescale;
  Servo.write(sin(Radians)*Distance;
  //do other stuff here
  //...
}

MarkT:
LOL, no!! That's one convoluted way of saying the angle x = the angle y

Unit circle equation: xx + yy = 1

Or parametrically in the angle w:

x = cos(w)
y = sin(w).

And how does this relate to servos? I don't know how else to say I'm a noob who needs help. I need to move a servo back and forth from 0-180 so that it moves faster in the middle and slower at the edges at the rate of sin.

Do not cross-post. Threads merged.

HauntedCircuit:
The horizontal code causes servoX to move from 0-180 and then from 180-0 and then moving up 15 degrees and repeats every for loop on servoY (this is one frame of animation). Then a for loop progresses the starting Y position up 1 degree every new frame of animation.

OK, now I can understand it better. Your indentation was poor and I had missed how the code was grouped into the different FOR loops.

I think it would be worth writing some simple test code that just got a single constant LED to move in different directions without bothering with frames and such.

I think the Horizontal() function just moves one servo in stages from left right, then from right to left. And after that it moves the other servo up a little bit.

If you want the LED to follow a sine wave the up/down servo needs to move at the same time as the left/right servo - you have that idea in your Circle() function.

Create a series of X/Y values and put them in an array and see what happens. You may need to fiddle with them to get the exact movement you want. My guess is that when you have the values you can increase or decrease the range with simple maths - halving or doubling the values, for example.

Write the shortest possible program you can to cause the sinewave movement and post that code. It will be easier to help than trying to deal with a lot of other irrelevant stuff.

...R

This cross-poster has been banned. Thread locked.

Three day time-out. He'll be back. (Unless he keeps tweaking my last nerve.)