Slow Down servos compared to mimic controller

I have the" build some stuff" designed mimic controller to control my laser cut robot arm. I have a geared 360 degree servo for base. 270 degree servos for movement arms and a 180 degree servo for claw. Eventually i would like to save movements to program replayable positioning.
but my immediat problem is how can i slow down the movement of the servos compared to the mimic robot arm they move too fast and slams the arm around. Is there simple code change i can use. Thanks in advance

#include <Wire.h>
 
#include <Adafruit_PWMServoDriver.h>

#define MIN_PULSE_WIDTH       650
#define MAX_PULSE_WIDTH       2350
#define FREQUENCY             50
 
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

int potWrist = A3;
int potElbow = A2;                        //Assign Potentiometers to pins on Arduino Uno
int potShoulder = A1;                     // reverse direction swap pos neg on pot.
int potBase = A0;

int hand = 11;
int wrist = 12;
int elbow = 13;                           //Assign Motors to pins on Servo Driver Board
int shoulder = 14;
int base = 15;

void setup() 
{
  delay(5000);                            // <-- So I have time to get controller to starting position
  
  pwm.begin();
  pwm.setPWMFreq(FREQUENCY);
  pwm.setPWM(11, 0, 90);                  //Set Gripper to 90 degrees (Close Gripper)
  
  pinMode(13,INPUT_PULLUP);
  
  Serial.begin(9600);
}
 
 
void moveMotor(int controlIn, int motorOut)
{
  int pulse_wide, pulse_width, potVal;
  
  potVal = analogRead(controlIn);                                                   //Read value of Potentiometer
  
  pulse_wide = map(potVal, 800, 240, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
  pulse_width = int(float(pulse_wide) / 1000000 * FREQUENCY * 4096);                //Map Potentiometer position to Motor
  
  pwm.setPWM(motorOut, 0, pulse_width);
 
}
 
void loop() 
{
  moveMotor(potWrist, wrist);
  
  moveMotor(potElbow, elbow);
                                                            //Assign Motors to corresponding Potentiometers
  moveMotor(potShoulder, shoulder);
  
  moveMotor(potBase, base);



  int pushButton = digitalRead(13);
  if(pushButton == LOW)
  {
    pwm.setPWM(hand, 0, 300);                             //Keep Gripper closed when button is not pressed
    Serial.println("Grab");
  }
  else
  {
    pwm.setPWM(hand, 0, 90);                              //Open Gripper when button is pressed
    Serial.println("Release");
  }
}

No. Your code repetitively reads four potentiometers and sends the associated servo to the corresponding pulsewidth created by each potentiometer, "Too fast."

To make the movement slower, you would need to delay the reading of the potentiometers, then "time share" the proportional movement of each joint from present angle to target angle. If any potentiometer were adjusted during the move, the target movement would need to be changed from the old target angle to a new target angle.

It might be easier to use the Servo library and with angles, rather than pulse width math.

1 Like

instead of immediately setting the servo to the position from the pot, the position can be incrementally changed by using the pot to set a target and having a sub-function to incrementally change using millis() and a timer to determine the increment period

  • don't understand why the MIN/MAX need to be re-scaled?
  • If they do, why not scale them to 133/481?
  • And why are the pot values contrained which can result in negative values?

look the following over

#define MY_HW
#ifdef MY_HW
struct Adafruit_PWMServoDriver {
    Adafruit_PWMServoDriver ()   { }
    void begin      ()           { }
    void setPWMFreq (int freg)   { }
    void setPWM     (int pin, int x, int pos) { }
};

#else
# include <Wire.h>
# include <Adafruit_PWMServoDriver.h>
#endif

#define MIN_PULSE_WIDTH       650
#define MAX_PULSE_WIDTH       2350
#define FREQUENCY             50

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver ();

struct Svm {
    const byte  PinSvm;
    const byte  PinPot;
    const int   rate;
    const char *label;

    int         pos;
}
svm [] =  {
    { 12, A3, 100, "wrist" },
    { 13, A2, 100, "elbow" },
    { 14, A1, 100, "shoulder" },
    { 15, A0, 100, "base" },
};
const int Nsvm = sizeof(svm)/sizeof(Svm);

const byte PinHand = 11;
      byte handLst;

char s [90];

// -----------------------------------------------------------------------------
void loop ()
{
    delay (20);         // limit servo update 50/sec

    for (int n = 0; n < Nsvm; n++)  {
        int pot  = analogRead (svm [n].PinPot);
        int targ = map (pot, 1023, 0, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);

        if (abs(targ - svm [n].pos) < svm [n].rate)
            svm [n].pos  = targ;
        else if (targ > svm [n].pos)
            svm [n].pos += svm [n].rate;
        else if (targ < svm [n].pos)
            svm [n].pos -= svm [n].rate;
            
        pwm.setPWM (svm [n].PinSvm, 0, svm [n].pos);

        sprintf (s, " (%4d %4d)", pot, svm [n].pos);
        Serial.print (s);
    }
    Serial.println ();                      // line feed

    byte hand = digitalRead (13);
    if (handLst != hand)  {
        handLst  = hand;
        if (LOW == hand)  {
            pwm.setPWM (PinHand, 0, 300);       // close
            Serial.println ("Grab");
        }
        else {
            pwm.setPWM (PinHand, 0, 90);        // open
            Serial.println ("Release");
        }
    }
}


// -----------------------------------------------------------------------------
void setup ()
{
    pwm.begin ();
    pwm.setPWMFreq (FREQUENCY);

    pinMode (13, INPUT_PULLUP);

    Serial.begin (9600);
}
1 Like

max/min (inverse) of the pot is mapped to min/max of the servo. Not all servos have the same min/max pulsewidth.

1 Like

why is it re-scale again

Now I see "wide" is not used again... I do not know why. Copy/paste from two sketches?

[edit to edit] one 13 is button pin the other 13 is on a servo/pwm mux
[edit]I see Pin 13 is a SERVO and a PUSHBUTTON... def. copy/paste... some out of here...

buildsomestuff is AWESOM3! Find his "Printables" link in his YouTube videos.
For reference... buildsomestuff is a maker but does not provide code to non-paying public. The code on this page seems to be an attempt at re-creating the buildsomestuff "arm" project.

Maybe start with this...

#include <Servo.h>
Servo servo[5];

int potPin[] = {A0, A1, A2, A3, A4}; // base, shoulder, elbow, wrist, grip
int srvPin[] = { 2,  3,  4,  5,  6}; // base, shoulder, elbow, wrist, grip

void setup() {
  Serial.begin(9600);

  for (int i = 0; i < 5; i++) {
    servo[i].attach(srvPin[i]);
  }

  for (int i = 0; i < 5; i++)
    while (analogRead(potPin[i])); // all pots to 0 before proceding
  }

void loop() {
  for (int i = 0; i < 5; i++) {
    servo[i].write(map(analogRead(potPin[i]), 0, 1023, 0, 180));
  }
}
diagram.json for wokwi.com
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": -14.4, "left": -77.3, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot1", "top": -20.5, "left": 249.4, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot2", "top": -20.5, "left": 326.2, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot3", "top": -20.5, "left": 95.8, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot4", "top": -20.5, "left": 172.6, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo1", "top": -136.4, "left": 105.6, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo2", "top": -184.4, "left": 105.6, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo3", "top": -232.4, "left": 105.6, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo4", "top": -280.4, "left": 105.6, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo5", "top": -328.4, "left": 105.6, "attrs": {} },
    {
      "type": "wokwi-pushbutton",
      "id": "btn1",
      "top": -80.2,
      "left": -86.4,
      "attrs": { "color": "green" }
    },
    { "type": "wokwi-potentiometer", "id": "pot5", "top": -20.5, "left": 403, "attrs": {} }
  ],
  "connections": [
    [ "pot1:GND", "nano:GND.1", "black", [ "v38.4", "h-211.2" ] ],
    [ "pot2:GND", "nano:GND.1", "black", [ "v38.4", "h-288" ] ],
    [ "pot3:GND", "nano:GND.1", "black", [ "v38.4", "h-57.6" ] ],
    [ "pot4:GND", "nano:GND.1", "black", [ "v38.4", "h-134.4" ] ],
    [ "pot3:VCC", "nano:5V", "red", [ "v28.8", "h-96.8" ] ],
    [ "pot4:VCC", "nano:5V", "red", [ "v28.8", "h-173.6" ] ],
    [ "pot1:VCC", "nano:5V", "red", [ "v28.8", "h-87.2" ] ],
    [ "pot2:VCC", "nano:5V", "red", [ "v28.8", "h-183.2" ] ],
    [ "pot3:SIG", "nano:A0", "green", [ "v19.2", "h-163.6", "v-9.6" ] ],
    [ "pot4:SIG", "nano:A1", "green", [ "h-0.4", "v19.2", "h-230.4" ] ],
    [ "pot1:SIG", "nano:A2", "green", [ "h-0.4", "v19.2", "h-297.6" ] ],
    [ "pot2:SIG", "nano:A3", "green", [ "h-0.4", "v19.2", "h-364.8" ] ],
    [ "nano:GND.1", "servo1:GND", "black", [ "v0" ] ],
    [ "nano:GND.1", "servo2:GND", "black", [ "v0" ] ],
    [ "nano:GND.1", "servo3:GND", "black", [ "v0" ] ],
    [ "nano:GND.1", "servo4:GND", "black", [ "v0" ] ],
    [ "nano:5V", "servo1:V+", "red", [ "v0" ] ],
    [ "nano:5V", "servo2:V+", "red", [ "v0" ] ],
    [ "nano:5V", "servo3:V+", "red", [ "v0" ] ],
    [ "nano:5V", "servo4:V+", "red", [ "v0" ] ],
    [ "nano:2", "servo1:PWM", "green", [ "v0" ] ],
    [ "nano:3", "servo2:PWM", "green", [ "v0" ] ],
    [ "nano:4", "servo3:PWM", "green", [ "v0" ] ],
    [ "nano:5", "servo4:PWM", "green", [ "v0" ] ],
    [ "nano:6", "servo5:PWM", "green", [ "v0" ] ],
    [ "nano:GND.1", "servo5:GND", "black", [ "v0" ] ],
    [ "nano:5V", "servo5:V+", "red", [ "v0" ] ],
    [ "nano:7", "btn1:2.r", "green", [ "v0" ] ],
    [ "nano:GND.1", "btn1:1.r", "black", [ "v0" ] ],
    [ "pot5:SIG", "nano:A4", "green", [ "v19.2", "h-432.4" ] ],
    [ "pot5:GND", "nano:GND.1", "black", [ "v38.4", "h-364.8" ] ],
    [ "pot5:VCC", "nano:5V", "red", [ "v28.8", "h-404" ] ]
  ],
  "dependencies": {}
}

