Lv. 83 (전무) 70,159 납
26%
Exp. 69,331/70,560 | 26%
회원가입 ID/PW 찾기
AA

제가 PID 제어로 DC 모터 속도 제어를 하려고 합니다.

일단 엔코더값을 받아 일정한 엔코더 값으로 속도를 제어하려 합니다.

 

 

DC 모터는 디엔지에서 만든 32pgm

엔코더는 모터에 부속된 엔코더..

모터 드라이브는 18200

mcu는 atmega128 입니다.

 

 

예를 들어 단위 시간당 50 펄스를 받고 있다가 100 펄스를 받게끔 하면 속도가 2배 되겠지요?

이러한 개념으로 펄스값으로 DC 속도 제어를 하려 하는데요.

 

 

우선 P제어를 하려고 하는데 모터를 빨리 돌리려면 fast pwm에서 ocr값으로 듀티비를 조절해야 하잖아요???

 

 

그런데 일단 목표값부터 어떻게 잡을지 난감합니다.

 

 

현제 모터가 정지해 있고 100펄스를 받게끔 하려면 얼마큼의 ocr값을 넣을지부터가 막막합니다.

상황에 따라 100펄스를 받게끔 하는 OCR값이 분명 존재하겠지만...

이걸 안다면 PID 제어를 할 필요도 없겠지요.

 

 

++ 증감 연산으로 하나하나씩 증가하다가 단위시간당 100펄스 넘으면 줄이고 100펄스 안넘기면 높이는 식으로 제어 할수도 있겠지만...

이런 단순 연산은  나중에 제어값이 수렴하지 않고 진동할게 뻔하기 때문이지요.

그리고 이건 on off 제어지 PID 제어도 아니잖아요. (그냥 P연산이라고 해야 하나...)

게다가 이런식으로 한다면 1씩 증가하는 단순 증가이기 때문에 D와 I연산을 구할수도 없을거 같습니다.

 

 

엔코더로 현제 속도를 인식하고 원하는 목표 속도에 수렴시키려면 어떻게 해야 하나요?

구글 검색에서도 DC 모터로 PID로 각도 제어하는건 많이 봤는데 속도 제어시키는건 못봐서요.

 

 

알려 주세요.

