1 Star 5 Fork 0

拉拉 / ps2手柄遥控车(基于freertos)

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

基于freertos的闭环控制遥控小车

介绍

基于freertos的闭环控制遥控小车

软件架构

基于stm32HAL库编写及移植,MCU为f407vet6 采用了freertos作为框架,便于后期增加功能,同时将闭环调速与主程序分离,并将ps2信号接收置于最高优先级。一方面可以灵活控制每层的运行周期,另一方面避免低优先级程序阻塞导致整个程序运行受到影响。

后续预计增加功能

预计会将云台的控制加入,采用freertos延迟中断形式

更新

1.UART不定长收发,基于IDLE中断帧判断。(与树莓派进行通信进行符文模块追踪,得到2位16进制模拟值(更新至stm32f4xxit.h与stm32f4xxit.c中 2.增加了控制电调及云台舵机的定时器控制,以及角度环PID计算(更新至stm32f4xxit.h与stm32f4xxit.c中

说明: DMA不定长收发及云台舵机pid

IDLE

​ IDLE中断实现DMA不定长收发原理

对IDLE中断使能并开启串口dma接收

void USRT_DMA_IDLE_Start()
{
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);        //使能IDLE中断
	HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX);    //开启DMA接收
}

DMA中断回调

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  UART_Rx_Callback(&huart1);
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

通过IDLE中断帧判断,如果未触发IDLE中断,则继续向缓存下一位写入

void UART_Rx_Callback(UART_HandleTypeDef *huart)
{
	//判断IDLE中断
	if(!__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))
	{
		return;
	}
	//清除IDLE中断--直接读取SR、DR寄存器即可清除IDLE中断
	__HAL_UART_CLEAR_IDLEFLAG(huart);
//	temp = huart->Instance->SR;
//	temp = huart->Instance->DR;
UART1_Rx_IDLE_Callback();
}

当IDLE中断触发后,进入IDLE回调,完成一帧的接收

void UART1_Rx_IDLE_Callback()
{
	//暂停DMA
	HAL_UART_DMAStop(&huart1);
	
	//获取DMA缓存区剩余长度
	//实际长度 = 总长度-剩余长度
	Uart1RxBuffLen = UART_DMA_BUFF_LEN_MAX-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
	
	
	//回发测试
	
	HAL_UART_Transmit(&huart1,Uart1RxBuff,Uart1RxBuffLen,100);
	if(state==1){	
	if(Uart1RxBuff[1]>17)
		AngleV+=Angle_PID_P( 128-Uart1RxBuff[1]);

		if(Uart1RxBuff[0]>17)
		AngleP+=Angle_PID_V(128-Uart1RxBuff[0]);
	}
	//开始DMA接收
	HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX);
}
//说明持续更新中...

使用说明:编码器及ps手柄

可调用函数分别封装在两个库中,ps2.h中封装了ps通信用函数,encoder.h中封装了包含闭环控制、编码器更新以及模拟值转化为电机pwm的函数。 关于encoder.h的部分代码

int WHEEL_SPD_UPD_A(void){
int Spd=(int)(int16_t)__HAL_TIM_GetCounter(&htim3)-10000;//由于类型转换太麻烦就直接设定10000初始值,减掉(计数器默认返回无符号整形)
	
return 60*25*Spd/1320;

}//编码器线数11,减速比1:30,采用ti1ti2双边沿触发计数,计数周期40ms,得到结果为每分钟转速

官方对于ti1ti2双边沿计数的示例,相当于11304

     官方对于ti1ti2双边沿计数的示例,相当于11*30*4个脉冲每圈

关于pid闭环计算

int Incremental_PID_calc_A(int spdw,int spdt){//spdw:期望速度,spdt:实际速度
	static int bias=0, last_bias=0, prev_bias=0; 
  bias=spdt-spdw;
  double P=kp*(bias-last_bias);
  double I=ki*bias;
	double D=kd*(bias-2*last_bias+prev_bias);
	prev_bias=last_bias;
	last_bias=bias;
	static int PID;PID-=(int)(P+I+D);
	if(PID>1000)//pid限幅
		PID=1000;
	if(PID<0)
		PID=0;
	return PID;
}

采用了增量式pid对轮速进行闭环计算,将比较得到的结果经过pid计算得到pwm输出值(其中重装载值为1000)。输入图片说明 实现转速闭环的原理

