这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » xxJian智能车DIY进程贴

共16条 1/2 1 2 跳转至

xxJian智能车DIY进程贴

菜鸟
2015-05-21 14:22:37     打赏

索引  Contents



实验一、Visual Scope使用体验............................................第2楼

[开发进程]平衡小车开箱、组装、测试.................................................................................第4楼

实验二、串口通信.........................................................................................................................第6楼

[开发进程]制作蓝牙遥控器之阶段一.....................................................................................第7楼

[开发进程]制作蓝牙遥控器之阶段二.....................................................................................第8楼

[开发进程]实验三、通过Usart1显示小车角度、左右编码器、电池电压并分析.......第11楼

[开发进程]实验四、通过蓝牙通信显示传感器数据并分析.............................................第12楼

[开发进程]实验五、蓝牙调试PID参数................................................................................第13楼

[开发进程]实验六、绕圈圈与绕8字走................................................................................第17楼

[开发进程]实验七、超声波实验...........................................................................................第19楼

[开发进程]实验八、通过编码器采样数据得到前进距离试验.........................................第23楼

[开发进程]实验九、移动中避障自平衡小车实验................................................................第24楼

[开发进程]实验十、另类走8字...........................................................................................第31楼

我跟同事将平衡小车改为寻迹小车part1.............................................................................第34楼




关键词: 智能车     开发进程    

菜鸟
2015-05-21 15:07:58     打赏
2楼

实验一、Visual Scope使用体验

1、引言:

小车已经快到手了,趁着时机预热一下。VisualScope是一款示波器上位机软件,作用是将串口通信的数据按照一定的协议,进行4组数据的识别、在软件界面上显示波形。由于楼主以前并没有在实验平台上调试PI代码,今天突然想自己手头上并没有示波器,所以对这软件起了很大的兴趣。

2、软件介绍:

图1、VisualScope软件界面

VisualScope其实是一款非免费的软件,没破解前不能配置串口,只能是COM19600的默认配置。注册(破 解)后可以自由配置。软件本身支持四通道的数据。这是一款绿色软件,软件主体只有一个文件,其大小为2.15MBconfig.dat是配置文件(串口配置信息关闭程序后仍然保存,应该就是存在这文件中)。

图2、软件目录


3、实验过程:

本次实验所需:配置好Uart的单片机、UartUsb模块一块。安装了Uartusb驱动的PC机。

哼哼,图中MCU板子为新唐Nuvoton的Nutiny-EVB-M451,+ PL2302 USB转串。

3.1、单片机调试:

首先肯定是先配置好单片机的Uart,主要代码:

unsigned short CRC_CHECK(unsigned char *Buf, unsigned char CRC_CNT)
{
    unsigned short CRC_Temp;
    unsigned char i,j;
    CRC_Temp = 0xffff;

    for (i=0;i>1 ) ^ 0xa001;
            else
                CRC_Temp = CRC_Temp >> 1;
        }
    }
    return(CRC_Temp);
}
void VisualScope_Output(float data1 ,float data2 ,float data3 ,float data4)
{
    int temp[4] = {0};
    unsigned int temp1[4] = {0};
	uint8_t databuf[10] = {0};
	unsigned char i;
	unsigned short CRC16 = 0;

	temp[0] = (int)data1;
	temp[1] = (int)data2;
	temp[2] = (int)data3;
	temp[3] = (int)data4;

	temp1[0] = (unsigned int)temp[0] ;
	temp1[1] = (unsigned int)temp[1];
	temp1[2] = (unsigned int)temp[2];
	temp1[3] = (unsigned int)temp[3];
  
  for(i=0;i<4;i++) 
  {
     databuf[i*2] = (unsigned char)(temp1[i]%256); 
     databuf[i*2+1] = (unsigned char)(temp1[i]/256);
  } 
  CRC16 = CRC_CHECK(databuf,8); 
  databuf[8] = CRC16%256; databuf[9] = CRC16/256; 
  UART_Write(UART0,databuf,10); /*这行代码按照不同的单片机自行修改,作用是往UART0发送10个数据,数据存放到databuf*/ 
} 


以上两个函数C语言的适用于所有拥有uart的单片机的,移植时候只要修改UART_write这个函数就OK

另外在main函数的死循环之前,计算得到了4组数据用于软件评估。

for(i=0;i<2000;i++) 
{
  datach1 = 1000*sin((float)2*PI*i/2000); 
  datach2 = 800*sin((float)2*PI*i/1000); 
  datach3 = 600*sin((float)2*PI*i/500); 
  datach4 = i; 
  VisualScope_Output(datach1,datach2,datach3,datach4);
 } 
while(1); 


datach1顾名思义,通道1的数据。Ch1-3都是正弦波,ch4为斜坡函数。代码编好了。之后就是VisualScope软件配置了。

3.2VisualScope软件配置

首先是串口配置。

现在很多串口软件都可以自动识别可用的串口。如免费的软件Tera Term。(记得还是开源的。)


图3、观察对应的串口数


配置过程:

1 菜单Setup -> Communication Protocol -> CRC16

2 菜单Setup -> Communication Setup ,选择Comm No.,我实验时是COM11,Bard rate115200(跟单片机初始化的波特率一样)

3 RUN

如无意外,就可以看到下图所示的波形了。不过要知道,软件看起来是先在RAM中初始化了四组数据,数据有多大,软件界面波形就有多长。。。显示的新数据的波形都在波形最后面哦。



图4、实验结果


本人对用串口调试PID参数并不熟悉,不过体验了VisualScope显示波形的快乐,在没有示波器的情况下,如果单片机还存在ADC电路,就可以将电气量转换为数字量传输到上位机用该软件配合调试了。

其实,若有无线转串口模块,就可以在电脑上面调试,这是一个很好的选择。预计这个实验以后会做到!


菜鸟
2015-05-22 15:54:49     打赏
3楼

[开发进程]平衡小车开箱、组装、测试

我身在广州,平衡小车要从成都物流到广州。今天终于可以收货了。

参考了车友的开箱帖子,这边也从箱子开始拍照!因为之前看到车友的开箱帖子,直到了自己的货物有多大,在收快递的时候马上就找到自己的了。可是路滑+手滑,撑着伞的时候一不小心把箱子摔到地上了。。。

