查看: 19327
回复: 25
开源作品参赛《进化传说 -- 上古蜥蜴》
风过琴弦
28
主题
169
回复
发表于2017-06-17 18:01:28 | 显示全部楼层
1# 电梯直达

【报名阶段需要填写的内容】


1. 参赛者姓名(必填项):

王锦迪

2. 单位或学校名称(必填项):

南京亨乾电子科技有限公司

3. 当前职务或职称(必填项):

工程师

4. 参赛作品的名字(必填项):

进化传说 -- 上古蜥蜴

5. 简要陈述您的idea和作品(必填项):

1)总体结构为履带底盘,电机及驱动电路,摄像头等。

2)控制结构包括树莓派运行上层软件SPI,stm32底层控制,另外包含电机驱动、供电控制等电路。STM32与树莓派通过SPI连接用于下传命令、上传状态。

3)软件包括速度控制、图像处理等
4)主要功能:

目前实现的内容包括:履带底盘的机械结构部分的设计及实物3D打印、组装调试;电机及驱动电路的调试;STM32 通过PWM调节电机转速;PID算法的调试;树莓派环境的搭建,包括SPI通讯测试、程序控制摄像头的测试、机器学习框架Tensorflow的安装;目前可以在树莓派上用Python程序通过SPI通讯给STM32发送命令,控制履带底盘的转速。

6. 拟用到的立创商城在售物料(必填项):

树莓派及摄像头,stm32芯片,电机驱动相关芯片,外围杂项元器件

7. 拟用到的非立创商城物料或其它补充(必填项):

电机、轴承、弹簧、螺丝等。stm32开发板。本项目结构件大量采用3D打印,使用自购3D打印机。


【作品正式发表(报名成功后进入设计阶段)需要填写的内容】


第一章 楔子


    不知你是否和我一样,在生命的某个阶段,如此强烈地希望自己的生活中会有一段插曲是变形金刚,或者是终结者。当我们终于能够吃饱穿暖、事业稳定,你会不会也像我一样,有时会无意中记起,小时候曾经在旧物摊上寻觅钟表的齿轮,希望能拼成一个玩具小车。

    蜥蜴项目的初始目标就是延续小时候的梦想,不同的是,现在我们有了更强的能力。凭借专业领域的长久学习和实践经验,将能够实现一个更强大、更灵活的大玩具。更诱人的是,凭借我们的学习能力,蜥蜴将会不断地改进,越来越强大,如同进化的传说。

    蜥蜴项目将作为一个开源项目长期存在,原型版完成之后将针对各环节进行优化以及增加新功能。电路图、PCB、sldprt、stl文档、单片机和树莓派的源代码都会保持公开和更新。


 


    可以先看一下视频,https://v.qq.com/x/page/f0540dnaye1.html

    前进的命令由PC机通过WiFi连接树莓派发出,树莓派的程序随后通过SPI通讯给STM32单片机发出命令,STM32采用PID算法驱动履带电机。目前小车重心在后面,后轮遇到障碍时,动力不足以越过,PID算法自动调整驱动功率,越过障碍。


第二章  原始生物的运动器官

    蜥蜴算是比较大型的项目,从最开始就计划按不同部分分帖讨论。第一帖在这里: http://club.szlcsc.com/article/details_7855_1.html

    其中有详细的设计思考过程,因为计划是个长期项目,设计文件也是随进度更新,都在帖子里。


部分外购零部件:

 


DNA片段 -- 机械结构部分设计图:

 


蜥蜴的脚趾? -- 零部件组装:

 


这应该算是完整的四肢了吧 -- 底盘及电机负重图 :


    到这一部分为止的演示视频,用外接12V直接接电机:https://v.qq.com/x/page/s0540u1rlfv.html

    这个视频直观演示底盘悬挂结构的负重、缓冲、动态分担负载的性能,比起网上动辄上千元还不带缓冲结构的底盘,也算不枉我花费大量时间从头学习研究设计一直到把它做出来。

 

第三章 从末梢神经到脊髓 -- 电机测速、调速、PWM驱动、PID驱动

 

    想想蜥蜴的神经系统还挺专业,因为它分神经组织和神经信号两部分。其实电路的硬件就相当于生物的神经组织,而神经信号当然就是相应的软件和电路信号了。

