|
|
View previous topic :: View next topic |
Author |
Message |
aaaaamartin
Joined: 17 Apr 2005 Posts: 39 Location: Germany Stuttgart
|
small Servo (DC-Brushed-Motor + Slider-Pot) needs control |
Posted: Mon Sep 05, 2005 7:56 pm |
|
|
Dear readers,
I have been trying for weeks with no acceptable results to
control a small servo, I screwed together.
I have 3.219d / 16F877 /
Circuit is L293D H-Bridge connected to 2 Pins of PIC + PIC's PWM.
Servo is a small 12 V DC-Brushed Motor coupled to a Slider Potentiometer with 7 cm travel and a gear. The motor turns 15 times along the whole distance.
I'd very much like to see this thing positioning at 7 bit resulution with an
error +-2 or even 0.
Some things I've tried so far:
PID: directly working on the position error, no movement profile or what
so ever. Hard to implement, and results were not good.
TRAPEZOIDAL: just rising up PWM until half the move is done then down again, when tuned a bit, works very well, can achieve zero error, but works sluggishly and overshot can be large.
One of my probs is that I don't have a clue, how PID and a movement-profile do interact. I've read a lot so far, but still no idea.
Until now I'd explain it like this:
The movement profile always estimates the point where the motor is to be decallerated. PID therfore adjusts the output to stay within the limits of the profile.
You see, no idea.
So please could someone make it clear.
The other thing, which way to go for a working loop ?
What can I expect with this kind of Servo, will it ever work ?
I really hope someone can help.
Very unhappy Martin, thanks |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Mon Sep 05, 2005 9:34 pm |
|
|
Use a stepper motor or you'll need some sort of positioning sensor, optical encoder, ect... |
|
|
Eugeneo
Joined: 30 Aug 2005 Posts: 155 Location: Calgary, AB
|
|
Posted: Tue Sep 06, 2005 1:21 am |
|
|
I've built a simular app based on a H bridge and a DC motor. Instead of a sliding pot, it used a worm gear reduction and a rotational conductive plastic pot. I first used a PID loop. It worked, but not as well as I expected. Then I used just a PI loop and started to get better.
I finally just went with a simple look-up table that was based on the distance from the target(your analog sliding pot) vs. voltage(PWM percent). This worked really well for its simplicity. I was going to put a PID correction on top of this, but it worked so well even under load or no load, it wasn't needed.
I figured that with such small moving parts with so little inertia, the response for a dc motor is so fast that a PID would only be good when used as a minor correction or compensation due to load or no load.
This project was built around a windowed PIC16C74 so it only had an 8 bit a/d resolution with very good results. It hits the target everytime.
So I would suggest starting with a very simple table. You could even extrapolate the values in between if your using the 10 bit converter. |
|
|
Ttelmah Guest
|
|
Posted: Tue Sep 06, 2005 2:06 am |
|
|
Email me directly, with a return address ([email protected]), and I'll send you the code for a PID servo system I did a while ago.
This is a PID control loop, that is fed with a trapezoidal motion 'profile', based on an optical quadrature encoder (you will need to replace this code with the ADC code - remember that your servo 'loop' must not stop for the ADC reading, so you will have to use the 'read_adc(ADC_START_ONLY)' in one loop of the servo, and then a few loops latter take the reading with 'read_adc(ADC_READ_ONLY)', otherwise the ADC will interfere with the servo loop - this may be part of the problem you were having with PID). Also the servo factors will need to be tweaked for your motor/inertia. You don't say how the system is told 'where' to go to?. The code I have accepts a serial command for position, or to move by single steps etc..
Best Wishes |
|
|
sseidman
Joined: 14 Mar 2005 Posts: 159
|
|
Posted: Tue Sep 06, 2005 7:59 am |
|
|
Mark wrote: | Use a stepper motor or you'll need some sort of positioning sensor, optical encoder, ect... |
The slider pot should be all the feedback he needs.
Motors have a PWM "Deadband"-- below a certain duty cycle, the motor won't go. Find out what this is for your motor, and add it to your duty cycle.
Also, make sure your H-bridge is functioning how you want it to. For the 293, I've been successful by driving the direction bits directly from dig outs, and then using PWM on the enable pin.
Scott |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Sep 06, 2005 9:18 am |
|
|
sseidman wrote: | Mark wrote: | Use a stepper motor or you'll need some sort of positioning sensor, optical encoder, ect... |
The slider pot should be all the feedback he needs.
Motors have a PWM "Deadband"-- below a certain duty cycle, the motor won't go. Find out what this is for your motor, and add it to your duty cycle.
Also, make sure your H-bridge is functioning how you want it to. For the 293, I've been successful by driving the direction bits directly from dig outs, and then using PWM on the enable pin.
Scott |
I see now that he is using that as the feedback. I thought that was his control |
|
|
aaaaamartin
Joined: 17 Apr 2005 Posts: 39 Location: Germany Stuttgart
|
|
Posted: Tue Sep 06, 2005 10:18 am |
|
|
Hello,
thank You all for quick suggestions.
Eugeneo wrote: | finally just went with a simple look-up table that was based on the distance from the target(your analog sliding pot) vs. voltage(PWM percent). |
You mean you've implemeted some kind of modified P-Control.
How do you estimate the values used in the table.
What if very little distance is to be traveled, isn't it the same as with P-Control, the output is so small the motor won't move ? Even when adding a deadband correction.
I, at present do add a fixed number to PWM starting with min_out (offset,deadband) until i get max_out or half the move is done. ( a flatcount is there as well, but since timing is crucial here, with a good working loop time, the flatcount stays unused i.e. never reaching max_out).
Done half the move (flatcount is zero) i reduce PWM by the same fixed number. Until min_out.
If the motor overshoots, I do a full stop with max_out and enable a,b both same level, wait 40 ms for the system to stop, and run the loop again.
I also do a full stop, when the motor is within the tolerance.
Ttelmah wrote: | You don't say how the system is told 'where' to go to?. |
Hyperterminal does the trick, I printf an overview of some vars, also I input new values over the keyboard. atol () does the trick.
Format is a number representing the variable + a "," + the desired value + "enter". I also have an auto mode were i use get_timer2()>>1 as fake randoms for the position.
I will email you.
Interesting would be how a movement profile and PID work together, and
if this makes sense as I'm having just a pot as feedback.
Here's the code from the ramp up and down thing (just another test).
Thanks Martin
Code: |
#include <16F877.h>
#device adc=8, *=16
#fuses NOWDT,HS, PUT, NOPROTECT, BROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG
#use delay(clock=20000000)
#ZERO_RAM
#use rs232(baud=38400, xmit=PIN_C6, rcv=PIN_C7, disable_ints)
#priority rda,rtcc,timer2
// pot
int potg = 0;
int potv = 0;
signed int pos_err = 0;
signed int pos_err1 = 0;
// TERMINAL
int rcnt = 0;
int const rbs = 10;
char rbuf [rbs];
char rcv = 0;
int rcr = 0;
//serial out
int16 ser_delay = 1000;
int16 ser_cnt;
//auto_mode
int auto_on = 0;
int16 auto_delay = 10000;
int16 auto_cnt;
//trapezoid
int out;
int min_out = 100;
int max_out = 255;
int step_inc = 1;
int16 step_cnt;
int16 step_delay = 5;
int phase = 0;
int16 phase_cnt;
int run_profile = 0;
//*****************************************************************************//
//*********************************** FUNCTIONS *******************************//
//*****************************************************************************//
signed long atol(char *s)
{
signed long result;
int sign, base, index;
char c;
index = 0;
sign = 0;
base = 10;
result = 0;
if (!s)
return 0;
c = s[index++];
// increase index if either positive or negative sign is detected
if (c == '-')
{
sign = 1; // Set the sign to negative
c = s[index++];
}
else if (c == '+')
{
c = s[index++];
}
if (c >= '0' && c <= '9')
{
if (c == '0' && (s[index] == 'x' || s[index] == 'X'))
{
base = 16;
index++;
c = s[index++];
}
// The number is a decimal number
if (base == 10)
{
while (c >= '0' && c <= '9')
{
result = 10*result + (c - '0');
c = s[index++];
}
}
else if (base == 16) // The number is a hexa number
{
c = toupper(c);
while ( (c >= '0' && c <= '9') || (c >= 'A' && c <='F'))
{
if (c >= '0' && c <= '9')
result = (result << 4) + (c - '0');
else
result = (result << 4) + (c - 'A' + 10);
c = s[index++];c = toupper(c);
}
}
}
if (base == 10 && sign == 1)
result = -result;
return(result);
}
void
get_rda (void)
{
while (kbhit ())
{
rcv = getc ();
if ((rcv == 0x0D) && (rcnt > 0))
{
rcr = 1;
putc (0x0D);
}
else if (rcnt > rbs-1)
{
memset (rbuf, 0, rbs);
rcnt = 0;
}
else if (rcnt < rbs)
{
rbuf [rcnt] = rcv;
putc (rcv);
rcnt ++;
}
}
}
void
rdec (void)
{
char tmp [rbs];
int tmpcnt = 0;
int16 tmpvar;
int16 tmpval;
int com = 0;
char x [rbs];
char y [rbs];
int xcnt = 0;
int ycnt = 0;
while (tmpcnt < rbs)
{
if (rbuf [tmpcnt] == 0x2C)
{
com = 1;
tmpcnt++;
}
if (!com)
{
x [xcnt] = rbuf [tmpcnt];
xcnt++;
}
else
{
y [ycnt] = rbuf [tmpcnt];
ycnt++;
}
tmpcnt ++;
}
if (com)
{
tmpvar = atol (x);
tmpval = atol (y);
switch (tmpvar)
{
case 0:
{
potg=tmpval;
break;
}
case 1:
{
step_inc=tmpval;
break;
}
case 2:
{
min_out=tmpval;
break;
}
case 3:
{
max_out=tmpval;
break;
}
case 4:
{
step_delay=tmpval;
break;
}
case 5:
{
auto_on=tmpval;
break;
}
default: break;
}
}
}
mi ()
{
output_high (PIN_C0);
output_low (PIN_C1);
}
md ()
{
output_high (PIN_C1);
output_low (PIN_C0);
}
ms ()
{
output_low (PIN_C0);
output_low (PIN_C1);
}
//*****************************************************************************//
//*********************************** INTERRUPT *******************************//
//*****************************************************************************//
#int_rtcc
rtcc_isr ()
{
}
#int_rda
rda_isr ()
{
get_rda ();
}
#int_timer2
timer_2_isr ()
{
ser_cnt ++;
step_cnt ++;
auto_cnt ++;
}
//*****************************************************************************//
//*********************************** PROGRAMM ********************************//
//*****************************************************************************//
main()
{
port_b_pullups(TRUE);
set_rtcc (0);
setup_counters (RTCC_INTERNAL,RTCC_DIV_32);
setup_timer_2 (T2_DIV_BY_4, 200, 1);
setup_ccp1 (ccp_pwm);
//enable_interrupts (INT_RTCC);
enable_interrupts (INT_TIMER2);
enable_interrupts (INT_RDA);
enable_interrupts (GLOBAL);
setup_adc_ports (AN0);
setup_adc (ADC_CLOCK_INTERNAL);
set_adc_channel (0);
set_pwm1_duty (0); //65535
output_d (1);
delay_us (30);
while (1)
{
start:
if (rcr)
{
disable_interrupts (global);
rdec ();
memset (rbuf, 0, rbs);
rcr = 0;
rcnt = 0;
enable_interrupts (global);
}
if (ser_delay <= ser_cnt)
{
printf("\033[2J");
printf ("\r\n(0)potg\tpotv\tpos_err1\tpos_err");
printf ("\r\n%03U\t%03U\t%D\t\t%D", potg, potv, pos_err1, pos_err);
printf ("\r\nrun_profile\tphase\tphase_cnt");
printf ("\r\n%03U\t\t%03U\t%06lu", run_profile, phase, phase_cnt);
printf ("\r\n(1)step_inc");
printf ("\r\n%03U", step_inc);
printf ("\r\nouptut");
printf ("\r\n%03U", out);
ser_cnt = 0;
}
if ((auto_on) && (auto_delay <= auto_cnt))
{
potg = get_timer2 () >> 1;
auto_cnt = 0;
}
if (step_delay <= step_cnt)
{
potv = read_adc () >> 1;
delay_us (30);
pos_err1 = (potg - potv);
if (run_profile == 0)
{
out = 255;
set_pwm1_duty ((int)out);
ms ();
phase_cnt = 0;
phase = 0;
delay_ms (40);
potv = read_adc () >> 1;
delay_us (30);
pos_err1 = (potg - potv);
pos_err = pos_err1;
if (abs (pos_err) >= 3)
{
run_profile = 1;
out = min_out;
}
}
if (run_profile == 1)
{
if (pos_err < 0)
{
md ();
}
else
{
mi ();
}
if (phase == 0)
{
(out <= (max_out-step_inc)) ? (out+=step_inc) : (out = max_out);
if (out == max_out)
{
phase_cnt++;
}
}
else
{
if (phase_cnt == 0)
{
(out >= (min_out+step_inc)) ? (out-=step_inc) : (out = min_out);
}
(phase_cnt >= 1) ? (phase_cnt--) : (0);
}
if (pos_err > 0)
{
if ( (signed long)((pos_err / 2) - pos_err1) >= 0)
{
phase = 1;
}
}
else
{
if ( (signed long)((pos_err / 2) - pos_err1) <= 0)
{
phase = 1;
}
}
if (abs (pos_err1) < 2)
{
run_profile = 0;
}
if (((signed long)pos_err1 * (signed long)pos_err) <= 0)
{
run_profile = 0;
}
if ((phase == 1) && (out == min_out))
{
run_profile = 0;
}
}
set_pwm1_duty ((int)out);
step_cnt = 0;
}
} // end while (1)
} // end main
|
|
|
|
|
|
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
|