当前位置:   article > 正文

电机应用-直流有刷电机多环控制实现_位置环串级pid

位置环串级pid

目录

直流有刷电机多环控制实现

硬件设计

直流电机三环(速度环、电流环、位置环)串级PID控制-位置式PID

编程要点

配置ADC可读取电流值

配置基本定时器6产生定时中断读取当前电路中驱动电机的电流值并执行PID运算

配置定时器1输出PWM控制电机

配置定时器3读取编码器的计数值

ADC数据处理

编写位置式PID算法


直流有刷电机多环控制实现

外环的输出会作为内环的输入。外环一般是最终要控制的效果, 

硬件设计

可选:L298N电机驱动板、野火MOS搭建的驱动板。

直流电机三环(速度环、电流环、位置环)串级PID控制-位置式PID

编程要点

配置ADC可读取电流值。

配置基本定时器TIM6产生定时中断执行PID运算。

配置高级定时器TIM1输出PWM控制电机。

配置通用定时器TIM3读取编码器的计数值。

ADC数据处理。

编写位置式PID算法。

编写位置环、速度环、电流环控制函数。

增加上位机曲线观察相关代码。

编写按键控制代码。

配置ADC可读取电流值
  1. #define VBUS_MAX 14 // 电压最大值
  2. #define VBUS_MIN 10 // 电压最小值
  3. #define VBUS_HEX_MAX ((VBUS_MAX/37.0+1.24)/VREF*65536) // 电压最大值(测量电压是电源电压的1/37)
  4. #define VBUS_HEX_MIN ((VBUS_MIN/37.0+1.24)/VREF*65536) // 电压最小值(测量电压是电源电压的1/37)
  5. __IO uint16_t ADC_ConvertedValue;
  6. DMA_HandleTypeDef DMA_Init_Handle;
  7. ADC_HandleTypeDef ADC_Handle;
  8. static uint16_t adc_buff[1024];
  9. static uint16_t vbus_adc_mean = 0; // 电源电压 ADC 采样结果平均值
  10. static uint32_t adc_mean_sum = 0; // 平均值累加
  11. static uint32_t adc_mean_count = 0; // 累加计数
  12. /**
  13. * @brief 电流采集初始化
  14. * @param 无
  15. * @retval 无
  16. */
  17. void ADC_Init(void)
  18. {
  19. GPIO_InitTypeDef GPIO_InitStructure;
  20. __GPIOB_CLK_ENABLE();
  21. __DMA2_CLK_ENABLE();
  22. __ADC1_CLK_ENABLE();
  23. // PB1--电流
  24. GPIO_InitStructure.Pin = GPIO_PIN_1;
  25. GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
  26. GPIO_InitStructure.Pull = GPIO_NOPULL;
  27. HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
  28. // PB0--电压
  29. GPIO_InitStructure.Pin = GPIO_PIN_0;
  30. HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
  31. // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的
  32. DMA_Init_Handle.Instance = DMA2_Stream0;
  33. DMA_Init_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
  34. DMA_Init_Handle.Init.PeriphInc = DMA_PINC_DISABLE;
  35. DMA_Init_Handle.Init.MemInc = DMA_MINC_ENABLE;
  36. DMA_Init_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  37. DMA_Init_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  38. DMA_Init_Handle.Init.Mode = DMA_CIRCULAR;
  39. DMA_Init_Handle.Init.Priority = DMA_PRIORITY_HIGH;
  40. DMA_Init_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  41. DMA_Init_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
  42. DMA_Init_Handle.Init.MemBurst = DMA_MBURST_SINGLE;
  43. DMA_Init_Handle.Init.PeriphBurst = DMA_PBURST_SINGLE;
  44. // 选择 DMA 通道,通道存在于流中
  45. DMA_Init_Handle.Init.Channel = DMA_CHANNEL_0;
  46. //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
  47. HAL_DMA_Init(&DMA_Init_Handle);
  48. __HAL_LINKDMA(&ADC_Handle, DMA_Handle, DMA_Init_Handle);
  49. ADC_Handle.Instance = ADC1;
  50. ADC_Handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
  51. ADC_Handle.Init.Resolution = ADC_RESOLUTION_12B;
  52. ADC_Handle.Init.ScanConvMode = ENABLE;
  53. ADC_Handle.Init.ContinuousConvMode = ENABLE;
  54. ADC_Handle.Init.DiscontinuousConvMode = DISABLE;
  55. ADC_Handle.Init.NbrOfDiscConversion = 0;
  56. ADC_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  57. ADC_Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  58. ADC_Handle.Init.DataAlign = ADC_DATAALIGN_LEFT;
  59. ADC_Handle.Init.NbrOfConversion = 2;
  60. ADC_Handle.Init.DMAContinuousRequests = ENABLE;
  61. ADC_Handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  62. HAL_ADC_Init(&ADC_Handle);
  63. ADC_ChannelConfTypeDef ADC_Config;
  64. ADC_Config.Channel = ADC_CHANNEL_9;
  65. ADC_Config.Rank = 1;
  66. ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  67. ADC_Config.Offset = 0;
  68. HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);
  69. /** Configure the analog watchdog
  70. */
  71. ADC_AnalogWDGConfTypeDef AnalogWDGConfig = {0};
  72. AnalogWDGConfig.WatchdogMode = ADC_ANALOGWATCHDOG_SINGLE_REG;
  73. AnalogWDGConfig.HighThreshold = VBUS_HEX_MAX;
  74. AnalogWDGConfig.LowThreshold = VBUS_HEX_MIN;
  75. AnalogWDGConfig.Channel = ADC_CHANNEL_8;
  76. AnalogWDGConfig.ITMode = ENABLE;
  77. if (HAL_ADC_AnalogWDGConfig(&ADC_Handle, &AnalogWDGConfig) != HAL_OK)
  78. {
  79. while (1);
  80. }
  81. /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  82. */
  83. ADC_Config.Channel = ADC_CHANNEL_8;
  84. ADC_Config.Rank = 2;
  85. ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  86. ADC_Config.Offset = 0;
  87. if (HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config) != HAL_OK)
  88. {
  89. while (1);
  90. }
  91. // 外设中断优先级配置和使能中断配置
  92. HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 4, 0);
  93. HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
  94. HAL_NVIC_SetPriority(ADC_IRQn, 3, 0);
  95. HAL_NVIC_EnableIRQ(ADC_IRQn);
  96. HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t *)&adc_buff, 1024);
  97. }