在蜥蜴项目的最初规划阶段曾经明确过一个原则:尽量避免重新造轮子。按这一原则,在使用单片机的时候我毫不犹豫地选择了开发板--压在箱子底、放在阁楼上、落满灰尘,但是仍然好用的野火STM32开发板。


    电路硬件其实很简单,单片机本身能用到的几乎就是个最小系统,电机测速的霍尔传感器可以用3.3V电源,这样信号也是3.3V,可以直接接单片机。电机驱动电路L9110也可以使用3.3V输入而给电机供应12V的电压。这样简单的数字信号,直接用杜邦线连接就可以了。

 


    电机需要12V电源,采用TPS60188升压。电源电路对抗干扰要求高,况且60188的封装也没法手工焊,在立创做了样板

 

    蜥蜴作为一个“动物”,肯定要用个移动电源,TP5602是不错的选择,3A充放电,各种保护。。。广告时间到!购买链接 http://www.szlcsc.com/product/details_81512.html

    但是在调试过程中,电池电量耗费很快,充电则太耽误时间,所以调试还是用AC-DC的电源模块供电,谁让咱就是做这个的呢,从桌子底下翻出来两个就好了。

 

    程序没有太多可说的,大部分是相应外设的初始化及控制、读取

    把所有函数折叠一下,便于看个总貌:

 

    直接对电机作用的是PWM信号。PID控制算法也需要将PWM占空比作为直接的被控对象,因此PWM是电机驱动的基础,首先完成的是PWM的软硬件调试:

    需要改变某个通道的占空比时,只需要修改一个寄存器TIM2->CCRx就可以了。

// TIM2作为pwm输出,4个通道用于驱动两个履带电机正反转。引脚为23、24、25、26,即PA0、1、2、3,
void PWM_User_Init(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
	u16 init_CCR1_Val = 0;   // 占空比 = (此变量/ (TIM_Period+1))* 100%,TIM_Period这里设为9999

	// ....................... 引脚功能初始化 ..................     
  	/*GPIOA Configuration: TIM2 channel 1 and 2 as alternate function push-pull */
  	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		    // 复用推挽输出
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  	GPIO_Init(GPIOA, &GPIO_InitStructure);

	//.......................TIM2 Time base..............................
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;	//设置时钟分频系数:不分频
	TIM_TimeBaseStructure.TIM_Prescaler = 36;	    			//设置预分频,分频后为 2MHz
	TIM_TimeBaseStructure.TIM_Period = 9999;      				//当定时器从0计数到9999,即为10000次,为一个定时周期,即频率200Hz
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
	
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

	//........................... 各通道 .................................
	// 通道1
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	    //配置为PWM模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	
	TIM_OCInitStructure.TIM_Pulse = init_CCR1_Val;	   		//设置跳变值,当计数器计数到这个值时,电平发生跳变
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //当定时器计数值小于CCR1_Val时为高电平
	
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
	
	// 通道2
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);	  //使能通道2
	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
	
	// 通道3
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);	 //使能通道3
	TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
	
	// 通道4
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);	//使能通道4
	TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);

	//................................................................
	TIM_ARRPreloadConfig(TIM2, ENABLE);			// 使能TIM2重载寄存器ARR
	TIM_Cmd(TIM2, ENABLE);                   	//使能定时器2
}    


PID控制

    PWM是最简单的开环调速,仔细想想还不如自然界的蜥蜴,蜥蜴的行为毕竟还是闭环反馈的。一般来说,闭环控制总是必不可少的。闭环控制中,PID属于用得比较多也比较简单的了,当然性能也就是差强人意。不过好在蜥蜴项目的重点不是放在这方面,说得过去就行了。

    在我的第二贴里, http://club.szlcsc.com/article/details_7856_1_8.html#floor_8 这里对PID算法做了简要的解说,

自我感觉还是很容易懂的,不熟悉的朋友可以去看看。应该加个醒目的标题什么的,最近两天时间太紧了,当时好长的文本,写完就忽略了标题的问题,要改的时候已经晚了不能改了。看起来好像是一大堆废话一样。。。

    PID的核心算法其实很简单,就这么一点代码。在上面提到那个帖子里,有其他辅助的内容,包括结构的定义、具体参数值、定时计算的机制等

	err1 = Mot1PIDData.HallSetValue - Mot1HallTotal;	// 设定值 减 霍尔传感器在上次刷新周期内(100mS)的计数值 记为 err
	Mot1PIDData.LastErr = Mot1PIDData.CurErr;			// 用当前误差替代上次误差
	Mot1PIDData.CurErr = err1;							// 更新当前误差
		Mot1PIDData.SigmaErrBefore = Mot1PIDData.SigmaErrBefore * 3 / 4 + err1;	  //对误差历史逐次衰减
	
	u16deltaPulse = Mot1PIDData.KP * err1 +				// 需要修正的值 等于 P、I、D 三项分别乘系数之后求和。 				即 KP * err +
		Mot1PIDData.KD * (Mot1PIDData.CurErr - Mot1PIDData.LastErr) +	// ( 当前误差 减 上次误差 ) / 计数周期 近似为微分	KD * DeltaErr +
		Mot1PIDData.KI * Mot1PIDData.SigmaErrBefore;					// 近似历史积分(改进型)							KI * SigmaErr

	Mot1PIDData.DeltaPWM = u16deltaPulse / HALL_CNT_MAX;	// 计算出来的脉冲修正值 / 可能的最大值 * 100 = PWM的0-99范围; 三个K值是乘以100的,此处要除以100,所以-
	if(Mot1PIDData.Clockwise == 1)	// 如果电机1当前正转
		ch = 1;
	else 
		ch = 2;

	i16t = Mot1PIDData.CurrentPWM;
	i16t += Mot1PIDData.DeltaPWM;
	if(i16t >= 100)
		i16t = 99;
	if(i16t < 0)
		i16t = 0;
	PWM_SetMotorSpeed(ch,i16t );

