毕业设计
这个比较熟悉,暂时不需要整理
(1)两次握手,对三次握手的改进
(2)结合TCP、UDP的传输机制
(3)基于UDP的可靠传输机制
基于openmv的自动跟踪小车
实现思路:项目的完整代码
1、基础工作
(1)首先要能驱动小车
写一个run函数,能够给A、B两个直流电机施加动力,并给出PWM调速参数,给两个PWM引脚施加PWM波。驱动直流电机
以下为详细解读:
(1)小车模块:驱动电机运动
使用N20直流减速电机,自带驱动
(2)驱动的作用:
单片机——>弱电——>电机驱动器——>强电——>电机
(3)引脚介绍:
引脚名称 | 作用 |
---|---|
AIN1/AIN2 | 控制A路电机转动,一个给高电平,一个给低电平,给的顺序决定转动方向 |
BIN1/BIN2 | 控制B路电机 |
PWMA/PWMB | 与单片机可输出PWM波的IO口相连,控制电机转速 |
(4)具体代码:
ch1 = tim.channel(1, Timer.PWM, pin=pwma)//tim是一个定时器对象,为这个定时器创建一个通道,向pwma引脚输出PWM波
ch1.pulse_width_percent(value)//value介于0-100之间,设置脉冲活动占定时器周期的百分比
run(30,30)//左右电机都以30%的PWM波驱动,向前运动
(2)PID的初始化及运算:==
创建一个PID class,给定kp,ki,kd值来构造一个PID类。然后能够进行PID运算,输入偏差,输出PWM调速参数==。见上述代码
PID调速原理
公式:Pwm=Kpe(k)+Ki∑e(k)+Kd[e(k)-e(k-1)]
e(k):本次偏差
e(k-1):上一次的偏差
∑e(k):e(k)以及之前的偏差的累积和;其中k为1,2,k;
其中KP为比例系数;Ti为积分时间常数;Td为微分时间常数,每个系统不一样所以需要实验调试获得较为理想的,即能满足稳定性、快速性、准确性。
2、main函数内完成的工作
main函数全部代码:
# Blob Detection Example
#
# This example shows off how to use the find_blobs function to find color
# blobs in the image. This example in particular looks for dark green objects.
import sensor, image, time
import car
from pid import PID
# You may need to tweak the above settings for tracking green things...
# Select an area in the Framebuffer to copy the color settings.
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
sensor.skip_frames(10) # Let new settings take affect.
sensor.set_auto_whitebal(False) # turn this off.
clock = time.clock() # Tracks FPS.
# For color tracking to work really well you should ideally be in a very, very,
# very, controlled enviroment where the lighting is constant...
green_threshold = (76, 96, -110, -30, 8, 66)
size_threshold = 2000
x_pid = PID(p=0.5, i=1, imax=100)
h_pid = PID(p=0.05, i=0.1, imax=50)
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
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
blobs = img.find_blobs([green_threshold])
if blobs:
max_blob = find_max(blobs)
x_error = max_blob[5]-img.width()/2
h_error = max_blob[2]*max_blob[3]-size_threshold
print("x error: ", x_error)
'''
for b in blobs:
# Draw a rect around the blob.
img.draw_rectangle(b[0:4]) # rect
img.draw_cross(b[5], b[6]) # cx, cy
'''
img.draw_rectangle(max_blob[0:4]) # rect
img.draw_cross(max_blob[5], max_blob[6]) # cx, cy
x_output=x_pid.get_pid(x_error,1)
h_output=h_pid.get_pid(h_error,1)
print("h_output",h_output)
car.run(-h_output-x_output,-h_output+x_output)
else:
car.run(18,-18)
(1)使用openmv自带图像算法
求得目标物体的相关坐标。不管是apriltag标记还是色块,都有固定的识别算法,获取到物体的坐标、偏移角等参数。
blobs = img.find_blobs([green_threshold])//寻找全部色块
max_blob = find_max(blobs)//寻找最大的那个色块
(2)计算两个方向上的误差
(色块的识别例子中的方法,apriltag应该也可以这样用)
左右方向:将物体的中心坐标移到视野的中心位置上
前后方向:使视野内物体的面积达到一定阈值,面积使用“宽X高”来计算,阈值可以自己设置
上下方向:假设该方向固定
计算误差的代码如下:
x_error = max_blob[5]-img.width()/2 //5号索引色块的中心x位置
h_error = max_blob[2]*max_blob[3]-size_threshold //2号索引边界框的w坐标(宽度),3号索引边界框的h坐标(高度),计算得到视野内的色块面积。后面的是设置的阈值
(3)利用初始参数初始化PID类
这里的参数在实例中已经给出,现实中是需要自己调节的。将上述计算得到的两个方向上的误差作为输入参数进行计算,得到下一步的PWM参数值。代码如下:
x_output=x_pid.get_pid(x_error,1)
h_output=h_pid.get_pid(h_error,1)
(4)将PWM参数输入run函数,驱动电机旋转。
car.run(-h_output-x_output,-h_output+x_output)
这里比较有意思。
1、x_output代表左右这个方向的输出值,小车运动过程中的左右调整是通过两电机的差速实现的,所以run函数的参数中x_output的符号不同。
2、h_output是前后方向的输出值,两电机在这个方向上“共进退”,所以参数中是相同符号。
基于stm32的步进电机控制系统
1、主要器件的型号及特性
器件名称 | 型号 | 作用 | 特性 |
---|---|---|---|
四相五线步进电机 | 28BYJ48 | 控制脉冲频率(即两个高电平之间的delay)控制速度,控制给高电平的顺序控制转动方向。 | A->B->C->D或者反过来,挨个给高电平,完成一个循环后,转过一个齿距 |
大电流驱动阵列 | ULN2003 | 放大电流,增强驱动能力 | IO口的高电平输出可以直接作为ULN2003的控制输入 |
字符型液晶显示模块 | LCD1602 | 对转速、转动方向进行显示 | 首先用1号命令清理显示界面。把字符串存在数组,将数组作为形参进行显示;若想显示变量,需要使用sprintf将变量格式转换为字符指针,然后将该指针作为形参进行显示 |
2、具体实现细节
贴一下代码:
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "lcd1602.h"
#include "timer.h"
#include "stdio.h"
#include "beep.h"
u8 speed=0;//档位,是一个全局变量
short count=0;//圈数,同样是全局变量
u8 flag=1;//旋转标志,高电平顺时针,低电平逆时针
void step_motor_forward(void);//顺时针旋转子程序
void step_motor_back(void);//逆时针旋转子程序
void check(); //按键检查
void display(); //LCD显示
/************************************************
ALIENTEK精英STM32开发板实验3
按键输入实验
技术支持:www.openedv.com
淘宝店铺:http://eboard.taobao.com
关注微信公众平台微信号:"正点原子",免费获取STM32资料。
广州市星翼电子科技有限公司
作者:正点原子 @ALIENTEK
************************************************/
int main(void)
{
vu8 key=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为 115200
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //按键初始化
BEEP_Init();
GPIO_Configuration();
LCD1602_Init(); //LCD初始化
LCD1602_ClearScreen();
check();
}
void check()
{
vu8 key=0;
speed=0;
count=0;
while(1)
{
key=KEY_Scan(0); //得到键值
if(key)
{
switch(key)
{
case KEY1_PRES: //进入顺时针旋转子程序
speed=1;
count=0;
step_motor_forward() ;
break;
case KEY0_PRES: //进入逆时针旋转子程序
speed=1;
count=0;
step_motor_back();
break;
case WKUP_PRES://进入LCD显示子程序
display();
break;
}
}
} ;
}
void step_motor_forward(void)
{
vu8 key=0;
int delaytime=7;
int n1=0;//循环数
flag=1;
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5); //引脚GPIOB.5置位
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_0);
GPIO_ResetBits(GPIOE,GPIO_Pin_1);
delay_ms(delaytime); //延时
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_5); //引脚GPIOE.5置位
GPIO_ResetBits(GPIOE,GPIO_Pin_0);
GPIO_ResetBits(GPIOE,GPIO_Pin_1);
delay_ms(delaytime); //延时
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_0); //引脚GPIOE.0置位
GPIO_ResetBits(GPIOE,GPIO_Pin_1);
delay_ms(delaytime); //延时
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_0);
GPIO_SetBits(GPIOE,GPIO_Pin_1); //引脚GPIOE.1置位
delay_ms(delaytime); //延时
n1++;
if((n1%400==0)&&(n1>0))
{
n1=0;//清0
count++;//圈数加1
}
key = KEY_Scan(0);
if(key==KEY1_PRES)
{
delaytime--;
speed++;
}
if(key==KEY0_PRES)
{
speed=1;
count=0;
step_motor_back();
}
if((delaytime==1)||(speed==7))
check();
if(key==WKUP_PRES)
display();
}
}
void step_motor_back(void)
{
vu8 key=0;
int delaytime=7;
int n1=0;
flag=0;
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_0);
GPIO_SetBits(GPIOE,GPIO_Pin_1); //GPIOE.1置位
delay_ms(delaytime); //延时
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_0); //GPIOE.0置位
GPIO_ResetBits(GPIOE,GPIO_Pin_1);
delay_ms(delaytime); //延时
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_5); //GPIOE.5置位
GPIO_ResetBits(GPIOE,GPIO_Pin_0);
GPIO_ResetBits(GPIOE,GPIO_Pin_1);
delay_ms(delaytime); //延时
GPIO_SetBits(GPIOB,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉高,灭 等同LED1=1;
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_0);
GPIO_ResetBits(GPIOE,GPIO_Pin_1);
delay_ms(delaytime); //延时300ms
n1++;
if((n1%400==0)&&(n1>0))
{
n1=0;//记得清0
count++;
}
key = KEY_Scan(0);
if(key==KEY0_PRES)
{
delaytime--;
speed++;
}
if(key==KEY1_PRES)
{
speed=1;
count=0;
step_motor_forward();
}
if((delaytime==1)||(speed==7))
check();
if(key==WKUP_PRES)
display();
}
}
void display(void)
{
u8 direction1[] = "clkwise";//顺时针
u8 direction2[] = "anticlk";//逆时针
u8 gear[]="gear:";//档位
u8 circle[] = "circle:";//圈数
u8 s0[3],s1[3];
u8 key = 0;
int n1=0;
while(1)
{
if(n1%200==0)
{
LCD1602_Write_Cmd(0x01);
LCD1602_Show_Str(9, 0,gear);// 0列1行
LCD1602_Show_Str(0, 1, circle);
if(flag==1)
LCD1602_Show_Str(0, 0, direction1);
else
LCD1602_Show_Str(0, 0, direction2);
}
n1++;
sprintf((char*)s0,"%d",speed);
sprintf((char*)s1,"%d",count);
LCD1602_Show_Str(15, 0, s0);
LCD1602_Show_Str(9, 1,s1);// 0列1行
TIM_SetCompare2(TIM3,10);
TIM_SetCompare2(TIM5,420);
key = KEY_Scan(0);
if((key==KEY1_PRES)||(key==KEY0_PRES)||(n1==400))
{
return;
}
if(key==WKUP_PRES)
n1=0;
}
}