OpenServo.com Forum Index OpenServo.com
Discussion of the OpenServo project
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Won't Get To The Exact Position
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    OpenServo.com Forum Index -> Software
View previous topic :: View next topic  
Author Message
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Wed Jun 30, 2010 6:39 am    Post subject: Won't Get To The Exact Position Reply with quote

Hey all,

I'm running some simple python scripts for calibrating 8 of my motors and we are running into problems with the motor reaching the exact position. My code asks for a position, move the motor until it reaches the position and then getposition() and print, and then repeats the process, however, after inputing one position, it wont ask for another. I am not sure whether this is gain settings or something is wrong with my encoder. It appears as if it reads positions fine, as they all comeback fairly legitimately.

Thoughts?

--Will
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
jharvey
co-admin


Joined: 15 Mar 2009
Posts: 349
Location: Maine USA

PostPosted: Wed Jun 30, 2010 8:57 pm    Post subject: Reply with quote

I don't think I fully follow, but here's by stab at it. I think the issue is that when commaned to a position, it waits until the position matches exactly the position it was command to. When the servo is commanded to a postition, it tries to go to that position as fast as possible. If you command it to another position in mid rotation, it will try to move to the new position as fast as possible. I don't recall a feed back mechanism that idicates the servo has reached the position. That would be done via position readings called for by the python script in this case.

Perhaps the python script needs to add a tolerance, such that if the reported position is +/- .01 degrees from the commanded position, it's close enough, and the script then considers it to be located.
Back to top
View user's profile Send private message Visit poster's website
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Thu Jul 01, 2010 5:28 am    Post subject: Reply with quote

Yes, I building in a buffer zone where if it was within x-distance of the position assigned it would stop, but sometimes, the motor would simlply stop after completing a majority of the path.

Based on my understanding of PID controllers, I now realize that the problem is most likely in bad PID settings. I will change them and write back.
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Thu Jul 01, 2010 9:59 am    Post subject: Reply with quote

I am back with more clarifcation. I am now nearly certain that it is the PID settings. I made a device that allowed me to trace the paths of the motor rotation on graph paper and I tried out many combinations.

The only variable that would change the movement was P. Both I and D had no bearing whatsoever on the movement. I am using the set of python scripts and the osv2 file that you gave me. I called all functions from your files, however, I am running my own script. I don't think it is my script that has the error though. And it appears that your python files write to the right ports based on the chart at www.openservo.com/TWIProtocol. So my only conclusion is that something is wrong with the PID in the firmware. I wouldn't have the faintest idea where to look though so I am just throwing ideas out there though.

Thoughts?

- Will
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
jharvey
co-admin


Joined: 15 Mar 2009
Posts: 349
Location: Maine USA

PostPosted: Thu Jul 01, 2010 8:58 pm    Post subject: Reply with quote

The P is simply gain, and the final position has little bearing based on the I and D term. The I and D are mostly for transition adjustements. For example, if you are making a large change in position, and you have a very heavy weight on the servo, all that stored up energy will cause the arm to over shoot. The I and D terms help change the exerted energy such that you can dample the over shoot, under shoot, ect. The I term is often handy for pulling to an exact position when under load, however, the I term is not implimented. I understand this is compensated for by a large P. These motors typically have a low inertia, and make high enough torque, that the I term is typically not all that important for exact positioning. Perhaps you have some weight attached to it, that is causing a need for the I term?

When you do this test, is that with an unloaded arm? It sounds like the arm doesn't match the same spot on the paper, does OE report the same position each time? How much does it appear to be off?
Back to top
View user's profile Send private message Visit poster's website
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Fri Jul 02, 2010 5:29 am    Post subject: Reply with quote

The openservo reports the same position each time, however, with just P, the motor will oscillate twice before reaching its final postiion. If I make the P gain higher, it over shoots by less but oscillates more. The D value should help minimize oscillation and overshoot, but as long as the p-values are the same (ignoring i), the graphs will be the same for whatever d-value I put in whether it is large or small or nothing. Therefore, I am concluding that the D value is useless. I attempt to do the same thing with the I value and it shows that it does nothing at well, so I only have proportional gain control.