PID运行演示视频

https://v.qq.com/x/page/x0540pae5xw.html


    视频中,PID的参数采用楼上图片中的参数,可以看到明显的PID调整的特点。两次手握住履带后,电机受到阻力立刻减速,然后由于PID算法的作用,PWM占空比增加,履带又顽强地开始加速到目标速度,此时占空比很大,履带底盘在泡沫的架子上咔哒咔哒乱动,尤其第二次更明显。

    由于占空比很大,突然松手履带就会转得很快,此时PID调节再次起作用,速度迅速降低,然而存在超调现象,也就是速度降低到预设速度之后并没有停止下降,而是继续减慢。然后又在算法调整下慢慢逼近设定值,这就是超调现象。超调的成因,固然是PID参数(也就是Kp、Ki、Kd)需要精细调整,但是,PID算法很难在大范围内保持良好的性能,例如在负载变化很大的情况下兼顾调节速度与超调量。如果需要更好的性能,需要更“高端”的控制理论



SPI通讯

    树莓派只有一个串口,分配给了蓝牙。虽然也可以用作串口,但是那样以后就注定用不成蓝牙了。和单片机通讯,SPI也是可选项之一。

    SPI的一个特点就是他是同步通信。这意味着需要一个时钟信号来为双方提供同步,而这又意味着,只有主机主动给出时钟信号,从机才能发送数据;主机的时钟信号一停,丛机的发送过程就中断。尽管主机的时钟信号一般不会突然停止,但是从机收到主机数据后需要时间来处理,很难保证立刻就开始发送应答数据。所以在SPI通讯中,从机最好是预先准备好数据,而主机发送完数据后,要继续发送无意义的字符以便保持时钟信号不会停止,这样才能收到从机的应答。

    对于单片机来说,SPI有不少参数要指定。通讯双方的参数要保持一致。

    下面这段程序只给出了STM32的SPI参数配置,没有包括引脚分配、中断设置等。

  ///////////////////////// SPI参数配置 ////////////////////////////////
  // W25X16: data input on the DIO pin is sampled on the rising edge of the CLK. 
  // Data on the DO and DIO pins are clocked out on the falling edge of CLK.
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; //SPI_Mode_Master;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(SPI1, &SPI_InitStructure);

  /* Enable SPI1  */
  SPI_Cmd(SPI1, ENABLE);


第四章 原始的眼睛和大脑 -- 树莓派


树莓派软硬件简单介绍

    树莓派是国外的一款“开源”的处理模块(带引号是他的主控芯片是专供的,你自己没办法做出来),主要特性包括:主频1.2G,内存1G,带WiFi,四个USB口可外接键盘鼠标,带HDMI口可外接显示器,一堆引出的IO脚,可以运行Linux等图形操作系统。

    有人说树莓派比起同样性能的其他模块价格贵,但是它同样有一个压倒性的优势,就是广泛流行带来的大量的资料和社区探讨。基本上,需要用到的东西在网上都很容易找到。

出于科普的目的,树莓派的官方系统(基于Debian)中集成了很多编程工具,对Python也有编译环境。我们采用Python作为开发语言。说实话,个人觉得Python是个大坑,到处充斥着奇技淫巧,一个数组能变化出成千上万的操作方法,初看起来很容易看懂,但是如果要精确写出来,尤其是要想看到别人的程序能精确知道是什么结果,好像比C语言要记的东西多多了。奈何,现在主流的各种工具都提供对Python的支持,像机器学习框架Tensorflow,就明确说有些功能在C语言的库里没有实现。我们也就只好跳进这个大坑了。



