赞
踩
目录
经过几天对平衡车的理论学习和动手实践,终于完成了平衡车的基本功能,实现前进,后退,直立,转向。本项目用到了两个电机,一个两块亚力克板,一个mpu6050陀螺仪,TB6612FNG电机驱动模块,通信方式使用蓝牙模块进行简单的通信,主控芯片使用stm32f103c8t6。
VM 接12V电源,给电机供电
STBY 置1使能AIN1,AIN2,BIN1,BIN2
VCC 接5V电源
GND 接地
AO1,AO2 输出控制电机1正反转
BO1,BO2 输入控制电机2正反转
PWMA 控制AO1,AO2使能
PWMB 控制BO1,BO2使能
AIN1,AIN2 输入控制控制电机1正反转
BIN1,BIN2 输入控制控制电机2正反转
具体代码如下
- void time2_pwm_init(uint16_t arr,uint16_t pre)
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//定时器2使能
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//gpio引脚使能
-
- GPIO_InitTypeDef gpio_init={0};
- gpio_init.GPIO_Pin=GPIO_Pin_2 | GPIO_Pin_3;
- gpio_init.GPIO_Mode=GPIO_Mode_AF_PP;
- gpio_init.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&gpio_init);
-
- TIM_TimeBaseInitTypeDef tim2_base={0};
- tim2_base.TIM_ClockDivision=TIM_CKD_DIV1;
- tim2_base.TIM_CounterMode=TIM_CounterMode_Up;
- tim2_base.TIM_Period=arr;
- tim2_base.TIM_Prescaler=pre;
- tim2_base.TIM_RepetitionCounter=DISABLE;
- TIM_TimeBaseInit(TIM2,&tim2_base);
-
- TIM_OCInitTypeDef time2_oc={0};
- time2_oc.TIM_OCMode=TIM_OCMode_PWM1;
- time2_oc.TIM_OCPolarity=TIM_OCPolarity_High;
- time2_oc.TIM_Pulse=0;
- time2_oc.TIM_OutputState=TIM_OutputState_Enable;
- TIM_OC3Init(TIM2,&time2_oc);
- TIM_OC4Init(TIM2,&time2_oc);
-
- TIM_Cmd(TIM2,ENABLE);//启动定时器
- }
代码
- void mator_gpio_init(void)
- {
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
- GPIO_InitTypeDef gpio_init={0};
- gpio_init.GPIO_Pin=GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
- gpio_init.GPIO_Mode=GPIO_Mode_Out_PP;
- gpio_init.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOB,&gpio_init);
-
- GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_RESET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET);
- }
-
- void mator_forward(void)
- {
- GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_SET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_RESET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_RESET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_SET);
- }
-
- void mator_back(void)
- {
- GPIO_WriteBit(GPIOB,GPIO_Pin_12,Bit_RESET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_13,Bit_SET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_14,Bit_SET);
- GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET);
- }
主要使用的是mpu6050官方的dmp库
代码如下
- //采集俯仰角,翻滚角,偏航角
- void MPU6050_Pose(void)
- {
-
- dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors,&more);
-
- if(sensors & INV_WXYZ_QUAT )
- {
- q0 = quat[0] / q30;
- q1 = quat[1] / q30;
- q2 = quat[2] / q30;
- q3 = quat[3] / q30;
-
- Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch
- Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)*57.3; // roll
- Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3; //yaw
-
- }
- }
-
-
- //mpu6050初始化
- void MPU6050_Init(void)
- {
- int result=0;
- //IIC_Init();
- result=mpu_init();
- if(!result)
- {
-
- printf("mpu initialization complete......\n "); //mpu initialization complete
-
- if(!mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL)) //mpu_set_sensor
- printf("mpu_set_sensor complete ......\n");
- else
- printf("mpu_set_sensor come across error ......\n");
-
- if(!mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL)) //mpu_configure_fifo
- printf("mpu_configure_fifo complete ......\n");
- else
- printf("mpu_configure_fifo come across error ......\n");
-
- if(!mpu_set_sample_rate(DEFAULT_MPU_HZ)) //mpu_set_sample_rate
- printf("mpu_set_sample_rate complete ......\n");
- else
- printf("mpu_set_sample_rate error ......\n");
-
- if(!dmp_load_motion_driver_firmware()) //dmp_load_motion_driver_firmvare
- printf("dmp_load_motion_driver_firmware complete ......\n");
- else
- printf("dmp_load_motion_driver_firmware come across error ......\n");
-
- if(!dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation))) //dmp_set_orientation
- printf("dmp_set_orientation complete ......\n");
- else
- printf("dmp_set_orientation come across error ......\n");
-
- if(!dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP |
- DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | DMP_FEATURE_SEND_CAL_GYRO |
- DMP_FEATURE_GYRO_CAL)) //dmp_enable_feature
- printf("dmp_enable_feature complete ......\n");
- else
- printf("dmp_enable_feature come across error ......\n");
-
- if(!dmp_set_fifo_rate(DEFAULT_MPU_HZ)) //dmp_set_fifo_rate
- printf("dmp_set_fifo_rate complete ......\n");
- else
- printf("dmp_set_fifo_rate come across error ......\n");
-
- run_self_test(); //自检
-
- if(!mpu_set_dmp_state(1))
- printf("mpu_set_dmp_state complete ......\n");
- else
- printf("mpu_set_dmp_state come across error ......\n");
- }
- else //MPU6050状态指示灯 STM32核心板 PC13 绿色灯亮起为不正常
- {
- GPIO_ResetBits(GPIOC, GPIO_Pin_13); //MPU6050状态指示灯 STM32核心板 PC13 绿色灯亮起为不正常
- while(1);
- }
-
- }
使用定时器3,和4的通道1和通道2进行4倍频地计数,主要代码如下:
- void time3_encoder_init()
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//定时器2使能
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//gpio引脚使能
-
- GPIO_InitTypeDef gpio_init={0};
- gpio_init.GPIO_Pin=GPIO_Pin_6 | GPIO_Pin_7;
- gpio_init.GPIO_Mode=GPIO_Mode_IN_FLOATING;
- gpio_init.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&gpio_init);
-
- TIM_TimeBaseInitTypeDef time3_base={0};
- time3_base.TIM_ClockDivision=TIM_CKD_DIV1;
- time3_base.TIM_CounterMode=TIM_CounterMode_Up;
- time3_base.TIM_Period=65535;
- time3_base.TIM_Prescaler=0;
- time3_base.TIM_RepetitionCounter=DISABLE;
- TIM_TimeBaseInit(TIM3,&time3_base);
-
- TIM_ICInitTypeDef time3_ic={0};
- time3_ic.TIM_Channel=TIM_Channel_1;
- time3_ic.TIM_ICFilter=0;
- time3_ic.TIM_ICPolarity=TIM_ICPolarity_Rising;
- time3_ic.TIM_ICPrescaler=TIM_ICPSC_DIV1;
- time3_ic.TIM_ICSelection=TIM_ICSelection_DirectTI;
- TIM_ICInit(TIM3,&time3_ic);
- time3_ic.TIM_Channel=TIM_Channel_2;
- TIM_ICInit(TIM3,&time3_ic);
- TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//配置编码器计数。BothEdge(底部边缘)
-
- //初始化标志位,计数器
- TIM_ClearFlag(TIM3,TIM_FLAG_Update);//清除标志位
- TIM_SetCounter(TIM3,0);//TIM3->CNT=0;
-
- //配置中断
- NVIC_InitTypeDef nvic_init={0};
- nvic_init.NVIC_IRQChannel=TIM3_IRQn;//中断通道
- nvic_init.NVIC_IRQChannelCmd=ENABLE;//中断使能
- nvic_init.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级;
- nvic_init.NVIC_IRQChannelSubPriority=1;//响应优先级
- NVIC_Init(&nvic_init);
-
- TIM_ITConfig(TIM3,TIM_IT_Update | TIM_IT_CC1 |TIM_IT_CC2,ENABLE);//配置定时器,允许更新中断,CC1,CC2捕获中断
- TIM_Cmd(TIM3,ENABLE);//开启定时器
- }
-
- void time4_encoder_init()
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//定时器2使能
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//gpio引脚使能
-
- GPIO_InitTypeDef gpio_init={0};
- gpio_init.GPIO_Pin=GPIO_Pin_6 | GPIO_Pin_7;
- gpio_init.GPIO_Mode=GPIO_Mode_IN_FLOATING;
- gpio_init.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOB,&gpio_init);
-
- TIM_TimeBaseInitTypeDef time4_base={0};
- time4_base.TIM_ClockDivision=TIM_CKD_DIV1;
- time4_base.TIM_CounterMode=TIM_CounterMode_Up;
- time4_base.TIM_Period=65535;
- time4_base.TIM_Prescaler=0;
- time4_base.TIM_RepetitionCounter=DISABLE;
- TIM_TimeBaseInit(TIM4,&time4_base);
-
- TIM_ICInitTypeDef time4_ic={0};
- time4_ic.TIM_Channel=TIM_Channel_1;
- time4_ic.TIM_ICFilter=0;
- time4_ic.TIM_ICPolarity=TIM_ICPolarity_Rising;
- time4_ic.TIM_ICPrescaler=TIM_ICPSC_DIV1;
- time4_ic.TIM_ICSelection=TIM_ICSelection_DirectTI;
- TIM_ICInit(TIM4,&time4_ic);
- time4_ic.TIM_Channel=TIM_Channel_2;
- TIM_ICInit(TIM4,&time4_ic);
- TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//配置编码器计数。BothEdge(底部边缘)
-
- //初始化标志位,计数器
- TIM_ClearFlag(TIM4,TIM_FLAG_Update);//清除标志位
- TIM_SetCounter(TIM4,0);//TIM3->CNT=0;
-
- //配置中断
- NVIC_InitTypeDef nvic_init={0};
- nvic_init.NVIC_IRQChannel=TIM4_IRQn;//中断通道
- nvic_init.NVIC_IRQChannelCmd=ENABLE;//中断使能
- nvic_init.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级;
- nvic_init.NVIC_IRQChannelSubPriority=1;//响应优先级
- NVIC_Init(&nvic_init);
-
- TIM_ITConfig(TIM4,TIM_IT_Update | TIM_IT_CC1 |TIM_IT_CC2,ENABLE);//配置定时器,允许更新中断,CC1,CC2捕获中断
- TIM_Cmd(TIM4,ENABLE);//开启定时器
- }
使用定时器1每隔5ms进行一次mpu6050的数据采集,并进行直立环控制,每40ms,进行一次速度环控制,防止影响直立控制。
小车的偏转角度作为Kp的系数,角加速度作为Kd的系数
代码如下:
- float angle_pid_realize(struct _pid *pid,float angle)
- {
- if(Pitch==0||Pitch<-20||Pitch>20) //MPU6050状态指示灯 STM32核心板 PC13 绿色灯亮起为不正常
- {
- //Pitch=1;
- GPIO_ResetBits(GPIOC, GPIO_Pin_13); //MPU6050状态指示灯 STM32核心板 PC13 绿色灯亮起为不正常
- }
- else
- {GPIO_SetBits(GPIOC, GPIO_Pin_13);} //MPU6050状态正常时LED灯熄灭
-
- pid->err=angle-pid->target_val;//计算目标值与实际值的误差
- pid->actual_val=pid->Kp*pid->err+pid->Kd*gyro[0];//角度PD控制,gyro[0]x轴偏转角速度
- return pid->actual_val;
- }
速度环控制小车的位移,实现定点停下的功能,代码如下
- void speed_control(void)
- {
- mator.car_speed = (mator.left_tal_count + mator.right_tal_count );// * 0.5 ; //左右电机脉冲数平均值作为小车当前车速
- mator.left_tal_count =mator.right_tal_count = 0; //全局变量 注意及时清零
- BST_fCarSpeedOld *= 0.7;//一阶滤波,占上次的70%
- BST_fCarSpeedOld +=mator.car_speed*0.3;//一阶滤波,占本次的30%
-
- BST_fCarPosition += BST_fCarSpeedOld; //路程 即速度积分 1/11 3:03
- BST_fCarPosition +=BST_fBluetoothSpeed;
-
- //积分上限设限//
- if((s32)BST_fCarPosition > SPEED_INTEGRAL_MAX) BST_fCarPosition = SPEED_INTEGRAL_MAX;
- if((s32)BST_fCarPosition < SPEED_INTEGRAL_MIN) BST_fCarPosition = SPEED_INTEGRAL_MIN;
-
- mator.speed_pwm = (BST_fCarSpeedOld -CAR_SPEED_SET) * BST_fCarSpeed_P + (BST_fCarPosition - CAR_POSITION_SET) * BST_fCarSpeed_I; //速度PI算法 速度*P +位移*I=速度PWM输出
- }
主要是偏航角作为Kd的系数
代码如下
- float turn_pid_realize(uint8_t ccd,short yaw)
- {
- float turn=0;
- turn=(Turn_val-yaw)*Turn_Kd;
- //printf("turn=%d\r\n",yaw);
- return turn;
- }
使用串口3与蓝牙进行通信,手机通过蓝牙助手给蓝牙发送消息,蓝牙模块通过串口发送消息到小车。
通信协议代码如下
- void USART3_IRQHandler(void) //串口x中断服务程序
- {
- uint8_t Res;
- // uint8_t i;
- if(USART_GetITStatus(USART3,USART_IT_RXNE) != RESET)//判断中断位
- {
- USART_ClearITPendingBit(USART3, USART_IT_RXNE);
- Res = USART_ReceiveData(USART3); //接收数据
- if(Res!='\n')
- {
- rx_buf[rx_size++]=Res;
- }
- else
- {
- // for(i=0;i<rx_size;i++)
- // printf("%c",rx_buf[i]);
- // printf("\r\n");
- if(memcmp("a1",rx_buf,2)==0)//前进
- {
- BST_fBluetoothSpeed=100;
- //printf("前进\r\n");
- }
- else if(memcmp("a2",rx_buf,2)==0)//后退
- {
- BST_fBluetoothSpeed=-100;
- //printf("后退\r\n");
- }
- else if(memcmp("a3",rx_buf,2)==0)//左转
- {
- BST_fBluetoothSpeed=0;
- Turn_val=90;
- Yaw_old=Yaw;
- //printf("左转\r\n");
- }
- else if(memcmp("a4",rx_buf,2)==0)//右转
- {
- BST_fBluetoothSpeed=0;
- Turn_val=-90;
- Yaw_old=Yaw;
- //printf("右转\r\n");
- }
- else if(memcmp("a0",rx_buf,2)==0)//停下
- {
- BST_fBluetoothSpeed=0;
- //printf("停下\r\n");
- }
- rx_size=0;
- }
- }
- }
经过对平衡车的学习,我对pid算法有了更加深刻的理解,在mpu6050陀螺仪的使用也越来越熟练,平衡小车的核心就是pid算法,所以pid算法对小车和控制类的学习是十分重要的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。