用刀子刮开了透明胶,看到了箱子里面空隙都用报纸塞好了。可见发货人的细心。

把东西摊开,好奇里面放的都是什么仔细看从左到右应该是电池和充电器、车主板、车轮、OLED、亚克力板、轮轴、下载器以及STLINKv2

拆开保护层的器件,虽然手机像素不高,可我还是挺喜欢看自己拍出来的效果。在组装之前打开《【EEPW专版】平衡小车使用说明(必看)》这个PDF文件,前车之鉴,不看着组装必定后悔当然楼主也很欣赏喜欢不看使用使用说明书直接组装的这种冒险精神!

以下是组装的过程:


1、首先是组装车轮

车轮也是直接将轮子插到轴上就可以了。

车轮组装完毕



2、装四根双通铜柱

使得轮轴上有四根小螺丝的面朝上,依次安装四根铜柱。这四根铜柱的作用主要就是稳定住电池,同时为电池提供空间。


3、安放电池


放电池时候折腾了一段时间。使用说明书上的电池为1100mAh,明显比发货的1300mAh小一点点。安装电池时候遇到的问题就是,如果带有文字的面朝上,会出现电池放不下的情况。


如果这样子安装,后果就是双通铜柱和固定主控板的铜柱之间的缝隙太大,组装不可完成。如下图。


所以安装电池时候一定要文字面(正面)朝下,如此一来,为电池而准备的空间就可以得到充分利用!如下图。

备注: 对比车友的电池,看来是因为我的电池有轻微差异,其实要文字面向上才是正确的...2015年05月22日晚修改


哎哟,最近接触电子多,总是喜欢用双手接触地面,使地面带走手上的多余电荷。这样就不怕静电损坏器件了……



期间用万用表测试了一下电池白色的接口是否带电,是的,为12.5V

同时安装的时候走了些弯路,以为主控板上白色的接头是供电用的,错!我真的醉了。。其实电池的接口就在左上方….如下图。

这图在taobao店的宝贝描述中有,特别鸣谢平衡小车之家。

我觉得这图放到资料内多好啊,可是我没找到只能在taobao店内找到。



4、安装主板

安装主板时候注意开关的朝向是接近电池的供电接口的噢。图中的手就是在掰双通铜柱,真的如同《【EEPW专版】平衡小车使用说明(必看)》所述,铜柱是对得不太准的。不过没关系,掰一下就好。



5、搞定OLED模块


我买的是套餐3,带有oled模块,排针自己焊接,很快完成!自己另外有一个超声波模块,顺利安装!



6、连接电机线

我有一条电机线不够长,把那带子剪断了,ok

图中是一条带子剪了另外一条很好!最后安装上了没有碰到地面,应该不影响行车安全!

注意接头红色线在右边,这我是对照着使用说明来接的。。


7、亚克力保护板


刀子细细的方便弄开保护层。随后用手撕开!

好了!



8、最后的电源线


电源线最后才安装会有点麻烦,电源线很硬,看起来可靠,可是第1-7步完成了根本插不进电源线,只能拆了螺丝插线!另外电池的白色的充电插头的线我用电池压着,就不影响行走了。


其他问题不大,组装过程很有乐趣,就是手机拍照声影响别人了。。。



因为图片太大,好像晒得太严重,于是普遍调整到不大于500。。。


开机测试 自平衡小车!


视频地址:http://player.youku.com/player.php/sid/XOTYxNTI5ODY4/v.swf

启动不够温柔,车子一下子把脸摔到地上。不过平衡的效果还是不错,一前一后移动的平衡。



这个视频中,录制的就是暴走的小车,

如果在正常运行中,用手将车子抬高,车轮就会突然转很快,简直失控了!


视频地址:http://player.youku.com/player.php/sid/XOTYxNTMxMzA4/v.swf



谢谢观看!


菜鸟
2015-05-22 21:32:50     打赏
4楼

实验二、串口通信


这次实验的目的是将平衡车刷成砖头……

其实是以开发板的角度,自学啦。我一开始就有念头自己建立工程来学习。慢就慢吧……

本实验目的以平衡车为实验平台,新建起一个只有串口通信的工程。

实验材料

硬件:平衡车标配,串口转USBSTlinkv2(可选),win7电脑

软件:MDK5STM32CubeMX

实验过程

首先接线,把硬件都准备好!

我的电脑跟平衡车有一段距离,普通杜邦线根本没有用!不怕,我有好长的信号线!



两个白色接线头都只有3根线,分别是两根信号线和一根GND,实验操作完成才发现硬件设计者真是用心良苦。我最怕的就是将VCC接错,现在VCC不必连接,串口和STLINK模块都由电脑USB供电,方便多了!

注意MCU主控板上,都带有丝印指示引脚,PA9连接TTLUSBRXPA10连接TTLUSBTX。至于STLINK,都已经标记好了并不需要另外翻电子说明书!


一红一绿。绿色不闪就正确了,stlink的硬件以及驱动都没有问题!



工程生成过程很简单,STM32CubeMX已经帮我完成了。

在用户自定义代码输入的地方,输入了几行代码,就可以实现芯片主动不断的发送数据。。。



  /* USER CODE BEGIN 3 */
	HAL_UART_Transmit_IT(&huart1, (uint8_t*)aTxBuffer, TXBUFFERSIZE);
	HAL_Delay(2000);
  }
  /* USER CODE END 3 */

 

  以上这种在 CODE BEGIN   到  END之内的地方就是可以输入代码的地方!其他地方的代码,都会收到CubeMX重新生成代码的影响。也就是如果在CubeMX中修改初始化配置,若点重新生成代码的话,在工程内只会保留 CODE BEGIN  到  END 之内的代码,其他地方的代码都会被刷掉~


实验结果:



我的mdk是5,之后用STM32CubeMX在没删除V5文件的情况下,在原目录生成了mdkv4的工程,不知道用v4版本的编译有没有问题啊...

myUsart1.zip


菜鸟
2015-05-23 20:21:53     打赏
5楼

[开发进程]制作蓝牙遥控器之阶段一

 

