Trossen Force Sensor  Top Banner

Tutorial: Get position feedback from a standard hobby servo

  1. jes1510's Avatar
    jes1510 jes1510 is offline Johnny-5
    Category
    How To, DIY
    Views
    44,887
    Replies
    9
     

    Get position feedback from a standard hobby servo

    Difficulty
    Moderate
    Estimated Time
    30 minutes
    Skills Required
    Moderate soldering skills
    Parts Required
    Hobby servo, an HS-422 was used for this tutorial
    Wire, flexible 22AWG or smaller, at least two different colors to construct feedback cable
    Arduino
    Resistors
    Servo extension cable
    Connectors for Arduino
    Tools Required
    Good qualiy temperature controlled soldering iron
    Small phillips head screwdriver
    Desoldering braid
    Voltmeter that will also measure resistance
    Hobby knife or nibbling tool
    Hot glue gun
    Introduction
    The purpose of this tutorial is to show how to get positional feedback from a standard hobby servo. With positional feedback it is possible to tell the exact position of the servo arm at any given time. This will allow you to have tighter control of servo movements as well as enabling us to manually move the servo arm to an arbitrary position and capture that position for playback. The position data can also tell us when the servo is overloaded and stalled.

    A word of warning

    This process will require the disassembly and modification of a hobby servo. This will almost certainly void any warranties and may very well destroy the servo. While the process was proven to work on a particular servo (HS-422), your results may vary. We are not responsible for any damage or harm inflicted by anything presented in this tutorial.

    How a servo works
    A hobby servo works by sending pulses to the servo from a controller. There is a small potentiometer inside the servo that is attached to the servo arm. The electronics inside the servo compares the position of the potentiometer with the desired position from the pulses and moves the arm as required until they match. The servo does not give any feeback to the controller, therefore the controller has to assume that the servo is always at the desired position.

    This tutorial demonstrates how to modify the servo so that the potentiometer inside the servo provides us with information about the location of the servo arm. We can "close the loop" with this data and always know the location of the arm.

    Modifying the servo

    First, remove the four screws on the back of the servo casing. The back of the casing should just slide off, exposing the circuit board. See figure 1.

    Figure 1

    The board is only held in place by compression so it should lift free of the casing. Gently pry it up, taking care not to damage the board or the wires holding the board. The potentiometer should now be exposed. The wire in the middle of the potentiometer will be the wiper. This is the wire that we will want to connect to in the future. See figure 2.

    Figure 2

    Connect the red and black wires from the servo cable to a power supply or battery that will supply the voltage for the servo. Move the servo arm all the way to one side and measure the voltage from the wiper of the potentiometer (yellow wire in figure 2) to ground (black wire of servo cable). Write down the voltage, move the servo arm all the way around to the opposite position, and record that voltage as well. These values will be used in a moment.

    Follow the wire that is attached to the wiper on the potentiometer to the where it connects to the circuit board. This is shown as the yellow wire in the upper right corner of the circuit board in figure 3.

    Figure 3

    Gently heat the solder on the wire an remove it from the circuit board. Strip one piece of wire that will be used to make the feedback cable and solder it to the yellow wire. Resolder both wires back to where the wire was just removed. Take extreme care not to damage the board or apply too much heat. Place the board back into the casing an fold the new wire so that it exits next to the motor. See figure 4.

    Figure 4

    Locate a good ground by checking resistance from various points on the top of the board to the back wire on the servo cable. The anode of the surface mount diode shown in figure 5 makes a nice place to attach the ground wire. Strip the second wire of the feedback cable and solder it to the ground. See figure 5.

    Figure 5

    Widen the slot on the servo casing cover using the nibbling tool or knife. It just needs to be wide enough to allow the two extra wires to exit the case. Be careful to widen the slot on the correct side. Line it up by eye before cutting the plastic to prevent cutting the slot on the wrong side of the servo cable. See figure 6.

    Figure 6

    Move the feedback cable so that it is parallel to the servo cable and secure it with a very small amount of hot glue. This will protect the cables by giving them a small amount of strain relief. Place the cover back on the servo and tighten down all of the screws. See figure 7.

    Figure 7

    Connect the servo as shown below (A servo extension cable can be used if you don't want to modify the servo cable):

    Servo cable red: Positive voltage to power servo
    Servo cable black: Ground from servo power supply and Arduino ground
    Servo cable yellow: Arduino Digital pin 9
    Servo feedback cable ground: Arduino ground
    Servo feedback cable poteniometer: Arduino analog pin 5

    The voltages measured on the potentiometer wiper of the HS-422 were well below 5v. This means the analog pin used to measure the position will not get full resolution and the feedback will not be as accurate as it can be. An external voltage reference can correct this. A simple voltage divider from 5v to the Aref pin on the Arduino will work perfectly. Click here for tutorial that will explain how to calculate the values for a voltage divider. Make sure that the voltage applied to the AREF pin on the Arduino is greater than the highest voltage measured on the potentiometer of the servo to prevent possible damage to the Arduino. Figure 8 shows an example setup.

    Figure 8

    Connect the Arduino to the computer and copy the following code to it.

    [code]

    Code:
    /*  Servo Recorder
    This software has no warranty either real or implied.  Use at
    your own risk.
    
    Based in part on the servo and smoothing tutorials on www.arduino.cc
    
    Records and plays back servo positions using a servo
    modified with feedback.
    
    The feedback connects to analog pin 5
    
    The following commands are supported:
    a records the current servo position
    b releases the servo to record the position
    c plays back the recorded position
    d calibrates the end points of the servo
    
    Every servo should be calibrated when first started to get more accurate
    end points.
    
    The minimum and maximum pulse width will vary with different servo models.  If the calibration routine does not work correctly then look up the minimum and maximum pulse widths for your servo and use them in the servo.attach lines like so:
    servo.attach(9,minimumPulseWidth,maximumPulseWidth);
    */
    
    #include <Servo.h>
    
    Servo servo;
    
    int angle = 90;
    int position = 90;
    int feedbackPin = 5;
    int val = 0;
    int calVal[] = {191, 1011};  // initial cal values
    int calStartPos = 0;
    int final = 90;
    
    #define NUMREADINGS 10      // Number of readings to take for smoothing
    
    
    int readings[NUMREADINGS];                // the readings from the analog input
    int index = 0;                            // the index of the current reading
    int total = 0;                            // the running total
    int average = 0;                          // the average
    
    void setup()
    {
      Serial.begin(9600);
      analogReference(EXTERNAL);   // Use the external analog reference  
    }
    
    void loop()
    {
     static int v = 0;
      position = analogRead(feedbackPin);  
      position = smooth(position);
    
      if ( Serial.available()) {
        char ch = Serial.read();
    
        switch(ch) {
          case '0'...'9':
            v = v * 10 + ch - '0';
            break;
            
          case 'a':   //  record the position of the pot  
            servo.detach();    
            Serial.print("Recording position: ");
            Serial.println(position);      
            final = position;  
            v = 0;
            break;
            
          case 'b':  //  stop playing
            Serial.println("Stop");
            servo.detach();    
            v = 0;
            break;
            
          case 'c':   // play            
            angle = map(final, calVal[0], calVal[1], 0, 180);
            Serial.print("playing: ");
            Serial.println(angle);        
            servo.attach(9);  
            servo.write(angle);  
            v = 0;
            break;
            
            
          case 'd' :  // calibrate      
            Serial.println("calibrating");
            servo.attach(9);
            servo.write(1);
            delay(1000);  // wait 1 second for servo to reach the position                
            calVal[0] = analogRead(feedbackPin);  
            servo.write(180);
            delay(1000);
            calVal[1] = analogRead(feedbackPin);
            Serial.print("Cal values: ");
            Serial.print(calVal[0]);
            Serial.print(",");
            Serial.println(calVal[1]);
            v = 0 ;
            break;      
        }
      }
    }
    
    int smooth(int data) {
        total -= readings[index];               // subtract the last reading
        readings[index] = analogRead(feedbackPin); // read from the sensor
        total += readings[index];               // add the reading to the total
        index = (index + 1);                    // advance to the next index
    
        if (index >= NUMREADINGS)               // if we're at the end of the array...
        index = 0;                            // ...wrap around to the beginning
    
        val = total / NUMREADINGS;          // calculate the average
        return val;
    }


    The completed system can now be tested by opening a 9600bps serial connection to the Arduino and sending commands to the Arduino. The following commands are supported:
    a records the current servo position
    b releases the servo to record the position
    c plays back the recorded position
    d calibrates the end points of the servo

    Conclusion
    There are many possibilities for this system. Multiple positions could be recorded to the EEPROM on the Arduino and played back to create complex movements. A haptic joystick or input device could be constructed that provides force feedback and position recording.


    If you would like a GUI interface then the following Python script wraps the serial communications in a GUI wrapper. The Pyserial module is required.

    Code:
    #! /usr/bin/python
    #  Change the above line to match the appropriate OS
    
    
    
    """  Servo Recorder Interface Example
    
    Modify and distribute freely
    
    This is an example program to control a modified servo with feedback.  It is meant to be run with the "ServoFeedback" example firmware for the arduino microcontroller.  It generates a 4 button interface that will record a servo position to be played back later.  
    
    The buttons have the following functions:
    
    Record:  Tells the arduino to save the current position of the servo.
    Play: Plays the recorded servo position
    Stop: Stops playback.  The servo can be repositioned to another position.
    
    Usage
    1. Move the servo arm to the required position
    2. Press the record button.  The servo arm can still be moved freely.
    3. Press the play button to move the arm to the recorded position.
    4. Press the stop button to allow the arm to be moved to another position.  Pressing play again at any time will return the arm to the last recorded position.
    
    This software requires the pySerial library.  It can be downloaded here:
    http://pyserial.wiki.sourceforge.net/pySerial
    """
    
    from Tkinter import *
    import time
    import serial
    
    master = Tk()                    #  Create a TK instance
    master.title("Servo Recorder")   #  Title
    
    
    """Connect to the Serial Port.  This line should be changed for a different port.  Under windows the com number can be used.  The com port in windows will be one less than the one reported by the Device Manager since pySerial enumerates it's ports starting at 0.
    
    For example, to connect to com 8 use this:
     ser = serial.Serial("7", 9600)  
     """
    
    ser = serial.Serial("/dev/ttyUSB0", 9600)   
    
    def record():           #  Send "a" out the serial port when record is pressed
       ser.write("a")
    
    def stop():          #  Send "b" out the serial port when the stop button is pressed
        ser.write("b")
    
    def play():          #  Send "c" out the serial port when the play button is pressed
        ser.write("c")
        
    def calibrate():        #  Send "d" out the serial port when the play button is pressed
        ser.write("d")
        
    def quit():        #  Send "d" out the serial port when the play button is pressed
        ser.close()
        master.destroy()
        
        
    recordButton = Button(master, text="Record", command=record)   # Create the record button on the grid and hook it to the record function
    recordButton.grid(row=1, column=1)
    
    stopButton = Button(master, text="Stop", command=stop)         #  Create the stop button on the grid and hook it to the stop function
    stopButton.grid(row=1, column=2)
    
    playButton = Button(master, text="Play", command=play)         #  Create the play button on the grid and hook it to the play function
    playButton.grid(row=1, column=4)
    
    calButton = Button(master, text="Calibrate", command=calibrate)         #  Create the calibrate button on the grid and hook it to the play function
    calButton.grid(row=1, column=5)
    
    quitButton = Button(master, text="Quit", command=quit)         #  Create the calibrate button on the grid and hook it to the play function
    quitButton.grid(row=2, column=3)
    
    mainloop()
    Attached Files
    • dscn0123


Replies to Tutorial: Get position feedback from a standard hobby servo
  1. Re: Get position feedback from a standard hobby servo

    Is there a reason why there are 2 ground cables? Wouldn't the already present servo ground work fine?
        

  2. Re: Get position feedback from a standard hobby servo

    Yeah, it will work with only 1 ground. The second one is to help prevent ground loops and it makes the servo a bit more flexible on how it can be connected.
    "If A is a success in life, then A equals x plus y plus z. Work is x; y is play; and z is keeping your mouth shut."
    -
    Einstein

    Don't be a HelpVampire
        

  3. Re: Get position feedback from a standard hobby servo

    Thanks to GDubb for testing with a digital servo (I don't own any) and finding a minor bug and Connor for posting a fix before I even had time to look at it.
    "If A is a success in life, then A equals x plus y plus z. Work is x; y is play; and z is keeping your mouth shut."
    -
    Einstein

    Don't be a HelpVampire
        

  4. Trossen Accelerometer Gyro Thread Banner
  5. Re: Get position feedback from a standard hobby servo

    Great work Jes1510!! will try the code someday.
    Is it possible to do a version on DC Brushed servo?. I have a chip that makes a dc servo to follow the rotation of a rotary encoder, but I want to record it too...by any chances that you might be interested in this?
        

  6. FunnyRobot Guest

    Re: Get position feedback from a standard hobby servo

    Verry helpful.

  7. tom Guest

    Re: Get position feedback from a standard hobby servo

    Good stuff, thanks.

    One quesiton, though - you mention creating a potential divider for the ARef pin on the arduino, but couldn't you just use the +V pin of the potentiometer as your reference? Wouldn't then the ADC result be as accurate as possible?

  8. filipposanfilippo Guest

    Re: Get position feedback from a standard hobby servo

    Hi,

    First of all thanks for your how to!
    However I have some problems. When I try to move the servo manually, it resists a lot to the movement and it is almost impossible to move by hand.

    The servo I am using is exactly the same model as yours but I noticed that the circuit board is very different from yours.

    Thanks a lot for your help in advance, Filippo

  9. GavilanSteinman Guest

    Re: Get position feedback from a standard hobby servo

    jes1510, you're brilliant! My servo was different, but I was able to figure out how to do this hack on my servo(sg-5010) with all this help.

    I took what you have here (read one position and play it back) and I extended it (read 30 frames a second for 5 seconds and play back animation). Here's a video of it in action: http://www.youtube.com/watch?v=ygX7sQM2gq0

    I also made a video of the hardware hack: http://www.youtube.com/watch?v=LlhaG12EpQs

    I hope these videos help someone.

  10. ss32 Guest

    Re: Get position feedback from a standard hobby servo

    I hate to be bumping an old thread but I figure this is the best place for it as I found the forum looking for a way to control my servo. I've come across a bunch of blog posts/videos online showing how to tap into the pot but my motor doesn't have 3 leads going to it, only two. There looks to be a small controller on the board for the servo itself. I thought the wiper might be the middle post of the trim pot but I soldered a wire on and ran the calibration code and got bad numbers (thanks for the code, btw, it will be really handy once I get this sorted). I've tried a couple other spots on the board with no luck, so I'm hoping someone might be able to point me in the right direction.

    I'm using a large servo from Sparkfun
    https://www.sparkfun.com/products/9347

    Click for full res




Reply To Tutorial