1. PID 제어란?

    자동제어 방식 가운데서 가장 흔히 이용되는 제어방식으로 PID 제어라는 방식이 있다. 
    이 PID란,

    P: Proportinal(비례) 
    I: Integral(적분) 
    D: Differential(미분)
     
    의 3가지 조합으로 제어하는 것으로 유연한 제어가 가능해진다.


2. 단순 On/Off 제어

    단순한 On/Off 제어의 경우에는 제어 조작량은 0%와 100% 사이를 왕래하므로 조작량의 변화가 너무 크고, 실제 목표값에 대해 지나치게 반복하기 때문에, 목표값의 부근에서 凸凹를 반복하는 제어로 되고 만다. 
    이 모양을 그림으로 나타내면 아랫 그림과 같이 된다. 
     



3. 비례 제어

    이에 대해 조작량을 목표값과 현재 위치와의 차에 비례한 크기가 되도록 하며, 서서히 조절하는 제어 방법이 비례 제어라고 하는 방식이다. 
    이렇게 하면 목표값에 접근하면 미묘한 제어를 가할 수 있기 때문에 미세하게 목표값에 가까이 할 수 있다.
    이 모양은 아랫 그림과 같이 나타낼 수 있다. 


 


4. PI 제어

    비례 제어로 잘 제어할 수 있을 것으로 생각하겠지만, 실제로는 제어량이 목표값에 접근하면 문제가 발생한다.
    그것은 조작량이 너무 작아지고, 그 이상 미세하게 제어할 수 없는 상태가 발생한다. 결과는 목표값에 아주 가까운 제어량의 상태에서 안정한 상태로 되고 만다. 
    이렇게 되면 목표값에 가까워지지만, 아무리 시간이 지나도 제어량과 완전히 일치하지 않는 상태로 되고 만다. 
    이 미소한 오차를 "잔류편차"라고 한다. 이 잔류편차를 없애기 위해 사용되는 것이 적분 제어이다. 
    즉, 미소한 잔류편차를 시간적으로 누적하여, 어떤 크기로 된 곳에서 조작량을 증가하여 편차를 없애는 식으로 동작시킨다.
    이와 같이, 비례 동작에 적분 동작을 추가한 제어를 "PI 제어"라 부른다. 
    이것을 그림으로 나타내면 아랫 그림과 같이 된다. 


     

 


5. 미분 제어와 PID 제어

    PI 제어로 실제 목표값에 가깝게 하는 제어는 완벽하게 할 수 있다. 그러나 또 하나 개선의 여지가 있다. 
    그것은 제어 응답의 속도이다. PI 제어에서는 확실히 목표값으로 제어할 수 있지만, 일정한 시간(시정수)이 필요하다. 
    이때 정수가 크면 외란이 있을 때의 응답 성능이 나빠진다. 
    즉, 외란에 대하여 신속하게 반응할 수 없고, 즉시 원래의 목표값으로는 돌아갈 수 없다는 것이다.
    그래서, 필요하게 된 것이 미분 동작이다. 
    이것은 급격히 일어나는 외란에 대해 편차를 보고, 전회 편차와의 차가 큰 경우에는 조작량을 많이 하여 기민하게 반응하도록 한다.
    이 전회와의 편차에 대한 변화차를 보는 것이 "미분"에 상당한다. 
    이 미분동작을 추가한 PID 제어의 경우, 제어 특성은 아랫 그림과 같이 된다. 
    이것으로 알 수 있듯이 처음에는 상당히 over drive하는 듯이 제어하여, 신속히 목표값이 되도록 적극적으로 제어해 간다. 


     

 