背景:近阶段对蓝牙通信比较有兴趣,借次机会(指平衡小车),网购了几块蓝牙模块,学习如何使用蓝牙模块进行无线通信。之前一直担心自己的手机无法配对小车上的蓝牙模块,虽然昨天已经做了实验证实了自己的手机可以与小车上的SPP-CA模块配对且通信成功,但还是顺着这个势头实践了一下蓝牙模块的使用。手机上的APP都很不错,例如HCAPP(共享出来了,见附件)还有平衡小车官方推荐的APP。可是暂时还是有转弯控制难的问题。

 

 

实验目的:学习蓝牙模块的使用

          学会使用AT指令

实验材料SPP-C蓝牙模块x1、万用板x1、蓝牙串口模块BC04-Bx1TTLUSB模块x1



制作蓝牙遥控器另外需要单片机、还有遥杆这部分打算在下一个阶段会做出来。

实验过程

按照用户手册的应用电路图,在万用板上焊接好

其中焊接过程省略

  

再来个合照

模块的引脚间距为1.5mm,上面的图中,模块都是通过1.5mm2.54mm来转接,用排针连接排母。

缺点是这种转接板都是每次只能接三个脚,假如接了P01 P02 P03这三个脚,P04就只能用剪断的电阻引脚或电线来接了。


完成以后,上电测试,下图的接线,220V通过5V电源、通过3.3V LDO给蓝牙模块供电。蓝牙模块的UART直接接TTLUSB


接好第一次记得用AT指令测试蓝牙模块是否正常运行!


平衡小车开机,测试蓝牙模块


串口显示已经与SPP-C配对成功!


另外也可以用电脑串口控制小车的前进后退或者转弯了..尝试了一下,由于代码关系,(也有可能是STM32C8T6的性能不够强,毕竟得运算速度和功耗要取舍),转弯难以控制,原地转就转得很好看。。。还有前进后退也是可以的。


菜鸟
2015-05-24 11:26:31     打赏
6楼

[开发进程]制作蓝牙遥控器之阶段二

上楼介绍了如何进行硬件的焊接,今天尝试用单片机读取摇杆的状态,并且用平衡小车所能识别的控制信号

第一部分、控制信号的识别

用手机连接蓝牙转串口工具(上楼提到的SPP-C)或者童鞋们也可以用电脑自带的蓝牙T-T,进行手机控制信号的接收。做实验可以知道,安卓手机上的Mini Balance App中,软件界面主要有白色背景、灰色大圆、蓝色小圆组成。灰色大圆主要用于参照用,用于观测小圆的位置。蓝色小圆是提示手指触摸的位置,以及将蓝色小圆发生变化后的位置第一时间发送到平衡小车中,需要注意的是此处的变化。即只有蓝色小圆的位置变化,才发信号;而不变化,不发信号。

那应该怎样读取摇杆的状态再将对应的数据发送到平衡小车中呢?

摇杆一般为平面控制的用XY两个自由度来描述摇杆所在位置。翻开摇杆的用户手册,可以知道其实其原理很简单。两个可调电位器并联,两头分别连到VCCGND。摇杆转动时候,电位器的有效数值也随着变化。

下图表明了手机APP和平衡小车已经定好的控制协议。若控制端(摇杆或者手机APP的蓝色小圆,移动到原点),应该通过蓝牙将0x00发送到小车上的SPP-C蓝牙模块中。如果控制端往x轴的正方向移动了一定距离,就发送0x03。还有一个控制信号,是急停信号,控制方法是在手机APP中,在灰色圆下方从左到右或从右到左滑动。不过这是在小车代码中实现识别,并不需要在控制端发送特别的信息。

X(平均值) y(平均值)

0x00 2048 2048

0x01 2048 0

0x02 2048*(1+sin(pi/4)) 2048*(1-sin(pi/4))

0x03 4096 2048

0x04 2048*(1+sin(pi/4)) 2048*(1+sin(pi/4))

0x05 2048 4096

0x06 2048*(1-sin(pi/4)) 2048*(1+sin(pi/4))

0x07 0 2048

0x08 2048*(1-sin(pi/4)) 2048*(1-sin(pi/4))

以上的理所当然的错误……

具体的数据是要测量出来的>>>>

X(平均值) y(平均值)

0x00 2048 2048

0x01 2048 0

0x02 4096 0

0x03 4096 2048

0x04 4096 4096

0x05 2048 4096

0x06 0 4096

0x07 0 2048

0x08 0 0

这个才是正确的。。。在代码中,由于将xy平面分成8个区间,只要实际值到了以上平均值的上下100之内,就算是逼近了。

以上过程的实现代码:


ControlStatusPrev = ControlStatus;
if((abs((int32_t)Result1.Result-2048)<100)&&(abs((int32_t)Result2.Result-2048)<100)) ControlStatus = 0x00; else if((abs((int32_t)Result1.Result-2048)<100)&&(abs((int32_t)Result2.Result-0)<100)) ControlStatus = 0x01; else if((abs((int32_t)Result1.Result-4096)<100)&&(abs((int32_t)Result2.Result-0)<100)) ControlStatus = 0x02; else if((abs((int32_t)Result1.Result-4096)<100)&&(abs((int32_t)Result2.Result-2048)<100)) ControlStatus = 0x03; else if((abs((int32_t)Result1.Result-4096)<100)&&(abs((int32_t)Result2.Result-4096)<100)) ControlStatus = 0x04; else if((abs((int32_t)Result1.Result-2048)<100)&&(abs((int32_t)Result2.Result-4096)<100)) ControlStatus = 0x05; else if((abs((int32_t)Result1.Result-0)<100)&&(abs((int32_t)Result2.Result-4096)<100)) ControlStatus = 0x06; else if((abs((int32_t)Result1.Result-0)<100)&&(abs((int32_t)Result2.Result-2048)<100)) ControlStatus = 0x07; else if((abs((int32_t)Result1.Result-0)<100)&&(abs((int32_t)Result2.Result-0)<100)) ControlStatus = 0x08; if(ControlStatusPrev!=ControlStatus) { UART001_WriteDataBytes(&UART001_Handle0, &ControlStatus, 1); }



第二部分、ADC采样与串口发送数据在Infineon XMC2Go板子中实现



该目标板子是英飞凌公司生产的,板载jlink仿真器,主MCUCortexM0内核,型号是XMC1100。板子是去年参加研讨会赠送的,原则上是非卖品。。。

其实还可以用M4做的,可是我觉得M0 比较适合做这个简单的控制器。