配置基本定时器6产生定时中断读取当前电路中驱动电机的电流值并执行PID运算
  1. TIM_HandleTypeDef TIM_TimeBaseStructure;
  2. /**
  3. * @brief 初始化基本定时器定时,默认50ms产生一次中断
  4. * @param 无
  5. * @retval 无
  6. */
  7. void TIMx_Configuration(void)
  8. {
  9. HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 1, 3);
  10. HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
  11. __TIM6_CLK_ENABLE();
  12. TIM_TimeBaseStructure.Instance = TIM6;
  13. TIM_TimeBaseStructure.Init.Period = 50 * 50 - 1;
  14. TIM_TimeBaseStructure.Init.Prescaler = 1680 - 1;
  15. TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
  16. TIM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  17. HAL_TIM_Base_Init(&TIM_TimeBaseStructure);
  18. // 开启定时器更新中断
  19. HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);
  20. uint32_t temp = (__HAL_TIM_GET_AUTORELOAD(&TIM_TimeBaseStructure) + 1) / 50.0; // 计算周期,单位ms
  21. set_computer_value(SEND_PERIOD_CMD, CURVES_CH1, &temp, 1); // 给通道 1 发送目标值
  22. }
  23. /**
  24. * @brief 定时器更新事件回调函数
  25. * @param 无
  26. * @retval 无
  27. */
  28. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  29. {
  30. if (htim == (&TIM_TimeBaseStructure))
  31. {
  32. motor_pid_control(); // 每50ms执行一次PID运算
  33. }
  34. }