댓글 8
  • No Profile
    글쓴이 스텝빠스텝 08.09.16 01:24 댓글 좋아요 0 싫어요 0

    예를 들어... 현제 모터가 정지해 있고, 모터를 엔코더로 단위시간당 100 카운트 한다고 칩시다. 그럼 p=100-0 으로 p값은 100이잖아요.


    근데 OCR=100; 이라고 한다면... 8비트 fast PWM에서는 50%도 안되는 값이잖아요.


    다시말해 현제 DC 모터의 상태를 파악하는 엔코더 펄스 값과 모터의 속도를 조절하는 듀티비와는 별로 상관이 없는데 저걸 가지고 모터의 전압을 조절하는 PID식을 만든다는게 이해가 가지 않습니다.


    굳이 상관 관계를 따진다면 모터의 전압이 선형적으로 상승할수록 엔코더 값도 선형적으로 늘어난다 정도???

    하지만 엔코더 값과 듀티비값을 본다면 두 숫자가 별로 상관 있는게 아니잖아요.


    명석하게 설명해 주실 분 안계신가요???

  • No Profile
    엔코더값 : 모터의 회전수(rpm)측정 센서
    모터의 전압 : 모터의 회전속도를 결정

    pid 제어를 통해 듀티비를 변경하게 되면 모터드라이버단에서 듀티에 따라서 모터의 전압을 변경하여 출력
  • No Profile

    PWM의 듀티가 50%일때 0인경우가 있고 50인경우가 있고 드라이버마다 다르므로 확인하세요.

  • PID 제어 방식이라는 것은 어떠한 에러 값에 곱을 하는 것 입니다.

    즉, 에러가 10이고 P 제어기라고 할 경우 p=1 일때는 제어값은 10이 되는 것 입니다.

    err * P

    에러는 보통 목표치 - 현재값 입니다.

    P 제어로는 목표치에 수렴 하지 못하여 I,D 제어기가 있는 것 입니다.

  • No Profile

    보통 PID제어는 상당한 경험과 노하우가 필요하고 쉽게 사용하기에는 PD제어기가 유용합니다.
    전 성격상 PID전부 사용하지만요

  • No Profile
    음...간만에 유익한 질문인 듯 싶어서 답변을 달아봅니다..
    우선 PID를 이렇게 댓글로 올려서 질문하신 분께서 이해를 할 수 있도록
    설명을 드리는 것이 과연 가능할까 라는 생각이 드는 군요..
    왜냐하면 제가 모터쪽 수업을 하면서 PID를 구현할 수 있도록 하나하나 소스까지 설명해 가면서.
    설명해도 잘 이해를 못하는 사람들이 많았던 관계로....

    스텝빠스텝께서 이해하시려면 고민을 많이 하셔봐야 할 듯 합니다.
    거두 절미하고...간단히 설명을 드려보도록 하겠습니다.

    우선..모터의 속도를 PID제어기를 사용하여 제어하기 위해서는

    첫번째로 : 모터의 속도를 구해야 합니다.
    두번째로 : 모터의 현재속도와 목표속도간의 차이를 구합니다(오차를 구합니다.)
    세번째로 : 구해진 오차를 PID제어기에 넣어서 오차에 비례하는 어떤값(편의상 이렇게 표현함)을 구합니다.
    네번째로 : 구해진 어떤값을 모터의 속도 명령으로 전송합니다.(OCR 입력)

    대략 이런 순서인데요.. 우선..C언어를 좀 하실수 있으시고 제어공학을 좀 공부해 보셨다면 책 하나 추천해 드리지요.
    "실습및 활용을 중심으로한 디지털 제어 시스템"  고경철, 최병옥, 이수영, 이장명 공저, 홍릉과학출판사
    모터제어를 처음 접하는 사람이라면 상당히 도움이 될 책이라고 생각합니다.

    물론 제어공학을 잘 몰라도 PID제어기를 구현하는데 전혀 문제될 게 없습니다.
    (플랜트가 들어가고 안정도 해석을 위해서는 제어공학을 하는게 좋겠지만요)

    또 어먼데로 잠시 이야기가 흐르는 군요..
    다시 본론으로 돌아가서...위의 4개의 순서대로 모터를 제어하기 위해서..하나하나 필요한 것들을 설명해 보자면.

    /////////////////////////////////////////////////////////
    우선 "첫번째로 : 모터의 속도를 구해야 합니다."
    그럼 속도를 구해야 하는데 과연 어떻게 구하냐!! 이게 문제겠군요..
    우선 모터의 회전 속도를 구하려면 엔코더 값을 받아올 수 있어야 합니다. 그리고 시간을 측정할 수 있어야 합니다.
    그럼 우선은 어떻게 엔코더값을 받아올 수 있는가가 문제군요....여기에는 여러가지 방법이
    있을 수 있는데 엔코더 리딩 전용칩 LS7166(대따 비쌉니다)을 사용해서 Atmega128에서 읽거나
    혹은 Atmega128의 외부인터럽트기능을 이용해서(돈이 안드니 좋습니다) 엔코더 값을 카운팅 할 수 있습니다.
    그럼 외부인터럽트 기능을 이용해서 엔코더 값을 카운팅하는 방법을 잠시 간단하게나마 설명 드립니다.
    외부인터럽트 0,1,2,3 중의 2개를 사용해서 카운팅하게되면 2체배가 가능합니다.(Raising or Falling Edge)
    외부인터럽트 4,5,6,7 중의 2개를 사용해서 카운팅하게되면 4체배가 가능합니다.(Any Change)

    제가 예전에 Atmega8에서 사용하던 부분을 잠시 인용하자면.(Atmega8은 외부인터럽트 0과 1이 Any Change모드를 지원합니다.)
    interrupt [EXT_INT0] void ext_int0_isr(void)
    {
        if( PIND.2 ^ PIND.3 ) g_encCount-- ;   
        else g_encCount++ ;
    }

    interrupt [EXT_INT1] void ext_int1_isr(void)
    {
        if( PIND.2 ^ PIND.3 ) g_encCount++ ;   
        else g_encCount-- ;
    }
    이런식으로 엔코더 값을 카운팅 합니다.

    그러면 엔코더 값을 카운팅 했을 때 중요한 건 이전 시점에 읽은 엔코더 값과 이번에 읽은 엔코더 값의 차이를
    계산하여 모터가 회전한 정도를 계산 할 수 있습니다.
    이것을 할 때 잠시 생각해야 할 부분이 있습니다. 오버플로우가 발생한 다는 거죠. 만일 엔코더 자료형으로 unsigned short를 썼을때
    2byte니깐 65535에서 1이 더해지면 0으로 값이 되버리죠. 마찬가지로 0에서 1이 빼지면 65535로 가게 되는 거구요.
    그런 부분들을 고려해서 짜 놓은 소스가  아래에 있습니다. 그래프를 한번 잘 그려보세요.
    (잘 보시면 약간 논리적으로 문제가 있는 부분이 있긴한데 동작하는데는 전혀 지장없습니다.)

    int getClockCountsFromPrev( void )
    {
        int l_dRtnData = 0;
        int l_dTickData1, l_dTickData2 ;
       
        l_dTickData1 = g_encCount - g_prevEncCount ;    // general case..
       
        if( g_prevEncCount > g_encCount )               // overflow case..
        {
            l_dTickData2 = g_encCount - g_prevEncCount + 65536 ;
           
            if( abs(l_dTickData1) < abs(l_dTickData2) )
            {
                l_dRtnData = l_dTickData1 ;             // general case accepted..   
            }
            else
            {
                l_dRtnData = l_dTickData2 ;             // Overflow case accepted..           
            }
        }
        else
        {
            l_dTickData2 = g_encCount - g_prevEncCount - 65536 ;

            if( abs(l_dTickData1) < abs(l_dTickData2) )
            {
                l_dRtnData = l_dTickData1 ;             // general case accepted..   
            }
            else
            {
                l_dRtnData = l_dTickData2 ;             // Overflow case accepted..           
            }       
        }

        g_prevEncCount = g_encCount ;   // 이전 틱 저장
       
        return l_dRtnData ;


    이렇게 하면 우선 타임 t 에서의 엔코더 값과 타임 t+1에서의 엔코더 값을 바탕으로 이동한 엔코더 펄스수를 알 수 있구요
    이 펄스수를 모터가 한바퀴 돌았을 때 펄스수로 나누어 주면 모터가 얼마나 돌았는지 회전수로 나오게 될 겁니다.
    혹은 각속도를 구하기 위해서 라디안 값으로 만들어 주려면 2pi를 곱해주면 되겠네요.

    그 다음에 해야 할 일이 시간을 측정하는 일인데 이건 타이머를 사용하여 Time Stamp를 찍어주면 됩니다. 쉽게 이야기 하자면
    타이머를 하나 돌립니다. 이 타이머에서 하는 일은 타이머 오버플로우가 걸릴 때 마다 시간을 위한 변수를 계속 1씩 더하는 겁니다.
    간단히 소스를 또 올려봅니다.

    interrupt [TIM0_OVF] void timer0_ovf_isr(void)
    {
        // 초당..3600번의 펄스가 생성된다..
        g_tmrCount++ ;
    }
    이런식으로 하시면 됩니다. 그런데 우리가 원하는 것은 두번의 엔코더를 읽는 시점의 시간차를 구하는 것이 목표입니다.
    그렇기 때문에 아래와 같은 식으로 함수를 만들어서 사용합니다.

    unsigned int getTimeInterval( void )
    {
        unsigned int l_udRtnData = 0;
       
        if( g_prevTmrCount > g_tmrCount )
        {
            l_udRtnData = 65536 - g_prevTmrCount + g_tmrCount ;
        }
        else
        {
             l_udRtnData = g_tmrCount - g_prevTmrCount ;
        }
       
        g_prevTmrCount = g_tmrCount ;   // 이전 타임틱 저장
       
        return l_udRtnData ;
    }

    엔코더 할때와 유사한데 이것은 오버플로우가 증가하는 방향으로 밖에 발생하지 않으니깐.
    조금 더 간단하지요.

    그럼 이제 모터의 이동거리와 시간을 알게 됐으니 모터의 현재 속도는 당연히 구할 수 있겠죠.
    /////////////////////////////////////////////////////////

    우선은 여기까지 하실수 있으시면 PID제어기를 구현하는 부분의 70프로정도 진행 되었다고 생각하시면 되겠습니다.
    제가 소스를 올려드린 것은 이해를 하시는데 도움이 되셨으면 하는 부분에서 올려드린거고
    프로세서도 다르기 때문에
    제가 올려드린 내용을 잘 이해하시고 스텝빠스텝님께서 직접 구현하는 것이 더 애로사항이 없을 것으로 생각됩니다.

    두번째, 세번째, 네번째 부분은 다음 댓글에 하나하나 올려보도록 하지요.

  • 저희 제어계측과 지도교수님이 고경철 교수님이신데요.

    저 책으로 수업 받았었는데 흠.. 어렵더군요.

    그런데 실무적인것으로 나온것 중에는 저만한 책이 없어요.

    보드도 같이 있으니 사서 해 보시면 좋을듯 하네요.

    저것에 관한 보고서가 있는데 찾아서 올려 드릴께요.

    emoticon
  • No Profile
    두번째 세번째 네번째는 한꺼번에 하도록 하겠습니다.

    ///////////////////////////////////////////////////////////////////////////
    두번째로 : 모터의 현재속도와 목표속도간의 차이를 구합니다(오차를 구합니다.)
    우선 목표속도는 사용자에 의해 주어지는 값이 될 것입니다. 그리고 모터의 속도는 위의 댓글에서
    구했기 때문에
    오차 "e = 목표속도 - 현재속도" 에 의해 구할 수 있습니다.
    여기서 구해진 e를 가지고 세번째로 갑니다.
    ///////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////
    세번째로 : 구해진 오차를 PID제어기에 넣어서 오차에 비례하는 어떤값(편의상 이렇게 표현함)을 구합니다.

                u = 0 ;

                kp = 적당한값 ;
                ki= 적당한값 ;
                kd = 적당한값;    // set parameter       

                e = l_fVelRef - l_fVelocity ;     // error value.  <= 에러를 구하는 겁니다.
              
                up = kp*e ;                     // P
                ui = ui1 + ki*(int)e ;          // I
                ud = kd*(e-e1);               // D

                u = up + ui + ud;
              
                e1 = e ;
                ui1 = ui ;

    이런식으로 u를 구하게 됩니다. (모터에 들어갈 전압량과 관계가 있는 결과값)
    ///////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////
    네번째로 : 구해진 어떤값을 모터의 속도 명령으로 전송합니다.(OCR 입력)

    세번째에서 구해진 u는 모터의 에러를 보상할 수 있는 값입니다.
    그런데 여기에서 생각해봐야 할 문제가 하나 있습니다.

    우선 첫번째로 PID를 거쳐서 나오는 u값은 float 값입니다. 그리고 PWM은 정수형입니다.
    두번째로 PID를 거쳐서 나오는 u값은 PWM의 한계값을 넘게 나올수도 있습니다. 다시 이야기 하면
    10Bit PWM이라면 값을 1023까지 밖에 줄 수 없는데  u값은 이 값보다 크게 나올 수 있습니다.
    이런 경우에 u값을 1023이상으로 나오지 않게 해주는 부분이 필요합니다.
    그리고 u값이 음수인경우 모터의 방향을 뒤집어 줘야 합니다.

    소스를 간단히 적어보면.

                l_dGain = (int)(u) ;

                if( l_dGain >= 0 )
                {
                     if( l_dGain > 1023 ) l_dGain = 1023 ;
                     setMotorSpeed((unsigned int)l_dGain, MOTOR_FORWARD);
                }
                else
                {
                     l_dGain *= -1 ;
                     if( l_dGain > 1023 ) l_dGain = 1023 ;
                    setMotorSpeed((unsigned int)l_dGain, MOTOR_REVERSE);               
                }
               
                if( l_dGain > 1023 )
                {
                    l_dGain = 1023 ;
                }

    위의 소스에서 setMotorSpeed부분이 OCR부분에 값을 넣어주는 부분입니다. 그리고 그 소스는 아래에 있습니다.
    물론 이것은 ATmega8에서 작성되어 있는 내용이고 회로부분도 다를 것이기 때문에 이 부분또한 직접 만드셔야 합니다.

    void setMotorSpeed( unsigned int a_udSpeed, unsigned char a_ucDir )
    {   
        if( a_ucDir == MOTOR_FORWARD )
        {
            // CW
            TCCR1A=0x83;      // 정방향
            TCCR1B=0x0A;
        }
        else if( a_ucDir == MOTOR_REVERSE )
        {
            // CCW 
            TCCR1A=0x23;      // 역방향
            TCCR1B=0x0A;
        }
        else
        {
            // Stop
            TCCR1A=0x03;     
            TCCR1B=0x0A;
        }

        if( a_ucDir == MOTOR_FORWARD)
        {
            // CW
            OCR1AH= (unsigned char)((a_udSpeed >> 8) & 0x03);
            OCR1AL= (unsigned char)(a_udSpeed & 0xFF);
            OCR1BH=0x00; OCR1BL=0x00;
        }
        else if( a_ucDir == MOTOR_REVERSE )
        {
            // CCW 
            OCR1AH= 0x00; OCR1AL= 0x00;
            OCR1BH= (unsigned char)((a_udSpeed >> 8) & 0x03);
            OCR1BL= (unsigned char)(a_udSpeed & 0xFF);
        }
        else
        {
            // Stop
            OCR1AH=0x00; OCR1AL=0x00;
            OCR1BH=0x00; OCR1BL=0x00;
        }   
    }
    ///////////////////////////////////////////////////////////////////////////

    건투를 빕니다.
    그럼 이만.

하드웨어 설계 및 개발에 대하여 개발자들이 자유롭게 토론하는 공간입니다.
- Q&A, 자유주재 토론, 관련 정보 공유
- 분야 : 마이크로프로세서 응용, 전기/전자(아날로그/디지털) 회로 설계, C/C++ 프로그래밍, 펌웨어,
         PCB Artwork, 트러블슈팅 등 하드웨어 설계에 관한 전반인 내용
※ 게시글에 맞는 분류를 선택하여 글을 작성해 주시면 쾌적한 사이트 운영에 많은 도움이 됩니다.
※ 하드웨어 인사이트는 회원들간의 거래정보를 게재할 뿐이지, 그 어떤 책임과 의무도 가지지 않습니다.

search
번호 분류 제목 글쓴이 조회 수 날짜
1184 마이크로프로세서 HOT모터의 상 연결에 대해서 질문드립니다.1 너구리♬ 781 2008.09.18
1183 마이크로프로세서 HOTPID 제어에서 Auto turning 이라는 개념이 잘 잡히지 않네요. 장작 2201 2008.09.18
1182 자유주제 HOT명절 지나고 나니까 더 좋네요^^ 아크마 2424 2008.09.16
1181 Software & IDEs HOTMatlab 시리얼통신을 통한 그래프만들기..;;1 GreatSSang 3695 2008.09.16
마이크로프로세서 HOTPID 속도 제어에서 OCR 레지스터에 어떤 값을 넣어줘야 하지요???8 스텝빠스텝 8824 2008.09.15
1179 자유주제 HOT추석 잘 보내세요.3 빛나는 영혼 2522 2008.09.12
1178 자유주제 HOT즐거운 명절 보내세요 an-king 2516 2008.09.12
1177 자유주제 HOT즐거운 추석 보내시기 바랍니다...^^ ddragon 2380 2008.09.12
1176 자유주제 HOT1 빛나는 영혼 3071 2008.09.11
1175 드론/로봇/라인트레이서 HOT질문한가지만 드려도 될까요?3 흥분건반 836 2008.09.11
1174 개발용역 HOT제작의뢰 부탁한 kit 설명( 회로도 첨부) 띠앙 6724 2008.09.11
1173 마이크로프로세서 HOT모터의 하드웨어적인 질문 드립니다.1 너구리♬ 731 2008.09.10
1172 자유주제 HOT그냥 건의 사항 한번 올려봄니다..2 눈물겨운행복 3081 2008.09.10
1171 자유주제 HOT출쵁~~~~~~ㅎㅎㅎ 눈물겨운행복 3068 2008.09.10
1170 전기전자 아무거나 HOT테트리스 만들려고 하는데 무슨 컨트롤러로 하는게 구현하기 어려움이 덜할까요?2 이카루스 732 2008.09.10
1169 마이크로프로세서 HOT지금 보니깐요 4거리 신호등이요4 마모루 1037 2008.09.08
1168 마이크로프로세서 HOT자료실에 있는 신호등 제어에서요1 마모루 819 2008.09.08
1167 펌웨어 & 코딩언어 HOT8051 4거리 신호등에서의 소스에서요1 마모루 1156 2008.09.08
1166 자유주제 HOT토요일인데 하루가 힘이드네요^^ ddragon 4697 2008.09.06
1165 자유주제 HOT출서!! 내일은 토요일 그렇지만 쉬지는 못하내요...^^ ddragon 2894 2008.09.05
  • 인생은 왕복차표를 발행하지 않는다. 일단 떠나면 다시는 돌아오지 못한다.
    - R.롤랑
  • * 납포인트 정보 *
  • 글 작성 : 3
  • 댓글 작성 : 1
  • 내 글이 추천받음 : 1
저작권법에 위배되는 콘텐츠는 등록 불가하며, 저작물에 대한 권리는 저작자에게 있습니다.
Copyright 2006-2021 © hardwareis.com, All rights reserved.