基于MSP432P401R爬坡小车【2020年电赛C题】

文章目录


一、任务清单

1. 硬件部分

  • 主控板: MSP432P401R
  • 数据显示: OLED
  • 电机: 霍尔编码器电机
  • 电池: 7.3V航模电池
  • 巡线: OpenMV H7 Plus
  • 警报: 蜂鸣器
  • 电机驱动: TB6612
  • 仪器: 3D打印机
  • 视觉云台: 3D建模打印
  • 转向: 数字舵机
  • 其他: 铜柱、螺丝螺母、开关、面包板等。

2. 软件部分

  • 编译器: Keil、OpenMV IDE
  • 建模软件: SketchUp Pro 2022
  • 切片软件: Cura
  • 编程方式: 库函数
  • 编程语言: C、Python

二、OpenMV巡线

这里巡线不用红外探头的原因是考虑到市面上大多红外对该虚线采集的信号并不是很准确,存在偶然因素,故选择摄像头巡线。

这里我通过寻找有效区域内的最大色块,将其x轴坐标返回给单片机,在单片机端进行数据处理,例如摄像头下的分辨率为(w:320,h:240),设感应区域ROI为(0,90,320,60) ,

采集到的有效色块X坐标为162,下图可见,被圈出的大白框矩形为感应区域ROI,中间被圈出的为最大有效色块(图中色块阈值选择为黑色),下方红色圈出的为该色块的X轴坐标。

获取目标色块:

c 复制代码
img = sensor.snapshot()  //截取摄像头的一个图像
blob=img.find_blobs([thresholds[0]], roi=ROI1, x_stride=10, y_stride=5,pixels_threshold=10, area_threshold=10, merge=True)  

筛选出最大色块:

c 复制代码
def find_max(blobs):
    max_size=0
    for blob in blobs:
        if blob[2]*blob[3] > max_size:
            max_blob=blob
            max_size = blob[2]*blob[3]
    return max_blob

输出最大色块X坐标:

c 复制代码
x=max_ID.cx()

将有效色块坐标发送至单片机端后该怎样对数据进行处理呢?

  • 方法一:

    例如采集到的坐标为140,可知中心坐标为160,已速度闭环为例,可先将坐标差值Err=140-160=-20,可拟定为负值为偏左,正值为偏右,假如你的闭环在每10ms单相双边沿产生15个脉冲信号,可设定一个线性系数k,有Err/k=P,Err2=Err_Last-Err=I,则Target_L=15+PI,Target_R=15-PI,Target_L和Target_R则为计算后的目标脉冲,具体公式推导这里不做介绍,仅供参考!

  • 方法二:

    这种方法也是更为简单,更为方便的。

    对色块的X坐标进行区域划分,例如中线为160,则:

    ①中心区域可划分为(150,170)

    ②偏左区域可划分为(120,150)、(90,120)、(60,90)、(30,60)、(0,30)

    ③偏右区域可划分为(170,200)、(200,230)、(230,260)、(260,290)、(290,320)

    即可根据不同位置发送不同指令。单片机段根据不同指令执行不同程度的回正操作。

示例如下:

c 复制代码
def Send_X(err):
    if(err>=150 and err<=170):
        uart.write("F")

    elif(err>=120 and err<150):
        uart.write("0")
    elif(err>=90 and err<120):
        uart.write("1")
    elif(err>=60 and err<90):
        uart.write("2")
    elif(err>=30 and err<60):
        uart.write("3")
    elif(err>=0 and err<30):
        uart.write("4")

    elif(err>170 and err<200):
        uart.write("5")
    elif(err>=200 and err<230):
        uart.write("6")
    elif(err>=230 and err<260):
        uart.write("7")
    elif(err>=260 and err<290):
        uart.write("8")
    elif(err>=290 and err<320):
        uart.write("9")

其中Err表示踩到的X坐标,根据不同坐标区域发送不同指令,在单片机端接收操作如下:

c 复制代码
	if( openmv_rx=='F' )
	{

	}	

	//偏向左边
	else if( openmv_rx=='0' )
	{
		
	}
	else if( openmv_rx=='1' )
	{
	
	}
	else if( openmv_rx=='2' )
	{
			
	}
	......
	
	//偏向右边
	else if( openmv_rx=='5' )
	{
	
	}
	else if( openmv_rx=='6' )
	{
		
	}
	......

三、舵机转向

舵机转向问题,在学习较为常用的为180°舵机,这里我以180°数字舵机进行简单介绍

常用的舵机大多都分为3根线,其中俩根为VCC和GND,也就是电源线,剩下一根为信号线,也就是控制舵机的线

  • 舵机控制
    关于舵机的控制说的通俗易懂点就是控制信号线的脉宽,通过控制不同脉冲舵机转动不同角度,如下所示:
c 复制代码
                20ms为例
        0.5ms ------------ 0度;
        1.0ms ------------ 45度;
        1.5ms ------------ 90度;
        2.0ms ------------ 135度;
        2.5ms ------------ 180度;

设一个PWM周期为20ms,则控制有效电平为0.5ms,也就是一百分之一的时候,舵机保持0°,依次类推。

  • 实时转向

    可根据上面巡线返回的信号进行转向,如当接收到信号F时,表示在中心区域,则舵机保持直线行驶状态,也就是90°,若接收到1-4信号,则舵机向左偏向,120°、150°、180°等,如接收到5-9信号,则舵机向右偏向,60°、30°、0°等。

  • 舵机初始化

    这里以定时器A0的通道三为例,如下代码:

c 复制代码
	    /*初始化引脚*/
	MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P2, GPIO_PIN6, GPIO_PRIMARY_MODULE_FUNCTION);
	
    Timer_A_PWMConfig TimA0_PWMConfig;
    /*定时器PWM初始化*/
    TimA0_PWMConfig.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;             //时钟源
    TimA0_PWMConfig.clockSourceDivider = psc;                            //时钟分频 范围1-64
    TimA0_PWMConfig.timerPeriod = ccr0;                                  //自动重装载值(ARR)
    TimA0_PWMConfig.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_3; //通道三
    TimA0_PWMConfig.compareOutputMode = TIMER_A_OUTPUTMODE_TOGGLE_SET;   //输出模式                              

    MAP_Timer_A_generatePWM(TIMER_A0_BASE, &TimA0_PWMConfig); /* 初始化比较寄存器以产生 PWM1 */	
  • 角度控制
c 复制代码
void SG90_angle(int a)
{
    int pwm=500+2000/180*a;
    MAP_Timer_A_setCompareValue(TIMER_A0_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_3, pwm);
} 

四、停止线识别

  • 方法一
    通过判断有效色块的面积或者宽度,因为在巡线中都是1cm1cm的虚线组成,而停止线为5cm1cm,所以可通过色块的面积或者宽度等其他因素进行识别,返回停止信号。
c 复制代码
w=max_ID.w()
if w>=80:
    uart.write("S")
  • 方法二
    因为整个赛图小车走的路程是固定的,则也可用通过小车走的路程进行停止。
c 复制代码
if(sum>=1)
{
	Stop();
	PID_SetPoint(&L_pid,0);
	PID_SetPoint(&R_pid,0);			
}

五、技术交流

疑难解答或技术交流联系下方wx即可👇👇👇

相关推荐
追梦少年时10 分钟前
STM32-Flash闪存
stm32·单片机·嵌入式硬件·51单片机
ChoSeitaku21 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
娅娅梨23 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
DdddJMs__13527 分钟前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
汤米粥29 分钟前
小皮PHP连接数据库提示could not find driver
开发语言·php
冰淇淋烤布蕾32 分钟前
EasyExcel使用
java·开发语言·excel
拾荒的小海螺38 分钟前
JAVA:探索 EasyExcel 的技术指南
java·开发语言
weixin_452600691 小时前
《青牛科技 GC6125:驱动芯片中的璀璨之星,点亮 IPcamera 和云台控制(替代 BU24025/ROHM)》
人工智能·科技·单片机·嵌入式硬件·新能源充电桩·智能充电枪
马剑威(威哥爱编程)1 小时前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++