初始化了ADC 两个通道,进行Continuous Scan采样,初始化了Uart用来与蓝牙模块进行通信,另外是一个GPIO口,用来控制板载的LED灯用于工作指示!

工程在DAVE3上搭建,此软件是英飞凌转为其公司生产的单片机设计的编程环境,类似于TI 公司的CCS

MCU没有STM32系列的普及,因此而少人了解

我还是放上代码


图示控制器与小车的连接。。控制器的MCU板为用弯针排针接排母。


期间在做试验的时候,由于MCU接摇杆的GND杜邦线掉了,造成控制器一直发送0x04, 害我的小车一直转,要开手机APP才得以停止...随后把线连上,就行了。。还有之前说的转弯控制不好转,原因是自己在控制转弯没有突然松开手,才让小车 一直原地转,原来是自己的控制问题,不是小车的问题……


谢谢观看。如果有问题可以直接提出。

祝各位工作顺利 生活愉快!



BT_Controller_XMC2Go_v1_20150524.zip



菜鸟
2015-05-26 13:30:01     打赏
7楼

[开发进程]实验三、通过Usart1显示小车角度、左右编码器、电池电压并分析

第一次接触平衡车,对控制电机方面是小白。借着活动举办方分享的全套代码,熟悉一下通过传感器进行单片机采样的几个重要的代码,其中分别是通过MPU6050得到的MCU主板的角度、通过编码器采集得到的电机转动速度、通过电阻分压得到的电池电压。其中前三个参数在控制小车的平衡中必不可少,而采集电池电压的作用主要是为了实时检测电池电压、有助于避免电池过放电。

好了,这次的实验所需要的实验材料是:EEPW平衡小车标配x1PL2302模块x1,还有可以使用串口的win7电脑及其所需软件分别是串口调试助手&VisualScope

主要原理说明

首先对比一下,VisualScope.exe(下文简称“VS.exe”)和上位机波形显(请在win7下以管理员的身份运行,不支持WIN8)示.exe(下文简称“上.exe”)的不同,当然只是个人理解而已!

1、 数据的通道数VS.exe最多支持4通道、而上.exe最多支持10通道。

2、 数据可靠性与快速型:VS.exe支持Check SumCRC16进行对数据的检验,而上.exe暂时没发现。因此不校验的上.exe是比较快速的显示波形的。

3、 界面的友好程度VS.exe支持Stretch功能,可以用鼠标将波形分别沿X轴和Y轴的放大。同时支持Move功能,将波形随意上下左右的平移。上.exe笔者接触了半天,没找到移动波形的方法。

也是第三个的区别,同时还不排除个人的偏见,让我偏爱VisualScope这款软件。

实验步骤:

在前面我已经介绍过VisualScope的使用(见本贴2楼) ,这次就直接放出代码。注意我改的工程是:Mini Balance V2.5 标准版源码(集成DMP 卡尔曼滤波 互补滤波)

首先在main.c初始化Flag_Show定义的地方,将该参数初始化为0

u8 Flag_Stop=1,Flag_Show=0;


然后,在show.c源文件中,将第64行以后的代码删除掉,改为下面的代码:

//=-----------------------------------------------------------------------
//this is xxJian code 1		
    unsigned short CRC_CHECK(unsigned char *Buf, unsigned char CRC_CNT)
    {
        unsigned short CRC_Temp;
        unsigned char i,j;
        CRC_Temp = 0xffff;

        for (i=0;i<CRC_CNT; i++){      
            CRC_Temp ^= Buf[i];
            for (j=0;j<8;j++) {
                if (CRC_Temp & 0x01)
                    CRC_Temp = (CRC_Temp >>1 ) ^ 0xa001;
                else
                    CRC_Temp = CRC_Temp >> 1;
            }
        }
        return(CRC_Temp);
    }
void VisualScope_Output(float data1 ,float data2 ,float data3 ,float data4)
{
		int temp[4] = {0};
		unsigned int temp1[4] = {0};
		uint8_t databuf[10] = {0};
		unsigned char i;
		unsigned short CRC16 = 0;

		temp[0] = (int)data1;
		temp[1] = (int)data2;
		temp[2] = (int)data3;
		temp[3] = (int)data4;

		temp1[0] = (unsigned int)temp[0] ;
		temp1[1] = (unsigned int)temp[1];
		temp1[2] = (unsigned int)temp[2];
		temp1[3] = (unsigned int)temp[3];
  
		for(i=0;i<4;i++) 
		{ 
			databuf[i*2] = (unsigned char)(temp1[i]%256); 
			databuf[i*2+1] = (unsigned char)(temp1[i]/256); 
		} 
		CRC16 = CRC_CHECK(databuf,8); 
		databuf[8] = CRC16%256; 
		databuf[9] = CRC16/256; 		
		for( i = 0 ; i < 10; i++) 
		{ 
			while((USART1->SR&0X40)==0);  
			USART1->DR = databuf[i]; 
		}
}
		//end of xxJian code 1	
//------------------------------------------------------------------------


void DataScope(void)
{   
//=-----------------------------------------------------------------------
	//this is xxJian code 2
		float Volt;
		Volt = Voltage/100;
		//end of xxJian code 2
//-----------------------------------------------------------------------------	

	  if(++Count==1)
		{	
		OLED_Clear();  
		OLED_Display_Off();		
		}	
		
//=-----------------------------------------------------------------------
                //this is the official source code		
		/*
		DataScope_Get_Channel_Data( Angle_Balance, 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(0, 5 ); 
		DataScope_Get_Channel_Data(0 , 6 );
		DataScope_Get_Channel_Data(0, 7 );
		DataScope_Get_Channel_Data( 0, 8 ); 
		DataScope_Get_Channel_Data(0, 9 );  
		DataScope_Get_Channel_Data( 0 , 10);
		Send_Count = DataScope_Data_Generate(10);
		for( i = 0 ; i < Send_Count; i++) { while((USART1->SR&0X40)==0);  
		USART1->DR = DataScope_OutPut_Buffer[i]; 
		}
		*/
		//end of official source code 
//-----------------------------------------------------------------------------	

//=-----------------------------------------------------------------------
	//this is xxJian code 3
		VisualScope_Output(Angle_Balance,Encoder_Right,Encoder_Left,Volt);

		//end of xxJian code 3
//-----------------------------------------------------------------------------	
		delay_ms(50); //20HZ
}

 


