ColeYao 发表于 2023-2-4 10:56

步进电机运行使用PID加减速控制方式的策略讨论

本帖最后由 ColeYao 于 2023-2-4 11:20 编辑

   如题,使用PID方式控制步进电机运行时,对于加速段,直接使用常规PID方式即可,将电机速度从0提高到最大速度即可,但对于减速段,直接使用常规PID方式就无法实现了,电机速度可以从最大速度降到0,但不能同时保证停留在指定位置上。
对于以上问题,当单片机资源足够时,记录加速段的所有速度数据,在减速段重新反向回放记录数据即可,但对于一些资源比较少的单片机如51、M0之类的,此方法不适用。(附图是一种基于PID的控制方式,可以看到黄色曲线中减速段的加速度明显要比加速段的加速度大了不少)
附图说明:灰色是目标位置,紫色是电机运行实际位置,黄色是加速度曲线,绿色是理论计算得到的速度曲线,红色是实际速度曲线(速度范围较小,因此平滑性较差)
我想请教的是:有没有一种步进电机加减速控制方式,1)可以适用一些资源比较少的单片机(RAM占用少); 2)几乎实时平滑调速;      3)加减速曲线的加速度部分近似对称(效果比附图更完美);       4)速度曲线为近似S曲线(要求是从A出发,经历加速-匀速-减速三个阶段后停在指定位置B,要求停止后无正反向微调过程,停止精度越高越好)

ColeYao 发表于 2025-6-10 10:12

本帖最后由 ColeYao 于 2025-7-22 10:36 编辑

已找到新算法其速度曲线接近理想的S曲线,如图从上到下依次为目标位置,实际电机位置,电机速度(图二使用一个定时器实时控制四路电机速度),电机加速度!
代码:
void TestMove(uchar MtNo)
{
switch(MtNo)
{
    case 1:
      Mt1.MoveT0(200*Mt1.RadioV,800);
      while(Mt1.Runing)osDelay(1);
      osDelay(500);
      Mt1.MoveT0(400*Mt1.RadioV,200);
      while(Mt1.Runing)osDelay(1);
      osDelay(500);
      Mt1.MoveT0(1600*Mt1.RadioV,800);
      while(Mt1.Runing)osDelay(1);
      osDelay(500);
      Mt1.MoveT0(800*Mt1.RadioV,800);      //220转速配合32细分时共振明显
      while(Mt1.Runing)osDelay(1);
      osDelay(500);
      break;
    case 2:
      Mt2.MoveT0(200*Mt2.RadioV,800);
      while(Mt2.Runing)osDelay(1);
      osDelay(500);
      Mt2.MoveT0(400*Mt2.RadioV,200);
      while(Mt2.Runing)osDelay(1);
      osDelay(500);
      Mt2.MoveT0(1600*Mt2.RadioV,800);
      while(Mt2.Runing)osDelay(1);
      osDelay(500);
      Mt2.MoveT0(800*Mt2.RadioV,800);      //220转速配合32细分时共振明显
      while(Mt2.Runing)osDelay(1);
      osDelay(500);
      break;
    case 3:
      Mt3.MoveT0(200*Mt3.RadioV,800);
      while(Mt3.Runing)osDelay(1);
      osDelay(500);
      Mt3.MoveT0(400*Mt3.RadioV,200);
      while(Mt3.Runing)osDelay(1);
      osDelay(500);
      Mt3.MoveT0(1600*Mt3.RadioV,800);
      while(Mt3.Runing)osDelay(1);
      osDelay(500);
      Mt3.MoveT0(800*Mt3.RadioV,800);      //220转速配合32细分时共振明显
      while(Mt3.Runing)osDelay(1);
      osDelay(500);
      break;
    case 4:
      Mt4.MoveT0(200*Mt4.RadioV,800);
      while(Mt4.Runing)osDelay(1);
      osDelay(500);
      Mt4.MoveT0(400*Mt4.RadioV,200);
      while(Mt4.Runing)osDelay(1);
      osDelay(500);
      Mt4.MoveT0(1600*Mt4.RadioV,800);
      while(Mt4.Runing)osDelay(1);
      osDelay(500);
      Mt4.MoveT0(800*Mt4.RadioV,800);      //220转速配合32细分时共振明显
      while(Mt4.Runing)osDelay(1);
      osDelay(500);
      break;
}

}

void MonMtr(uchar MtNo)
{
switch(MtNo)
{
    case 1:
      Mon1.DesPos=Mt1.Pos_Des;
      Mon1.Pos=Mt1.Pos_Pul;
      Mon1.ACpm=Mt1.v_CirpMin-Mon1.VCpm;
      Mon1.VCpm=Mt1.v_CirpMin;
      break;
    case 2:
      Mon1.DesPos=Mt2.Pos_Des;
      Mon1.Pos=Mt2.Pos_Pul;
      Mon1.ACpm=Mt2.v_CirpMin-Mon1.VCpm;
      Mon1.VCpm=Mt2.v_CirpMin;
      break;
    case 3:
      Mon1.DesPos=Mt3.Pos_Des;
      Mon1.Pos=Mt3.Pos_Pul;
      Mon1.ACpm=Mt3.v_CirpMin-Mon1.VCpm;
      Mon1.VCpm=Mt3.v_CirpMin;
      break;
    case 4:
      Mon1.DesPos=Mt4.Pos_Des;
      Mon1.Pos=Mt4.Pos_Pul;
      Mon1.ACpm=Mt4.v_CirpMin-Mon1.VCpm;
      Mon1.VCpm=Mt4.v_CirpMin;
      break;
}
}

/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
for(;;)
{
    osDelay(10);
    TestMove(4);
}
/* USER CODE END StartDefaultTask */
}

/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
    osDelay(5);
    Mt1.Move();
    Mt2.Move();
    Mt3.Move();
    Mt4.Move();
    MonMtr(1);
    MonMtr(2);
    MonMtr(3);
    MonMtr(4);
}
/* USER CODE END StartTask02 */
}

/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
    osDelay(10);
    TestMove(3);
}
/* USER CODE END StartTask03 */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
void StartTask04(void *argument)
{
/* USER CODE BEGIN StartTask04 */
/* Infinite loop */
for(;;)
{
    osDelay(10);
    TestMove(2);
}
/* USER CODE END StartTask04 */
}

void StartTask05(void *argument)
{
/* USER CODE BEGIN StartTask05 */
/* Infinite loop */
for(;;)
{
    osDelay(10);
    TestMove(1);
}
/* USER CODE END StartTask05 */
}


触觉的爱 发表于 2023-2-9 17:44

测试一个适合的加减速曲线,做成数组,存放在ROM中,读取比实时计算快

ColeYao 发表于 2023-2-9 20:28

触觉的爱 发表于 2023-2-9 17:44
测试一个适合的加减速曲线,做成数组,存放在ROM中,读取比实时计算快

固定数组肯定比实时计算快,但是缺点很明显:1)对ROM需求大;2)一般不适用最大速度经常变动的场合,比较适合最大速度只有一个值或有限的几个值。
页: [1]
查看完整版本: 步进电机运行使用PID加减速控制方式的策略讨论