Arduino with NXT servo position won't stabilize?

I'm using the arduino PID and encoder libraries to run an NXT servo for position.  Problem is, it won't move quickly to where I want, or accurately, or without oscillating all at the same time (and never for accuracy).  I need it to go to exact encoder points with minimal oscillation or delay (I plan on using it in a delta bot.)  I know it's not the best motor for the job, but 1) I already have enough, it costs me nothing more, 2)they're very accurate, and I know they CAN be run just fine (the NXT itself works great).  

I've looked on websites and stuff for tuning, but nothing I found works, because of the weirdness of the motor itself (which can start quickly, but takes up to 400 milliseconds to stop).  The best tuning parameters I found so far are 5.5, 15, 0.75 (P,I,D).     

sort by: active | newest | oldest
You're right, I misunderstood. Then you are in the Dark Art of servo tuning.

You really need accurate means of examining position versus time from your servo. If you have one, use your oscilloscope, otherwise, sample and spit the data out of the serial port to plot the step response of the servo. You need to increase the serial port output speed to the maximum to get the best response. Once you can plot it, then we can make progress.

The FASTEST response from a classic PID controller is called "Under-Over-On", when you have to accept some overshoot. If you CAN'T accept overshoot, you have to accept slower response. What are you driving the motor with ? It sounds like its only single quadrant - not sufficient for a position servo.
jduffy54 (author)  steveastrouk3 years ago
I have been able to graph the sevo position fairly fast, the problem I ran into is that it won't stabilize close enough to the desired position, and increasing anything beyond what it already is sets it oscillating. It does the Under-Over-under-over-under-over-settle-for-a-value-12-off-and-overshoot-any-further-correction. I think it may have some backlash, because there is a pretty significant time difference between starting and movement. I tested it with another motor that I attached the encoder directly, and that worked significantly nicer. Problem is, that encoder has about 1/5 the resolution, and a much slower motor (which may also have helped).

Finially, what did you mean by single quadrant?
Single -quadrant supplies current to the motor in the forward direction, two quadrant allows the forward spinning motor to GENERATE back into the supply. FOUR quadrant motor controls allows the motor to be driven and generate forward or backwards.

It sounds to me very much like backlash is killing your response.
That's what servos DO, tuning them can be very tricky. Your problem is you are cascasding control, you have the internal feedback on the NXT servo, and you are imposing a control loop from the Arduino.

You could get somewhere if you did the whole job yourself, removing the internals of the servo and just relying on the Arduino.
jduffy54 (author)  steveastrouk3 years ago


jduffy54 (author) says: Aug 31, 2013. 6:49 AMReply

I believe you misunderstood, the NXT servo is just a DC gearmotor and encoder-no controls of any kind. The control loop on the arduino directly regulates the power to the motor. I'll post the code below.

#include
#include
Encoder enc1(21, 20);
Encoder enc2(19, 18);
double setpoint, input, output;
float dpos1;
double cpos1;
int oldout;
unsigned long ptime;
PID pid1(&input, &output, &setpoint,5.5,20,0.75, DIRECT);

unsigned long serialTime;
void setup()
{
Serial.begin(57600);
pinMode(13,OUTPUT);
// setpoint = 100;
pid1.SetMode(AUTOMATIC);
pid1.SetOutputLimits(-510,510);//output will be divided by 2
}

void loop(){
delay(1);
if (millis()-ptime > 3000){//sets a new, random position every 3 seconds
ptime = millis();
dpos1 = random(10,700);
}

//Serial.print(dpos1);
//Serial.print(" ");
//Serial.println(cpos1);
// setpoint = dpos1; //used in lineu of the PID front end
if(abs(dpos1- setpoint)>5){
if (dpos1 > setpoint){
setpoint+=1;
}else{
setpoint-=1;//this makes the setpoint ramp up and down, instead of moving instantly
}}

cpos1 = enc1.read();//the actual feedback code.
input = cpos1;
pid1.Compute();
analogWrite(11,abs(output/2));
if(output < 0){
digitalWrite(13,LOW);
}else{
digitalWrite(13,HIGH);
}}
What's your pid sample time ? 1 Second ?
jduffy54 (author)  steveastrouk3 years ago
No, should be 1 millisecond.