我实际上是在一个基于freeRTOS的机器人项目上工作。我遇到了控制机器人车轮速度的PI控制器的一些问题
#define To 100 // 100 ms
#define Te 5 // 5 ms
#define Kp 0.01
#define Ti 10 //0.1*To
#define Ki 0.05/10 //Te/ti
void corrNum(int consigneVitesse) // takes speed as parameter
{
int eL,eR = 0; errors left and right
float cdeL = 0 ;
float UpL = 0 ;
int dutyL = 0 ;
float cdeR = 0 ;
float UpR = 0 ;
int dutyR = 0 ;
float UiL= 0 ;
float UiR = 0 ;
if(consigneVitesse)
{
tickL = quadEncoder_GetSpeedL();
tickR = quadEncoder_GetSpeedR();
eL = (int)(consigneVitesse - tickL);
eR = (int)(consigneVitesse - tickR);
UpL = Kp*eL ;
UpR = Kp*eR ;
UiL= UiL + Kp*Ki*eL ;
UiR= UiR + Kp*Ki*eR ;
UiL_prev = UiL ;
UiR_prev = UiR ;
cdeL = UpL + UiL ;
cdeR = UpR + UiR ;
dutyL = cdeL < 100 && cdeL > -100 ? (int)cdeL +100 : 200 ;
dutyR = cdeR < 100 && cdeR > -100 ? (int)cdeR +100 : 200 ;
motorLeft_SetDuty(dutyL);
motorRight_SetDuty(dutyR);
HAL_Delay(5); // sampling period 5 ms
term_printf("MOTOR LEFT ::::::> CMD : %d SPEED : %d Err : %d DUTY : %d\n\r",consigneVitesse,tickL,eL,dutyL`enter code here`);
term_printf("MOTOR RIGHT ::::::> CMD : %d SPEED : %d Err : %d DUTY : %d \n\r",consigneVitesse,tickR,eR,dutyR);;
tickL = 0 ;
tickR = 0;
}
else
{
motorLeft_SetDuty(100);
motorRight_SetDuty(100);
}
}`
问题在于,当误差达到0.0而不是稳定时,开始振荡。
答案 0 :(得分:-1)
目前尚不清楚如何在此解决方案中使用FreeRTOS - 片段中没有FreeRTOS调用,非RTOS HAL_delay()
和时间关键控制循环中的终端输出无济于事 - 您在5ms内只能输出57个字符 - 你没有时间输出文字。
您的错误信号计算错误,编码器提供的相对位置不是速度;你必须根据时间的变化计算速度。
您应该将任务专用于控制回路并使用RTOS定时来确保高效和准确的调度。该任务应该相对于其他任务以足够高的优先级运行,以确保无抖动时序。
以下内容应视为伪代码;我不熟悉FreeRTOS API,所以操作系统调用不是真的,我没有处理实例化任务本身,也没有处理你没有提供足够信息的扩展和其他细微之处 - 这是建议的您应该从中获取的控制器结构 - 而不是逐字的实际代码:
typedef enum
{
LEFT = 0,
RIGHT = 1
} tMotorId
volatile static struct
{
float integral ;
int speed ;
int previous_position ;
} motor_ctrl_data[2] = {{0,0},{0,0}} ;
void setSpeed( tMotorId id, int speed )
{
motor_ctrl_data[id] = speed ;
}
void motor_ctrl_task()
{
// Configure RTOS time for 5ms update period
OS_TIMER timer = creatTimer( 5 ) ;
startTimer( timer ) ;
// Get initial encoder counts
motor_ctrl_data[LEFT].previous_position = getEncoder(LEFT) ;
motor_ctrl_data[RIGHT].previous_position = getEncoder(RIGHT) ;
// PI control loop
for(;;)
{
// Loop update timer wait
waitTimer( timer ) ;
// For each motor
for( int m = 0; m < 2; m++ )
{
// Calculate speed
int speed = getEncoder[m] - motor_ctrl_data[m].previous_position ;
// Calculate speed error
int error = motor_ctrl_data[m].speed - speed[m] ;
// Calculate error integral
motor_ctrl_data[m].integral += error[m] ;
motorDuty( LEFT, Kp * error + Ki * motor_ctrl_data[m].integral ) ;
}
}
}
这当然不能解决振荡,但至少是一个能够在给定适当系数的情况下进行控制的控制器结构。调整环路设置Ki = 0并增加Kp,直到电机尽可能快地移动但没有振荡。这不会让你达到目标速度,因为在目标上错误将为零,因此Kp单独无法工作。然后开始增加Ki直到达到目标速度。即使是小的Ki,最终也会达到目标速度,但动态响应会很迟钝。太多,你会得到过冲或振荡。
根据电机的动态响应,您可能需要进行其他调整,例如积分限制或死区补偿。您甚至可以从差异术语中受益,但对于简单的速度控制器,这通常是不必要的并且通常难以调整。
如果需要输出调试数据,请将循环写入调试数据写入共享变量(例如,如果使用上面建议的结构,则在motor_ctrl_data
中),并以较低优先级任务异步打印它们,速率为终端输出可以维持。
文章&#34; PID Without a PhD&#34;作者Tim Wescott可能是一本有用的读物。