配置定时器1输出PWM控制电机
  1. TIM_HandleTypeDef DCM_TimeBaseStructure;
  2. /**
  3. * @brief 初始化控制通用定时器
  4. * @param 无
  5. * @retval 无
  6. */
  7. void Motor_TIMx_Configuration(void)
  8. {
  9. GPIO_InitTypeDef GPIO_InitStruct;
  10. TIM_OC_InitTypeDef TIM_OCInitStructure;
  11. __HAL_RCC_GPIOA_CLK_ENABLE();
  12. __TIM1_CLK_ENABLE();
  13. // PA8--PWM_TIM_CH1
  14. GPIO_InitStruct.Pin = GPIO_PIN_8;
  15. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  16. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  17. GPIO_InitStruct.Alternate = PWM_TIM_GPIO_AF;
  18. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  19. // PA9--PWM_TIM_CH2
  20. GPIO_InitStruct.Pin = GPIO_PIN_9;
  21. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  22. // TIM1 66.7us一次周期
  23. DCM_TimeBaseStructure.Instance = TIM1;
  24. DCM_TimeBaseStructure.Init.Period = 5600 - 1;
  25. DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
  26. DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
  27. DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  28. HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
  29. /*PWM模式配置*/
  30. TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
  31. TIM_OCInitStructure.Pulse = 0;
  32. TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
  33. TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  34. TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
  35. TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  36. /*配置PWM通道*/
  37. HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
  38. /*开始输出PWM*/
  39. HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
  40. /*配置PWM通道*/
  41. HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
  42. /*开始输出PWM*/
  43. HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
  44. }
  45. /**
  46. * @brief 设置TIM通道的占空比
  47. * @param channel 通道 (1,2,3,4)
  48. * @param compare 占空比
  49. * @note 无
  50. * @retval 无
  51. */
  52. void TIM1_SetPWM_pulse(uint32_t channel, int compare)
  53. {
  54. switch (channel)
  55. {
  56. case TIM_CHANNEL_1:
  57. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_1, compare);
  58. break;
  59. case TIM_CHANNEL_2:
  60. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_2, compare);
  61. break;
  62. case TIM_CHANNEL_3:
  63. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_3, compare);
  64. break;
  65. case TIM_CHANNEL_4:
  66. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_4, compare);
  67. break;
  68. }
  69. }

配置定时器3读取编码器的计数值
  1. TIM_HandleTypeDef DCM_TimeBaseStructure;
  2. /**
  3. * @brief 初始化控制通用定时器
  4. * @param 无
  5. * @retval 无
  6. */
  7. void Motor_TIMx_Configuration(void)
  8. {
  9. GPIO_InitTypeDef GPIO_InitStruct;
  10. TIM_OC_InitTypeDef TIM_OCInitStructure;
  11. __HAL_RCC_GPIOA_CLK_ENABLE();
  12. __TIM1_CLK_ENABLE();
  13. // PA8--PWM_TIM_CH1
  14. GPIO_InitStruct.Pin = GPIO_PIN_8;
  15. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  16. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  17. GPIO_InitStruct.Alternate = PWM_TIM_GPIO_AF;
  18. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  19. // PA9--PWM_TIM_CH2
  20. GPIO_InitStruct.Pin = GPIO_PIN_9;
  21. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  22. // TIM1 66.7us一次周期
  23. DCM_TimeBaseStructure.Instance = TIM1;
  24. DCM_TimeBaseStructure.Init.Period = 5600 - 1;
  25. DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
  26. DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
  27. DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  28. HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
  29. /*PWM模式配置*/
  30. TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
  31. TIM_OCInitStructure.Pulse = 0;
  32. TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
  33. TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  34. TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
  35. TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  36. /*配置PWM通道*/
  37. HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
  38. /*开始输出PWM*/
  39. HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
  40. /*配置PWM通道*/
  41. HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
  42. /*开始输出PWM*/
  43. HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
  44. }
  45. /**
  46. * @brief 定时器更新事件回调函数
  47. * @param 无
  48. * @retval 无
  49. */
  50. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  51. {
  52. if (htim == (&TIM_EncoderHandle))
  53. {
  54. /* 判断当前计数器计数方向 */
  55. if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle))
  56. /* 下溢 */
  57. {
  58. Encoder_Overflow_Count--;
  59. }
  60. else
  61. /* 上溢 */
  62. {
  63. Encoder_Overflow_Count++;
  64. }
  65. }
  66. }

