stm32零基础从知识总结到实践:基于openmv的32循迹小车

该文介绍了如何利用OpenMV模块进行轨迹识别,通过与STM32的UART通信传递数据,结合PID算法实现小车的循迹功能。文中提到了GPIO、PWM控制、编码器以及USART串口通信在系统中的应用,并提供了部分代码示例。
摘要由CSDN通过智能技术生成

功能实现:利用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串口通信 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值