功能实现:利用openmv模块识别轨迹,基于各模块和软件控制实现pid循迹功能
技术模块概述:
1.openmv模块(与stm32的uart通信,识别轨迹&线性回归返回直线数据)
2.STM32各软件功能:GPIO输入输出操作 / USART串口通信 /
PWM控制 / 中断操作 / 编码器使用
3.pid算法:速度环 / 位置环 & pid调试
/*大家也可以在此基础上添加 避障/蓝牙遥控 等功能*/
一,openmv模块
想要把openmv玩明白,建议去详细学习opencv库的各种函数。本文我们只是简单应用。
代码及注释如下:
THRESHOLD = (0, 32, -128, 127, -128, 127) # 颜色阈值
import sensor, image, time,lcd
from pyb import UART
import ustruct
# 本处为初始化操作,各类运用此处操作大同小异
uart = UART(3,115200,bits=8, parity=None, stop=1, timeout_char = 1000)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQQVGA)
sensor.set_vflip(True)
sensor.set_hmirror(True)
sensor.skip_frames(time = 2000) # If you use QQVGA it may take seconds
clock = time.clock()
# 传输函数
def sending_data(data1):
global uart;
data=bytearray([0xA5,data1,0XA6])
uart.write(data); # !!!必须要传入一个字节数组
while(True):
clock.tick()
img = sensor.snapshot().binary([THRESHOLD])
line = img.get_regression([(100,100)], robust = True) # 线性回归函数
if (line):
if line.theta()>90:
theta_err = line.theta()-90
else:
theta_err = 90 - line.theta()
#处理后:绝对值为直线同Y+轴的夹角,右正左负
img.draw_line(line.line(), color = 127)
err = 90
if line.magnitude()>10:
sending_data((int)(theta_err))
time.sleep_ms(50)
else:
sending_data((int)('0xE5')) #错误标志位
print(line.magnitude(),theta_err)
二,STM32 ***
1.GPIO口输入输出模式
GPIO_Mode_AIN 模拟输入 //电压信号直接输入到片上外设,如ADC等
GPIO_Mode_IN_FLOATING 浮空输入 // 使用较多,电平高低由外部输入决定
GPIO_Mode_IPD 下拉输入 //默认输出低电平
GPIO_Mode_IPU 上拉输入 //默认输出高电平
GPIO_Mode_Out_OD 开漏输出 // 使用较少,自行搜寻
GPIO_Mode_AF_OD 复用开漏输出 // 用于特定场景,如iic
GPIO_Mode_Out_PP 推挽输出 // 使用较多,可以输出高低电平,如led
GPIO_Mode_AF_PP 复用推挽输出 // 主要用在具有复用功能的情况下,比如USART的TX引脚
例:控制led灯亮灭
void LED_Init(void) //PB8
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
void LED_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
void LED_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
2,PWM输出控制
定时器有输入捕获和输出比较两种模式(前者只读,后者只写)
下为PWM控制电机转速示例,为定时器输出比较功能,用setcompare函数设置速度
void TIM3_PWM_Init(u16 arr,u16 psc)
{
//make structure
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// open RCC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
// GPIO_Init
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//TIM3通道2
// TIM_TimeBaseStructure_Init
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// TIM_OC_Init
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_Cmd(TIM3, ENABLE);
}
3.编码器使用
PWM可以通过占空比设置速度值,但是由于外界干扰存在,error不可避免。此时我们就引入了pid闭环控制,而编码器负责的是测量小车的速度值,用于后续的pid计算。
void Encoder_Init_TIM2(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定时器2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始GPIOB
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising,
TIM_ICPolarity_Rising);//使用编码器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM2,0);
TIM_Cmd(TIM2, ENABLE);
}
之后通过中断函数调取编码器的值,从而计算速度
4.pid控制
原理简单,实操要求较高,可以去搜索专业教程
5.usart串口通信