at robotics sessions, i've been helping students learyn to program. The students worked on code to drive servos on a multi-segment arm. we're working on the code so that the joy sticks control the x,y position of the gripper, not individual servos

@gcjr - Here is the link containing the original .INO file on Printables via Youtube

That re-map of a re-map seems to be for the PWM mux.

The pots read the joints of a movable arm whose joint positions are then replicated on the robot arm, and the button is to actuate the gripper.

i'm familiar with some other code using the pca9685 where the MIN and MAX constants were the values to set the pca9685, not the pulse widths. I see now that pulse_wide) / 1000000 * FREQUENCY * 4096 is actually converting the pulse width to the pca9685 constants

again, i think it's better to define the pca constants, rather than recompute (yes, they can be interpolated using map())

but perhaps as you've suggested, each joint needs it's own limits, for both the servo and pots

Thank you for your reply the original code was copied and I have been trying to get some help from ChatGPT as I am bad on coding and can spend days getting no where or going around in circles. Chat GPT has short term memory problems and can also be frustrating.
I do have an updated code which is workable but still bit to fast. (the start/Resting position and servo direction had been an issue as Chat GPT had it jumping to a middle position then slowly moving to the start servo positions. when I get my arm back together (it fell out of the back seat of my ute and broke the base. I will try your supplied code.

Thank you for your reply I just did a long reply to gcjr of where i am up to. i can also try the code you supplied. I will upload the latest code I have.

You students programing. I was thinking I could use the mimic to move to a position, button press and record the position. then play back the recorded sequence as a program.

After power is applied to the "arm" the servos will seek "90 degrees" unless the servos are first commanded to a position before getting the command servo.attach(). Storing the "old" position will be necessary AND you must ensure the servos are not moved while not powered.

DroneBotWorkShop made a tutorial of recording and playing-back servo positions controlled by a potentiometer...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.