Objeto de seguimiento visual del mecanismo de dirección bidimensional K210 basado en CW32

Prefacio
Recientemente, quiero hacer un proyecto que involucre el uso de MCU--CW32 y K210 domésticos para controlar el mecanismo de dirección para lograr el propósito de rastrear objetos. Una función que quiero lograr es identificar el objetivo y transmitir la información de coordenadas del objetivo a la MCU a través del puerto serie. La MCU controla. El mecanismo de dirección se usa para el control, por lo que creo que la mejor opción para la visión es usar k210. No solo tiene un bajo costo y un buen rendimiento, sino que también es fácil de Uso basado en el desarrollo de MicroPython. La microcomputadora de un solo chip es el chip doméstico CW32 de Wuhan Core Semiconductor Company. .

¿Qué es CW32?

CW32 es un microcontrolador de 32 bits basado en el núcleo Cortex-M0+ lanzado por Wuhan Xinyuan Semiconductor: MCU de la serie CW32 Los requisitos de MCU de diferentes mercados de aplicaciones, como el consumo de energía y el alto rendimiento.

Qué es k210:
K210 es un sistema en un chip (CPU RSIC-V) integrado con capacidades de visión artificial y audición artificial de Cannaan Technology. Utilizando el proceso avanzado de 28 nm de consumo de energía ultra bajo de TSMC, con un procesador de 64 bits de doble núcleo, tiene un mejor rendimiento, estabilidad y confiabilidad de consumo de energía. La solución se esfuerza por lograr un desarrollo de umbral cero y se puede implementar en el producto del usuario en el menor tiempo posible, dotando al producto de inteligencia artificial (IA). Se puede decir que es una combinación de rendimiento sólido y rendimiento de alto costo.
 

 1. CW32 emite PWM para controlar el servo para rastrear de acuerdo con los datos enviados desde el puerto serie K210

    CW32F030x6/x8 es un microcontrolador de un solo chip basado en eFlash que integra un núcleo ARM® Cortex®-M0+ con una frecuencia principal de hasta 64MHz, una memoria integrada de alta velocidad (hasta 64K bytes de FLASH y hasta 8K bytes de SRAM) y una serie de periféricos mejorados integrales y puertos de E/S.
Todos los modelos proporcionan un conjunto completo de interfaces de comunicación (tres UART, dos SPI y dos I2C), ADC de alta velocidad de 12 bits, siete conjuntos de temporizadores básicos y de uso general y un conjunto de temporizadores PWM de control avanzado.
El CW32F030x6/x8 puede funcionar en un rango de temperatura de -40 °C a 105 °C con un amplio voltaje de suministro de 1,65 V ~ 5,5 V. Admite Sleep y DeepSleep dos modos de bajo consumo de energía.

El transceptor asíncrono universal (UART) admite los modos dúplex completo asíncrono, semidúplex síncrono y semidúplex de un solo cable, admite control de flujo de datos de hardware y comunicación entre varias máquinas; estructura de marco de datos programable; puede proporcionar un ancho de banda amplio a través del generador de velocidad de transmisión fraccionaria gama de selecciones de velocidad en baudios.
El controlador UART funciona en dominios de reloj dual, lo que permite la recepción de datos en modo de suspensión profunda, y la interrupción de finalización de recepción puede activar la MCU y volver al modo de ejecución. El puerto serie 1 seleccionado
en esta rutina

El código detallado es el siguiente

1. Configurar el reloj del sistema

