-1

I have an Arduino Nano & am success fully controlling 4 servos from it but it then occurred to me that I am controlling these in a serial manner, ie I set 1 position then the next then the next....

I would like the servos to act in a parallel manner i.e. that their motions can happen at the same time but at different speeds or different position.

Is this possible using just 1 controller (maybe some sort of polling/interrupt setup?) or will I need to run separate controllers for each set of servos & synchronize them?

EDIT: Added the PC side code & the Arduino side code

PC Python code:

from Tkinter import *
import serial

usbport = 'COM5'
ser = serial.Serial(usbport, 9600, timeout=1)

def init():
    for servo in range(1, 5):
        print servo
        ser.write(chr(255))
        ser.write(chr(servo))
        ser.write(chr(90))

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()
        self.scale1 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(1), bd=5, bigincrement=2, length=360, width=30, label='Servo 1')
        self.scale1.set(90)
        self.scale1.pack(side=LEFT)
        self.scale2 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(2), bd=5, bigincrement=2, length=360, width=30, label='Servo 2')
        self.scale2.set(90)
        self.scale2.pack(side=LEFT)
        self.scale3 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(3), bd=5, bigincrement=2, length=360, width=30, label='Servo 3')
        self.scale3.set(90)
        self.scale3.pack(side=LEFT)
        self.scale4 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(4), bd=5, bigincrement=2, length=360, width=30, label='Servo 4')
        self.scale4.set(90)
        self.scale4.pack(side=LEFT)
        self.centre = Button(frame, text="Centre All", command=self.centre)
        self.centre.pack(side=TOP)


    def getAngle(self, slider):
        if slider==1:
            ang = self.scale1.get()
        if slider==2:
            ang = self.scale2.get()            
        if slider==3:
            ang = self.scale3.get()
        if slider==4:
            ang = self.scale4.get()
        ser.write(chr(255))
        ser.write(chr(slider))
        ser.write(chr(ang))

    def centre(self):
        for servo in range(1, 5):
            ser.write(chr(255))
            ser.write(chr(servo))
            ser.write(chr(90))
        self.scale1.set(90)
        self.scale2.set(90)
        self.scale3.set(90)
        self.scale4.set(90)

init()
root = Tk()
app = App(root)
root.mainloop()

Arduino code

