行業新聞

          14-1 學習PID--步進電機梯形加減速實現代碼實現

          2024-10-01 01:00:10

          理論實現

          由于算法在計算過程中涉及到一些浮點型運算,大量的浮點型運算會使得效率大大降低為了使在計算浮點型的速度得到更好的優化。

          控制步進電機需要四個描述速度曲線的參數;速度曲線從零速度開始,加速到給定速度并持續到減速開始,并且最后減速至零給定步數的速度。

          1.不同速度段的處理方法

          通過第14篇文章思路大概已經清晰,接下來就是軟件代碼的設計,其實使用定時器產生脈沖,并按梯形規律加速即可,使用定時器中斷產生步脈沖并且只有在步進電機移動時進入。根據我們前面的長篇大論概述,需要在這個中斷中處理四個不同速度的狀態,分別是 stop——accel——run——decel——stop

          每一個速度變化的階段的劃分是前面計算得出的對應脈沖個數,當脈沖個數到達限制則啟動切換即可,這種操作可以通過我們熟悉的狀態機在定時器中斷中實現。STOP 為停止狀態,ACCEL 為加速狀態,RUN 為勻速狀態(對應最大速度 speed),DECEL為減速狀態。

          當應用程序啟動或者步進電機停止狀態機處于 STOP 狀態。當輸入移動步數建立計算完成,一個新狀態被置位同時定時器的中斷被使能。當運行的步數超過 1 步狀態機進入 ACCEL 狀態,如果只移動 1 步,那么狀態直接變為 DECEL,因為只移動一步是不需要加速的。當狀態變為 ACCEL,應用程序一直加速步進電機達到期望的最大速度,這時狀態變為 RUN, 或者減速必須開始,狀態變為 DECEL。當狀態為 RUN 時候,步進電機會持續保持最大速度 speed 旋轉,直到必須開始減速然后把狀態改變為 DECEL。它會一直保持DECEL 狀態并一直減速到期望的步數并且速度為 0。然后狀態變為 STOP。

          加減速情況分析

          對于加減速的情況,由于已經設定好了步進電機加速度、減速度、最大速度和步數,一共分為兩種情況:

          第一種情況:持續加速到最大速度然后再減速到 0

          第二種情況:在沒達到最大速度之前就需要開始減速到 0

          第一種情況

          根據上圖 7 個參數,其中:

          speed: 算法設置的最大速度

          accel:加速度

          decel:加速度

          step :總步數

          以上的參數都是程序里直接給出的。

          max_s_lim:速度從 0 加速到 speed 所需的步數

          accel_lim:在忽略雖大速度的情況下,開始減速之前的步數,也可以理解為加速度曲線與減速度曲線的交點;

          decel_val:實際減速的步數;

          以上的參數都是需要根據前面的計算

          max_s_lim:根據速度與路程的物理公式,所以有以下公式

          speed 是擴大 100 倍后的數值,那么平方就是 10000 倍,所以分子需要乘以100,才能保證結果不變

          accel_lim:

          如果 max_s_lim<accel_lim ,則通過達到所需速度來限制加速度;所以減速度取決于此,在這種情況下,通過以下方法找到="" decal_val:<="" p="">

          decel_val:

          推出decel_val 的表達式;但是由于是減速度的步數,所以需要帶上負號,

          具體公式如下:

          第二種情況

          這種情況是在還未達到最大速度時就已經開始減速了;其中 accel_lim、max_s_lim 不需要重復計算了;

          當 max_s_lim>accel_lim 時,如上圖加速受減速開始的限制,所以 decel_val 表達式為:

          中斷狀態

          第一種情況:當步數為 1 時,毫無疑問直接進入到減速階段然后到停止狀態

          第二種情況:當步數大于 1,并且會加到最大速度,會經過:加速狀態-> 勻速狀態-> 減速狀態-> 停止狀態

          第三種情況:當步數大于 1,并且不會加到最大速度,會經過:加速狀態-> 減速狀態-> 停止狀態

          對于加減速的每一步來說,都需要重新計算下一步的時間,計算的過程中可能會出現除不盡的項式,為了更有利的加減速,可以采用加速向上取整,減速向下取整的原則來做運算,也可以采用余數累計的方法,在這里使用的是將余數累計的方法來提高間隔時間的精度和準確性。

          2.梯形加減速的控制實現程序設計

          編程要點

          (1) 通用 GPIO 配置

          (2) 步進電機、定時器中斷初始化

          (3) 在定時器中對速度和狀態進行決策

          (4) 通過對步進電機的步數、加減速度和最大速度的設置來決定步進電機的運動

          對于加減速來說有兩個部分的框架,分別是中斷函數里面的速度決策調用和 stepper_move_T() 函數相關數值計算。

          2.1 速度決策

          /** * @brief 速度決策 *@note 在中斷中使用,每進一次中斷,決策一次 * @retval 無 */void speed_decision(void){    uint32_t tim_count=0;    uint32_t tmp = 0;    // 保存新(下)一個延時周期    uint16_t new_step_delay=0;    // 加速過程中最后一次延時(脈沖周期).    static uint16_t last_accel_delay=0;    // 總移動步數計數器    static uint32_t step_count = 0;    static int32_t rest = 0;    //定時器使用翻轉模式,需要進入兩次中斷才輸出一個完整脈沖    static uint8_t i=0;    if(__HAL_TIM_GET_IT_SOURCE(&TIM_TimeBaseStructure, MOTOR_TIM_IT_CCx) !=RESET)    {        // 清楚定時器中斷        __HAL_TIM_CLEAR_IT(&TIM_TimeBaseStructure, MOTOR_TIM_IT_CCx);        // 設置比較值        tim_count=__HAL_TIM_GET_COUNTER(&TIM_TimeBaseStructure);        tmp = tim_count+srd.step_delay;        __HAL_TIM_SET_COMPARE(&TIM_TimeBaseStructure,MOTOR_PUL_CHANNEL_x,tmp);        i++;     // 定時器中斷次數計數值        if(i==2) // 2次,說明已經輸出一個完整脈沖        {            i=0;   // 清零定時器中斷次數計數值            switch(srd.run_state)            {                /*步進電機停止狀態*/                case STOP:                step_count = 0;  // 清零步數計數器                rest = 0;        // 清零余值                // 關閉通道                TIM_CCxChannelCmd(MOTOR_PUL_TIM, MOTOR_PUL_CHANNEL_x, TIM_CCx_DISABLE);                        __HAL_TIM_CLEAR_FLAG(&TIM_TimeBaseStructure, MOTOR_TIM_FLAG_CCx);                status.running = FALSE;                break;                /*步進電機加速狀態*/                case ACCEL:                step_count++;                srd.accel_count++;                new_step_delay = srd.step_delay - (((2 *srd.step_delay) + rest)/(4 * srd.accel_count + 1));//計算新(下)一步脈沖周期(時間間隔)                rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 計算余數,下次計算補上余數,減少誤差                //檢查是夠應該開始減速                    if(step_count >= srd.decel_start) {                        srd.accel_count = srd.decel_val;                        srd.run_state = DECEL;                    }                    //檢查是否到達期望的最大速度                    else if(new_step_delay <= srd.min_delay) {                        last_accel_delay = new_step_delay;                        new_step_delay = srd.min_delay;                            rest = 0;                                                  srd.run_state = RUN;                    }                    break;                /*步進電機最大速度運行狀態*/                case RUN:                    step_count++;                    new_step_delay = srd.min_delay;                    //檢查是否需要開始減速                    if(step_count >= srd.decel_start)                    {                        srd.accel_count = srd.decel_val;                        //以最后一次加速的延時作為開始減速的延時                        new_step_delay = last_accel_delay;                        srd.run_state = DECEL;                    }                    break;                /*步進電機減速狀態*/                case DECEL:                    step_count++;                    srd.accel_count++;                      new_step_delay = srd.step_delay - (((2 * srd.step_delay) + rest)/(4 * srd.accel_count + 1)); //計算新(下)一步脈沖周期(時間間隔)                      rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 計算余數,下次計算補上余數,減少誤差                    //檢查是否為最后一步                    if(srd.accel_count >= 0)                    {                        srd.run_state = STOP;                    }                    break;            }            /*求得下一次間隔時間*/            srd.step_delay = new_step_delay;        }    }}

          47~65 行這部分是加速狀態,在加速狀態中需要時刻計算下一次的脈沖間隔時間,由于加減速分為兩種情況,這兩種情況可以參考 加減速情況分析所以需要判斷當前的步數是否到達了需要減速步數或者已經達到了設置的最大速度需要開始減速了,根據不同條件判斷下一狀態;

          67~81 行這部分是以最大速度運行的狀態;如果說在加速階段判斷下一階段可以達到最大速度,那么就會跳轉到這個狀態中,那么這個狀態的下一狀態一定是減速,所以說需要在這部分使用步數 step_count 的條件來判斷是否到達了減速階段;

          stepper_move_T

          tepper_move_T() 這個函數主要是對給定步數和加減速度等參數的計算,將加減速整個過程的最大速度位置最小速度位置以及到達加減速區域的步數等。

          /*! \brief 以給定的步數移動步進電機 * 通過計算加速到最大速度,以給定的步數開始減速 * 如果加速度和減速度很小,步進電機會移動很慢,還沒達到最大速度就要開始減速 * \param step 移動的步數 (正數為順時針,負數為逆時針). * \param accel 加速度,如果取值為100,實際值為100*0.01*rad/sec^2=1rad/sec^2 * \param decel 減速度,如果取值為100,實際值為100*0.01*rad/sec^2=1rad/sec^2 * \param speed 最大速度,如果取值為100,實際值為100*0.01*rad/sec=1rad/sec */void stepper_move_T( int32_t step, uint32_t accel, uint32_t decel, uint32_t speed){      //達到最大速度時的步數.    unsigned int max_s_lim;    //必須開始減速的步數(如果還沒加速到達最大速度時)。    unsigned int accel_lim;    /*根據步數和正負判斷*/    if(step == 0)    {            return ;    }    else if(step < 0)//逆時針    {        srd.dir = CCW;        step = -step;    }    else//順時針    {        srd.dir = CW;    }    // 輸出電機方向        MOTOR_DIR(srd.dir);    // 如果只移動一步    if(step == 1)    {        // 只移動一步        srd.accel_count = -1;        // 減速狀態        srd.run_state = DECEL;        // 短延時        srd.step_delay = 1000;        // 配置電機為運行狀態        status.running = TRUE;     }    // 步數不為零才移動    else if(step != 0)    {        // 設置最大速度極限, 計算得到min_delay用于定時器的計數器的值。        // min_delay = (alpha / tt)/ w        srd.min_delay = (int32_t)(A_T_x10/speed);        // 通過計算第一個(c0) 的步進延時來設定加速度,其中accel單位為0.1rad/sec^2        // step_delay = 1/tt * sqrt(2*alpha/accel)        // step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100        srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);        // 計算多少步之后達到最大速度的限制        // max_s_lim = speed^2 / (2*alpha*accel)        max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));        // 如果達到最大速度小于0.5步,我們將四舍五入為0        // 但實際我們必須移動至少一步才能達到想要的速度        if(max_s_lim == 0)        {                max_s_lim = 1;        }        // 計算多少步之后我們必須開始減速        // n1 = (n1+n2)decel / (accel + decel)        accel_lim = (uint32_t)(step*decel/(accel+decel));        // 我們必須加速至少1步才能才能開始減速.        if(accel_lim == 0)        {            accel_lim = 1;        }        // 使用限制條件我們可以計算出第一次開始減速的位置        //srd.decel_val為負數        if(accel_lim <= max_s_lim)        {            srd.decel_val = accel_lim - step;        }        else{            srd.decel_val = -(max_s_lim*accel/decel);        }        // 當只剩下一步我們必須減速        if(srd.decel_val == 0)        {            srd.decel_val = -1;        }        // 計算開始減速時的步數        srd.decel_start = step + srd.decel_val;        // 如果最大速度很慢,我們就不需要進行加速運動        if(srd.step_delay <= srd.min_delay)        {            srd.step_delay = srd.min_delay;            srd.run_state = RUN;        }        else        {            srd.run_state = ACCEL;        }        // 復位加速度計數值        srd.accel_count = 0;        status.running = TRUE;    }    /*獲取當前計數值*/    int tim_count=__HAL_TIM_GET_COUNTER(&TIM_TimeBaseStructure);    /*在當前計數值基礎上設置定時器比較值*/    __HAL_TIM_SET_COMPARE(&TIM_TimeBaseStructure,MOTOR_PUL_CHANNEL_x,tim_count+srd.step_delay);    /*使能定時器通道*/    TIM_CCxChannelCmd(MOTOR_PUL_TIM, MOTOR_PUL_CHANNEL_x, TIM_CCx_ENABLE);    MOTOR_EN(ON);}

          點擊“閱讀原文”可查看詳情

          Copyright ? 2021-2022 精北傳動機械(上海)有限公司 All Rights Reserved. 滬公網安備31012002005970 滬ICP備20024360號-1