User Tools

Site Tools


Writing /var/lib/dokuwiki/data/meta/teaching/ie0117/proyectos/2013/ii/carro_espia.meta failed
teaching:ie0117:proyectos:2013:ii:carro_espia

Autor

  • Andrey Jiménez Oses

Introducción

En este proyecto de desarrollo se elaborará un carro “espía” con el cual se podrá navegar mientras se tiene una imagen en vivo del lugar por el cual se está trasladando el carro. Este carro de podrá mover en cualquier dirección debido las llantas omnidireccionales que este posee. El mismo se podrá controlar inalámbricamente con la computadora mediante conexión WI Fi y un Joystick que estará conectado a la computadora; el cual nos permitirá el control de una manera más fácil y dinámica. El objetivo de este será por ejemplo para ver partes de tu casa sin tener que levantarte de la cama o sillón, para recorrer lugares donde no podrías accesar fácilmente, o ser usado como vigilante de tu casa .

Objetivos

Objetivo General

  • Realizar el montaje mecánico completo del carro e integrar todas sus partes para un correcto funcionamiento.

Objetivos Específicos

  • Construir la base para el montaje mecánico de los motores y demás componentes utilizados.
  • Instalar Raspbian en el Raspberry Pi.
  • Instalar Wiring Pi en el Raspberry Pi.
  • Programar el STM32F4DISCOVERY para controlar los puentes H y demás dependencias para el correcto funcionamiento del mismo.
  • Realizar la comunicación computadora-Pi-stm32.

Raspberry Pi

Instalación

Lo primero que se debe hacer es instalar Raspbian en una tarjeta SD de al menos 4 GB, esto se puede hacer siguiendo los pasos del siguiente link

http://www.linuxito.com.ar/gnu-linux/nivel-medio/241-como-instalar-raspbian-en-raspberry-pi

En la parte de habilitar directamente el escritorio LXDE desde el menú “Enable Boot to Desktop”, lo mejor es ignorarla, asi cuando reinicias el Pi caes en la consola, haces LogIn y ejecutas “Startx”, lo cual te enviará al escritorio, esto es porque dependiendo de la versión de Rapsbian que descargues, al entrar a esa opcion aparecen cosas q no se mencionan es este tutorial.

Uno de los problemas mas frecuentes al trabajar con el Pi es que no tiene pantalla, por lo que hay que buscar una pantalla o TV con entrada HDMI para conectarlo, o buscar convertidores de HDMI para las diferentes entradas de los monitores de computadora usuales. Esto es muy sencillo de solucionar si usamos SSH

Para Windows seguir los pasos de este tutorial

http://www.youtube.com/watch?v=RFqLIBSxFqE

Para Debian solo debes conectarlo a un monitor, para averiguar si IP, y teniendo la computadora que vas a usar conectada al mismo enrutador a la que esta conectado el Pi, teniendo esto te vas a la consola de debia y ejecutas

ssh -x user@IPdelPi

WirinPi

WiringPi es una biblioteca de acceso GPIO para el BCM2835 escrito en C utilizado en el Raspberry Pi. Está liberado bajo la licencia GNU LGPLv3 y se puede utilizar desde C y C + + y otros lenguajes adecuados (Ver más abajo). Está diseñado para que le sea familiar a las personas que han utilizado el Arduino. WiringPi incluye una utilidad de línea de comandos GPIO que se puede utilizar para programar y configurar los pines GPIO. Usted puede utilizar esto para leer y escribir los pines e incluso utilizarlo para controlarlos desde “shell scripts”.