值得注意的地方就是,int voltage这个参数,其实是实际的电压值乘以100。 也就是假如电池电压是12.50V,这个int voltage的值就是1250(不考虑误差)


经过在keil上编译,用串口下载的方法,将程序下载到STM32C8内。详细用串口下载的过程请看

STM32使用MCUISP下载程序教程http://forum.eepw.com.cn/thread/273193/1


实验结果

下载程序后,运行,要通过拨码开关将boot0置到on的位置。串口连接好,pl2302模块接到电脑上。打开VIsualScope将串口配置好,并点run.

然后就可以改变平衡小车的状态,观看通过传感器采集得到的数据的变化!





谢谢你的观看!如果有问题,可以留言,也可以QQ上联系!另外软件我已经放到群共享了。有需要请自行下载!


菜鸟
2015-05-26 14:36:48     打赏
8楼
[开发进程]实验四、通过蓝牙通信显示传感器数据并分析

实验三完成了传感器数据通过串口(USART1+PL2302模块)发送到电脑上,电脑使用上位机软件显示了数据的变化波形。本实验使用的是蓝牙通信将数据发送到电脑。

硬件需要:台式机电脑+蓝牙主模块(或者使用带有蓝牙2的手提电脑)。笔者使用的是台式机+前文的蓝牙控制器(单片机+蓝牙串口BC04-B)。在这个实验中,蓝牙控制器的功能只是接收到数据,并将数据通道串口发送到电脑上面。

软件代码:代码跟实验3的大致相同,唯一不同的地方就是show.c里面的USART1改为USART3。初始化在工程里面已经完成,将USART1改为USART3,说明使用的是连接蓝牙模块的USART3发送数据。

实验结果(意外)

在图中,就可以看到车子并不能边行走变将数据发送到蓝牙中。

图片中从左到右指示的是时间。图中的波形表示数据随着时间而发生变化。

波形表明,在Flag_stop =1 时候,车子是可以正常顺利的将数据通过蓝牙发送到电脑中,但是使用这工程代码,想在车子达到平衡的过程中通过蓝牙发送数据,就不是那么简单了,需要修改代码。

大概代码还需要进一步的优化



//----------------------------------------------------------------------------------------------------

//以下2015/5/26  15:05更新

//-----------------------------------------------------------------------------------------------------


//电机关闭后或者没有使用DMP时,开启上位机监控

这一行注释让我想到:这个故障会不会是滤波算法造成的。。。我没学这几个算法,总之先试试了


main.c改了一下滤波算法,以及在死循环内加上几行代码,让其在电机运行时候再进行蓝牙传输传感器数据。

u8 Way_Angle=2;

...

...

while(1)
{
...

//-------------------------------------------------------------------------

//这几行没有改动  begin

if(Flag_Stop==1||Way_Angle>1)
{
Temperature=Read_Temperature();
if(1==Flag_Show)        oled_show();
else                  DataScope();
}

//这几行没有改动  end


//-------------------------------------------------------------------------


if((Flag_Stop==0)&&(Way_Angle<30))
DataScope();
}

为小车更新一下代码看结果