ADC数据处理
  1. static uint16_t flag_num = 0;
  2. /**
  3. * @brief 常规转换在非阻塞模式下完成回调
  4. * @param hadc: ADC 句柄.
  5. * @retval 无
  6. */
  7. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
  8. {
  9. uint32_t adc_mean = 0;
  10. HAL_ADC_Stop_DMA(hadc); // 停止 ADC 采样,处理完一次数据在继续采样
  11. /* 计算电流通道采样的平均值 */
  12. for (uint32_t count = 0; count < 1024; count += 2)
  13. {
  14. adc_mean += (uint32_t)adc_buff[count];
  15. }
  16. adc_mean_sum += adc_mean / (1024 / 2); // 保存平均值
  17. adc_mean_count++;
  18. adc_mean = 0;
  19. /* 计算电压通道采样的平均值 */
  20. for (uint32_t count = 1; count < 1024; count += 2)
  21. {
  22. adc_mean += (uint32_t)adc_buff[count];
  23. }
  24. vbus_adc_mean = adc_mean / (1024 / 2); // 保存平均值
  25. HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t *)&adc_buff, 1024); // 开始 ADC 采样
  26. }
  27. /**
  28. * @brief 在非阻塞模式模拟看门狗回调
  29. * @param hadc: ADC 句柄.
  30. * @retval 无
  31. */
  32. void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef *hadc)
  33. {
  34. float temp_adc;
  35. flag_num++; // 电源电压超过阈值电压
  36. temp_adc = get_vbus_val();
  37. if (temp_adc > VBUS_MIN && temp_adc < VBUS_MAX)
  38. {
  39. flag_num = 0;
  40. }
  41. if (flag_num > 10) // 电源电压超过阈值电压10次
  42. {
  43. set_motor_disable();
  44. flag_num = 0;
  45. printf("电源电压超过限制!请检查原因,复位开发板在试!\r\n");
  46. while (1);
  47. }
  48. }
  49. /**
  50. * @brief 获取电流值(应定时调用)
  51. * @param 无
  52. * @retval 转换得到的电流值
  53. */
  54. int32_t get_curr_val(void)
  55. {
  56. static uint8_t flag = 0;
  57. static uint32_t adc_offset = 0; // 偏置电压
  58. int16_t curr_adc_mean = 0; // 电流 ACD 采样结果平均值
  59. curr_adc_mean = adc_mean_sum / adc_mean_count; // 保存平均值
  60. adc_mean_count = 0;
  61. adc_mean_sum = 0;
  62. if (flag < 17 && is_motor_en == 0) // 仅在电机未启动时记录
  63. {
  64. adc_offset = curr_adc_mean; // 多次记录偏置电压,待系统稳定偏置电压才为有效值
  65. flag += 1;
  66. }
  67. if (curr_adc_mean >= adc_offset)
  68. {
  69. curr_adc_mean -= adc_offset; // 减去偏置电压
  70. }
  71. else
  72. {
  73. curr_adc_mean = 0;
  74. }
  75. float vdc = (float)curr_adc_mean/(float)65536 * 3.3f; // 获取电压值
  76. return (float)vdc / 8.0f / 0.02f * 1000.0f;
  77. }
  78. /**
  79. * @brief 获取电源电压值
  80. * @param 无
  81. * @retval 转换得到的电流值
  82. */
  83. float get_vbus_val(void)
  84. {
  85. float vdc = (float)vbus_adc_mean/(float)65536 * 3.3f; // 获取电压值
  86. return ((float)vdc - (float)1.24) * (float)37.0; // 电源电压值(测量电压是电源电压的1/37)
  87. }