void RCC_Configuration(void)
{     // 1. Activación y calibración de HSE, frecuencia del oscilador de cristal externo = 8MHz     RCC_HSE_Enable(RCC_HSE_MODE_OSC,8000000, RCC_HSE_DRIVER_NORMAL, RCC_HSE_FLT_CLOSE);     // 2. Establece el factor de división de frecuencia de HCLK&PCLK     RCC_HCLKPRS_Config(R CC_HCLK _DIV1);     RCC_PCLKPRS_Configuración (RCC_PCLK_DIV1);     // 3. Habilitar PLL, multiplicado por 6 a 48MHz     RCC_PLL_Enable(RCC_PLLSOURCE_HSEOSC, 8000000, 6);     RCC_PLL_OUT();     // 4. Establecer el ciclo de espera de lectura de Flash: 48M ≥ fuente de reloj HCLK > 24M, 2 ciclos; reloj Fuente HCLK>48M, 3 ciclos     __RCC_FLASH_CLK_ENABLE();     FLASH_SetLatency(FLASH_Latency_2);     // 5. Cambiar el reloj a PLL     RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);     RCC_SystemCoreClockUpdate(48000000); //48000000 }


    



    



    



    




2. Inicializar el puerto IO

  void GPIO_Configuration(void)
{     // Habilitar reloj GPIOB: porque se usan los pines PB06 (motor M4) y PB07 (motor M3)     __RCC_GPIOB_CLK_ENABLE();     // Habilitar reloj GPIOA: porque se usan PA08 (motor M1) y PA11 (motor M2 ) pin     __RCC_GPIOA_CLK_ENABLE();     // configurar el temporizador: GTIM3_CH4 (PA12 pin)     PA12_AFx_GTIM3CH4();     PA12_DIGTAL_ENABLE(); // habilitar el modo digital     PA12_DIR_OUTPUT(); // salida     PA12_PUSHPULL_ENABLE(); // modo: salida push-pull     PA12_SPEED_HIGH( ); // Alta velocidad     // Declara la configuración de inicialización del puerto GPIO     GPIO_InitTypeDef GPIO_InitStructure;     // Modo de interrupción: Elija no usar     GPIO_InitStructure.IT = GPIO_IT_NONE;     // Modo: Salida push-pull     GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;     // Configure el temporizador : ATIM_CH2A (clavija PB06)


    


    
    
 
    






    
    


    


    


    
 
    

    PB06_AFx_ATIMCH2A();
    
    // Inicializar PB06 (Y) pin
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
    
    
    // Seleccionar pin: PB07 (X) pin
    GPIO_InitStructure.Pins = GPIO_PIN_7;
    
    // Configurar temporizador: ATIM_CH3A (PB07 pin)
    PB07_AFx_ATIMCH3A();
    
    / / Inicializar pin PB07
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
    
    
    

}

3. Inicialice el puerto serie y la función de recepción del puerto serie

vacío UART_init (vacío)
{     GPIO_InitTypeDef GPIO_InitStructure;     USART_InitTypeDef USART_InitStructure;


    


     RCC_APBPeriphClk_Enable2(RCC_APB2_PERIPH_UART1, ENABLE);
     RCC_AHBPeriphClk_Enable( RCC_AHB_PERIPH_GPIOA, ENABLE);  

     PA08_AFx_UART1TXD();
     PA09_AFx_UART1RXD(); 
    
  GPIO_InitStructure.Pins = GPIO_PIN_8; //PA8
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStructure.Speed ​​= GPIO_SPEED_HIGH;
  GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
    
  GPIO_InitStructure.Pins = GPIO_PIN_9; //PA9
  GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
  GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
        

  USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_Over = USART_Over_16;
  USART_InitStructure.USART_Source = USART_Source_PCLK;
  USART_InitStructure.USART_UclkFreq = 64000000;
  USART_InitStructure.USART_StartBit = USART_StartBit_FE;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No ;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_Init(CW_UART1, &USART_InitStructure); 
    
    
  //使能UARTx RC中断
  USART_ITConfig(CW_UART1, USART_IT_RC, ENABLE);
  //优先级,无优先级分组
  NVIC_SetPriority(UART1_IRQn, 0);
  //habilitar interrupción de UARTx
  NVIC_EnableIRQ(UART1_IRQn);

}
 

void UART1_IRQHandler(void)
{   /* INICIO DEL CÓDIGO DE USUARIO */ static int i=0; //¡Tenga cuidado de no usar la misma variable para matrices diferentes!   if(USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)   {          USART_ClearITPendingBit(CW_UART1, USART_IT_RC);          k210_data[i++] =USART_ReceiveData_8bit(CW_UART1); //Recibir datos                 if(k210_data[0]!=0xa3) i=0; //Juzgar el encabezado del primer cuadro         if((i==2)&&(k210_data[ 1] !=0xb3)) i=0; //Evaluar el encabezado del segundo cuadro      si(i==7) //Representa que se completó un conjunto de transmisión de datos         { PA00_SETHIGH();              i = 0;             if( k210_data_test(k210_data) ) // Juzgando la racionalidad de los datos             {


   




   
        

        

      





            /**************************************************** *** ***
                                                            DATOS ACTUALIZADOS
             ******************************************** ** *****/    
                //【1】x error:
                  
                k210_error_x = k210_data[2];
                
                //【2】x flag:
                x_flag = k210_data[3];
                
                //【3】x error:
                k210_error_y = k210_data [ 4];
                
                //【4】x bandera:
                y_flag = k210_data[5];
                
                longitud = k210_data[6];
           // Data_Handle();
              OLED_Show();
               
             }
         }
  /* FIN DEL CÓDIGO DE USUARIO */
}
}