上图是按键运行之前,OLED的第一行显示WAY-2 kalman (MPU6050滤波是用卡尔曼算法)。这是就没有使用USART3交换数据了  (绿色的万用板中,下面好多直插的IC的,那是单相逆变器的驱动电路,可以不管的


下图是按一下按键之后,小车自动保持平衡了。


下图是小车保持自平衡时候的数据

黄色蓝色都是编码器的数据。显示车轮是在较稳定的转动。

假如串口引出来的线碰到车轮了,车子的移动幅度就会减少。也可以试试将车放到草稿本上,更在不怎么移动了...


菜鸟
2015-05-27 16:07:00     打赏
9楼

[开发进程]实验五、蓝牙调试PID参数

实验背景:在经过对代码的初步学习以后,了解到影响小车直立是否成功的参数有(Mini Balance V2.5 标准版源码):

MiniBalance.c

49行的Bais=Angle+0; 这个0,是应该随着硬件变化而变化的(注释中已经说明了感谢工作人员的注释)。

50balance=35*Bias+Gyro*0.125; 350.125分别是直立PD控制的比例和微分参数。

79Velocity=Encoder*4+Encoder_Integral/140; 41/140分别是速度PI控制器的比例参数和积分参数。

124Turn=Turn_Bias_Integral*2+gyro/12; 21/12分别是转向PD控制器的比例参数和微分参数。

实验准备:首先必须调试好蓝牙通信,此时必须具备蓝牙通信APP或者其替代的上位机蓝牙通信工具。(实验中需要发送ascII码调节参数)。本实验使用安卓串口小助手app。适用的安卓手机是具有蓝牙3.0以及以下的,我的安卓版本是2.3.5,能正常运行。

安卓串口小助手下载链接:http://www.wavesen.com/downloadDis.asp?id=32

实验原理

在上面实验背景已经提到过,影响小车平衡效果的变量,看起来起码有7个。在实验过程中,我将直立PD控制器改为PID控制器,原因是:只有积分控制,具有使被控对象的输出等于给定值的作用。

因此我将背景中提到的代码都改了:

全局变量初始化:

float B_Kp=16.5,B_Ki=0.050,B_Kd=0.107,V_Kp=4,V_Ki= 0.0071,T_Kp=2,T_Kd=0.0833;

float AngleBias = -1.0;   



我将背景中提到的代码,改为:

Bias=Angle+AngleBias;

balance=B_Kp*Bias+Gyro*B_Kd + Balance_Integral*B_Ki

Velocity=Encoder*V_Kp+Encoder_Integral*V_Ki;

Turn=Turn_Bias_Integral*T_Kp+gyro*T_Kd;



仿照开源的代码中,通过接收多个数据来控制电机停止的方法,自己编写了接收’B’’p’’+’三个数来控制Balance子程序中比例参数的增加,依次类推,编写了多组指令来控制其他几个参数。

控制指令有以下:

指令 执行语句:

‘AB+’: AngleBias= AngleBias + 0.1;

‘AB-’: AngleBias= AngleBias - 0.1;

‘Bp+’: B_Kp= B_Kp+0.1;

‘Bp-’ : B_Kp= B_Kp-0.1;

‘Bi+’ :B_Ki=B_Ki+0.001;

‘Bi-’ : B_Ki=B_Ki-0.001;

‘Bd+’ : B_Kd=B_Kd+0.001;

‘Bd-’ : B_Kd=B_Kd-0.001;

‘Vp+’: V_Kp= V_Kp+0.1;

‘Vp-’ : V_Kp= V_Kp-0.1;

‘Vi+’ :V_Ki=V_Ki+0.001;

‘Vi-’ ::V_Ki=V_Ki-0.001;

‘Tp+’: B_Kp= B_Kp+0.1;

‘Tp-’ : B_Kp= B_Kp-0.1;

‘Td+’ : T_Kd=T_Kd+0.001;

‘Td-’ : T_Kd=T_Kd-0.001;

//上面的适合微调,比例参数都比积分、微分参数都大。因此每次只须调节0.1就够了。

积分、微分变量,每次调节的幅度是0.001

实验代码

首先说说,源代码好多警告都是说Statement unreachable的。

问题出在DataScope_DP.c这里,return x;后面还有break;警告就是在return 6;后的break;在任何时候都不会执行。

改为以下就没警告了。

//函数说明:生成 DataScopeV1.0 能正确识别的帧格式

//Channel_Number,需要发送的通道个数

//返回发送缓冲区数据个数

//返回0表示帧格式生成失败

unsigned char DataScope_Data_Generate(unsigned char Channel_Number)

{

       if ( (Channel_Number > 10) || (Channel_Number == 0) ) { return 0; }  //通道个数大于10或等于0,直接跳出,不执行函数

  else

  { 

        DataScope_OutPut_Buffer[0] = '$';  //帧头

             

        switch(Channel_Number)  

   {

               case 1:   DataScope_OutPut_Buffer[5]  =  5; return  6; //break;  

               case 2:   DataScope_OutPut_Buffer[9]  =  9; return 10; //break;

               case 3:   DataScope_OutPut_Buffer[13] = 13; return 14; //break;

               case 4:   DataScope_OutPut_Buffer[17] = 17; return 18; //break;

               case 5:   DataScope_OutPut_Buffer[21] = 21; return 22; //break;

               case 6:   DataScope_OutPut_Buffer[25] = 25; return 26; //break;

               case 7:   DataScope_OutPut_Buffer[29] = 29; return 30; //break;

               case 8:   DataScope_OutPut_Buffer[33] = 33; return 34; //break;

               case 9:   DataScope_OutPut_Buffer[37] = 37; return 38; //break;

     case 10:  DataScope_OutPut_Buffer[41] = 41; return 42; //break;

               default: break;

   }      

  }

       return 0;

}

 



另外蓝牙调试PID代码:

//Usart3.c我改为以下内容:

#include "main.h"
#include "usart3.h"
#include "MiniBalance.h"
/**************************************************************************
作者:平衡小车之家 
淘宝店铺:http://shop114407458.taobao.com/
**************************************************************************/
 u8 mode_data[8];
 u8 six_data_1[4]={6,5,4,0};
 u8 six_data_2[4]={4,5,6,0};

 //xxJian code 1 begins
 u8 TuningPIDdata[5];
 //xxJian code 1 ends
 
void uart3_init(u32 pclk2,u32 bound)
{  	 
	float temp;
	u16 mantissa;
	u16 fraction;	   
	temp=(float)(pclk2*1000000/2)/(bound*16);//得到USARTDIV
	mantissa=temp;				 //得到整数部分
	fraction=(temp-mantissa)*16; //得到小数部分	 
  mantissa<<=4;
	mantissa+=fraction; 
	//AFIO->MAPR &= ~AFIO_MAPR_USART1_REMAP;
	RCC->APB2ENR|=1<<3;   //使能PORTA口时钟  
	RCC->APB1ENR|=1<<18;  //使能串口时钟 
	GPIOB->CRH&=0XFFFF00FF; 
	GPIOB->CRH|=0X00008B00;//IO状态设置
	GPIOB->ODR|=1<<10;	  
	RCC->APB1RSTR|=1<<18;   //复位串口1
	RCC->APB1RSTR&=~(1<<18);//停止复位	   	   
	//波特率设置
 	USART3->BRR=mantissa; // 波特率设置	 
	USART3->CR1|=0X200C;  //1位停止,无校验位.
	//使能接收中断
	USART3->CR1|=1<<8;    //PE中断使能
	USART3->CR1|=1<<5;    //接收缓冲区非空中断使能	    	
	MY_NVIC_Init(1,3,USART3_IRQChannel,2);//组2,最低优先级 
}

/**************************************************************************
函数功能:串口3接收中断
入口参数:无
返回  值:无
作    者:平衡小车之家
**************************************************************************/
void USART3_IRQHandler(void)
{	
	if(USART3->SR&(1<<5))//接收到数据
	{	  
	  static	int uart_receive=0;//蓝牙接收相关变量
		uart_receive=USART3->DR; 
		if(uart_receive<10)    mode_data[0]=uart_receive;

			if((mode_data[0]==six_data_2[0]
			&&mode_data[1]==six_data_2[1]
			&&mode_data[2]==six_data_2[2]
			&&mode_data[3]==six_data_2[3])
			||(mode_data[0]==six_data_1[0]
			&&mode_data[1]==six_data_1[1]
			&&mode_data[2]==six_data_1[2]
			&&mode_data[3]==six_data_1[3]))
		{	
			Flag_Stop=!Flag_Stop;
			mode_data[0]=0;	mode_data[1]=0;	mode_data[2]=0;	mode_data[3]=0;
		}
		if(uart_receive==0x00)	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////刹车
		if(uart_receive==0x01)	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////前
		if(uart_receive==0x05)	Flag_Qian=0,Flag_Hou=1,Flag_Left=0,Flag_Right=0;//////////////后
		else if(uart_receive==0x02||uart_receive==0x03||uart_receive==0x04)	
													Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;
		else if(uart_receive==0x06||uart_receive==0x07||uart_receive==0x08)	
													Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;

		mode_data[7]=mode_data[6];
		mode_data[6]=mode_data[5];
		mode_data[5]=mode_data[4];
		mode_data[4]=mode_data[3];
		mode_data[3]=mode_data[2];
		mode_data[2]=mode_data[1];
		mode_data[1]=mode_data[0];
		
		
		//xxJian code 2 begins
		
		if(uart_receive>0x20)    TuningPIDdata[0]=uart_receive;
		
		
		//-------------------------------------------------------------
		//Angle Bias
		//update Angle Bias
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='B'
			&&TuningPIDdata[2]=='A')
		{	
			AngleBias = AngleBias + 0.1;	
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'A';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'n';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'g';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'l';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'e';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'i';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'a';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 's';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			if(AngleBias<0)	
			{				
				while((USART3->SR&0X40)==0);  
				USART3->DR = '-';
			}
			while((USART3->SR&0X40)==0);	
			if(AngleBias<0)	
				USART3->DR = (int32_t)-AngleBias%10+'0';
			else
				USART3->DR = (int32_t)AngleBias%10+'0';				
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  			
			if(AngleBias<0)			
				USART3->DR = (uint32_t)(-AngleBias*10)%10+'0';
			else
				USART3->DR = (uint32_t)(AngleBias*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';			
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='B'
			&&TuningPIDdata[2]=='A')
		{	
			AngleBias = AngleBias - 0.1;	
		while((USART3->SR&0X40)==0);  
				USART3->DR = 'A';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'n';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'g';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'l';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'e';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'i';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'a';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 's';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			if(AngleBias<0)	
			{				
				while((USART3->SR&0X40)==0);  
				USART3->DR = '-';
			}
			while((USART3->SR&0X40)==0);	
			if(AngleBias<0)	
				USART3->DR = (int32_t)-AngleBias%10+'0';
			else
				USART3->DR = (int32_t)AngleBias%10+'0';				
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  			
			if(AngleBias<0)			
				USART3->DR = (uint32_t)(-AngleBias*10)%10+'0';
			else
				USART3->DR = (uint32_t)(AngleBias*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';	
		}
		//-------------------------------------------------------------
		//Balance PID
		//update Balance Kp		
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='p'
			&&TuningPIDdata[2]=='B')
		{	
			B_Kp = B_Kp + 0.1;			
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'p';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = B_Kp/10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (int32_t)B_Kp%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kp*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='p'
			&&TuningPIDdata[2]=='B')
		{	
			B_Kp = B_Kp - 0.1;		
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'p';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = B_Kp/10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (int32_t)B_Kp%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kp*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';			
		}
		//update Balance Ki
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='i'
			&&TuningPIDdata[2]=='B')
		{	
			B_Ki = B_Ki + 0.001;			
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'i';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Ki*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Ki*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Ki*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';	
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='i'
			&&TuningPIDdata[2]=='B')
		{	
			B_Ki = B_Ki - 0.001;		
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'i';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Ki*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Ki*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Ki*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';				
		}
		//update Balance Kd
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='d'
			&&TuningPIDdata[2]=='B')
		{	
			B_Kd = B_Kd + 0.001;			
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'd';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kd*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kd*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kd*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';	
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='d'
			&&TuningPIDdata[2]=='B')
		{	
			B_Kd = B_Kd - 0.001;		
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'B';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'd';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kd*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kd*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(B_Kd*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';				
		}
		//-------------------------------------------------------------
		//Velocity PID
		//update Velocity Kp
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='p'
			&&TuningPIDdata[2]=='V')
		{	
			V_Kp = V_Kp + 0.1;	
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'V';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'p';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = V_Kp/10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (int32_t)V_Kp%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Kp*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';			
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='p'
			&&TuningPIDdata[2]=='V')
		{	
			V_Kp = V_Kp - 0.1;			
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'V';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'p';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = V_Kp/10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (int32_t)V_Kp%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Kp*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';	
		}
		//update Velocity Ki
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='i'
			&&TuningPIDdata[2]=='V')
		{	
			V_Ki = V_Ki + 0.001;			
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'V';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'i';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Ki*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Ki*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Ki*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';			
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='i'
			&&TuningPIDdata[2]=='V')
		{	
			V_Ki = V_Ki - 0.001;				
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'V';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'i';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Ki*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Ki*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(V_Ki*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';	
			
		}
		//-------------------------------------------------------------
		//Turn PID
		//update Turn Kp
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='p'
			&&TuningPIDdata[2]=='T')
		{	
			T_Kp = T_Kp + 0.1;			
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'T';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'p';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = T_Kp/10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (int32_t)T_Kp%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kp*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';	
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='p'
			&&TuningPIDdata[2]=='T')
		{	
			T_Kp = T_Kp - 0.1;			
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'T';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'p';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = T_Kp/10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (int32_t)T_Kp%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kp*10)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';			
		}
		//update Turn Kd
		if(TuningPIDdata[0]=='+'
			&&TuningPIDdata[1]=='d'
			&&TuningPIDdata[2]=='T')
		{	
			T_Kd = T_Kd + 0.001;				
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'T';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'd';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kd*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kd*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kd*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';	
			
		}
		if(TuningPIDdata[0]=='-'
			&&TuningPIDdata[1]=='d'
			&&TuningPIDdata[2]=='T')
		{	
			T_Kd = T_Kd - 0.001;				
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'T';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '_';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'K';
			while((USART3->SR&0X40)==0);  
				USART3->DR = 'd';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '=';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '.';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kd*10)+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kd*100)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = (uint32_t)(T_Kd*1000)%10+'0';
			while((USART3->SR&0X40)==0);  
				USART3->DR = '\n';				
		}
		
		TuningPIDdata[4]=TuningPIDdata[3];
		TuningPIDdata[3]=TuningPIDdata[2];
		TuningPIDdata[2]=TuningPIDdata[1];
		TuningPIDdata[1]=TuningPIDdata[0];
		//xxJian code 2 ends
	}  		
		
} 

 



