MIRO es una plataforma abierta de robot de interior. Parte 4 - Componente de software: ARDUINO (AVR)

imagen

Continuamos desmontando el componente de software de la plataforma MIRO. Me gustaría examinar con más detalle exactamente el software bajo AVR. Por lo tanto, dedicaremos dos partes al tema. En el primero, describimos la estructura general de la biblioteca, y en el segundo, la implementación de algunos métodos de clase clave.

Tabla de contenido: Parte 1 , Parte 2 , Parte 3 , Parte 4 , Parte 5 .

El software bajo ARDUINO resultó ser el más grande de los autoescritos. En general, casi toda la lógica de trabajar directamente con actuadores y sensores del robot recae en el AVR. Y a este nivel, se implementa API: una biblioteca de software de desarrollo rápido para MIRO.

La estructura API se describe en la sección wiki del repositorio correspondiente. Hasta ahora, solo en ruso. Y ahora analizaremos el código con más detalle. Deliberadamente no daré una declaración completa de clases, reduciéndola con los puntos suspensivos "...", dejando solo cosas que son significativas en este momento.

En nuestro modelo de software, cada robot MIRO consta de un chasis y un conjunto de dispositivos conectados. Al diseñar, se asumió que el chasis del robot, siempre será una especie de chasis con ruedas, los robots que caminan o los robots que usan otros principios de movimiento deben considerarse por separado.

class Miro : public Robot { public: Miro(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) Miro(byte *PWM_pins, byte *DIR_pins, byte *ENCODER_pins); #endif ~Miro(); ... }; 

La clase Miro es una clase de nivel superior y describe la configuración completa del robot. Esta clase es la descendiente de la clase Robot, que describe solo la funcionalidad más básica del robot.

 class Robot { public: Robot(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) Robot(byte *PWM_pins, byte *DIR_pins, byte *ENCODER_pins); #endif ~Robot(); Chassis chassis; void Sync(); int attachDevice(Device *dev); int dettachDevice(Device *dev); ... protected: Device* _devices[ROBOT_MAX_DEVICES]; byte _device_count; }; 

El diseñador realiza la configuración inicial de los pines del chasis y los valores iniciales de la configuración del robot.

El método Sync () implementa las operaciones necesarias para el chasis y para todos los dispositivos conectados al robot, cada paso del ciclo del bucle principal () del boceto ARDUINO. Los métodos Miro Sync () de la clase Miro invocan los métodos Sync () correspondientes del chasis y todos los dispositivos conectados al robot.

La clase Robot también contiene un puntero a una matriz de dispositivos conectados al robot, métodos para trabajar con esta matriz (conectar un nuevo dispositivo, desconectar, buscar por índice y nombre). La clase Robot también contiene un objeto de la clase Chasis: el chasis.

Pero comencemos con algo más simple: con dispositivos. Cada dispositivo que se puede conectar al robot, ya sea un LED, un sensor o un actuador que no esté directamente relacionado con el chasis (carro), se describe por su clase sucesora, que es común para todos los dispositivos en la clase de dispositivo virtual:

 class Device { public: virtual void Sync(); virtual void setParam(byte pnum, byte *pvalue); virtual void getParam(byte pnum, byte *pvalue); virtual byte getPinsCount(); virtual char* getName(); virtual byte getParamCount(); protected: byte *pins[2]; }; 

Los métodos virtuales setParam, getParam, getParamCount están asociados con la asignación, recepción y determinación del número de parámetros del dispositivo. El parámetro puede ser cualquier propiedad: el brillo del LED, la posición del servoaccionamiento, etc. La clase sucesora de cada dispositivo implementa estos métodos a su manera. El propósito de los métodos getName, getPinsCount, creo, está claro por el nombre. El método Sync recién encontrado es un método especial para el control de dispositivos sin bloqueo y la automatización de algunas operaciones con el dispositivo que deben realizarse regularmente, cada iteración del bucle principal.

Veamos ahora una implementación más o menos general de la clase descendiente.

 class MIROUsonic : virtual public Device { public: void Sync(); void setParam(byte bnum, byte *pvalue); void getParam(byte bnum, byte *pvalue); byte getPinsCount(); char* getName(); byte getParamCount(); void Init(byte trig_pin, byte echo_pin); void On(unsigned int max_dist); void On(); void Off(); int getDist(unsigned int max_dist); unsigned int getMesCount(); private: bool _isOn; unsigned int _mesCount; unsigned int _dist; unsigned int _max_dist; }; 

Al determinar la clase de un telémetro ultrasónico (arriba), además de los métodos de los padres, también hay métodos:

  • Init - inicialización;
  • Encendido y apagado: enciende el dispositivo (telémetro);
  • getDist: devuelve la distancia medida por el buscador de rango;
  • getMesCount: devuelve el número de mediciones realizadas desde que se encendió el dispositivo.

Para almacenar el estado interno del dispositivo, se utilizan los siguientes campos:

  • _isOn (VERDADERO - el dispositivo está encendido, controlado por los métodos de encendido y apagado);
  • _mesCount (almacena el número de dimensiones utilizadas en el método getMesCount);
  • _max_dist: distancia máxima requerida para la medición *;
  • _dist es la distancia medida real.

Sobre el rango de medición máximo
* Se sabe que el extenso HC-SR04 según el pasaporte es capaz de medir distancias de hasta 4 metros. Sin embargo, el método de medición en sí implica esperar el retorno de la señal ultrasónica, seguido de la codificación de la duración de la señal en la línea Echo. Y, de hecho, si el usuario definitivamente no necesita medir distancias en el rango de hasta 4 metros, pero suficiente, el rango, digamos 1 metro, entonces puede esperar 4 veces menos la señal reflejada. El telémetro mismo genera una señal en la línea Echo tan pronto como la recibe y realiza la modulación. Es decir Esto puede no afectar la duración del período entre mediciones adyacentes, pero la duración de una sola medición de esta manera se puede reducir.

Y ahora para la explicación del método de sincronización. Si el dispositivo tiene el estado _isOn == TRUE (activado), el ciclo de medición se realizará en el método Sync y el resultado de la medición se registrará en el campo _dist. En este caso, cuando llame a getDist, el método devolverá inmediatamente el valor registrado en _dist, no habrá ciclo de medición. Si _isOn == FALSE (off), el ciclo de medición, por el contrario, se realiza solo durante la llamada a getDist, no se medirá nada en el método de sincronización. Se supone que el programador llamará al método Sync de todo el robot, que a su vez llamará a los métodos Sync del mismo nombre de todos los dispositivos conectados al robot y un objeto de la clase Chassis (chasis).

De los dispositivos en la API, solo las cosas que MIRO tiene ahora están implementadas: LED, buscador de rango ultrasónico, sensor de luz fotorresistivo, servoaccionamiento, sensor de línea.

Toque ligeramente el chasis. Esta clase implementa el "carro abstracto" del robot. Contiene métodos que le permiten controlar los motores.

 class Chassis { public: Chassis(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) Chassis(byte *PWM_pins, byte *DIR_pins, byte *ENCODER_pins); #endif ~Chassis(); void Sync(); float getVoltage(); int wheelRotatePWMTime(int *speedPWM, unsigned long time); int wheelRotatePWM(int *speedPWM); bool wheelIsMoving(byte wheel) {return this->_wheel_move[wheel];} byte getWheelCount() { return WHEEL_COUNT; } #if defined(ENCODERS_ON) int wheelRotateAng(float *speed, float *ang, bool en_break); unsigned long wheelGetEncoder(byte wheel); ... #endif //ENCODERS_ON private: float _vbat; //Battery volgage bool _wheel_move[WHEEL_COUNT]; char _wheelDir[WHEEL_COUNT]; byte _wheel_PWM_pins[WHEEL_COUNT]; byte _wheel_DIR_pins[WHEEL_COUNT]; void _init(byte *PWM_pins, byte *DIR_pins); #if defined(ENCODERS_ON) byte _wheel_ENCODER_pins[WHEEL_COUNT]; bool _wheel_sync_move; float _wheelAngSpeed[WHEEL_COUNT]; float _wheelSetAng[WHEEL_COUNT]; float _wheelSetAngSpeed[WHEEL_COUNT]; ... #endif //ENCODERS_ON }; 

Si consideramos un carro sin codificadores y generalmente sin retroalimentación, entonces existen métodos de control simples para esto usando una señal PWM. Si hay codificadores en el carro, la clase se vuelve mucho más complicada. Para simplificar la vida del usuario, los métodos como aparecen en él:

  • wheelRotateAng - rotación de la rueda en ángulos de rotación predeterminados con velocidades angulares dadas;
  • wheelGetPath: devuelve la longitud de la ruta recorrida por cada rueda;
  • wheelGetLinSpeed: devuelve la velocidad lineal actual de cada rueda;
  • wheelGetAngSpeed: devuelve la velocidad angular actual de cada rueda;
  • wheelGetEncoder: devuelve el número de respuestas de los codificadores de cada rueda.

Y una serie de métodos auxiliares. Así como un método de calibración de propulsión. Pero con más detalle, los métodos clave de la clase Chassis se considerarán la próxima vez.

Corriendo un poco más adelante, es en este lugar donde será apropiado notar que esta biblioteca completa de Miro se puede adaptar o complementar fácilmente con cualquier otro robot con un esquema de movimiento diferencial de dos ruedas. Y con cierto esfuerzo, y con otras configuraciones de propulsión y dirección. En el caso de un circuito diferencial, solo necesita describir correctamente el archivo de configuración config.h. Y sin ningún RPi. Por ejemplo, en menos de una hora, lanzamos todo en tan pequeños para nuestro torneo regional de ciberseguridad BlackMirrorCTF-2019 en nuestra universidad ( enlace ).

imagen

Los robots tenían una interfaz de acceso a través de TELNET y un sistema de comando para control remoto. El documento con el sistema de comando estaba oculto o codificado en algún lugar. Los participantes escanearon las direcciones IP y abrieron puertos en los propios robots. Con una conexión exitosa, los robots emitieron una invitación y los participantes entendieron que habían "entrado". Bueno, entonces los equipos llevaron los robots a lo largo de la carretera hasta la línea de meta. Inicialmente, querían hacer toda la pista con robots en algún lugar de una habitación aislada con una cámara IP instalada, pero los organizadores tuvieron algunos problemas con la cámara IP y perdieron parte del encanto.


Eso es todo por ahora. Es posible que en el curso del desarrollo, el modelo del programa sufra cambios. Por cierto, recientemente se ha modificado ligeramente, después de que el código parecía un poco más experimentado OOP-shchik.

Antes de la quinta parte, hablemos de codificadores, ángulos y calibraciones.

Source: https://habr.com/ru/post/475512/


All Articles