WiringPi es extensible y se proporcionan módulos para extender wiringPi el uso de dispositivos de interfaz analógica en la Gertboard, y para utilizar los chips populares MCP23x17/MCP23x08 (7 I2C SPI) GPIO de expansión, así como del módulo que permitirá bloques de hasta 4 × 74 595 registros de desplazamiento para ser conectados en serie por un valor de 32 bits adicionales de la producción como una sola unidad. (Puede tener varios bloques de 4 74x595s si es necesario) Uno de los módulos de extensión le permite utilizar un ATmega (por ejemplo Arduino o el Gertboard) como una mayor expansión GPIO también a través del puerto serie del Pi. Adicionalmente, usted puede escribir sus propios módulos de expansión para integrar sus propios dispositivos periféricos con wiringPi según sea necesario. WiringPi soporta lectura analógica y la escritura, y si bien no hay hardware analógico nativo en un Pi de forma predeterminada, se proporcionan módulos para apoyar los Gertboards chips analógicos y otros A / D y D / A dispositivos pueden aplicarse con relativa facilidad.

Instalación

WiringPi se mantiene bajo GIT para facilitar el seguimiento de cambios . Si usted no tiene instalado GIT, a continuación, en cualquiera de las versiones de Debian (por ejemplo Raspbian), puede instalarlo con:

sudo apt-get install git-core	

Si tienes cualquier error aquí, asegúrese de que su Pi está al día con las últimas versiones de Raspbian:

sudo apt-get update
sudo apt-get upgrade

Para obtener WirinPi usando GIT:

git clone git://git.drogon.net/wiringPi

Si esta utilizado la operación de clonación, por primera vez, a continuación,

cd wiringPi
git pull origin

Con esto obtendrá una versión actualizada, entonces usted puede volver a ejecutar el script de compilación a continuación.

Para compilar/instalar hay una nueva secuencia de comandos simplificado:

cd wiringPi
./build

El nuevo script build compilará e instalará todo por tí. Si utiliza el comando sudo en un momento dado, lomejor es revisar el guión antes de ejecutarlo.

STM32F4DISCOVERY

El STM32 es una familia de circuitos integrados basados en microcontroladores de 32 bits con núcleos RISC ARM Cortex-M4F, Cortex-M3 y Cortex-M0 de STMicroelectronics y el procesador ARM IP de ARM Holdings. Los diseños de núcleo ARM tienen numerosas opciones configurables, y ST elige la configuración individual de uso para cada diseño. ST concede sus propios periféricos al núcleo antes de convertir el diseño en una pastilla de silicio.

El STM32 serie F4 es el primer grupo de microcontroladores STM32 basado en el núcleo ARM Cortex-M4F. El F4 es también la primera serie STM32 en tener DSP y las instrucciones de coma flotante. La F4 es pin a pin compatible con el F2 de la serie STM32 y añade una mayor velocidad de reloj, 64K CCM RAM estática, full duplex I ² S, mejorado en tiempo real, y ADCs más rápido.

Instalacion

Necesitamos añadir la arquitectura i386

dpkg --add-architecture i386

haga un apt-get update

Ahora obtenga las dependencias

apt-get install libncurses5
apt-get install libncurses5:i386

Descargue el último tarball de instalación de Herramientas de GNU para procesadores embebidos ARM y descomprimir:

cd ~/local/src
wget https://launchpad.net/gcc-arm-embedded/4.7/4.7-2013-q1-update/+download/gcc-arm-none-eabi-4_7-2013q1-20130313-linux.tar.bz2
tar -xjf gcc-arm-none-eabi-4_7-2013q1-20130313-linux.tar.bz2

Hacer que los ejecutables de herramientas a disposición de los usuarios, agregue el directorio bin a la PATH variable:

echo 'export PATH=$PATH:/home/****user****/local/src/gcc-arm-none-eabi-4_7-2013q1/bin/' >> /home/****user****/.bashrc

Recordemos que para recargar el archivo .bashrc tenemos 2 opciones:

  • Cierre seción y logueese de nuevo
  • Recargue el directorio del archivo .bashrc
. .bashrc

Openocd

Instale openocd

sudo apt-get install openocd

Libopencm3

Primero debemos tener instalado python-yaml , si no esta instalado hacemos

sudo apt-get install python-yaml

Luego creamos la librería

cd ~/local/src/
git clone https://github.com/libopencm3/libopencm3-examples
cd libopencm3-examples
git submodule init
git submodule update
cd libopencm3
make

Primero necesitamos tener instalado el paquete: libusb-1.0

Ahora ingresamos a la carpeta