void PWM_SET(char a,int pwm){
	if(pwm<0)
		pwm=-pwm;
if(a=='A')
		__HAL_TIM_SET_COMPARE(&htim9,TIM_CHANNEL_1,pwm);
if(a=='B')
		__HAL_TIM_SET_COMPARE(&htim9,TIM_CHANNEL_2,pwm);
if(a=='C')
		__HAL_TIM_SET_COMPARE(&htim12,TIM_CHANNEL_1,pwm);
if(a=='D')
		__HAL_TIM_SET_COMPARE(&htim12,TIM_CHANNEL_2,pwm);
}//pwm输出

手柄接收到方向控制为模拟值,无摇杆时为128,为了避免抖动导致错误的移动,设置了手柄控制盲区。

//将模拟值转化为pwm输出
void AutoCtrl(uint8_t X_val,uint8_t Y_val){
A=0,B=0,C=0,D=0;
	if(((X_val>=114)&&(X_val<=138))&&((Y_val>=114)&&(Y_val<=138)))
	{	PWM_SET('A',0);
	PWM_SET('B',0);
		PWM_SET('C',0);
		PWM_SET('D',0);

	}//所有轮子刹车
	else{//计算每个轮子速度
	if(X_val>138)
	{A+=0.3*(X_val-138);
	D+=0.3*(X_val-138);
	C+=0.3*(114-X_val);
	B+=0.3*(114-X_val);}
	
	if(X_val<114)
	{A-=0.3*(114-X_val);
	D-=0.3*(114-X_val);
	B-=0.3*(X_val-138);
	C-=0.3*(X_val-138);}
	
	if(Y_val>138){
	A+=0.5*(Y_val-100);
	B+=0.5*(Y_val-100);
	C+=0.5*(Y_val-100);
	D+=0.5*(Y_val-100);
	}
	if(Y_val<114){
		A+=0.5*(Y_val-130);
	B+=0.5*(Y_val-130);
	C+=0.5*(Y_val-130);
	D+=0.5*(Y_val-130);
	}
//跟据结果进行方向控制及pwm输出
	if(A>=0)
	{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
	PWM_SET('A',Incremental_PID_calc_A(A,V_A));}
	else
	{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
	PWM_SET('A',Incremental_PID_calc_A(-A,-V_A));}
		if(B>=0)
	{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);PWM_SET('B',Incremental_PID_calc_B(B,V_B));}
	else
	{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);PWM_SET('B',Incremental_PID_calc_B(-B,-V_B));
	}
			if(C>=0)
	{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_6,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_7,GPIO_PIN_RESET);	PWM_SET('C',Incremental_PID_calc_C(C,V_C));}
	else
	{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_7,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_6,GPIO_PIN_RESET);	PWM_SET('C',Incremental_PID_calc_C(-C,-V_C));}
			if(D>=0)
	{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_5,GPIO_PIN_RESET);	PWM_SET('D',Incremental_PID_calc_D(D,V_D));}
	else
	{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_5,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_RESET);	PWM_SET('D',Incremental_PID_calc_D(-D,-V_D));}

}
	
}

代码调用示例

//手柄信号读取(注意不要在一个周期内多次读取datakey,否则有可能出现未知错误,推测为通信的时序问题)
void StartDefaultTask(void *argument)
{

  for(;;)
  {
			PS2_LX=PS2_AnologData(PSS_LX);  //读取模拟值  
			PS2_LY=PS2_AnologData(PSS_LY);
			PS2_RX=PS2_AnologData(PSS_RX);
			PS2_RY=PS2_AnologData(PSS_RY);
			PS2_KEY=PS2_DataKey();	//更新按键及模拟值并返回按键

		osDelay(40);
  
  }
 
}
//定时更新计数器并重置
void StartTask02(void *argument)
{

  for(;;)
  { CNT_init();//重置计数器
    osDelay(40);//计数四十毫秒
		V_B=WHEEL_SPD_UPD_B();
		V_A=WHEEL_SPD_UPD_A();
		V_C=WHEEL_SPD_UPD_C();
		V_D=WHEEL_SPD_UPD_D();

  }

}
//将得到的ps手柄模拟值转化为pwm输出及换向
void StartTask03(void *argument)
{

  for(;;)
{
	AutoCtrl(255-PS2_LX,PS2_LY)	;//封装好的模拟值转换
    osDelay(30);
  }

}

空文件

简介

一个普通的遥控小车开源 展开 收起
C/C++ 等 5 种语言
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
C/C++
1
https://gitee.com/chiplord/test.git
git@gitee.com:chiplord/test.git
chiplord
test
ps2手柄遥控车(基于freertos)
master

搜索帮助