PID简述:PID控制器简述(附代码)-CSDN博客
本篇文章将用最简单的语言讲解系统辨识,让一个小白快速对系统辨识有一个了解。因为本文以简单为主,所以存在一定的不严谨性。
首先,系统辨识顾名思义就是分析出一个未知的系统,如电机。通过系统辨识我们可以获得系统的传递函数,构建输入和输出的关系,然后利用simulink中的PID控制器进行系统调参。
以云台为例,在智能车上的云台一般有YAW轴和PITCH轴。我们先对YAW轴电机进行辨识,在辨识的时候我们一般会用到正弦扫频信号(由于很多小白就是卡在正弦扫频信号这一步,所以作者会免费提供keil5的C语言代码),一般我们是发给电机一个目标速度,得到电机的反馈速度(首先我们要根据经验调PID参数,使系统有一个较好的跟随效果效果,建议只用P)。
然后我们将目标速度和电机反馈速度用串口助手输出出来(作者使用的VOFA),我们将数据保存为CSV格式并输入到MATLAB中。
点击打开,选择保存的CSV文件即可,改为列向量,导入成MATLAB的变量即可。
利用MATLAB中的系统辨识工具箱system identification ,将数据导入,即可分析出系统的传递函数。如果我们不确定传递函数的零点和极点数量也没有关系,该工具箱可以帮我们自动分析出来。
我们点击左上侧的Import data,选择Time domain data...
输入input和output,Start Time改为0,Sample time根据自己采样时间更改,作者是0.04。
选择Select range功能。
选择Transfer Function Models...传递函数模型。
得到传递函数之后我们变可以在simulink中建模,利用PID控制器设置自己的需求,让其自动调节出PID参数。注意,PID控制器不仅会调节参数,还会有一个低通滤波,在代码中我们最好也加上低通滤波。
下图为simulink中PID控制器的自动调节页面,上方可以根据自己的需求选择响应时间和瞬态特性,然后更新模块。该页面在打开PID控制器之后,点击右下角的“调节”按键即可打开。
注:受各方面影响我们辨识的效果可能并不好,所以根据PID控制原理经验调参也是非常重要的。
正弦扫频函数:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "can.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "math.h"
#include "arm_math.h"
#include "struct_typedef.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define get_motor_measure(ptr, data) \
{ \
(ptr)->last_ecd = (ptr)->ecd; \
(ptr)->ecd = (uint16_t)((data)[0] << 8 | (data)[1]); \
(ptr)->speed_rpm = (uint16_t)((data)[2] << 8 | (data)[3]); \
(ptr)->given_current = (uint16_t)((data)[4] << 8 | (data)[5]); \
(ptr)->temperate = (data)[6]; \
}
#define pi 3.1415926535
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
uint8_t Rx_data[8];
int cnt_w,cnt_t,cnt_T;
typedef struct motor_struct
{
uint16_t ecd;
int16_t speed_rpm; //角速度
int16_t given_current;
uint8_t temperate;
int16_t last_ecd;
} motor_measure_t;
motor_measure_t Motor_measure;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
CAN_TxHeaderTypeDef CAN1_Txheader1;
CAN_RxHeaderTypeDef Rx_Header; //数据帧帧头
float speed;
float w[64] = {1,1.499,2,2.5,3.0,3.496,4,4.504,5,5.494,5.988,6.493,6.993,7.518,8,8.474,9.009,9.523,10,10.526,10.989,11.494,12.048,12.500,12.987,13.513,14.084,14.492,14.925,15.384,15.873,16.393,16.949,17.543,17.857,18.518,18.867,19.607,20,20.408,20.833,21.276,22.222,23.809,26.315,27.777,30.303,32.258,34.482,35.714,38.461,40,50,58.823,71.428,76.923,90.909,100,111.111,125,200,250,333.333,500};
float T[64] = {1000,667,500,400,333,286,250,222,200,182,167,154,143,133,125,118,111,105,100,95,91,87,83,80,77,74,71,69,67,65,63,61,59,57,56,54,53,51,50,49,48,47,45,42,38,36,33,31,29,28,26,25,20,17,14,13,11,10,9,8,5,4,3,2};
float data[8000];
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern DMA_HandleTypeDef hdma_usart3_tx;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
//滤波器初始化
void can_filter_init(void)
{
CAN_FilterTypeDef can_filter_st;
can_filter_st.FilterActivation = ENABLE;
can_filter_st.FilterMode = CAN_FILTERMODE_IDMASK;
can_filter_st.FilterScale = CAN_FILTERSCALE_32BIT;
can_filter_st.FilterIdHigh = 0x0000;
can_filter_st.FilterIdLow = 0x0000;
can_filter_st.FilterMaskIdHigh = 0x0000;
can_filter_st.FilterMaskIdLow = 0x0000;
can_filter_st.FilterBank = 0;
can_filter_st.FilterFIFOAssignment = CAN_RX_FIFO0;
HAL_CAN_ConfigFilter(&hcan1, &can_filter_st);
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
can_filter_st.SlaveStartFilterBank = 14;
can_filter_st.FilterBank = 14;
HAL_CAN_ConfigFilter(&hcan2, &can_filter_st);
HAL_CAN_Start(&hcan2); //使能CAN2
HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING); //CAN接收中断的使能
}
pid_type_def Motor_pid;
float PID[5] = {5,0.0,0.0,25000,6500};
uint8_t TX_message[8];
uint8_t a=0;
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO0,&Rx_Header,Rx_data);
if(hcan->Instance == CAN2)
{
if(Rx_Header.StdId == 0x000)
get_motor_measure(&Motor_measure,Rx_data);//获取电机数据
}
}
/*正弦扫频函数生成*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
cnt_t++;
speed = 300*arm_sin_f32(2*pi/1000.0*w[cnt_w]*cnt_t);
if(cnt_t >= T[cnt_T])
{
cnt_t = 0;
cnt_T++;
cnt_w++;
}
if(cnt_T>=64) speed = 0;
PID_calc(&Motor_pid,Motor_measure.speed_rpm,speed);
if(a==0) Motor_pid.out=0;
TX_message[0] = (int16_t)Motor_pid.out>>8;
TX_message[1] = (int16_t)Motor_pid.out;
TX_message[2] = (int16_t)Motor_pid.out>>8;
TX_message[3] = (int16_t)Motor_pid.out;
TX_message[4] = (int16_t)Motor_pid.out>>8;
TX_message[5] = (int16_t)Motor_pid.out;
TX_message[6] = (int16_t)Motor_pid.out>>8;
TX_message[7] = (int16_t)Motor_pid.out;
HAL_CAN_AddTxMessage(&hcan2,&CAN1_Txheader1,TX_message,0);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
CAN1_Txheader1.DLC = 0x08;
CAN1_Txheader1.RTR = CAN_RTR_DATA;
CAN1_Txheader1.IDE = CAN_ID_STD;
CAN1_Txheader1.StdId = 0x000; //标识符
PID_init(&Motor_pid,PID_POSITION,PID,PID[3],PID[4]);
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_CAN1_Init();
MX_CAN2_Init();
MX_TIM2_Init();
MX_USART3_UART_Init();
MX_USART6_UART_Init();
/* USER CODE BEGIN 2 */
can_filter_init();
HAL_TIM_Base_Start_IT(&htim2);
HAL_CAN_Start(&hcan1);
HAL_CAN_Start(&hcan2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
作者也是在学习之后写下此篇文章,能力有限,有错误之处还望指正。