cd ~/local/src/

Se descarga con git

git clone https://github.com/texane/stlink

Ingresamos a la carpeta

cd stlink

Ejecutamos el programa (para que funcione debe tener el paquete pkg-config)

./autogen.sh

Configuramos

./configure --prefix=/home/**user**/local/DIR/stlink

Copiamos y compilamos

make
make install
cd
cd local/DIR
xstow stlink

Ahora necesitamos copiar las siguientes reglas (tenemos que estar ubicados en /local/src/stlink),

sudo cp 49-stlinkv1.rules /etc/udev/rules.d/
sudo cp 49-stlinkv2.rules /etc/udev/rules.d/
sudo /etc/init.d/udev restart

Ahora antes de Flashear el STM realizamos los siguiente:

Nos vamos a la dirección local/src/libopencm3-examples/examples/stm32/f4 y aqui editamos el archivo Makefile.include

ifeq ($(BMP_PORT),)
ifeq ($(OOCD_SERIAL),)
%.flash: %.hex
      @printf " FLASH $<\n"
      @# IMPORTANT: Don't use "resume", only "reset" will work   correctly!
      $(Q)$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \
               -f board/$(OOCD_BOARD).cfg \
               -c "init" -c "reset init" \
             **-c "stm32x mass_erase 0" \**
               -c "flash write_image $(*).hex" \
               -c "reset" \
               -c "shutdown" $(NULL)
else

Y en la primera parte donde veamos el texto antesrior borramos la parte señalada entre asteriscos y luego guardamos

Luego dentro de la carpeta de cada unos de los diferentes ejemplos hay un archivo Makefile al cual le tenemos que agregar las siguientes lineas antes de la ultima linea de código

OOCD_INTERFACE=stlink-v2  
OOCD_BOARD=stm32f4discovery

Hacemos un mass erase con el STM conectado

st-flash erase

Ahora si podemos proceder a Flashear alguno de los ejemplos, desde dentro de alguno de los ejemplos y con el stm conectado hacemos

make
make flash

O para ver en consola lo que sucede hacemos

make V=1 flash

Y verás los Leds del STM encenderse según el ejemplo que hallas escogido

Generar PWM

Para generar el PWM que se enviará a los puentes H, se utilizó como base un ejemplo llamado src1 que se encuentra en la página

https://github.com/arcoslab/open-coroco

Este repositorio debemos descargarlo a la computadora, por lo que hacemos

cd /local/src
git clone https://github.com/arcoslab/open-coroco  

A este se le realizaron algunos cambios y se adaptó para el proyecto que se estaba realizando, a continuación se mostrarán algunas de las partes más importantes del código utilizado:

void tim1_init(void) {

      /* Enable TIM1 clock. and Port E clock (for outputs) */
      rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_TIM1EN);
      rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPEEN);
      
      //Set TIM1 channel (and complementary) output to alternate function push-pull'.
      gpio_mode_setup(GPIOE, GPIO_MODE_AF,GPIO_PUPD_NONE,GPIO9 | GPIO11 | GPIO13);
      gpio_set_af(GPIOE, GPIO_AF1, GPIO9 | GPIO11 | GPIO13);
      gpio_mode_setup(GPIOE, GPIO_MODE_AF,GPIO_PUPD_NONE,GPIO8 | GPIO10 | GPIO12);
      gpio_set_af(GPIOE, GPIO_AF1, GPIO8 | GPIO10 | GPIO12);

Con este código lo que se hace es inicializar los pines correspondientes al reloj del TIM1 y a los canales a utilizar, en este caso se activaron los 3 canales y su respectiva negación. Aquí también se le designa la función alternativa que se quiere que realicen los pines seleccionados, que en este caso es la de timers.

