CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

small Servo (DC-Brushed-Motor + Slider-Pot) needs control

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
aaaaamartin



Joined: 17 Apr 2005
Posts: 39
Location: Germany Stuttgart

View user's profile Send private message

small Servo (DC-Brushed-Motor + Slider-Pot) needs control
PostPosted: Mon Sep 05, 2005 7:56 pm     Reply with quote

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

View user's profile Send private message Send e-mail

PostPosted: Mon Sep 05, 2005 9:34 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Sep 06, 2005 1:21 am     Reply with quote

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







PostPosted: Tue Sep 06, 2005 2:06 am     Reply with quote

Email me directly, with a return address (ttelmah@ntlworld.com), 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

View user's profile Send private message

PostPosted: Tue Sep 06, 2005 7:59 am     Reply with quote

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

View user's profile Send private message Send e-mail

PostPosted: Tue Sep 06, 2005 9:18 am     Reply with quote

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 Embarassed
aaaaamartin



Joined: 17 Apr 2005
Posts: 39
Location: Germany Stuttgart

View user's profile Send private message

PostPosted: Tue Sep 06, 2005 10:18 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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