5. El error entre el eje X y el eje Y se obtiene mediante el algoritmo PID para generar PWM para controlar el mecanismo de dirección

 

int PID_xCalc(int error_x)
{      float Pout_x; // flotar Iout;      flotar Dout_x; // flota DelEk;      flotar fuera_x;    //判断时间......





  if(pid.C1ms>(pid.T_x)/*|| (Circle_Flag==4)|| Circle_Flag==2*/)  
    {     pid.Pv_x = error_x;       pid.Ek_x=pid.Pv_x-pid.Sv_x; //P        Salida_x=pid.Kp_x*pid.Ek_x;        Dout_x=pid.Kd_x*(pid.Ek_x-pid.Ek_1_x);          salida_x = Pout_x+Dout_x;      pid.SALIDA_x=salida_x;





    if(pid.SALIDA_x>pid.pwmcycle_x) {pid.SALIDA_x=pid.pwmcycle_x; }
    if(pid.SALIDA_x<-pid.pwmcycle_x) {pid.SALIDA_x=-pid.pwmcycle_x; }

 
        pid.Ek_1_x = pid.Ek_x;


        pid.C1ms=0;
          volver pid.OUT_x;
    }

}
int PID_yCalc(int error_y)
{      float Pout_y; // flotar Iout;      flotar Duda_y; // flotar PartEk;      flotar fuera_y;    //Clave específica......   if(pid.C2ms>(pid.T_y)/*|| (Circle_Flag==4)|| Circle_Flag==2*/)      {     pid.Pv_y = error_y;       pid.Ek_y=pid.Pv_y-pid.Sv_y; //P        Salida_y=pid.Kp_y*pid.Ek_y;        Dout_y=pid.Kd_y*(pid.Ek_y-pid.Ex_1_y);          out_y = Pout_y+Duda_y;      pid.SALIDA_Y=salida_y;          //Instalar la configuración predeterminada       if(pid.OUT_y>pid.pwmcycle_y) { pid.OUT_y=pid.pwmcycle_y; }       if (pid. SALIDA_y <-pid. pwmcycle_y) {pid. SALIDA_y = -pid. pwmcycle_y; } }
















 
        pid.Ek_1_y = pid.Ek_y;


        pid.C2ms=0;
     volver pid.OUT_y;
    } 
    
}


 2. K210 obtiene la posición del eje x y el eje y del objeto y transmite los datos a la MCU a través del puerto serie

módulo de importación

desde el temporizador de importación de la máquina, PWM
sensor de importación, imagen, lcd
desde fpioa_manager importar fm
de Maix importar GPIO
desde máquina importar UART
 
 
Configurar el puerto serie
fm.registro(10, fm.fpioa.UART1_TX, fuerza=Verdadero)
fm.registro(11, fm.fpioa.UART1_RX, fuerza=Verdadero)
 
 
usart1 = UART(UART.UART1, 115200)
usart1.init(115200, bits=8, paridad=Ninguna, parada=1) #iniciar puerto serie
 
 

establecer umbral de color