/* Disable outputs. */

      timer_disable_oc_output(TIM1, TIM_OC1);
      timer_disable_oc_output(TIM1, TIM_OC1N);
      timer_disable_oc_output(TIM1, TIM_OC2);
      timer_disable_oc_output(TIM1, TIM_OC2N);
      timer_disable_oc_output(TIM1, TIM_OC3);
      timer_disable_oc_output(TIM1, TIM_OC3N);
     
      /* -- OC1 and OC1N configuration -- */
      /* Configure global mode of line 1. */
      timer_enable_oc_preload(TIM1, TIM_OC1);
      timer_set_oc_mode(TIM1, TIM_OC1, TIM_OCM_PWM1);
      /* Configure OC1. */
      timer_set_oc_polarity_high(TIM1, TIM_OC1);
      timer_set_oc_idle_state_unset(TIM1, TIM_OC1); //When idle (braked) put 0 on output
      /* Configure OC1N. */
      timer_set_oc_polarity_high(TIM1, TIM_OC1N);
      timer_set_oc_idle_state_unset(TIM1, TIM_OC1N);
      /* Set the capture compare value for OC1. */
      timer_set_oc_value(TIM1, TIM_OC1, INIT_DUTY*PWM_PERIOD_ARR);//initial_duty_cycle*pwm_period_ARR);
      
      /* -- OC2 and OC2N configuration -- */
      /* Configure global mode of line 2. */
      timer_enable_oc_preload(TIM1, TIM_OC2);
      timer_set_oc_mode(TIM1, TIM_OC2, TIM_OCM_PWM1);
      /* Configure OC2. */
      timer_set_oc_polarity_high(TIM1, TIM_OC2);
      timer_set_oc_idle_state_unset(TIM1, TIM_OC2);
      /* Configure OC2N. */
      timer_set_oc_polarity_high(TIM1, TIM_OC2N);
      timer_set_oc_idle_state_unset(TIM1, TIM_OC2N);
      /* Set the capture compare value for OC2. */
      timer_set_oc_value(TIM1, TIM_OC2, INIT_DUTY*PWM_PERIOD_ARR);//initial_duty_cycle*pwm_period_ARR);
      
      /* -- OC3 and OC3N configuration -- */
      /* Configure global mode of line 3. */
      timer_enable_oc_preload(TIM1, TIM_OC3);
      timer_set_oc_mode(TIM1, TIM_OC3, TIM_OCM_PWM1);
      /* Configure OC3. */
      timer_set_oc_polarity_high(TIM1, TIM_OC3);
      timer_set_oc_idle_state_unset(TIM1, TIM_OC3);
      /* Configure OC3N. */
      timer_set_oc_polarity_high(TIM1, TIM_OC3N);
      timer_set_oc_idle_state_unset(TIM1, TIM_OC3N);
      /* Set the capture compare value for OC3. */
      timer_set_oc_value(TIM1, TIM_OC3,INIT_DUTY*PWM_PERIOD_ARR);//initial_duty_cycle*pwm_period_ARR);//100);
      
      /* Reenable outputs. */
      timer_enable_oc_output(TIM1, TIM_OC1);
      timer_enable_oc_output(TIM1, TIM_OC1N);
      timer_enable_oc_output(TIM1, TIM_OC2);
      timer_enable_oc_output(TIM1, TIM_OC2N);
      timer_enable_oc_output(TIM1, TIM_OC3);
      timer_enable_oc_output(TIM1, TIM_OC3N);

Este pedazo de código lo que hace es configurar las salidas de los canales del TIM1 y sus respectivas salidas negadas las cuales son llamadas como OC1 ó OC1N según corresponda el canal. cabe destacar que para el funcionamiento del TIM8 se realizó algo muy parecido, lo que cambian son los pines a utilizar y algunas funciones que son diferentes para el TIM8 respecto al TIM1, como por ejemplo la función que realiza las interrupciones. A continuación se mostrará la diferencia

void tim1_up_tim10_isr(void) {
  // Clear the update interrupt flag
  timer_clear_flag(TIM1,  TIM_SR_UIF);
  gen_pwm();
}

void tim8_up_tim13_isr(void) {
  // Clear the update interrupt flag
  timer_clear_flag(TIM8,  TIM_SR_UIF);
  gen_pwm1();
}

Ahora les voy a mostrar el código que realiza el PWM dependiendo de las entradas que tenga