6. 컴퓨터에 의한 PID 제어 알고리즘

    원래 PID 제어는 연속한 아날로그량을 제어하는 것이 기본으로 되어 있다. 그러나, 컴퓨터의 프로그램으로 PID 제어를 실현하려고 하는 경우에는 연속적인 양을 취급할 수 없다. 왜냐하면, 컴퓨터 데이터의 입출력은 일정시간 간격으로밖에 할 수 없기 때문이다. 
    게다가 미적분 연산을 착실히 하고 있는 것에서는 연산에 요하는 능력으로 인해 고성능의 컴퓨터가 필요하게 되고 만다. 
    그래서 생각된 것이 샘플링 방식(이산값)에 적합한 PID 연산 방식이다.

    우선, 샘플링 방식의 PID 제어의 기본식은 다음과 같이 표현된다.

    조작량=Kp×편차+Ki×편차의 누적값+Kd×전회 편차와의 차
                  (비례항)         (적분항)             (미분항)

    기호로 나타내면

    MVn=MVn-1+ΔMVn 
    ΔMV
    n=Kp(en-en-1)+Ki en+Kd((en-en-1)-(en-1-en-2))

    MVn, MVn-1: 금회, 전회 조작량
    ΔMVn: 금회 조작량 미분
    en, en-1, en-2: 금회, 전회, 전전회의 편차

    이것을 프로그램으로 실현하기 위해서는 이번과 전회의 편차값만 측정할 수 있으면 조작량을 구할 수 있다.



7. 파라미터를 구하는 방법

    PID 제어 방식에 있어서의 과제는 각 항에 붙는 정수, Kp, Ki, Kd를 정하는 방법이다. 
    이것의 최적값을 구하는 방법은 몇 가지 있지만, 어느 것이나 난해하며, 소형의 마이크로컴퓨터로 실현하기 위해서는 번거로운 것이다(tuning이라 부른다). 
    그래서, 이 파라미터는 cut and try로 실제 제어한 결과에서 최적한 값을 구하고, 그 값을 설정하도록 한다. 
    참고로 튜닝의 수법을 소개하면 스텝 응답법과 한계 감도법이 유명한 수법이다.

    또, 프로세스 제어 분야에서는 이 튜닝을 자동적으로 실행하는 Auto tuning 기능을 갖는 자동제어 유닛도 있다. 이것에는 제어 결과를 학습하고, 그 결과로부터 항상 최적한 파라미터값을 구하여 다음 제어 사이클에 반영하는 기능도 실장되어 있다.

    여기서 스텝 응답법에 있어서 파라미터를 구하는 방법을 소개한다. 
    우선, 제어계의 입력에 스텝 신호를 가하고, 그 출력 결과가 아랫 그림이라고 하자(파라미터는 적당히 설정해 둔다).





    윗 그림과 같이 상승의 곡선에 접선을 긋고, 그것과 축과의 교점, 정상값의 63%에 해당하는 값으로 된 곳의 2점에서, 
    L: 낭비시간 T: 시정수 K: 정상값의 3가지 값을 구한다. 
    이 값으로부터, 각 파라미터는 아래 표와 같이 구할 수 있다.

제어 동작 종별

Kp의 값

Ki의 값

Kd의 값

비례 제어

0.3~0.7T/KL

0

0

PI 제어

0.35~0.6T/KL

0.3~0.6/KL

0

PID 제어

0.6~0.95T/KL

0.6~0.7/KL

0.3~0.45T/K


이 파라미터에 범위가 있지만, 이 크기에 의한 차이는 특성의 차이로 나타나며, 아랫 그림과 같이, 파라미터가 많은 경우에는 미분, 적분 효과가 빨리 효력이 나타나므로 아랫 그림의 적색선의 특성과 같이 overshoot이 크게 눈에 띈다. 파라미터가 작은 쪽의 경우는 하측 황색선의 특성과 같이 된다.