编写位置式PID算法
  1. typedef struct
  2. {
  3. float target_val; // 目标值
  4. float actual_val; // 实际值
  5. float err; // 定义偏差值
  6. float err_last; // 定义上一个偏差值
  7. float Kp,Ki,Kd; // 定义比例、积分、微分系数
  8. float integral; // 定义积分值
  9. }_pid;
  10. _pid pid_location; // 位置环控制
  11. _pid pid_curr; // 电流环控制
  12. _pid pid_speed; // 速度环控制
  13. /**
  14. * @brief PID参数初始化
  15. * @note 无
  16. * @retval 无
  17. */
  18. void PID_param_init(void)
  19. {
  20. /* 位置相关初始化参数 */
  21. pid_location.target_val = 0.0;
  22. pid_location.actual_val = 0.0;
  23. pid_location.err = 0.0;
  24. pid_location.err_last = 0.0;
  25. pid_location.integral = 0.0;
  26. pid_location.Kp = 0.011;
  27. pid_location.Ki = 0.0018;
  28. pid_location.Kd = 0.0;
  29. /* 速度相关初始化参数 */
  30. pid_speed.target_val = 100.0;
  31. pid_speed.actual_val = 0.0;
  32. pid_speed.err = 0.0;
  33. pid_speed.err_last = 0.0;
  34. pid_speed.integral = 0.0;
  35. pid_speed.Kp = 2.0;
  36. pid_speed.Ki = 0.02;
  37. pid_speed.Kd = 0.00;
  38. /* 电流相关初始化参数 */
  39. pid_curr.target_val = 80.0;
  40. pid_curr.actual_val = 0.0;
  41. pid_curr.err = 0.0;
  42. pid_curr.err_last = 0.0;
  43. pid_curr.integral = 0.0;
  44. pid_curr.Kp = 0.0;
  45. pid_curr.Ki = 3.5;
  46. pid_curr.Kd = 0.00;
  47. float pid_temp[3] = {pid_location.Kp, pid_location.Ki, pid_location.Kd};
  48. set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3); // 给通道 1 发送 P I D 值
  49. pid_temp[0] = pid_speed.Kp;
  50. pid_temp[1] = pid_speed.Ki;
  51. pid_temp[2] = pid_speed.Kd;
  52. set_computer_value(SEND_P_I_D_CMD, CURVES_CH2, pid_temp, 3); // 给通道 2 发送 P I D 值
  53. pid_temp[0] = pid_curr.Kp;
  54. pid_temp[1] = pid_curr.Ki;
  55. pid_temp[2] = pid_curr.Kd;
  56. set_computer_value(SEND_P_I_D_CMD, CURVES_CH3, pid_temp, 3); // 给通道 3 发送 P I D 值
  57. }
  58. /**
  59. * @brief 设置目标值
  60. * @param val 目标值
  61. * @note 无
  62. * @retval 无
  63. */
  64. void set_pid_target(_pid *pid, float temp_val)
  65. {
  66. pid->target_val = temp_val; // 设置当前的目标值
  67. }
  68. /**
  69. * @brief 获取目标值
  70. * @param 无
  71. * @note 无
  72. * @retval 目标值
  73. */
  74. float get_pid_target(_pid *pid)
  75. {
  76. return pid->target_val; // 设置当前的目标值
  77. }
  78. /**
  79. * @brief 设置比例、积分、微分系数
  80. * @param p:比例系数 P
  81. * @param i:积分系数 i
  82. * @param d:微分系数 d
  83. * @note 无
  84. * @retval 无
  85. */
  86. void set_p_i_d(_pid *pid, float p, float i, float d)
  87. {
  88. pid->Kp = p; // 设置比例系数 P
  89. pid->Ki = i; // 设置积分系数 I
  90. pid->Kd = d; // 设置微分系数 D
  91. }
  92. /**
  93. * @brief 位置环位置式PID算法实现
  94. * @param actual_val:实际值
  95. * @note 无
  96. * @retval 通过PID计算后的输出
  97. */
  98. float location_pid_realize(_pid *pid, float actual_val)
  99. {
  100. /*计算目标值与实际值的误差*/
  101. pid->err = pid->target_val - actual_val;
  102. /* 限定闭环死区 */
  103. if ((pid->err >= -40) && (pid->err <= 40))
  104. {
  105. pid->err = 0;
  106. pid->integral = 0;
  107. }
  108. /* 积分分离,偏差较大时去掉积分作用 */
  109. if (pid->err > -1500 && pid->err < 1500)
  110. {
  111. pid->integral += pid->err; // 误差累积
  112. /* 限定积分范围,防止积分饱和 */
  113. if (pid->integral > 4000)
  114. {
  115. pid->integral = 4000;
  116. }
  117. else if (pid->integral < -4000)
  118. {
  119. pid->integral = -4000;
  120. }
  121. }
  122. /*PID算法实现*/
  123. pid->actual_val = pid->Kp * pid->err + pid->Ki * pid->integral + pid->Kd * (pid->err - pid->err_last);
  124. /*误差传递*/
  125. pid->err_last = pid->err;
  126. /*返回当前实际值*/
  127. return pid->actual_val;
  128. }
  129. /**
  130. * @brief 速度环位置式PID算法实现
  131. * @param actual_val:实际值
  132. * @note 无
  133. * @retval 通过PID计算后的输出
  134. */
  135. float speed_pid_realize(_pid *pid, float actual_val)
  136. {
  137. /*计算目标值与实际值的误差*/
  138. pid->err = pid->target_val - actual_val;
  139. if ((pid->err < 0.2f) && (pid->err > -0.2f))
  140. {
  141. pid->err = 0.0f;
  142. }
  143. pid->integral += pid->err; // 误差累积
  144. /*PID算法实现*/
  145. pid->actual_val = pid->Kp * pid->err + pid->Ki * pid->integral + pid->Kd * (pid->err - pid->err_last);
  146. /*误差传递*/
  147. pid->err_last = pid->err;
  148. /*返回当前实际值*/
  149. return pid->actual_val;
  150. }
  151. /**
  152. * @brief 电流环位置式PID算法实现
  153. * @param actual_val:实际值
  154. * @note 无
  155. * @retval 通过PID计算后的输出
  156. */
  157. float curr_pid_realize(_pid *pid, float actual_val)
  158. {
  159. /*计算目标值与实际值的误差*/
  160. pid->err = pid->target_val - actual_val;
  161. pid->integral += pid->err; // 误差累积
  162. /* 限定积分范围,防止积分饱和 */
  163. if (pid->integral > 2000)
  164. {
  165. pid->integral = 2000;
  166. }
  167. else if (pid->integral < -2000)
  168. {
  169. pid->integral = -2000;
  170. }
  171. /*PID算法实现*/
  172. pid->actual_val = pid->Kp * pid->err + pid->Ki * pid->integral + pid->Kd * (pid->err - pid->err_last);
  173. /*误差传递*/
  174. pid->err_last = pid->err;
  175. /*返回当前实际值*/
  176. return pid->actual_val;
  177. }
  178. #define TARGET_CURRENT_MAX 200 // 目标电流的最大值 mA
  179. #define TARGET_SPEED_MAX 200 // 目标速度的最大值 r/m
  180. /**
  181. * @brief 电机位置式 PID 控制实现(定时调用)
  182. * @param 无
  183. * @retval 无
  184. */
  185. void motor_pid_control(void)
  186. {
  187. static uint32_t louter_ring_timer = 0; // 外环环周期(电流环计算周期为定时器周期T,速度环为2T,位置环为3T)
  188. int32_t actual_current = get_curr_val(); // 读取当前电流值
  189. if (actual_current > TARGET_CURRENT_MAX)
  190. {
  191. actual_current = TARGET_CURRENT_MAX;
  192. }
  193. if (is_motor_en == 1) // 电机在使能状态下才进行控制处理
  194. {
  195. static int32_t Capture_Count = 0; // 当前时刻总计数值
  196. static int32_t Last_Count = 0; // 上一时刻总计数值
  197. float cont_val = 0; // 当前控制值
  198. /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD */
  199. Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
  200. /* 位置环计算 */
  201. if (louter_ring_timer++ % 3 == 0)
  202. {
  203. cont_val = location_pid_realize(&pid_location, Capture_Count); // 进行 PID 计算
  204. /* 目标速度上限处理 */
  205. if (cont_val > TARGET_SPEED_MAX)
  206. {
  207. cont_val = TARGET_SPEED_MAX;
  208. }
  209. else if (cont_val < -TARGET_SPEED_MAX)
  210. {
  211. cont_val = -TARGET_SPEED_MAX;
  212. }
  213. set_pid_target(&pid_speed, cont_val); // 设定速度的目标值
  214. int32_t temp = cont_val;
  215. set_computer_value(SEND_TARGET_CMD, CURVES_CH2, &temp, 1); // 给通道 2 发送目标值
  216. }
  217. /* 速度环计算 */
  218. static int32_t actual_speed = 0; // 实际测得速度
  219. if (louter_ring_timer % 2 == 0)
  220. {
  221. /* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数 */
  222. actual_speed = ((float)(Capture_Count - Last_Count) / 16 * 4 / 30) / (GET_BASIC_TIM_PERIOD() * 2 / 1000.0 / 60.0);
  223. /* 记录当前总计数值,供下一时刻计算使用 */
  224. Last_Count = Capture_Count;
  225. cont_val = speed_pid_realize(&pid_speed, actual_speed); // 进行 PID 计算
  226. if (cont_val > 0) // 判断电机方向
  227. {
  228. set_motor_direction(MOTOR_FWD);
  229. }
  230. else
  231. {
  232. cont_val = -cont_val;
  233. set_motor_direction(MOTOR_REV);
  234. }
  235. cont_val = (cont_val > TARGET_CURRENT_MAX) ? TARGET_CURRENT_MAX : cont_val; // 电流上限处理
  236. set_pid_target(&pid_curr, cont_val); // 设定电流的目标值
  237. int32_t temp = cont_val;
  238. set_computer_value(SEND_TARGET_CMD, CURVES_CH3, &temp, 1); // 给通道 3 发送目标值
  239. }
  240. /* 电流环计算 */
  241. cont_val = curr_pid_realize(&pid_curr, actual_current); // 进行 PID 计算
  242. if (cont_val < 0)
  243. {
  244. cont_val = 0; // 下限处理
  245. }
  246. else if (cont_val > 5500)
  247. {
  248. cont_val = 5500; // 速度上限处理
  249. }
  250. set_motor_speed(cont_val); // 设置 PWM 占空比
  251. set_computer_value(SEND_FACT_CMD, CURVES_CH1, &Capture_Count, 1); // 给通道 1 发送实际值
  252. }
  253. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/963185
推荐阅读
相关标签
  

闽ICP备14008679号