We are switching the current servo's out of our arm for these servos because they have increased precision, but if we cannot control I and D and it does oscillate, that will be a problem.
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
jharvey
co-admin


Joined: 15 Mar 2009
Posts: 349
Location: Maine USA

PostPosted: Fri Jul 02, 2010 10:19 am    Post subject: Reply with quote

The D term should be implemented and good. It minimizes over shoot, which helps settle down ringing, but if set to high, it can also make it go unstable. I seem to recall the I term was once drafted, but not completed. Implementing the I shouldn't be all that hard to do. The dev copy of the firmware source might offer some more insight into this.

From this link down around Ziegler–Nichols method, it give some helpful tips for setting your I and D terms.

http://en.wikipedia.org/wiki/PID_controller

I think that approach is still valid for a PD controller, and it should help get a feel for the range these values should be set at.

And a pinch more about PD tunning at this link.

http://www.embedded.com/columns/technicalinsights/207500063?_requestid=83271
Back to top
View user's profile Send private message Visit poster's website
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Fri Jul 02, 2010 10:35 am    Post subject: Reply with quote

Yes, thank you that is very helpful with setting the values. And I read over the firmware and the PID files and I see that the d-value is definitely in there and should function, but for some reason, it literally doesn't change for any D value from 0x0 to 0x100 to 0xf000. It is quite weird.