void gen_pwm(void) {
  
  duty_a=ref_freqA/10.0f;
  duty_b=ref_freqB/10.0f;
  duty_c=ref_freqC/10.0f;
  
  if (duty_a < 0.0f)
    {
      timer_set_oc_mode(TIM1, TIM_OC1, TIM_OCM_PWM1);
      timer_disable_oc_output(TIM1,TIM_OC1);
      timer_enable_oc_output (TIM1, TIM_OC1N);
      duty_a=-duty_a;
    }
  else
    {
      timer_set_oc_mode(TIM1, TIM_OC1, TIM_OCM_PWM1);
      timer_enable_oc_output(TIM1, TIM_OC1 );
      timer_disable_oc_output (TIM1, TIM_OC1N);
    }
  

Aquí podemos ver que si la entrada que recibe duty_a que es el valor que se le designó a una de las llantas en específico es menor que cero lo que hace es que activa la salida negada del canal correspondiente, lo que hará que el motor gire hacia atrás.esto se realiza para cada una de los canales utilizados.

Teniendo el código listo, procedemos a guardarlo en una carpeta dentro de la carpeta de open-coroco, luego de esto podemos copiar del ejemplo antes mencionado (src1) el archivo Makefile y el Makefile.include. Al Make hay que realizarle un cambio importante; lo que hacemos es que abrimos el documento y veremos algo como esto

BINARY = "nombre de su archivo .c"
OBJS :=
LDSCRIPT = ../libopencm3-plus/lib/libopencm3_plus_stm32f4discovery.ld
OOCD_INTERFACE = stlink-v2
OOCD_BOARD = stm32f4discovery
include Makefile.include

Y lo que hacemos es que donde dice “BINARY=” colocamos el nombre de nuestro archivo.c

Cuando hacemos en make en la carpeta de nuestro código veremos que nos da unos “warnings” de que las funciones utilizadas no están previamente definidas, esto se debe a que hace falta un archivo con el nombre de nuestro archivo.c, pero con la extención .h ; este archivo lo que contiene es las funciones creadas por nosotros en el archivo.c, deberá contener algo similar a esto

void tim8_init(void);
void tim1_init(void);
void gen_pwm(void);

Y aqui debe estar cualquier otra función que hallamos creado. Luego de tener todo esto prodecemos a flashear nuestro STM32 como se explicó anterirmente.

Estas salidas de PWM serán enviadas a los puentes H. Para este proyecto se utilizó el puente H L298n, que es un integrado que posee 2 puentes H. La conexión que se realizó la podemos ver en la hoja del fabricante que se puede encontrar en el siguiente link

https://www.sparkfun.com/datasheets/Robotics/L298_H_Bridge.pdf

Lectura de los datos del Joystick

Las llantas omnidireccionales basan su movimiento en las siguientes ecuaciones

  • V1 = Vd*sen(Ø+π/4)+Vø
  • V2 = Vd*sen(Ø+π/4)-Vø
  • V3 = Vd*sen(Ø+π/4)-Vø
  • V4 = Vd*sen(Ø+π/4)+Vø

A continuación vamos a ver que significa cada una de ellas

  • Vd = magnitud de la velocidad de las llantas
  • Ø = ángulo que da la dirección
  • Vø = magnitud de la velocidad de giro

Cada uno de los datos los vamos a leer de los Joysticks, estos rigen sus movimientos dentro de un círculo unitario, estos datos las vamos a obtener mediante coordenadas polares, tomando en cuenta que los joysticks lo que nos da es una lectura de “x” y “y”, hacemos

* Vd = sqtr(x²+y²)
* Ø = atan2(x+y)

Sabiendo esto lo que ahora necesitamos es es leer los datos del joystick, esto lo hacemos mediante Pygame, para lo que necesitamos instalar lo siguiente

sudo apt-get install python
sudo apt-get install python-pygame

Con esto instalado y el siguiente código obtendremos los datos deseados

#!/usr/bin/env python
import pygame
import math
# Define some colors
BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)