实验结果

见视频

MniBalance V2.5 标准版源码互补滤波版本,这个例程下载完以后,小车前后平衡需要8cm


视频地址:http://player.youku.com/player.php/sid/XOTY1NTY3NzQ0/v.swf

在经过蓝牙调试PID实验之后,小车平衡前后的位置只有3cm左右!


视频地址:http://player.youku.com/player.php/sid/XOTY1NTY2NzM2/v.swf




实验过程中,明白了


Balance_Pwm =balance(Angle_Balance,Gyro_Balance);        //===平衡PID控制
Velocity_Pwm=velocity(Encoder_Left,Encoder_Right);       //===速度环PID控制
Turn_Pwm    =turn(Encoder_Left,Encoder_Right,Gyro_Turn); //===转向环PID控制

这三行代码在小车平衡中是缺一不可的。

注释掉速度环,小车两个轮子的速度就没收到控制了。。也就是速度失控

注释掉转向环,小车两个轮子的速度就不一样了。也就是转弯失控。


菜鸟
2015-06-01 19:07:56     打赏
10楼

[开发进程]实验六、绕圈圈与绕8字走

本实验的目让小车苯苯的一直绕圈子走

实验背景:小车源代码中,已经实现了小车绕原地转弯、前进以及后退。绕原地转弯就是一个轮子转动速率很少,另外一个转动速率相对大,就能实现绕原地转弯的效果。假如自己另外设置代码,使得小车前进的同时,又转弯,就可以实现简单的例如绕圈圈在走这样的效果。当然也感谢小车的开源代码!没有代码的开源,本试验也不能完成。