`





https://ko.wikipedia.org/wiki/PID_%EC%A0%9C%EC%96%B4%EA%B8%B0



출처 http://www.ktechno.co.kr/pictech/motor05.html






좋은 동영상






한양대 지능형 모형차대회를 준비하면서 일정한 모터속도가 필요했다. 이를 위해 PID제어기법을 사용하여 일정한 모터속도를 얻을 수 있었다.



'공부 > 기타' 카테고리의 다른 글

C언어 hex string -> real hex  (0) 2017.12.22
INA333 테스트  (0) 2017.07.08
Error[Li006]: duplicate definitions for - IAR  (0) 2016.09.22
atmega128 tftlcd 한글출력  (0) 2016.09.01
atmega128 sd카드 제어  (0) 2016.09.01

stm32f4 프로세서를 이용해서 VCP 환경만들기


USB를 사용하기 때문에 케이블과 시리얼 포트가 필요없다. 때문에 앞으로 많이 사용 할 것 같다.

먼저 VCP를 사용하기 위해서는 드라이버가 필요하다.


http://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-utilities/stsw-stm32102.html


다운로드를 받고 설치를 하면 

VCP 포트가 활성화 된다.



https://github.com/mfauzi/STM32F4/tree/master/STM32F4%20Discovery%20Software%20Examples/STM32F4xx_USB_Example  << stm32f4 vcp 라이브러리


usbd_cdc_vcp.c 의  VCP_DataTx 를 수정해야한다. 이것 때문에 좀 고생했다.

uint16_t VCP_DataTx(uint8_t *Buf, uint32_t Len)

{

    uint32_t i;


    // Put the data into the buffer. It will be processed by the USB stack.

    for (i=0; i<Len; i++)

    {

        // Add a byte to the buffer

        APP_Rx_Buffer[APP_Rx_ptr_in] = Buf[i];


        // Update the circular buffer index

        APP_Rx_ptr_in++;


        // Loop the index if necessary

        if (APP_Rx_ptr_in == APP_RX_DATA_SIZE)

        {

            APP_Rx_ptr_in = 0;

        }

    }

    return USBD_OK;

}




 

uint8_t Buf[6] = "ABCDE";

VCP_DataTx(Buf, 6);




stm32f4 에 vcp 라이브러리 소스를 다운로드 받고 

ABCDE 를 PC에 전송한다.






PC에 정상적으로 전송되었다. 이로써 시리얼 케이블과 변환기를 연결하는 번거러움 없이 pc에 시리얼 데이터를 보낼 수 있게 되었다. 





'공부 > stm32' 카테고리의 다른 글

can통신 신호 분석  (1) 2016.07.27
stm32f4 can 통신 무작정  (2) 2016.07.25
stm32f4 클럭상태 출력하기  (0) 2016.07.25

다이오드 타버림



파워 다이오드가 날라갔지만  stm32f4 칩은 무사했다




굴러다니는 다이오드 장착 

다행이 st link도 잘 작동함.





can 통신을 할 때 통신 속도를 맞춰주어야 하는데, bitrate를 맞추기 위해서 디스커버리 칩의 클럭을 알맞은 값 을 설정해 주어야 한다. 디스커버리 보드에서 can1 은 APB1 클럭에 영향을 받는다. 먼저 APB1을 42MHz로 세팅한다.






STM32CubeMX 프로그램을 이용하여 원하는 클럭을 만들어 낼 수 있다.







/************************* PLL Parameters *************************************/
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M      4
#define PLL_N      168
 
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P      2
 
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
#define PLL_Q      7
 
/******************************************************************************/
cs
PLL을 수정하여 APB1 42MHz 세팅





CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
CANInitStructure.CAN_BS1 = CAN_BS1_3tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_1tq;
CAN_InitStructure.CAN_Prescaler = 840;


cs


BS와 프리스케일러를 수정하여 can통신 bitrate를 10Kbs로 설정






data[0= 0x01; data[1= 0xFF; data[2= 0xFF; data[3= 0xFF; data[4= 5; data[5= 6; data[6= 7; data[7= 8;
Can_TxMessage(0x0141, data);
cs


0x014 의 식별자로 1바이트의 데이터 date[0]을 보냈다.






값 을 받아낸다. 이제 오실로스코프로 찍어본다.






can 통신은 can_H선과 can_L선의 전압차이를 이용해서 데이터 통신을 한다. 위 사진은 can_H선과 can_L선의 파형을 오실로스코프에 나타낸 것이다. 아래 파형은 can_H can_L선 사이의 전압차이를 반전시킨 파형이다. 결국 아래파형이 통신 데이터 파형이다.





can 트랜시버에서 나온 데이터 파형







'공부 > stm32' 카테고리의 다른 글

stm32f4 VCP(virtual com port)  (0) 2016.08.30
stm32f4 can 통신 무작정  (2) 2016.07.25
stm32f4 클럭상태 출력하기  (0) 2016.07.25

 discovery 보드로 통신



http://blog.naver.com/solsol8711/188508351


쏠쏘리님 네이버 블로그 에서 많은 도움 받음



can통신을 하게될 디스커버리보드 2개






can통신을 하기위해서는 can트랜시버가 필요하다. Atmel ATA6660 IC를 사용






선을 연결하고




잘 나옴


아래는 소스 

개발환경은 IAR 

STM32F4 DISCOVRY



#include "stm32f4xx.h"
#include "stm32f4_discovery.h"
 
#include <stdio.h>
 
 
#ifdef __GNUC__
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
 
/*
static void delay(volatile unsigned int number)
{
    for(; number>0; number--);
}
*/
 
PUTCHAR_PROTOTYPE
{
    /* Write a character to the USART */  
    if( ch == '\n') {
        USART_SendData(USART1, '\r');
        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
        USART_SendData(USART1, '\n');
    }else {
        USART_SendData(USART1, (u8) ch);
    }
    /* Loop until the end of transmission */
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
 
    return ch;
}
 
 
 
void USART1_Configuration(void)
{
 
    /* USART1 clock enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    /* GPIOB clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
    
    GPIO_InitTypeDef GPIO_InitStructure;
    /*-------------------------- GPIO Configuration ----------------------------*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    /* Connect USART pins to AF */
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); // USART1_TX
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1); // USART1_RX
    
    
    USART_InitTypeDef USART_InitStructure;
    /* USARTx configuration ------------------------------------------------------*/
    /* USARTx configured as follow:
        - BaudRate = 115200 baud
        - Word Length = 8 Bits
        - One Stop Bit
        - No parity
        - Hardware flow control disabled (RTS and CTS signals)
        - Receive and transmit enabled
    */
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}
 
 
#include "drv_can.h"
 
SCan sCan[CAN_MAX_ID];
CanRxMsg RxMessage0;
CanTxMsg TxMessage;
 
void Can_Init()
{
    GPIO_InitTypeDef         GPIO_InitStructure;
      CAN_InitTypeDef            CAN_InitStructure;
      NVIC_InitTypeDef         NVIC_InitStructure;
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
 
      GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_CAN1);
      GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_CAN1); 
  
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
      GPIO_Init(GPIOB, &GPIO_InitStructure);
 
      CAN_DeInit(CAN1);
 
      CAN_InitStructure.CAN_TTCM = DISABLE;
      CAN_InitStructure.CAN_ABOM = DISABLE;
      CAN_InitStructure.CAN_AWUM = DISABLE;
      CAN_InitStructure.CAN_NART = DISABLE;
      CAN_InitStructure.CAN_RFLM = DISABLE;
      CAN_InitStructure.CAN_TXFP = ENABLE;
      CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
 
    
      CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
      CAN_InitStructure.CAN_BS1 = CAN_BS1_4tq;
      CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
      CAN_InitStructure.CAN_Prescaler = 60;
    
      CAN_Init(CAN1, &CAN_InitStructure);
 
       CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
      
      NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
}
 
/*
filterMode = CAN_FilterMode_IdMask, CAN_FilterMode_IdList
FiltNum = 0~13
*/
void Can_FilterConfig(uint8_t filterMode, uint8_t FiltNum, uint16_t IDHigh, uint16_t IDLow, uint16_t MaskHigh, uint16_t MaskLow)
{
    CAN_FilterInitTypeDef      CAN_FilterInitStructure;
      CAN_FilterInitStructure.CAN_FilterNumber = FiltNum;
      CAN_FilterInitStructure.CAN_FilterMode = filterMode;
      CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
      CAN_FilterInitStructure.CAN_FilterIdHigh = IDHigh << 5;
      CAN_FilterInitStructure.CAN_FilterIdLow = IDLow<<5;
      CAN_FilterInitStructure.CAN_FilterMaskIdHigh = MaskHigh;
      CAN_FilterInitStructure.CAN_FilterMaskIdLow = MaskLow;
      CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
      CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
      CAN_FilterInit(&CAN_FilterInitStructure);
}
 
/*
index = 0~CAN_MAX_ID 구조체 순번
id = 각 구조체의 CanID
func = 데이터 수신 시 실행될 함수
*/
void Can_SetID(uint8_t index, uint16_t id, void (*func)(void))
{
    sCan[index].CanID = id;
    sCan[index].CanFlag = 0;
    
    if ( func )
    {
        sCan[index].m_fp = func;
    }
}
 
void Can_TxMessage(uint16_t id, uint8_t length, uint8_t* data)
{
    uint8_t i;
    for ( i = 0 ; i < length ; i ++ )
        TxMessage.Data[i] = data[i];
    
    TxMessage.DLC = length;
    TxMessage.RTR = CAN_RTR_DATA;
      TxMessage.IDE = CAN_ID_STD;
    TxMessage.StdId = id;
    CAN_Transmit(CAN1, &TxMessage);
}
 
void CAN1_RX0_IRQHandler(void)
{
    uint8_t id;
        printf("can");
      CAN_Receive(CAN1, CAN_FIFO0, &RxMessage0);
    CAN_ClearITPendingBit(CAN1, CAN_IT_TME);
    
    for ( id = 0 ; id < CAN_MAX_ID ; id ++ )
    {
        if ( sCan[id].CanID == RxMessage0.StdId)
        {
            sCan[id].CanRxMessage = RxMessage0;
            sCan[id].CanFlag = 1;
            if ( sCan[id].m_fp )
            {
                sCan[id].m_fp();
            }
        }
    }
}
 
void test1()
{
  printf("ID 0x700, DLC: %d, data0= %d\r\n", sCan[0].CanRxMessage.DLC, sCan[0].CanRxMessage.Data[3]);
}
void test2()
{
  printf("ID 0x701, DLC: %d, data0= %d\r\n", sCan[1].CanRxMessage.DLC, sCan[1].CanRxMessage.Data[3]);
}
 
 
int main(void)
{
    uint8_t data[4]={0};
    USART1_Configuration();
    Can_Init();
    
    Can_FilterConfig(CAN_FilterMode_IdMask, 00x70000x7f00x0);
    Can_SetID(00x700, test1);
    Can_SetID(10x701, test2);
    
 
    RCC_ClocksTypeDef     RCC_Clocks;
    RCC_GetClocksFreq(&RCC_Clocks);
    printf("SYS:%d H:%d, P1:%d, P2:%d\r\n",
                                            RCC_Clocks.SYSCLK_Frequency,
                                            RCC_Clocks.HCLK_Frequency,        // AHB
                                            RCC_Clocks.PCLK1_Frequency,        // APB1
                                            RCC_Clocks.PCLK2_Frequency);    // APB2
    
    
    //printf("HELLO WORLD!");
    while(1)
    {
        data[0= 1; data[1= 2; data[2= 3; data[3= 'd';
        Can_TxMessage(0x7004, data);
    }
 
cs


'공부 > stm32' 카테고리의 다른 글

stm32f4 VCP(virtual com port)  (0) 2016.08.30
can통신 신호 분석  (1) 2016.07.27
stm32f4 클럭상태 출력하기  (0) 2016.07.25

stm32f407 현재 클럭 상태를 알고싶을때 사용 할 수 있는 코드


RCC_ClocksTypeDef     RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
printf("SYS:%d H:%d, P1:%d, P2:%d\r\n",
                                            RCC_Clocks.SYSCLK_Frequency,
                                            RCC_Clocks.HCLK_Frequency,         // AHB
                                            RCC_Clocks.PCLK1_Frequency,        // APB1
                                            RCC_Clocks.PCLK2_Frequency);     // APB2
cs


'공부 > stm32' 카테고리의 다른 글

stm32f4 VCP(virtual com port)  (0) 2016.08.30
can통신 신호 분석  (1) 2016.07.27
stm32f4 can 통신 무작정  (2) 2016.07.25

+ Recent posts