clase Color_Propiedad():
    cx = 0 # coordenadas del centro del eje x del bloque de color
    cy = 0 # coordenadas del centro del eje y del bloque de color
    bandera = 0 # bit de bandera de bloque de color 1 encontrado 0 no encontrado
    color = 0 # bandera de color del bloque de color, por ejemplo, puede usar 1 para representar el negro
    densidad = 0 # La relación de densidad del bloque de color refleja el valor del grado de bloqueo del bloque de color, cuanto mayor sea el grado de bloqueo, mejor
    pixels_max = 0 # máximo de píxeles del bloque de color
    led_flag = 0 # La bandera LED es conveniente para la depuración
 
 
    color_threshold = (0, 0, 0, 0, 0, 0) # umbral de color del bloque de color
    color_roi = (0,0,320,240) # área de búsqueda de bloque de color (región de interés)
    color_x_stride = 1 # El ancho mínimo de los píxeles del eje X del bloque de color Si el bloque de color es relativamente grande, puede aumentar este parámetro para aumentar la velocidad de búsqueda
    color_y_stride = 1 # ancho mínimo de píxel del eje y del bloque de color, si el bloque de color es relativamente grande, puede aumentar este parámetro para aumentar la velocidad de búsqueda
    color_pixels_threshold = 100 # El umbral del número de píxeles del bloque de color. Por ejemplo, si este parámetro se ajusta a 100, el bloque de color con menos de 100 píxeles del bloque de color se puede filtrar.
    color_area_threshold = 100 # El umbral del área del bloque de color que se está enmarcando. Por ejemplo, si este parámetro se ajusta a 100, se puede filtrar el bloque de color cuya área del bloque de color está enmarcada es inferior a 100.
    color_merge = True # si fusionar los bloques de color encontrados True, fusionar False, no fusionar
    color_margin = 1 #Distancia de fusión del bloque de color Por ejemplo, ajuste este parámetro a 1. Si se selecciona Verdadero arriba para fusionar bloques de color y los bloques de color encontrados tienen múltiples distancias de 1 píxel, estos bloques de color se fusionarán
 
 
 
 
# rojo
rojo = Propiedad_Color()
rojo.color_umbral = (15, 35, -37, -13, 0, 39)
 
 
#rojo.color_roi = (0,0,320,240)
rojo.color_roi = (0,110,320,20)
 
 
rojo.color_x_zancada = 1
rojo.color_y_zancada = 1
 
 
#rojo.color_pixels_umbral = 100
#rojo.color_area_umbral = 100
rojo.color_pixels_umbral = 10
rojo.color_area_umbral = 10
 
 
rojo.color_merge = Verdadero
rojo.color_margen = 1
 
 
Inicializar configuración de parámetros
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.ejecutar(1)
 
 
inicialización de #lcd
lcd.init()
 
 
def find_max(manchas):
        max_size=0
        para blob en blobs:
            si blob[2]*blob[3] > max_size:
                max_blob=mancha
                max_size = gota [2] * gota [3]
        devolver max_blob
 
 
obtener error
def get_target_err():
    img=sensor.instantánea()
    manchas = img.find_blobs([rojo.color_umbral])
    si manchas:
      max_blob = find_max(manchas)
      cx_error = max_blob.cx()-img.width()/2
      cy_error = max_blob.cy()-img.height()/2
      img.draw_rectangle(max_blob.rect()) # rect
      img.draw_cross(max_blob.cx(), max_blob.cy()) # cx, cy
      pantalla lcd (img)
      # En este rango [-5, 5], se considera que está en el centro
      si abs(cx_error) < 10:
          cx_error = 0
      si abs(cy_error) < 10:
          cy_error = 0
      img = img.draw_cross(int(cx_error), int(cy_error))
 
 
      devolver (cx_error, cy_error)
    demás:
      img = img.dibujar_cruz(160, 120)
      pantalla lcd (img)
      return (0, 0) # Si no se encuentra, regresa a las coordenadas centrales
 
 
# Función de envío de datos de puerto serie:
def usart_send(x, y):
    giro global
    #【1】Establecer el bit de bandera:
    si x>=0:
            bandera_x = 1
    si x<0:
            bandera_x = 0;
    si y>=0:
            y_bandera = 1
    si y<0:
            y_bandera = 0;
 
 
    #【2】Datos del proceso:
    error_x = int(x)
    y_err = int(y)
    #imprimir(x_err,y_err)
    #【3】envío de puerto serie:
    data = bytearray([0xa3,0xb3, x_err,x_flag ,y_err , y_flag, 0xc3]) #encabezado de cuadro + encabezado de cuadro + valor de seguimiento + cola de cuadro
    usart1.write(datos)
 
mientras (Verdadero):
    cx_error,cy_error=get_target_err()  
    usart_send(cx_error,cy_error)

 

Supongo que te gusta

Origin blog.csdn.net/qq_63790524/article/details/130776388
Recomendado
Clasificación