2.小车演示视频
3.电机驱动实验
6.PID调试
8.小车自主避障
这是快递刚到的散件,按照说明书一步步组装,DIY重要的是过程,组装很简单说明书上说电池安装过盈的,我的怎么感觉小呢,没有卡住电池,无奈在底部用胶固定一下
组装完成 OK!!!!
开始上电调试,顺便说一下,Keil软件最好是高版本的,我刚开始调试用了以前低版本(4.12)的,编译会出问题的,上电调试,能够正常直立行走,漫长的调试过程正式开始,后续更新,今天分享一个调试方法,在参数调整的时候如果一次次编译下载程序,这样效率会很慢,大家可以用APP控制方向的触控更改参数,需要改一下下位机程序,这样就可以不用去修改程序调整参数,并且可以实时看到小车的状态,能够加快调试进程,哦了
3.电机驱动实验
下面三个视频分别是左右单轮和双轮的电机测试实验,通过OLED显示屏可以看出,最大转速时,左右电机的转速是不一样的,系统中采用编码器来使得左右电机得到同步,并且在程序中设定的PWM值最大可以达到3600。
视频地址:http://player.youku.com/player.php/sid/XMTMyMDAwNzgxMg==/v.swf
视频地址:http://player.youku.com/player.php/sid/XMTMyMDAwNDc5Mg==/v.swf
4.nrf24l01无线通讯实验
该无线通讯实验是基于nrf24l01模块实现数据传输。
实验目的:实现上位机与下位机数据传输
官方给定的上位机波形显示软件能够实时采集下位机角度、速度等数据助于分析,但是传输方式上选择了232通讯,在对小车平衡、速度等调试过程中易受到传输线的影响,不利于调试,该实验采用无线通讯模式,使得调试过程不受外界信息影响。
实验过程:实验采用两个nrf24l01模块,均采用STM32芯片对数据进行处理,发送程序采用官方源程序nrf24l01配置程序,接收程序也基于源程序nrf24l01配置程序修改,接收到数据通过232接口与上位机连接,实现数据通讯。
上位机数据通讯函数程序:
void DataScope(void) { if(++Count==1) { OLED_Clear(); OLED_Display_Off(); } DataScope_Get_Channel_Data( Angle_Balance*3, 1 ); DataScope_Get_Channel_Data( Encoder_Right, 2 ); DataScope_Get_Channel_Data( Encoder_Left, 3 ); DataScope_Get_Channel_Data( Voltage , 4 ); DataScope_Get_Channel_Data(Z_data/1000, 5 ); DataScope_Get_Channel_Data(Balance_Pwm , 6 ); DataScope_Get_Channel_Data(Velocity_Pwm, 7 ); Send_Count = DataScope_Data_Generate(7); for( i = 0 ; i < Send_Count; i++) { NRFSetTxMode(DataScope_OutPut_Buffer); while(CheckACK()); } delay_ms(50); //20HZ }
5-1、实验名称:传感器驱动实验(nrf24L01驱动)
5-2、实验目的:通过实验,更好的了解nrf24l01使用原理
5-3、实验过程:
nRF24L01是工作在2.4GHz~2.5GHz的ISM 频段的单片无线收发器芯片。无线收发器包括:频率发生器、增强型“SchockBurst”模式控制器、功率放大器、晶体振荡器、调制器和解调器。在短距离无线通讯过程中发挥着很好的作用。
官方给定的程序中有nRF24L01的发送和接收程序,并且利用的是硬件IIC进行驱动,实验调试中出现数据不能进行通讯(原因未查明),就将我以前一直用的驱动程序进行重新对其驱动,实验效果很好。驱动程序使用模拟IIC,对IO口线没有限制。
PS:nRF24L01进行数据通讯时,收发地址必须匹配,这样的好处就是在有多个通讯模块时,务必修改原来的通讯地址,以避免干扰。附部分源代码
void init_io(void) { /* inerDelay_us(100); CE_0(); // chip enable CSN_1(); // Spi disable SCK=0; // Spi clock line init high */ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_5;//SPIÖ÷»úÊä³ö´Ó»úÊäÈë¶ËMOSI ʱÖÓÊä³ö¶ËSCLK GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //SPIÖ÷»úÊäÈë´Ó»úÊä³ö¶ËMISO GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //SPIÖ÷»úÊäÈë´Ó»úÊä³ö¶Ë IRQ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//CE GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;// CSN GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } void NRFSetTxMode(u8 *TxDate) { CE_0(); NRFWriteTxDate(W_REGISTER+TX_ADDR,(u8 *)TX_ADDRESS,TX_ADDR_WITDH); NRFWriteTxDate(W_REGISTER+RX_ADDR_P0,(u8 *)TX_ADDRESS,TX_ADDR_WITDH); NRFWriteTxDate(W_TX_PAYLOAD,TxDate,TX_DATA_WITDH); NRFWriteReg(W_REGISTER+EN_AA,0x01); NRFWriteReg(W_REGISTER+EN_RXADDR,0x01); NRFWriteReg(W_REGISTER+SETUP_RETR,0x0a); NRFWriteReg(W_REGISTER+RF_CH,0x40); NRFWriteReg(W_REGISTER+RF_SETUP,0x07); NRFWriteReg(W_REGISTER+CONFIG,0x0e); CE_1(); inerDelay_us(5); } /*****************NRFÉèÖÃΪ½ÓÊÕģʽ²¢½ÓÊÕÊý¾Ý******************************/ //Ö÷Òª½ÓÊÕģʽ void NRFSetRXMode(void) { CE_0(); NRFWriteTxDate(W_REGISTER+RX_ADDR_P0,(u8 *)TX_ADDRESS,TX_ADDR_WITDH); NRFWriteReg(W_REGISTER+EN_AA,0x01); NRFWriteReg(W_REGISTER+EN_RXADDR,0x01); NRFWriteReg(W_REGISTER+RF_CH,0x40); NRFWriteReg(W_REGISTER+RX_PW_P0,TX_DATA_WITDH); NRFWriteReg(W_REGISTER+RF_SETUP,0x07); NRFWriteReg(W_REGISTER+CONFIG,0x0f); CE_1(); inerDelay_us(5); } u8 CheckACK(void) { sta=NRFReadReg(R_REGISTER+STATUS); if(sta&0x30) { NRFWriteReg(W_REGISTER+STATUS,0xff); CSN_0(); NRFSPI(FLUSH_TX); CSN_1(); return(0); } else return(1); } u8 NRFRevDate(u8 *RevDate) { u8 RevFlags=0; sta=NRFReadReg(R_REGISTER+STATUS); if((sta&0x40)==0x40) { CE_0(); NRFReadRxDate(R_RX_PAYLOAD,RevDate,RX_DATA_WITDH); RevFlags=1; CE_1(); // } NRFWriteReg(W_REGISTER+STATUS,0xff); return(RevFlags); }
实验结果请看第四部分《nrf24L01通讯实验》
6-1、实验名称:PID调试
6-2、实验目的:通过实验,理解PID原理,能够进行PID参数整定
6-3、实验过程:
首先简单介绍一下PID算法:PID算法是最常用的一种闭环控制算法,以其简便性和灵活性在各种系统上都有广泛的应用。PID算法根据设定与输出的偏差,经过参数整定,得出合适的调节量,干预结果的输出,使得输出等于设定,这其实是一个动态平衡过程,结合自平衡小车对PID算法做详细说明:
自平衡小车的PID有三个方面的应用。其一是直立PID控制,直立需要系统的快速响应,故采用了PD控制。其二是速度控制,该部分要求速度的稳定性,故采用了PI控制。其三是转向控制,该部分要求快速响应,也采用了PD控制。决定选用PID控制还是PI、PD控制取决于系统要求,由于P、I、D控制产生不同的控制效果,对几种控制方式做简单介绍:
P控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系,当仅有比例控制时系统输出存在稳态误差。积分控制的输出与输入误差信号的积分成正比关系。积分控制可以消除稳态误差,控制器中引入“积分项”,积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,要使系统进入无稳态情况,必须引入积分项。微分控制的输出与输入误差信号的微分(即误差的变化率)成正比关系。微分控制能预测误差变化的趋势,所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。实际应用中,P、I、D根据自身调节特点及系统特性,进行灵活组合,以达到最优控制,最常用的有PID、PD、PI控制。
PID控制方式选定之后就需要对参数进行整定,参数整定的方法很多,概括起来有两大类:一是理论计算整定法。它主要是依据系统的数学模型,经过理论计算确定控制器参数。这种方法所得到的计算数据未必可以直接用,还必须通过工程实际进行调整和修改。二是工程整定方法,它主要依赖工程经验,直接在控制系统的试验中进行,且方法简单、易于掌握,在工程实际中被广泛采用。
7-1、实验名称:平衡小车直立时处于静止状态
7-2、实验目的:通过实验,运用PID相关知识进行实践,掌握PID参数整定
7-3、实验过程:平衡小车直立由三个函数决定,直立控制函数、速度控制函数、转向控制函数共同影响。调试过程中先匹配参数至平衡小车前后同幅度摆动,之后屏蔽直立控制微分控制,逐渐增大比例控制,直到小车不能稳定摆动,记下此刻数据,将该值乘以0.8得出的值确定为比例控制参数,增加微分控制项,逐渐消除摆动。
int balance(float Angle,float Gyro) { float Bias; int balance; Bias=Angle-0; balance=90*Bias+Gyro*0.130; return balance; }
int velocity(int encoder_left,int encoder_right) { static int Velocity,Encoder_Least,Encoder,Movement; static long Encoder_Integral; if(1==Flag_Qian) Movement=-1500; else if(1==Flag_Hou) Movement=1500; else Movement=0; Encoder_Least =(Encoder_Left+Encoder_Right)-0; Encoder *= 0.8; Encoder += Encoder_Least*0.3; if(Turn_Off(Angle_Balance,Voltage)==0) { Encoder_Integral +=Encoder; Encoder_Integral=Encoder_Integral-Movement; } if(Encoder_Integral>360000) Encoder_Integral=360000; if(Encoder_Integral<-360000) Encoder_Integral=-360000; Velocity=Encoder*4+Encoder_Integral/140; if(Turn_Off(Angle_Balance,Voltage)==1) Encoder_Integral=0; return Velocity; }
视频地址:http://player.youku.com/player.php/sid/XMTM2ODM0NTM4OA==/v.swf
8-1、实验名称:超声波避障
8-2、实验目的:熟练掌握运用超声波测距模块
8-3、实验过程:
超声波测距是借助于超声脉冲回波渡越时间法来实现的。设超声波脉冲由传感器发出到接收所经历的时间为t,超声波在空气中的传播速度为c,则从传感器到目标物体的距离D可用下式求出:
D = ct /2
本次程序控制策略如下:控制口发一个10US 以上的高电平,就可以在接收口等待高电平输出.一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,根据上面的公式方可算出距离.如此不断的周期测,就可以达到你移动测量的值了。
if((TIM2CH4_CAPTURE_STA&0X80)==0)
{
if(tsr&0X01)
{
if(TIM2CH4_CAPTURE_STA&0X40){
if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F){
TIM2CH4_CAPTURE_STA|=0X80; TIM2CH4_CAPTURE_VAL=0XFFFF;
}else TIM2CH4_CAPTURE_STA++;
}
}
if(tsr&0x10)
{
if(TIM2CH4_CAPTURE_STA&0X40) {
TIM2CH4_CAPTURE_STA|=0X80; TIM2CH4_CAPTURE_VAL=TIM2->CCR4;
TIM2->CCER&=~(1<<13); }else {
TIM2CH4_CAPTURE_STA=0; TIM2CH4_CAPTURE_VAL=0; TIM2CH4_CAPTURE_STA|=0X40;
TIM2->CNT=0;
TIM2->CCER|=1<<13;
}
}
}
void Read_Distane(void)
{
PAout(2)=1;
delay_us(20);
PAout(2)=0;
if(TIM2CH4_CAPTURE_STA&0X80)
{
Distance=TIM2CH4_CAPTURE_STA&0X3F;
Distance*=65536;
Distance+=TIM2CH4_CAPTURE_VAL;
Distance=Distance*170/1000;
TIM2CH4_CAPTURE_STA=0;
}
}
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |