飞道的博客

STM32毕业设计:基于stm32c8t6的坡道行驶巡线小车制作教程

237人阅读  评论(0)

化作尘所有项目开源!!!

视频代码资料文件包链接:https://download.csdn.net/download/mbs520/16529899

一、项目描述

项目是2020年TI杯电子设计大赛中的题目类似设计,用来巡线与速度调节都可以参考此教程源码

2020年TI杯电子设计大赛中的题目

1.任务
利用TI的MSP430/MSP432平台,设计制作一个四轮电动小车。要求小车能沿着指定路线在坡道上自动循迹骑线行驶。小车必须独立运行,车外不能使用任何设备(包括电源)。小车(含电池)重量小于1.5kg,外形尺寸在地面投影不大于25cm×25cm。坡道用长、宽约1m的细木工板制作,允许板上有木质本色及自然木纹。木工板表面铺设画有1cm×1cm黑白间隔的纸条(以下简称为标记线)作为路线指示;标记线起始段为直线,平行于木板两边;标记线在坡顶转向90°,转弯半径20cm;标记线平行坡顶距离≥30cm,距坡顶距离≤20cm;标记线总长度为1m。停车标记为宽1cm长5cm的黑色线条,垂直于坡顶标记线。小车坡度角示意及行驶线路顶视图如图1所示。

2.要求
(1)坡度角θ=0°,电动小车能够沿标记线自动骑线行驶,在停车点停车;小车上标记点到停车标记中心线的垂直距离误差≤2cm。停车时立即发出声音提示。小车行驶过程中,其地面投影不得脱离标记线。 (15分)
(2)在完成(1)的基础上,电动小车能够设定行驶时间,自动控制小车匀速通过1米长的线路,在停车点停车。行驶时间可在10s~20s间设定。误差绝对值≤1s。行驶过程中不得碾压、脱离标记线。时间误差每超过1s扣1分。 (20分)
(3)坡度角θ=10°,完成要求(2)的动作。 (20分)
(4)可任意指定坡度角θ在11°~30°,完成要求(2)的动作。 (20分)
(5)在完成(4)后,尽量增加坡度角θ,完成要求(2)动作。 (20分)
(6)其他。 (5分)
设计报告:

项 目 主要内容 满分
方案论证 比较与选择,方案描述 3
理论分析与计算 系统相关参数设计 5
电路与程序设计 系统组成,原理框图与各部分的电路图,系统软件与流程图 5
测试方案与测试结果 测试结果完整性,测试结果分析 5
设计报告结构及规范性 摘要,正文结构规范,图表的完整与准确性。 2
总分 20

3.说明
(1)本题目必须使用指定的MSP430/MSP432平台。并将该平台置于显著位置便于评测。不得另外使用其它CPU控制芯片。
(2)不得采用履带小车及带刺轮胎。小车轮胎采用橡胶塑料等柔性材质,不得在其表面涂抹粘性物质等。行驶路面不得铺设除标记线外的任何材料。小车全程在木工板上行驶。
(3)小车设定模式后自动行驶,中途不得人工介入控制。在要求(2)~(5)的测试中,小车应匀速行驶。停顿、打滑、碾压标记线每次扣除2分。
(4)小车标记点:小车到达停车线的标记点自定,并在行驶前明确标记在车体上,以便测量。
(5)所有测试中,行驶过程时间超过30s、小车投影脱离标记线或停车误差超过2cm,均视为失败。
(6)要求(5)中最大角度的测试,θ由选手自己选定。
(7)每项测试过程允许测试两次,取最好成绩。坡度角可以用安卓手机上的“指南针”APP软件测量。

二、制作选材

1、主控芯片stm32f103c8t6

STM32F103C8T6是一款基于ARM Cortex-M 内核STM32系列的32位的微控制器,程序存储器容量是64KB
STM32 F1系列为Cortex-M3基础型MCU 。其中增强型STM32F103- 72 MHz CPU,具有高达1MB的Flash、电机控制、USB和CAN。
其FLASH(KB)大小为64
RAM(KB)大小为20
封装方式采用LQFP48
通用I/O口个数为37
工作电压为2~3.6V
16位定时器个数4
电机控制定时器个数1
CAN个数1

2、编码电机
型号MG513 p30 12v
转动一圈390个脉冲
减速比1比30

然后通过获取每100毫秒的脉冲个数

就可以计算出速度的大小


3、7针0.96寸oled显示屏

4、舵机
看着挺高级的,马力大,不过控制代码与那种两块钱的一样,模拟控制,PWM控制,用来控制方向

4、红外传感器模块

4个红外对管,一块模拟转数字模块

5、电源18650+电池座
经济实惠

6、锂电池充电器
一个模块加一个电池座,加起来不到2块钱
商品链接我得整理一下。。。

7、旋转编码器,
用来调节时间的,按下小车就可以跑了

7、其实编码电机与舵机小车上面都有,直接买一个小车车身

买来是散装的,装了好久装好

三、原理图设计

首先就是要进行硬件设计,连接好了硬件,软件就好办了

原理图设计:(可能有点模糊,建议下载我的资料里面有原图)

然后开始连接硬件

连接好了硬件,写程序就可以调试了…

硬件与程序调试用了两天时间总算搞定了接下来开始测量数据

四、数据测量

功耗测量:

设置全程1.5m走的时间:

时间 电流 电压
静态 111mA 11.5V
10s 242mA 11.1v
11s 238mA 11.1v
12s 228mA 11.1v
13s 224mA 11.2v
14s 204mA 11.1v
15s 191mA 11.2v
16s 189mA 11.3v
17s 188mA 11.3v
18s 187mA 11.2v
19s 184mA 11.3v
20s 185mA 11.3v
21s 183mA 11.2v
22s 181mA 11.3v
23s 179mA 11.3v
24s 177mA 11.3v
25s 174mA 11.4v
26s 171mA 11.4v
27s 168mA 11.4v

2、OLED速度监测

显示屏数据说明:
v1 v2左右轮实时速度
pidout输出pwm值
time 设定时间 speed通过设定时间计算得到的速度,因为路程固定
running stop运行状态




可以看到PID算法起到了良好的效果,很好的把速度恒定在指定范围之内

平地测量场景:

记录数据

五、程序设计

1、主程序设计
主函数它是执行了整个程序的入口点。
首先初始设置定时器参数,设置TIM1 16位定时器递增计数,设置定时器时间10MS,主要用来在定时器中断测量小车速度与PID算法调用
配置TIM4为PWM模式,设置周期为30ms,298能够反应过来,周期太大小车会有抖动,通过脉宽来调节小车速度,脉宽越大,小车运动越快,设置计数器为1000向上计数,也就是重装载值设置1000小车最快,设置重装载值0小车不运动
配置TIM3为PWM模式,根据舵机的特性,配置周期为30MS,向上计数模式,计数器设置为3000,当脉宽长度为28MS时,舵机刚好再0度位置,所以初始化重装载寄存器为2800,上电默认向前方向
配置红外传感器4个输入引脚为输入模式,来检测输入的电平情况
配置旋钮编码器为3个引脚输入模式,用来检测是否旋转与按下
配置LED引脚为推挽输出模式,来驱动LED闪烁
配置串口与滴答定时器延时,串口主要用来调试,延时用来消耗某些时刻需要消耗的时间
循环:
循环检测旋钮状态,如果旋转了就根据旋转的方向与角度设定时间的大小,如果旋钮按键按下,那么前进状态变成1

main.c

//主函数
int main(void)
{
   
	u8 state;
	int ang,i;
	
    uart_init(9600);//蓝牙串口初始化
	delay_init(); //延时初始化
	SteerinMotor_Init(); //舵机初始化
	EncodeMotor_Init();//编码电机初始化
	OLED_Init();//oled初始化
	LED_Init();//led初始化
	TIM1_Int_Init(100,7200);//定时器初始化10ms
	Encodeing_Init();//旋钮编码器初始化
	//测试
    printf(" this is Ramp patorl trolley  by HUAZUOCHEN! ^_^ \r\n");
    while (1) {
   
		OLED_update();//更新OLED显示
		encodeing_scan();//编码器扫描
		//获取红外线传感器传回的数据
		state = Get_Infrared_Sensor();
		ang = 0;
		switch (state)//循迹转弯设置
		{
   
			case 0x00:ang = 0;
				break;
			case 0x01:ang = -55;
				break;
			case 0x03:ang = -45;
				break;
			case 0x02:ang = -10;
				break;
			case 0x06:set_steerMotor_ang(0);//设置舵机角度 0
				break;
			case 0x04:ang = 10;
				break;
			case 0x0c:ang = 45;
				break;
			case 0x08:ang = 55;
				break;
			case 0x0f:  //识别到杂乱信号角度设置为0
			case 0x09:
			case 0x05:
			case 0x0a:
			case 0x0e:
			case 0x0b:
					run_flag = 0;//停止	
				break;
			default:ang = 0;
				break;
		}
		if(ang != 0)
		{
   
			set_steerMotor_ang(ang);//设置舵机角度
			delay_ms(100);
		}
		state = 0;
    }
}

定时器1中断,10ms调用一次,调用时计数10次调用,也就是100ms进行一次速度测量,V = X/t ,速度等于位移处以时间,位移等一编码个数乘以编码单位长度,小车轮的直径为6.4厘米,周长为20.12cm,得到一个脉冲计数,大约是0.258毫米,得出左轮速度V_left = pause_cnt_left2.58;右轮速度V_right = pause_cnt_right2.58;当按下旋转编码器的按键run_flag = 1;使得PID算法开始运行,小车开始按照计算好的速度与当前测量到的速度做比较,进行闭环PID调节。

timer.c


u16 time_cnt,run_cnt;
//100ms计算一次速度
void TIM1_UP_IRQHandler(void)
{
   
	if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
	{
   
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);  //清除TIMx的中断待处理位:TIM 中断源 
		time_cnt ++;
		if(time_cnt >= 10)
		{
   
			V_left = pause_cnt_left*2.58;
			V_right = pause_cnt_right*2.58;
			pause_cnt_left=0;
			pause_cnt_right=0;
			time_cnt =0;
			LED = !LED;
		}
		
		if(run_flag)
		{
   
			speed = 1200/time;  //总路程/时间
			Set_Temp1 = Set_Temp = speed;
			set_pid_speed();
			run_cnt++;
			if(run_cnt == time*200)
			{
   
				run_flag = 0;
				run_cnt = 0;
			}
		}
		else 
		{
   
			PID_OUT = 0;
			PID_OUT1= 0;
			//run_cnt = 0;
			car_go_forward(0,0);//停止
		}
	}
}

霍尔编码器脉冲获取采用中断方式,中断设置上升沿下降沿都触发,
电机传动轴与编码器相连,编码器感应论上有13个感应线条,从而使得编码器齿轮转动一圈,或编码器能识别到13个脉冲
减速箱使得小车轮子转动一圈,电机轴转动30圈,使得编码器齿轮也转动30圈,得到,小车轮子转动一圈,编码器输出13×30,等于390个脉冲,又因为一个脉冲有一个上升,沿有一个下降沿,单片机通过检测上升沿和下降沿,可以得到有780个计数,

encode_motor.c

double V_left,V_right;//定时器计算
u16 pause_cnt_left,pause_cnt_right;//脉冲计数

//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
   
	pause_cnt_left++;
	EXTI_ClearITPendingBit(EXTI_Line2);  //清除LINE2上的中断标志位  
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
   
	
	pause_cnt_right++;
	EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志位  
}