On a development note, I considered putting support for "I" into the pid files (just by calculating total "distance" travelled. I have not compiled it yet and have no clue whether it will work or not but that is the right theory behind it? (Since P is based off of the current position error, D is based off of velocity, so I would be based off of the total position travelled.
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Fri Jul 02, 2010 12:11 pm    Post subject: Reply with quote

And after playing around with the velocity for a while, I noticed that if my velocity was too low, the motor would continuously spin and completely ignore any positions I sent to it. It would remain this way untill I increased my velocity. Now, I just have it set at 0xffff. I don't know if thats good or bad.
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
jharvey
co-admin


Joined: 15 Mar 2009
Posts: 349
Location: Maine USA

PostPosted: Sat Jul 03, 2010 12:25 am    Post subject: Reply with quote

I honestly haven't look to closely at the velocity stuff, and I'm not sure why it might cause a problem. Perhaps it has some kind of problem with the larger OE position, or perhaps it does something else. Not sure.

I've seen sample code for the I feature before. I'm not sure if what you explain is really an I feature, or a near approximation. If you can find a way to integrate, that's probably the best approach.
Back to top
View user's profile Send private message Visit poster's website
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Mon Jul 05, 2010 12:39 pm    Post subject: Reply with quote

For the sake of messing around with things, I tried writing I support into the AVR_OpenServo_V3 version of the software. I modified the pid_position_to_pwn function to the code below...the lines I added have comments after them. However, when I my testing code on them, the motor continuously spins around, which leads me to believe that something is wrong with the pwm. Thoughts?

Code:

int16_t pid_position_to_pwm(int16_t current_position)
// This is a modified pid algorithm by which the seek position and seek
// velocity are assumed to be a moving target.  The algorithm attempts to
// output a pwm value that will achieve a predicted position and velocity.
{
    // We declare these static to keep them off the stack.
    static int16_t deadband;
    static int16_t p_component;
    static int16_t i_component;  //added
    static int16_t d_component;
    static int16_t seek_position;
    static int16_t seek_velocity;
    static int16_t minimum_position;
    static int16_t maximum_position;
    static int16_t current_velocity;
    static int16_t filtered_position;
    static int32_t pwm_output;
    static uint16_t d_gain;
    static uint16_t i_gain;  // Added
    static uint16_t p_gain;
 

    // Filter the current position thru a digital low-pass filter.
    filtered_position = filter_update(current_position);

    // Use the filtered position to determine velocity.
#if FULL_ROTATION_ENABLED
    current_velocity = normalize_position_difference(filtered_position - previous_position);
#else
    current_velocity = filtered_position - previous_position;
#endif
    previous_position = filtered_position;

    // Get the seek position and velocity.
    seek_position = (int16_t) registers_read_word(REG_SEEK_POSITION_HI, REG_SEEK_POSITION_LO);
    seek_velocity = (int16_t) registers_read_word(REG_SEEK_VELOCITY_HI, REG_SEEK_VELOCITY_LO);

    // Get the minimum and maximum position.
    minimum_position = (int16_t) registers_read_word(REG_MIN_SEEK_HI, REG_MIN_SEEK_LO);
    maximum_position = (int16_t) registers_read_word(REG_MAX_SEEK_HI, REG_MAX_SEEK_LO);

    // Are we reversing the seek sense?
    if (registers_read_byte(REG_REVERSE_SEEK) != 0)
    {
        // Yes. Update the position and velocity using reverse sense.
        registers_write_word(REG_POSITION_HI, REG_POSITION_LO, (uint16_t) (MAX_POSITION - current_position));
        registers_write_word(REG_VELOCITY_HI, REG_VELOCITY_LO, (uint16_t) -current_velocity);

        // Reverse sense the seek and other position values.
        seek_position = MAX_POSITION - seek_position;
        minimum_position = MAX_POSITION - minimum_position;
        maximum_position = MAX_POSITION - maximum_position;
    }
    else
    {
        // No. Update the position and velocity registers without change.
        registers_write_word(REG_POSITION_HI, REG_POSITION_LO, (uint16_t) current_position);
        registers_write_word(REG_VELOCITY_HI, REG_VELOCITY_LO, (uint16_t) current_velocity);
    }

    // Get the deadband.
    deadband = (int16_t) registers_read_byte(REG_PID_DEADBAND);

    // Use the filtered position when the seek position is not changing.
    if (seek_position == previous_seek) current_position = filtered_position;
    previous_seek = seek_position;

    // Keep the seek position bound within the minimum and maximum position.
    if (seek_position < minimum_position) seek_position = minimum_position;
    if (seek_position > maximum_position) seek_position = maximum_position;

    // The proportional component to the PID is the position error.
#if FULL_ROTATION_ENABLED
    p_component = normalize_position_difference(seek_position - current_position);
#else
    p_component = seek_position - current_position;
#endif

   // The integral component to the PID is the amount of rotation traveled. NEWLY ADDED
   if ((current_position - previous_position) < 0)
   {
      i_component += (current_position - previous_position);
   }
   else
   {
      i_component -= (current_position - previous_position);
   }   
   // The derivative component to the PID is the velocity.
    d_component = seek_velocity - current_velocity;

    // Get the proportional, derivative and integral gains.
    p_gain = registers_read_word(REG_PID_PGAIN_HI, REG_PID_PGAIN_LO);
    i_gain = registers_read_word(REG_PID_IGAIN_HI, REG_PID_IGAIN_LO);
    d_gain = registers_read_word(REG_PID_DGAIN_HI, REG_PID_DGAIN_LO);

    // Start with zero PWM output.
    pwm_output = 0;

    // Apply proportional component to the PWM output if outside the deadband.
    if ((p_component > deadband) || (p_component < -deadband))
    {
        // Apply the proportional component of the PWM output.
        pwm_output += (int32_t) p_component * (int32_t) p_gain;
    }

    // Apply the derivative component of the PWM output.
    pwm_output += (int32_t) d_component * (int32_t) d_gain;
   
    // Apply the integral component of the PWN output. NEWLY ADDED
    pwm_output += (int32_t) i_component * (int32_t) i_gain;

    // Shift by 8 to account for the multiply by the 8:8 fixed point gain values.
    pwm_output >>= 8;

    // Check for output saturation.
    if (pwm_output > MAX_OUTPUT)
    {
        // Can't go higher than the maximum output value.
        pwm_output = MAX_OUTPUT;
    }
    else if (pwm_output < MIN_OUTPUT)
    {
        // Can't go lower than the minimum output value.
        pwm_output = MIN_OUTPUT;
    }

    // Return the PID output.
    return (int16_t) pwm_output;
}
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
jharvey
co-admin


Joined: 15 Mar 2009
Posts: 349
Location: Maine USA

PostPosted: Mon Jul 05, 2010 1:25 pm    Post subject: Reply with quote

Did you have full "FULL_ROTATION_ENABLED" defined?

If you put your I gain to 0 via I2C config, does it work correctly? Well PD controller correctly that is. From what I'm reading, if the I gain is set to 0, that should cancel the math that backs it.
Back to top
View user's profile Send private message Visit poster's website
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Tue Jul 06, 2010 5:52 am    Post subject: Reply with quote

Even when I is set to 0, it continously rotates without stopping. I do not know if FULL_ROTATION is enabled because I can't seem to find the where I would be toggled on/off.

If it is not the full rotation that is causing it, would it be the PŴM? Now the PWM is P+I+D. As such, would I have to shift it by a number other than 8?

Code:

    // Shift by 8 to account for the multiply by the 8:8 fixed point gain values.
    pwm_output >>= 8;
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
wpotter



Joined: 18 Jun 2010
Posts: 50
Location: Valencia, Spain

PostPosted: Tue Jul 06, 2010 8:14 am    Post subject: Reply with quote

While reviewing the code, I noticed that the P is calculated from the position error and the D is calculated from the velocity error. Doesn't that seem a bit arbitrary. Shouldn't the P and D both tie off of the position? That way all three variables only tie off of one method of feedback.

I'm not sure, but doesn't it not make sense to calculate a pwm_out that relies off two separate feedback functions? (getvelocity, getposition)
Back to top
View user's profile Send private message Yahoo Messenger MSN Messenger
jharvey
co-admin


Joined: 15 Mar 2009
Posts: 349
Location: Maine USA

PostPosted: Tue Jul 06, 2010 9:55 am    Post subject: Reply with quote

The feed back from OE gives a different number than the number from the A/D. I would recommend that you first get the firmware to compile, and operate as it currently does, then start making modifications. Heres a copy of the patch file that shows the changes you need to make to the checked out copy of the code. Once this patch is applied, you should be able to generate the firmware you currently have.
Code:
 config.h    |    6 +++---
 registers.h |    4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/config.h b/config.h
index ae4ca14..215029f 100644
--- a/config.h
+++ b/config.h
@@ -39,7 +39,7 @@
 // functions.  These functions allow the OpenServo to be controlled
 // by keypoints along a cubic Hermite curve with each keypoint
 // indicating servo position and velocity at a particular time.
-#define CURVE_MOTION_ENABLED        1
+#define CURVE_MOTION_ENABLED        0
 
 // Enable (1) or disable (0) some test motion code within the
 // main.c module.  This test code can be enabled to test basic
@@ -61,10 +61,10 @@
 #define SWAP_PWM_DIRECTION_ENABLED  0
 
 // Enable (1) or disable (0) support for I2C connected encoder.
-#define ENCODER_ENABLED             0
+#define ENCODER_ENABLED             1
 
 // Enable (1) or disable (0) support for full rotation
-#define FULL_ROTATION_ENABLED       0
+#define FULL_ROTATION_ENABLED       1
 
 // The known OpenServo hardware types are listed below.
 #define HARDWARE_TYPE_UNKNOWN           0
diff --git a/registers.h b/registers.h
index 070698c..10f6d35 100644
--- a/registers.h
+++ b/registers.h
@@ -113,8 +113,8 @@
 #define MIN_WRITE_PROTECT_REGISTER  0x20
 
 #if ENCODER_ENABLED
-#define MAX_WRITE_PROTECT_REGISTER  0x35
-#define MIN_UNUSED_REGISTER         0x35
+#define MAX_WRITE_PROTECT_REGISTER  0x33
+#define MIN_UNUSED_REGISTER         0x34
 #else
 #define MAX_WRITE_PROTECT_REGISTER  0x2F
 #define MIN_UNUSED_REGISTER         0x30
I seem to recall getvelocity, uses getposition to generate the velocity. The derivative of the position, is velocity, hence the D for Derivative.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    OpenServo.com Forum Index -> Software All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group