实验原理以及相关代码

智能小车相关程序运行过程:

各部分模块初始化->5ms进入一次中断计算相关代码让车子运行(包括前进、后退、左转、右转)[通过配置定时器3实现]

定时器3中断子程序执行代码主中跟本实验有关的就是:

int turn(int encoder_left,int encoder_right,float gyro);//转向控制


以及

int velocity(int encoder_left,int encoder_right);




个人认为,其中MiniBalance.c的第120行代码中,Turn_Amplitude明显就是转向的速度。另外velocity子程序中第7274行代码中的Movement就是前进后退的速度。

如果想要走8字,前进速度以及转弯速度必须要控制好。两个速度影响着绕的8字的形状。

相关代码:

注意:本代码在Mini Balance V2.5 标准版源码的基础上修改。

修改1

Main.c第六行:

u8 Way_Angle=2;




修改原因:=1的话,电机运行以后,OLED就不会刷新了,我不想,所以我喜欢改为2

修改2

MiniBalance.c中,第21行开始到TIM1_UP_TIM16_IRQHandler函数结束,改为以下内容。

int FiveMsCount=0;

void TIM1_UP_TIM16_IRQHandler(void)  
{    
	if(TIM1->SR&0X0001)//5ms定时中断
	{   
			if(FiveMsCount<500)
			{
				Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=1;
			}
			else if(FiveMsCount==500)
			{
				Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=0;
			}	
			else if(FiveMsCount<1000)
			{
				Flag_Qian=1,Flag_Hou=0,Flag_Left=1,Flag_Right=0;
			}
			else if(FiveMsCount>=1000)
			{
				FiveMsCount=0;
				Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=0;
			}
			FiveMsCount++;
			
		  TIM1->SR&=~(1<<0);                                       //===清除定时器1中断标志位		 
			readEncoder();                                           //===读取编码器的值
  		Led_Flash(400);                                          //===LED闪烁;	
  		Get_battery_volt();                                      //===获取电池电压	          
			key(100);                                                //===扫描按键状态
		  Get_Angle(Way_Angle);                                    //===更新姿态	
 			Balance_Pwm =balance(Angle_Balance,Gyro_Balance);        //===平衡PID控制	
 			Velocity_Pwm=velocity(Encoder_Left,Encoder_Right);       //===速度环PID控制
 	    Turn_Pwm    =turn(Encoder_Left,Encoder_Right,Gyro_Turn); //===转向环PID控制     
 		  Moto1=Balance_Pwm+Velocity_Pwm-Turn_Pwm;                 //===计算左轮电机最终PWM
 	  	Moto2=Balance_Pwm+Velocity_Pwm+Turn_Pwm;                 //===计算右轮电机最终PWM
   		Xianfu_Pwm();                                            //===PWM限幅
      if(Turn_Off(Angle_Balance,Voltage)==0)                   //===如果不存在异常
 			Set_Pwm(Moto1,Moto2);                                    //===赋值给PWM寄存器    		
	}       
} 

 


原理:中断程序每5ms执行一次,这里就按照500*5ms的时间改变一次转弯的方向,这里500*5ms差不多绕着0字转了刚刚好的一圈。


假如需要绕着圈圈走,那就直接使用

Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=1;

 

而不必往另外一个方向绕圈圈了。


修改3

MiniBalance.c中,在int turn(int encoder_left,int encoder_right,float gyro);函数内的

Turn_Amplitude=1500/Way_Angle+800;




改为:

Turn_Amplitude=300/Way_Angle;




减慢转弯速度。主要是我不喜欢小车走太快。。。

代码修改就这么简单。。


修改4:另外也可以通过手机来控制实现小车绕圈圈走,方法:

将Usart3.c内Usart3中断子程序中的内容部分(下面代码已注释掉的部分),改为下面没有注释的代码:


	
		/*original
		if(uart_receive==0x00)	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////刹车
		if(uart_receive==0x01)	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////前
		
		if(uart_receive==0x05)	Flag_Qian=0,Flag_Hou=1,Flag_Left=0,Flag_Right=0;//////////////后
		else if(uart_receive==0x02||uart_receive==0x03||uart_receive==0x04)	
													Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;
		else if(uart_receive==0x06||uart_receive==0x07||uart_receive==0x08)	
													Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;
		*/
		if(uart_receive==0x00)	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////刹车
		if(uart_receive==0x01)	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////前
		else if(uart_receive==0x02)	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=1;//////////////前转右
		else if(uart_receive==0x03)	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;//////////////原地转右
		else if(uart_receive==0x04)	Flag_Qian=0,Flag_Hou=1,Flag_Left=0,Flag_Right=1;//////////////后转右
		else if(uart_receive==0x05)	Flag_Qian=0,Flag_Hou=1,Flag_Left=0,Flag_Right=0;//////////////后
		else if(uart_receive==0x06)	Flag_Qian=0,Flag_Hou=1,Flag_Left=1,Flag_Right=0;//////////////后转左
		else if(uart_receive==0x07)	Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;//////////////原地转左
		else if(uart_receive==0x08)	Flag_Qian=1,Flag_Hou=0,Flag_Left=1,Flag_Right=0;//////////////前转左
		




视频地址:http://player.youku.com/player.php/sid/XMTI1MjQ4Mzc5Mg==/v.swf

共16条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]