# This is a simple class that will help us print to the screen
# It has nothing to do with the joysticks, just outputing the
# information.
class TextPrint:
    def __init__(self):
        self.reset()
        self.font = pygame.font.Font(None, 20)
        
    def printd (self, screen, textString):
        textBitmap = self.font.render(textString, True, BLACK)
        screen.blit(textBitmap, [self.x, self.y])
        self.y += self.line_height
      
    def reset(self):
        self.x = 10
        self.y = 10
        self.line_height = 15
      
    def indent(self):
        self.x += 10
      
    def unindent(self):
        self.x -= 10  
          
pygame.init() 

#Loop until the user clicks the close button.
 done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# Initialize the joysticks
pygame.joystick.init()    
# Get ready to print

textPrint = TextPrint()

# -------- Main Program Loop -----------
while done==False:
    # EVENT PROCESSING STEP
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done=True # Flag that we are done so we exit this loop
            
    # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
        if event.type == pygame.JOYBUTTONDOWN:
            print("Joystick button pressed.")
            
      if event.type == pygame.JOYBUTTONUP:        
            print("Joystick button released.") 
                    
    # Get count of joysticks
    joystick_count = pygame.joystick.get_count()
       
    # For each joystick:
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()  
             
    #Usually axis run in pairs, up/down for one, and left/right for the other
      
        axes = joystick.get_numaxes()
      
        for i in range( axes ):
            axis = joystick.get_axis( i )
                
        y=joystick.get_axis(3);
        x=joystick.get_axis(2);
        
        velocidad= math.sqrt(x**2+y**2);
        direccion=math.atan2(y,x);
     
        v1=-(velocidad*math.sin(direccion+(math.pi/4)));
        v2=velocidad*math.cos(direccion+(math.pi/4));
        v3=velocidad*math.cos(direccion+(math.pi/4));
        v4=-(velocidad*math.sin(direccion+(math.pi/4)));
        print v1
        print v2
        print v3
        print v4        
         
       # Limit to 20 frames per second
       clock.tick(20)    
       pygame.quit ()

Ya con esto vamos a obtener los datos y de una vez la velocidades necesitadas para el funcionamiento de las llantas omnidireccionales

Comunicación entre cada una de las partes

Comunicación entre la computadora y el Pi

Como la comunicacion entre la computadora y el RaspberryPi será mediante WI FI, se utilizará YARP para poder enviar los datos de las velocidades y dirección tomadas del joystick, por lo que tendremos que instarlar los siguiente tanto en el raspberrypi como en la computadora

XSTOW

YARP

Esto se hará siguiendo la siguiente guía

http://arcoslab.eie.ucr.ac.cr/dokuwiki/doku.php?id=installing_yarp_in_debian

Con esto instalado, se enviarán los datos de las velocidades mediante un bottle

Comunicación entre el Pi y el STM32

esta se realizará mediante Piserial

Comunicación entre el Pi y la computadora para Video

Streaming usando NetCat

Para probar el streaming del Rpi se utilizó Raspivid junto con NetCat en el Raspberry Pi lo que va a transmitir video a través de un puerto específico y en una computadora, con NetCat y algún reproductor de video como MPlayer o VLC, se puede ver el video que se transmite en tiempo real.

  • Para transmitir video se ejecuta en un consola en el Rpi el comando:
raspivid -t 0 -o - | nc -l 5001

El 5001 es un puerto que se escoje para hacer la transmisión, se puede escoger cualquier otro puerto, de preferencia de 4 dígitos porque los puertos de menos dígitos generalmente están reservados para alguna función. El -l transmite a cualquier computadora que esté “escuchando” a ese puerto en específico.

  • Una vez ejecutado este comando, la cámara debería estar funcionando continuamente (luz roja de la cámara encendida).
  • Ahora, para ver el video desde la computadora se ejecuta el comando:
nc [dirección IP del Rpi] 5001 | mplayer -fps 200 -demuxer h264es -

El comando demuxer h264es descomprime la imagen proveniente del Rpi para que se pueda ver correctamente.

teaching/ie0117/proyectos/2013/ii/carro_espia.txt · Last modified: 2022/09/20 00:08 (external edit)