*
 * ------------------------------
 *   MultipleSerialServoControl
 * ------------------------------
 *
 * Uses the Arduino Serial library
 *  (http://arduino.cc/en/Reference/Serial)
 * and the Arduino Servo library
 *  (http://arduino.cc/en/Reference/Servo)
 * to control multiple servos from a PC using a USB cable.
 *
 * Dependencies:
 *   Arduino 0017 or higher
 *     (http://www.arduino.cc/en/Main/Software)
 *   Python servo.py module
 *     (http://principialabs.com/arduino-python-4-axis-servo-control/)
 *
 * Created:  23 December 2009
 * Author:   Brian D. Wendt
 *   (http://principialabs.com/)
 * Version:  1.1
 * License:  GPLv3
 *   (http://www.fsf.org/licensing/)
 *
 */

// Import the Arduino Servo library
#include <Servo.h> 

// Create a Servo object for each servo
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;

// Common servo setup values
int minPulse = 600;   // minimum servo position, us (microseconds)
int maxPulse = 2400;  // maximum servo position, us

// User input for servo and position
int userInput[3];    // raw input from serial buffer, 3 bytes
int startbyte;       // start byte, begin reading input
int servo;           // which servo to pulse?
int pos = 90;        // servo angle 0-180
int i;               // iterator

// LED on Pin 13 for digital on/off demo
int ledPin = 13;
int pinState = LOW;

void setup() 
{ 
  // Attach each Servo object to a digital pin
  servo1.attach(3, minPulse, maxPulse);
  servo2.attach(4, minPulse, maxPulse);
  servo3.attach(5, minPulse, maxPulse);
  servo4.attach(9, minPulse, maxPulse);

  // LED on Pin 13 for digital on/off demo
  pinMode(ledPin, OUTPUT);

  // Open the serial connection, 9600 baud
  Serial.begin(9600);
} 

void loop() 
{ 
  // Wait for serial input (min 3 bytes in buffer)
  if (Serial.available() > 2) {
    // Read the first byte
    startbyte = Serial.read();
    // If it's really the startbyte (255) ...
    if (startbyte == 255) {
      // ... then get the next two bytes
      for (i=0;i<2;i++) {
        userInput[i] = Serial.read();
      }
      // First byte = servo to move?
      servo = userInput[0];
      // Second byte = which position?
      pos = userInput[1];
      // Packet error checking and recovery
      if (pos == 255) { servo = 255; }

      // Assign new position to appropriate servo
      switch (servo) {
        case 1:
          servo1.write(pos);    // move servo1 to 'pos'
          break;
        case 2:
          servo2.write(pos);
          break;
        case 3:
          servo3.write(pos);
          break;
        case 4:
          servo4.write(pos);
          break;

        // LED on Pin 13 for digital on/off demo
        case 99:
          if (pos == 180) {
            if (pinState == LOW) { pinState = HIGH; }
            else { pinState = LOW; }
          }
          if (pos == 0) {
            pinState = LOW;
          }
          digitalWrite(ledPin, pinState);
          break;
      }
    }
  }
}
  • How are you currently controlling them? With buttons or through USB with a PC? Or just hardcoded movements? What type of servo's do you use? The servo's I usually use have just 1 speed and don't give any feedback. With moving slow you would mean delaying between small steps of rotation? – Paul Oct 13 '15 at 13:15
  • I assume you are using a library for this. All the libraries already do this for you. As you need to continuously send position data (once every 20ms) to the servo. – Gerben Oct 13 '15 at 14:05
  • Thanks for the replies. I have made an edit to the OP with the relevant code added. I'm controlling the servos from a PC using Tinker's sliders. I think I'm just having a conceptual block here. Would I be right in saying that the above code controls the servos in a serial fashion (eg the centre() function in the Python code the servo's are looped through)? – DrBwts Oct 14 '15 at 10:59
  • 1
    The python code is polling through the servos sequentially/serialy, but the arduino code looks parallel. The arduino will parse the string and pass the command to the servo code, passing control back to the loop() command before the servo reaches its assigned position. Maybe that is your question: "is servo.write(position) a blocking call?" If so, the answer is no, it is non-blocking, the motors will drive at the motors speed to the final positions independently. If you want coordinated movement through this scheme, you have to break the movements up and coordinate it on the python side. – Dave X Dec 1 '15 at 5:25
  • In my opinion your code already does what you want to do. It's just that you can't move the sliders "in parallel" cause you, as human, are too slow. If you want to be able to make sinchronized movements, you can put a button on the UI which sends the sliders positions to the arduino instead of sending them when the sliders position change. This way you can set the coordinated moves. As for the speed, you'll have to slow down the movements making small increments instead of setting the final value to the servo. If you need it I can write some pieces of code for this – frarugi87 Dec 1 '15 at 9:47
1

The python code is polling through the servos sequentially/serialy, but the arduino code looks parallel. The arduino will parse the string and pass the command to the servo code, passing control back to the loop() command before the servo reaches its assigned position. Maybe that is your question: "is servo.write(position) a blocking call?" If so, the answer is no, it is non-blocking, the motors will drive at the motors speed to the final positions independently. If you want coordinated movement through this scheme, you have to break the movements up and coordinate it on the python side without feedback.

For instance, if you want to go from position (90,90,90,90) to position (81,0,90,90) you will need to know it takes 900ms (for example) to complete the -90 move, and break it up into 10 segments of 100ms to coordinate the (-1,0,-10,0) steps. Doing the coordination without feedback will be awkward, and might be better handled from your python/UI side.

| improve this answer | |
0

You can use an interpolation library and interpolate in-between as many servos as you like. The YouTube channel James Bruton does it on a lot of his robots so you can probably learn from his code. Also check out this GitHub repository:

https://github.com/luisllamasbinaburo/Arduino-Interpolation

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.