树莓派的摄像头

    有一个库是专门给树莓派的摄像头做的,叫做picamera(这个库在电脑的Linux系统下是运行不了的)

    使用picamera,可以轻易地控制树莓派的摄像头:

import time as tm
import picamera

#摄像头初始化
cam = picamera.PiCamera()
cam.resolution = (128,112)

#下面四行用于验证摄像头正常工作。preview可以在命令行运行,但是预览画面很难关掉,最好在程序中用stop_preview
cam.start_preview()    #开始播放预览画面
tm.sleep(2)
cam.stop_preview()    #停止预览
cam.capture('ff.jpg')    #拍照并保存到文件,记得说“茄子”


    更强大的是,可以直接把图像数据保存到numpy数组里(我用的是Python2.7,需要提供一个一维数组,Python3对应的库则用三维数组)

import numpy as np

RawImageData = np.empty((128*112*3),dtype=np.uint8)   #准备数组
cam.capture(RawImageData,'rgb')             #采集图像

机器学习框架 Tensorflow

    Tensorflow是谷歌的开源机器学习框架。框架的含义个人理解包含两层,一层是库。你要什么功能,就调用相应的函数。第二层意思是,这种库不能完全随意调用,要符合一定顺序,这个顺序是它背后实现的内在机制决定的。这个机制的主要部分就是计算图,你总要把图画好,然后才能开始计算。Tensorflow在背后帮你实现了这些东西,你只要对“业务逻辑”进行编程就可以了,大大提高了你的劳动效率。

 

    大概这么简单个小程序可以完成一个最简单的神经网络的构建、训练、结果输出。主要步骤是定义数据、定义loss、定义优化方法,然后初始化框架,最后就可以慢慢算了


树莓派的SPI

    树莓派自带一个SPI的Python库,叫做spidev。

    对SPI的初始化:

import spidev

spi = spidev.SpiDev()
spi.open(0,0) 
spi.mode = 3
spi.max_speed_hz = 30000


    SPI向下位机传送命令:

print spi.xfer([0x55,2,50,50,0,0,0]) 


    这条命令和前面的stm32的spi程序配合,向其传送命令,让左右履带电机都以50%的速度运行。视频中演示的动作就由这条命令来完成。同时将STM32返回的数据(100mS内测速脉冲读数值)显示出来。


临时的第五章 临时的milestone


    本来有了前面这些东西,基本就可以实现一些最简单的功能了,比如循迹小车,几乎是呼之欲出了。奈何一来时间紧,要赚钱养家,截止日期前这一段时间刚好工作上又比较忙,二来,简单的循迹小车也不是蜥蜴项目的目的。但是,花了这么多的时间精力,总归还是有些收获,可以总结点心得的。

    从最基础的底盘的机构可以说明,在纯电子行业,借助3D打印这类快速成型技术,可以实现一些简单、精度要求不高的机械机构。这些机械机构可以是工作上的工具,可以是家里的实用物件,也可以是小朋友的玩具。这些东西甚至不一定是很简陋的。就蜥蜴的悬挂来说,其负重、缓冲、一对负重轮负载均衡的性能应该说远好于模型和玩具的级别。如果做成金属的,应该完全可以进入专业领域。

就电子和控制算法来说,PID是最常用的调速算法之一,直流电机的PWM驱动也是小功率场合低成本、常用解决方案。而树莓派这一可以运行操作系统的模块,以及可以通过WiFi与PC机相连,则为“大脑”的实现提供了广阔的空间,即使拿目前的蜥蜴,去实现“走走看看,你在电脑前远程监视”这样的功能,也是十分简单的(网上有现成的文章)。


    临时的milestone是为了给竞赛一份答卷,未来,蜥蜴将会不断完善,真正演绎一段进化的传说


电源模块(小功率AC-DC)小体积,多年稳定应用,qq:1239283833
風火流云
2
主题
166
回复
发表于2017-06-26 14:32:06   |  显示全部楼层
9#
支持下楼主,多多开源我们也能有更多的进步

主题

回复
  • 温馨提示: 标题不合格、重复发帖、发布广告贴,将会被删除帖子或禁止发言。 详情请参考: 社区发帖规则
  • 您当前输入了 0 个文字。还可以输入 8000 个文字。 已添加复制上传图片功能,该功能目前仅支持chrome和火狐

禁言/删除

X
请选择禁言时长:
是否清除头像:
禁言/删除备注:
昵 称:
 
温馨提示:昵称只能设置一次,设置后无法修改。
只支持中文、英文和数字。

举报

X
请选择举报类型:
请输入详细内容:

顶部