速度平衡算法,给定一个速度,听过与编码电机反馈的速度进行比对,然后输出一个算出来的PWM值返回给电机调速,达成闭环速度调节

PID.c


void PID_calc(float V_1, float V_2) //PID算法
{
   
    /***********************左轮**************************/
    float Rate;//误差变化率
    float Rate1;//误差变化率
    Current_Error = Set_Temp - V_1;//当前误差
    Sum_Error += Current_Error; //误差积分
    Prev_Error = Last_Error;//存储误差积分
    Last_Error = Current_Error;//存储误差分析
    Rate = Current_Error - Last_Error; //变化速率计算


    if (Rate > 10) //不让ta大于5也不让ta小于5
        Rate = 10;
    if (Rate < -10)
        Rate = -10;

    P_OUT = P * Gain * Current_Error; //比列项
    I_OUT = I * Gain * Sum_Error; //积分项

    //积分限幅处理
    if (I_OUT > PID_I_MAX)  I_OUT = PID_I_MAX; //不能超过最大值不能低于最小值
    if (I_OUT < PID_I_MIN)  I_OUT = PID_I_MIN;

    //微分输出处理
    D_OUT = D * Gain * Rate;
    PID_OUT =  P_OUT  +  I_OUT  +  D_OUT ;
    if (PID_OUT >= V_DATA_MAX)  PID_OUT = V_DATA_MAX;
    if (PID_OUT <= V_DATA_MIN)  PID_OUT = V_DATA_MIN;

    /***********************右轮********************************/

    Current_Error1 = Set_Temp1 - V_2;//当前误差
    Sum_Error1 += Current_Error1; //误差积分
    Prev_Error1 = Last_Error1;//存储误差积分
    Last_Error1 = Current_Error1;//存储误差分析
    Rate1 = Current_Error1 - Last_Error1; //变化速率计算


    if (Rate1 > 10) //不让ta大于5也不让ta小于5
        Rate1 = 10;
    if (Rate1 < -10)
        Rate1 = -10;

    P_OUT1 = P * Gain1 * Current_Error1; //比列项
    I_OUT1 = I * Gain1 * Sum_Error1; //积分项

    //积分限幅处理
    if (I_OUT1 > PID_I_MAX1)  I_OUT1 = PID_I_MAX1; //不能超过最大值不能低于最小值
    if (I_OUT1 < PID_I_MIN1)  I_OUT1 = PID_I_MIN1;

    //微分输出处理
    D_OUT1 = D * Gain * Rate1;
    PID_OUT1 =  P_OUT1  +  I_OUT1  +  D_OUT1 ;
    if (PID_OUT1 >= V_DATA_MAX1)  PID_OUT1 = V_DATA_MAX1;
    if (PID_OUT1 <= V_DATA_MIN1)  PID_OUT1 = V_DATA_MIN1;
    /*******************************************************/
}

获取红外传感器值
红外对管用一个发射管与一个接收管组成,红外发射管发射出去的红外线遇到白色物体会反射回红外接收管,使之导通,反之遇到黑色物体不反射,使之截止。接收到的红外线越强,导通电流越大,输出电压越大,输出的电压经过逻辑电路转换为数字信号传回到单片机进行识别处理。单片机引出4个引脚对应接收电平状态,4个引脚配置为输入模式,循环检测电平的变化,电平为1则对应红外对管识别到黑色,电平0对应红外对管识别到白色

//获取传感器状态
u8 Get_Infrared_Sensor(void)
{
   
	u8 state=0;
	if(PBin(4) == 1)     
		state |= 0x08;	//IN4
	if(PBin(5) == 1)
		state |= 0x04;	 //IN3
	if(PBin(6) == 1)
		state |= 0x02;	 //IN2
	if(PBin(7) == 1)
		state |= 0x01;	 //IN1
	
	return state;
}


转载:https://blog.csdn.net/mbs520/article/details/115438122
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场