文章目录
- 一、任务清单
- 1. 硬件部分
- 2. 软件部分
- 二、OpenMV巡线
- 三、舵机转向
- 四、停止线识别
- 五、技术交流
一、任务清单
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轴坐标。
获取目标色块:
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)
筛选出最大色块:
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坐标:
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)
即可根据不同位置发送不同指令。单片机段根据不同指令执行不同程度的回正操作。
示例如下:
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坐标,根据不同坐标区域发送不同指令,在单片机端接收操作如下:
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,也就是电源线,剩下一根为信号线,也就是控制舵机的线
- 舵机控制
关于舵机的控制说的通俗易懂点就是控制信号线的脉宽,通过控制不同脉冲舵机转动不同角度,如下所示:
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的通道三为例,如下代码:
/*初始化引脚*/
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 */
- 角度控制
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,所以可通过色块的面积或者宽度等其他因素进行识别,返回停止信号。
w=max_ID.w()
if w>=80:
uart.write("S")
- 方法二
因为整个赛图小车走的路程是固定的,则也可用通过小车走的路程进行停止。
if(sum>=1)
{
Stop();
PID_SetPoint(&L_pid,0);
PID_SetPoint(&R_pid,0);
}
五、技术交流
疑难解答或技术交流联系下方wx即可👇👇👇