1、调试p,i,d;
http://www.360doc.com/content/14/0320/15/16303222_362183991.shtml
https://www.sohu.com/a/419217458_691475
看下动图和数据趋势;
首先,三个参数都等于0;
先调节p;
kp先调节。周期震荡。
初始值怎么定?
比如我调节速度,速度输入单位是r/s。这个大概是0-2.3r/s范围。但是实际是PWM控制。所以我的想法是,先大概找个范围。
PWM的范围是0–1000。实际转速范围是0—2.3r/s;
输入期望转速-------比较-----转换因子系数—速度PID------测速,返回到比较环节比较,同时输出。
期望假如是0.5r/s------------实际的是PWM,应该比较过后,速度和PWM的比例,我这里叫做转换因子。
这个比例应该是大概我猜测,就是1000/2=500。
所以用500作为P的初始化数据。后面先以100步长,加到1000–1300都可以。
狠下心,放大到KP=2000,看现象。再加Ki,,大概等于KP的0.5倍。试下。
看现象,按照上面的网址,动图的数据变化,调小,调大。。
2、借助SecureCRT,stm32和电脑通过串口进行通信,在这个软件里面,选择串口,然后这个工具可以用键盘在屏幕这里打入数据,单片机在串口中断中接收具体的信息,执行相关的动作。
具体的操作步骤。
单片机向电脑这个软件,发送printf,定向。构建这个界面。这个界面自己定义,就是人机交互界面。
最好和人的操作习惯一致,交互性好。
这个不能有中文。
比如我这个界面
怎么构建呢?
单片机端,一定要定义好printf重定向问题。stm32的正点原子的基本就可以了。
然后加入disp.h,disp.c文件。
里面有相关的函数。
disp.h
#ifndef __DISP_H
#define __DISP_H
#include "sys.h"
#include "usart.h"
void disp_clean(void);
void disp_cursor_reset(void);
void disp_gotoxy(int x,int y);
void disp_cursor_up(int y);
void disp_cursor_down(int y);
void disp_cursor_left(int x);
void disp_cursor_right(int x);
void disp_cursor_hide(void);
void disp_cursor_show(void);
#endif
disp.c
#include "disp.h"
#include "usart.h"
#include "stdio.h"
void disp_clean(void)
{
printf("\033[2J");
}
void disp_cursor_reset(void)
{
printf("\033[H");
}
void disp_gotoxy(int x,int y)
{
printf("\033[%d;%dH",y,x);
}
void disp_cursor_up(int y)
{
printf("\033[%dA",y);
}
void disp_cursor_down(int y)
{
printf("\033[%dB",y);
}
void disp_cursor_left(int x)
{
printf("\033[%dD",x);
}
void disp_cursor_right(int x)
{
printf("\033[%dC",x);
}
void disp_cursor_hide(void)
{
printf("\033[?25l");
}
void disp_cursor_show(void)
{
printf("\033[?25h");
}
我是写了个界面显示的程序,main中初始化资源之后,显示初始页面。后面再串口中断中,接收到数据,就更新数据到界面,做一个反馈,不然串口有没有接收到都不知道。
在做PID实验的时候,在采样计算PID的时候,间隔100ms,刷新一次数据。比如转速,行程等。
注意,这里的坐标x,y是从1开始的。
void DispScreen(void)
{
//12345678901234567890123456789012345678901234567890123
//speed,,,Kp=12345.02,Ki=12345.02,Kd=12345.02,Tar=10.05;
//Locat,,,Kp=12345.02,Ki=12345.02,Kd=12345.02,Tar=10.05;
//PidMode=1
//speed=12.02;WheelCir=100.25;
disp_gotoxy(1,1);
printf("speed,,,Kp=%.2f",Pid_Wheel1_Speed.Kp);
disp_gotoxy(21,1);
printf("Ki=%0.2f",Pid_Wheel1_Speed.Ki);
disp_gotoxy(33,1);
printf("Kd=%0.2f",Pid_Wheel1_Speed.Kd);
disp_gotoxy(45,1);
printf("Tar=%0.2f",Pid_Wheel1_Speed.target_val);
disp_gotoxy(1,2);
printf("locat,,,Kp=%.2f",Pid_Wheel1_Location.Kp);
disp_gotoxy(21,2);
printf("Ki=%0.2f",Pid_Wheel1_Location.Ki);
disp_gotoxy(33,2);
printf("Kd=%0.2f",Pid_Wheel1_Location.Kd);
disp_gotoxy(45,2);
printf("Tar=%0.2f",Pid_Wheel1_Location.target_val);
disp_gotoxy(1,3);
printf("pid_mode=%d",PID_mode);
disp_gotoxy(1,4);
printf("speed(r/s)=%.2f",WheelSpeed[0]);
disp_gotoxy(1,5);
printf("loc(cir)=%.2f",WheelCircles[0]);
disp_gotoxy(1,6);
printf("stepPar=%.2f",stepPar);
disp_gotoxy(1,7);
printf("TarCir=%.2f",TarCir[0]);
}
单片机用disp函数,跳到光标中,然后printf打印数据。
在timer.c定时器中断中,计算PID,20ms计算一次PID,然后10*20=200ms刷新一次具体的数据。
单片机发送到电脑的过程,就介绍到这里。
主要要注意两点:
1、要重定向printf;
2、假如我上面的h和C文件;
3、自己定义一个交互性能好的界面;
单片机如何接受电脑键盘消息呢?
当然首先要串口连接。
下载这个SecureCRT软件。
我这里没有z做校验。就是单个字符,就认为受到了,所以有时候会卡了。这时候重新按下单片机的复位。再开始了。后面可以优化代码。
在串口中断函数中。接收到不同的电脑额键盘按键字符,就执行相关动作,然后再实时更新界面的数据。
这里有几个功能:
1、调节步长:0.01,.0.1,1,10,100;用键盘的UIOP[这5个按键;
2、将步长取反,变成负数,后面就能实现减步长了。加负数,就是减法了。这里用0按键;
3、调节PIDmode,用BNM,四个。=0,=1速度PID,=2位置PID,=3串级PID,=4自己用;
4、加速度PID的数据,就用小键盘的789,7调节KP,8调节KI ,9 KD;
5、调位置PID数据,用456;
6、调节目标值,速度目标值和位置目标值,用1,2;
7、开始PID计算,用d;
8、停止PID,用s;
9、复位单片机系统,用v.
…
还有he那多的键盘按键没用用到。太方便了。
但是有时候会掉线。
void USART1_IRQHandler(void)
{
uint8_t data;//接收数据暂存变量
uint8_t bufcopy[128];//最多只取前64个数据
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
cmddat=USART_ReceiveData(USART1); //将缓冲区的数据一个字节,赋值给cmddat,中转一下,不然下次数据进来,把缓冲区刷新了,数据就不见了
switch(cmddat)
{
case 's' : // 前进forward
disp_gotoxy(1,10);
printf("cmd:q+w+s, Action:stop PID,stop TIMER13");
set_motor_rotate(1,0);
PID_mode=0;
TIM_Cmd(TIM13,DISABLE); //初始化 TIM_Cmd(TIM7,DISABLE); //关闭定时器7
break;
case 'd': // 后退backxxx
disp_gotoxy(1,10);
printf("cmd:q+w+d, Action:Start PID,start TIMER13");
TIM_Cmd(TIM13,ENABLE); //初始化
break;
case 'u': //步长0.01
disp_gotoxy(1,10);
printf("cmd:q+w+u, Action:Change Step=0.01");
stepPar=0.01f;
break;
case 'i': //步长0.01
disp_gotoxy(1,10);
printf("cmd:q+w+i, Action:Change Step=0.1");
stepPar=0.1f;
break;
case 'o': //步长0.01
disp_gotoxy(1,10);
printf("cmd:q+w+o, Action:Change Step=1.0");
stepPar=1.0f;
break;
case 'p': //步长0.01
disp_gotoxy(1,10);
printf("cmd:q+w+p, Action:Change Step=10.0");
stepPar=10.0f;
break;
case '[': //步长0.01
disp_gotoxy(1,10);
printf("cmd:q+w+[, Action:Change Step=100");
stepPar=100.0f;
break;
case '4': //位置kP 参数加
Pid_Wheel1_Location.Kp +=stepPar;//pid->Kp
break;
case '5': //位置kP 参数加
Pid_Wheel1_Location.Ki+=stepPar;
break;
case '6': //位置kP 参数加
Pid_Wheel1_Location.Kd+=stepPar;
break;
case '7': //位置kP 参数加
Pid_Wheel1_Speed.Kp+=stepPar;
break;
case '8': //位置kP 参数加
Pid_Wheel1_Speed.Ki+=stepPar;
break;
case '9': //位置kP 参数加
Pid_Wheel1_Speed.Kd+=stepPar;
break;
case '1': //位置目标值参数加
Pid_Wheel1_Speed.target_val+=stepPar;
break;
case '2': //位置目标值参数加
Pid_Wheel1_Location.target_val+=stepPar;
break;
case '0': //位置目标值参数加
stepPar=-stepPar;
break;
case 'b': //位置目标值参数加
PID_mode=1;
break;
case 'n': //位置目标值参数加
PID_mode=2;
break;
case 'm': //位置目标值参数加
PID_mode=3;
break;
case ',': //位置目标值参数加
PID_mode=4;
break;
case 'q': //位置目标值参数加
TarCir[0]+=stepPar;
break;
case 'v': //位置目标值参数加
NVIC_SystemReset(); // 复位系统
break;
default:break;
}
DispScreen();
//data = USART_ReceiveData(USART1);
//Recv1[rx_cnt++]=data;//接收的数据存入接收数组
//protocol_data_recv(&data, 1);
//if(Recv1[rx_cnt])
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}