Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Profesor Guı́a:
Roberto Ramirez
La Dirección del Sistema de Bibliotecas a través de su encargado Biblioteca Campus Curicó certifica
que el autor del siguiente trabajo de titulación ha firmado su autorización para la reproducción en
Curicó, 2022
Junio, 2021
Gonzalo Rojas Garcı́a
Diseño y construcción de drone para tareas de inspección en mantenimiento
Resumen
En la presente memoria se desarrolla y expone los puntos más relevantes sobre el
proyecto de diseño y construcción de drone para tareas de inspección en mantenimiento,
tomando en cuenta desde una etapa temprana considerando cálculos y simulaciones
teóricas hasta el desarrollo correspondiente a la construcción de un prototipo orientado
al control sobre la variable de altura.
Se propone la integración de un control PID, en conjunto con el desarrollo de un
sistema basado en la metodologı́a ROS a través de comunicación inalámbrica con un
computador maestro para realizar la estimación de posición del prototipo.
Del trabajo realizado y mediante la experimentación con el prototipo construido se
obtuvo resultados que permiten identificar puntos de mejora y sus posibles alternativas
para un desarrollo futuro.
Dedicado a
todos aquellos que hicieron esta memoria posible
Agradecimientos
Primero que todo agradezco a mis padres Carlos y Raquel por todo su apoyo en mi formación
tanto profesional como persona, a mi polola Belén por motivarme y apoyarme durante estos
años, además de ser un pilar fundamental en cada uno de estos logros. Agradezco a mi
hermano Francisco, por creer en mis capacidades y brindarme su apoyo en todo momento,
a mi abuela Marı́a Eliana y a mi familia en general por apoyarme en lo que fuese necesario
para el logro de esta meta. Por ultimo y no menos importante agradezco a mi profesor guı́a
Roberto Ramı́rez y al profesor Daniel Diaz por su excelente disposición y constante apoyo a
mi formación como Ingeniero, por sobre todo en el desarrollo de la presente memoria.
1 Introducción 1
1.1 Introducción general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Estado del arte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.1 Gestión de activos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.2 Vehı́culos aéreos no tripulados (UAV) . . . . . . . . . . . . . . . . . . 3
1.2.3 Estrategias de control . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.4 Discusión estado del arte . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.5 Objetivo general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.6 Objetivos especı́ficos . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3 Alcances y limitaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3.1 Alcances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3.2 Limitaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4 Metodologı́a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4.1 Revisión bibliográfica . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4.2 Modelamiento y simulación matemática . . . . . . . . . . . . . . . . . 14
1.4.3 Diseño y selección de componentes . . . . . . . . . . . . . . . . . . . 14
1.4.4 Resultados experimentales . . . . . . . . . . . . . . . . . . . . . . . . 14
1.5 Temario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2 Desarrollo teórico 16
2.1 Modelamiento matemático . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.1.1 Sistemas de referencia . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.1.2 Comportamiento del UAV . . . . . . . . . . . . . . . . . . . . . . . . 18
2.1.3 Algoritmo de mezcla de motores . . . . . . . . . . . . . . . . . . . . . 21
2.2 Simulación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.2.1 Parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.2 Controlador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.3 Simulación con control sobre Roll, Pitch, Yaw y Thrust . . . . . . . . 23
2.2.4 Simulación con control sobre posición X, Y y Z . . . . . . . . . . . . 27
3 Etapa de diseño 30
3.1 Componentes necesarios para construccion de un UAV . . . . . . . . . . . . 30
3.1.1 Motores brushless . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.1.2 ESC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.1.3 Controlador de vuelo . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.1.4 Baterı́a LiPo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.1.5 Hélices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.1.6 PDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.1.7 Frame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.1.8 IMU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.9 Barómetro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.10 GPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
iv
Diseño y construcción de drone para tareas de inspección en mantenimiento
4 Etapa de construcción 43
4.1 Ensamble mecánico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2 Conexionado electrónico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.3 Programación y control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.3.1 Verificación de módulos . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.3.2 Transcripción de bibliotecas . . . . . . . . . . . . . . . . . . . . . . . 61
4.3.3 Generación de PWM . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3.4 ROS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.3.5 Algoritmo de control . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.3.6 Comunicación entre tarjetas . . . . . . . . . . . . . . . . . . . . . . . 78
5 Resultados 80
5.1 Revisión del funcionamiento del sistema integrado . . . . . . . . . . . . . . . 80
5.2 Primera prueba de vuelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
5.3 Control con compensación manual . . . . . . . . . . . . . . . . . . . . . . . . 85
Referencias 93
Anexos 96
Ñ Código con monitoreo por puerto serial para controlador de vuelo 183
vii
Diseño y construcción de drone para tareas de inspección en mantenimiento
ix
Diseño y construcción de drone para tareas de inspección en mantenimiento
Acrónimos
ABS Acrylonitrile butadiene styrene
DC Direct Current
PD Proportional-Derivative
PDB Power Distribution Board
PETG Polyethylene Terephthalate Glycol
PI Proportional-Integral
PID Proportional-Integral-Derivative
PLA Polylactic acid
PPM Pulse-Position Modulation
PPM Planed Preventive Maintenance
PWM Pulse Width Modulation
1. Introducción
1.1. Introducción general
El mantenimiento es comúnmente entendido como el realizar reparaciones en la maquina-
ria cuando esta falla, o pasa un determinado tiempo o ciclo dado por el fabricante u obtenido
de forma empı́rica, como por ejemplo el realizar cambio de aceite en un automóvil cada 10000
km recorridos o el cambio de plumillas al notar que no limpian adecuadamente el parabrisas
respectivamente. No obstante, muchas veces las fallas se producen antes de cumplir dichos
periodos, ya sea por una mala operación del equipo o por defectos de fábrica en los com-
ponentes. Es por ello que se hizo necesario incluir dentro de las tareas de mantenimiento
herramientas que permitan prever el comportamiento de los diversos elementos en el equipo
y anteponerse a las fallas de estos.
Es ası́, como surge la inclusión de técnicas tecnológicas tales como el análisis de vibracio-
nes, análisis termográfico y análisis de partı́culas en aceites; ası́ como el uso de la inspección
visual, que, como lo define la revista inspectioneering [1], corresponde al método más básico y
antiguo de inspección y se realiza observando directamente el equipo en busca de indicadores
de falla. Se señala además que se requiere la experiencia del inspector para que el método
sea completamente efectivo. Un ejemplo del uso de este tipo de inspección, es la facilidad
para identificar fallas en tuberı́as, como se indica en un artı́culo de la empresa metalúrgica
pipemasters [2], que, si bien se indica el uso de esta inspección en el proceso de soldadura, se
puede extrapolar a la inspección en operación, como punto de partida para la identificación
de potenciales fallas, esto dada a la ventaja de un bajo costo en su realización permitiendo
enfocar los análisis de mayor complejidad y costo, solo en las zonas ya selecciónadas. Como
lo señala el Instituto Renovetec de Ingenieria del Mantenimento (IRIM), organización dedi-
cada a mejorar el mantenimiento en la industria [3], el mantenimiento predictivo involucra
la implementación de las técnicas anteriormente mencionadas para realizar un análisis que
permita predecir las fallas en un equipo. A pesar de ello, estas técnicas poseen tanto puntos a
favor, como en contra. Y por ello surge el problema a analizar mediante el presente proyecto.
Al enfocarse en 2 de los tipos de inspección declarados anteriormente (inspección ter-
mográfica e inspección visual), se puede establecer que si bien son métodos que se realizan
de forma distinta ambos tienen una limitante en común, la cual es que se debe ver de forma
directa en el punto a inspeccionar y, dependiendo de la resolución del instrumento (cámara
o cámara termográfica), se requiere de cierta cercanı́a a dicha zona.
Ası́, el problema que se presenta se puede resumir como la necesidad de acceder a zonas
remotas o de difı́cil acceso para el mantenedor, ya sea zonas donde se requiera un trabajo
en altura o lugares en los que el espacio existente es muy pequeño para el trabajo seguro,
mediante un equipo que permita realizar la captura de la información requerida para su
posterior análisis y toma de decisiones respectiva.
Es por lo anterior, que este proyecto busca presentar una solución a dicha problemática
empleando la tecnologı́a de vehı́culos aéreos no tripulados para realizar la toma de datos
resolviendo la dificultad de acceso para el mantenedor, como se detallará más adelante.
Es importante resaltar que, si bien la motivación, enfoque y justificación del presente
proyecto corresponde a la integración de una determinada tecnologı́a a un nuevo campo
de aplicación, dada la situación actual de contingencia sanitaria en la que se encuentra
el mundo, el proyecto considera en su totalidad el diseño del prototipo y solo parte del
el mantenimiento preventivo cı́clico (el cual puede ser basado en tiempo o en edad), el
mantenimiento preventivo basado en condición y el mantenimiento preventivo predictivo,
donde en los dos últimos uno de los puntos más importantes consiste en la inspección, donde
su principal diferencia radica que en el último se incluye el uso de estadı́stica. Fuera de esta
última distinción, se resalta la primordial importancia de la inspección, la cual puede ser
realizada de diversas formas (dependiendo de la variable que se desea monitorear).
Por otro lado, en paralelo, dichos tipos de mantenimiento pueden tener la caracterı́stica
de ser posibles en uso, lo cual dependerá, principalmente, del equipo y variable que se desea
controlar. Algunos ejemplos del mantenimiento que puede ser realizado en uso corresponde a
los valores entregados por manómetros en zonas de presión, indicadores de nivel en estanques,
métodos de verificación de rpm en motores, entre otros. Los cuales pueden ser verificados de
forma directa para luego ser registrados, catalogados dentro de la inspección visual. Según [3]
es un método sencillo y económico por lo que se recomienda realizar de forma frecuente para
tener una base de datos adecuada a las predicciones a realizar.
Dentro de las tareas, dos de los métodos comúnmente utilizados son la inspección vi-
sual y la termografı́a, puesto que dichos métodos permiten realizar inspección de forma
relativamente remota y permiten detectar problemas fácilmente mientras el equipo está en
funcionamiento. En el caso de la inspección visual, permite verificar grietas, fugas y otro tipo
de defectos que pueden ser vistos directamente por las personas. Por otro lado, la termografı́a
permite verificar las concentraciones de calor mediante una cámara con sensor térmico, lo
que permite ver las concentraciones de calor en ciertos elementos del equipo como tuberı́as
o motores y asociarlas a problemas comunes en estos como lo son el exceso de roce en una
tuberı́a de transporte o un motor que está siendo sobre esforzado por algún problema en el
sistema de transmisión o en sus espiras, entre otras posibilidades [3].
Respecto a los UAV de ala fija, presentan la ventaja de que debido a su aerodinámica
pueden permitirse un tiempo de vuelo más extenso en comparación de sus contrapartes
ahorrando el consumo de energı́a, pero tienen en contra la necesidad de ser impulsados de
forma manual para el despegue y no pueden mantener una posición fija en el aire hover. Por
otro lado, los UAV de hélice rotatoria, si bien presentan menor tiempo de vuelo, presentan
mayor versatilidad gracias a su capacidad de despegue autónomo y un movimiento más
preciso incluyendo la posibilidad de mantener su posición [10].
Este último tipo de UAV funciona usando el giro rápido de un rotor que impulsa el aire
hacia abajo permitiendo generar una fuerza que lo pueda elevar. Su versión más conocida
corresponde a los helicópteros, los que poseen un rotor principal para generar el impulso
hacia arriba y un rotor de cola que le permita rotar y producir un torque opuesto al del
rotor principal para lograr estabilidad. No obstante, su construcción requiere de elementos
complejos para controlar su posición como el swashplate que permite cambiar el ángulo
de ataque del rotor principal [11]. Es por ello que surge otro tipo de UAV que simplifican
dicho control mediante la adición de rotores a su construcción. Debido a ello existen los
tricópteros, cuadricópteros, hexacópteros y octacópteros, los que poseen 3, 4, 6 y 8 rotores
respectivamente [10]. Dentro de estos dispositivos, los más populares y comunes son los
cuadricópteros, los cuales mediante 4 inputs (o entradas), pueden controlar los 6 grados de
libertad del UAV (3 angulares y 3 lineales). En la imagen obtenida de [12], presentada en
Fig. 1.2, se presentan los distintos tipos de UAV de hélice rotatoria considerando número y
disposición de sus hélices.
De estas clasificaciones se tiene, en primer lugar, los controles de tipo lineal robusto,
los cuales corresponden a los tipos de control más simple dentro de esta clasificación, no
obstante, su resultado es suficiente para realizar la estabilización de un UAV. Dentro de esta
categorı́a se encuentran los controles de tipo Proportional-Integral-Derivative (PID), control
lineal cuadrático y control en el espacio de Hardy, conocido también como H∞ .
En el caso del control PID, como se señala en [18], corresponde a un control con retroali-
mentación negativa y, en donde el error es modificado mediante una constante proporcional
permitiendo evitar que el sistema sobre reaccione ante los cambios en la variable a con-
trolar. Dicho error también es modificado mediante una acción integral, cuyo objetivo es,
principalmente, lograr un error nulo en estado estacionario. Finalmente, se aplica una acción
derivativa, la cual permite aumentar la estabilidad del sistema en lazo cerrado realizando
una estimación del error en el futuro, corrigiendo la demora inherente al tiempo que tarda
en cambiar la salida respecto del cambio en la entrada. Entre las ventajas de este tipo de
control, se puede señalar su facilidad de implementación y sintonización, siendo esta última,
posible de realizar incluso de forma empı́rica. En contraste su principal desventaja, orientada
a la presente aplicación, corresponde a que el modelo más cercano al comportamiento real de
un UAV se trata de un sistema no lineal sub-actuado, por lo cual, al aplicar un control lineal
(como lo es el PID), se debe linealizar el sistema utilizando las ecuaciones de Euler-Lagrange.
Ası́, mediante este control, como se señala en [17], se logra un comportamiento adecuado
para estabilizar el UAV mientras la situación no difiera mucho del modo hover.
En cuanto al control lineal cuadrático, se profundizará en particular respecto al Lineal-
Quadratic Regulator (LQR), el cual, como se señala en [19], corresponde a una estrategia de
control que facilita la solución del problema dinámico para sistemas continuos, realizándolo
mediante una función de costo y formulándolo como un sistema lineal invariante respecto del
origen. Entre sus ventajas, como se señala en [17], es posible conseguir un seguimiento de la
referencia sin error estacionario. No obstante, entre sus desventajas se presenta la necesidad
de un modelo muy preciso, debido a que en caso contrario su desempeño se ve considera-
blemente disminuido, además de ser un tipo de control poco tolerante a las perturbaciones
externas.
Respecto del control H∞ , como se señala en [20], todos los sistemas tienen incertezas
asociadas, difı́ciles de medir y por lo tanto de modelar. Es por ello que se desarrolla el
control H∞ , el cual se centra en el desempeño y la estabilidad de un sistema, logrando un
mejor comportamiento en lazo cerrado respecto del control PID. Como se indica en [17], para
el uso de este control se plantea el sistema como un problema de optimización matemática.
Entre sus ventajas, se encuentra la capacidad de realizar estimaciones precisas a pesar de las
incertezas y perturbaciones con una saturación limitada de los actuadores. Por otro lado, su
desventaja radica en la complejidad de su implementación y su necesidad (en algunos casos)
de apoyarse en otro tipo de control, como lo es el predictivo.
Como se señaló anteriormente, el comportamiento de un UAV se puede modelar como un
sistema no lineal sub actuado, por lo cual, a continuación, se presenta una serie de controles
de tipo no lineal que se encuentran entre los más usados para el control de un UAV.
En primer lugar, se tiene el método de linealización por realimentación, el cual, como se
indica en [21], corresponde a la idea más práctica para el diseño de control no lineal. Como
se señala en [17], en este método se transforma el sistema no lineal en un sistema equivalente
lineal, y mediante esta transformación se produce una matriz no singular. Ası́, si bien este
método de control corresponde a un método no lineal para un sistema de este mismo tipo,
presenta desventajas que radican al ruido de sensado, lo cual provoca acciones de control
erróneas.
Por otro lado, se presenta el control de tipo backstepping, que como se presenta en [22], este
método permite resolver problemas de control robusto de forma poco restrictiva, consiguiendo
un error de seguimiento muy pequeño. En [17], se presenta este tipo de control como una
técnica que se construye con base a subsistemas que se pueden estabilizar mediante otros
métodos. Se parte de un sistema estable y se itera añadiendo nuevos controladores hasta
lograr la estabilización de todos los subsistemas. Como ventaja, este tipo de controlador
permite un control preciso en el área de los UAV y comparándolo con el control PID, reduce
el error de posición compensando la aceleración angular en forma inmediata. No obstante,
su implementación es más compleja respecto a dicho controlador.
Finalmente, en cuanto a los controles no lineales, se tiene el control de modo deslizante;
el cual, como se señala en [23], se define una superficie en la cual, el error se aproxima a
cero de forma asintótica. Este proceso, no tiene noción de economı́a y apunta únicamente a
la funcionalidad. En [17], se presenta que este control, al ser aplicado en UAV, presenta un
desempeño exitoso, no obstante, si bien su desempeño es mejor que el método de linealización
es menos preciso que el backstepping.
La última categorı́a por revisar corresponde a los tipos control catalogados como “in-
teligente”, los cuales poseen particularidad de cubrir un amplio rango de incertidumbre de
forma mucho mejor en comparación a las estrategias vistas anteriormente. Estos consisten
en control predictivo de modelo, control de lógica fuzzy y control de redes neuronales.
En el primer caso, el Model Predictive Controller (MPC), como se presenta en [24],
corresponde a un tipo de control en el que se realiza una predicción en un horizonte dado
para permitir el ajuste de la entrada del sistema. Este control es desarrollado mediante el uso
de herramientas de optimización, como lo es una función de costo. Como se señala en [17],
Finalmente, para el caso del control de tipo de redes neuronales, como se señala en [26],
corresponde a un tipo de control basado en inteligencia artificial y que, a diferencia del control
fuzzy se desarrolla siguiendo una base booleana precisa. Su principio de funcionamiento
radica en el uso de diferentes tipos de neuronas agrupadas en distintas capas, de las cuales
se distinguen 3 tipos, siendo la primera capa la de entrada, la última conocida como capa
de salida y finalmente las n capas de interconexión se denominan capas ocultas. Como se
señala en [17], cada red neuronal presenta una serie de valores conocidos como elementos de
procesamiento, los que presentan un parámetro de peso que determina su comportamiento.
Este tipo de control es altamente fiable en un amplio rango de incertezas, pero presenta
como desventaja la necesidad de encontrar los valores apropiados para los pesos mediante
el “entrenamiento de la red” lo que lo hace más complejo a nivel de implementación. Un
ejemplo de red neuronal se aprecia en la Fig. 1.5.
En base a lo expuesto anteriormente, es que se puede realizar una tabla resumen con
las principales ventajas y desventajas que presenta cada método de control orientado a su
implementación en los UAV, como se observa en 1.1.
Es ası́ que, como se desarrolló previamente, gracias a las ventajas que permite un UAV, y
en particular, los de hélice rotatoria pudiendo realizar el hover en una posición determinada,
es que se decide determinar el uso de un cuadricóptero; esto, debido a que se considera que
es la mejor combinación en cuanto a estabilidad/peso para realizar la reducción de tamaño
necesaria considerando la aplicación de inspección de entornos cerrados, como lo son los
ambientes industriales. Ası́, se considera además, la versatilidad que permite uno de estos
dispositivos para soportar una cámara adecuada y, de esta forma, realizar la captura de
imágenes necesarias en la inspección.
En cuanto al desarrollo de estrategias de control, tomando en cuenta la aplicación y
etapas del proyecto que se describen en la presente memoria, es que se decidió utilizar como
método de control el de tipo PID. Esto debido a que, dadas las condiciones del proyecto,
se busca un control que permita realizar el adecuado posicionamiento del UAV y que a su
vez sea simple y barato (en términos de procesamiento) al momento de implementar. Cabe
destacar que, dado a que el presente proyecto considera el posicionamiento en torno a una
altura definida, no es problema el trabajo en torno al punto de operación definido para estos
efectos, permitiendo ası́ además una sintonización de forma empı́rica sin mayor dificultad.
Diseñar un drone-prototipo que cumpla las caracterı́sticas necesarias para los objetivos
de inspección seleccionando elementos existentes en el mercado optimizando el costo
asociado.
Justificación de su realización.
Diseño de un prototipo que permita cumplir los requerimientos en una etapa experi-
mental, considerando tanto su control como la captura de imágenes.
1.3.2. Limitaciones
El presente proyecto está restringido a:
1.4. Metodologı́a
Para el desarrollo del presente proyecto se hará empleo de las siguientes metodologı́as:
que es necesario conocer los fundamentos de los UAV y el mantenimiento para ası́ facilitar
la integración de estas dos áreas en el desarrollo del prototipo presentado.
Por un lado, se consideró investigar sobre las bases, alcances y limitaciones de los UAV
para ası́ establecer la viabilidad del proyecto previa ejecución. Por otro, se consideró la
investigación sobre el mantenimiento como disciplina y la importancia de la inspección en
este para poder tener un fundamento solido sobre la factibilidad de llevar a cabo el proyecto.
1.5. Temario
El presente documento se encarga de detallar punto a punto el proceso de desarrollo para
el proyecto orientado al diseño y la construcción de un UAV prototipo orientado a realizar
tareas de inspección en mantenimiento. Es por ello que, posterior al análisis de estado del
arte como base para la fundamentación del proyecto, durante el capı́tulo 2 se explica en
detalle el desarrollo teórico de este, incluyendo los modelos matemáticos, seguidos de las
simulaciones. A continuación en el capı́tulo 3 se detalla el proceso de diseño. Posteriormente
en el capı́tulo 4 se desarrolla el proceso de construcción del proyecto. Ası́, en el capı́tulo 4
se presentan los resultados obtenidos del desarrollo completo del proyecto. Y finalmente, en
el capı́tulo 5 se presenta las conclusiones y el trabajo futuro requerido para el desarrollo del
proyecto.
2. Desarrollo teórico
Al momento de realizar una memoria de ingenierı́a, es fundamental realizar un análisis
teórico-matemático que permita justificar los aspectos más relevantes en relación con el
comportamiento de el o los fenómenos fı́sicos involucrados en la respuesta del sistema sobre
el cual se desea trabajar. Su principal objetivo consiste en la posibilidad de predecir el
comportamiento del sistema sin la necesidad de construirlo/modificarlo, para ası́ reducir
al mı́nimo el costo asociado a las tareas requeridas para completar los objetivos de dicha
memoria.
Es ası́ que, a continuación, se procede a realizar un análisis teórico, con el objetivo de
desarrollar un modelo que se aproxime a la realidad fı́sica del comportamiento de un UAV
en condiciones ideales. Ası́, con dicho modelo se procede a realizar, en la segunda mitad
del capı́tulo, simulaciones mediante un solver matemático, con el objetivo de validar las
estrategias de control planteadas para el desarrollo del proyecto considerando, en primera
instancia, el control sobre la altura y los ángulos de Euler que definen la posición del prototipo
y ası́, posteriormente, plantear una estrategia que permita a futuro desarrollar un control
sobre la posición del prototipo en el plano XY. Finalmente, los resultados del presente
capı́tulo sentarán las bases de la etapa de diseño que se plantea posteriormente.
Y
θ Φ
X
Fig. 2.1: Sistema coordenado inercial del drone.
Por otro lado, como se presenta en la figura Fig. 2.2, está el sistema coordenado del
UAV, que tiene las coordenadas fijas al “frame” o marco del prototipo, siendo Xb orientado
hacia el frente del prototipo entre los motores 1 y 2, Yb de forma perpendicular al eje Xb
separando los motores 2 y 3, y por último Zb de forma perpendicular a los ejes previos en
dirección hacia la parte superior del prototipo.
Zb
Yb
M3
M2
Xb
M4
M1
Por otro lado, además, se puede establecer una matriz que permita relacionar la velocidad
angular del drone (ωφ , ωθ , ωψ ) con las velocidades angulares en los ángulos principales (Pitch
(θ), Roll (φ) y Yaw (ψ)) y viceversa. Esto se puede ver a continuación en las ecuaciones
(2.5) y (2.6), en donde es importante resaltar que además del uso de la notación S() y C()
para senos y cosenos respectivamente, se utiliza la expresión T() para hacer referencia a las
tangentes de los ángulos señalados.
φ̇ 1 SφT θ CφT θ ωφ
θ̇ = 0 Cφ −Sφ ωθ (2.5)
Sφ Cφ
ψ̇ 0 Cθ Cθ
ω ψ
ωφ 1 0 −Sθ φ̇
ωθ = 0 Cφ CθSφ θ̇ (2.6)
ωψ 0 −Sφ CθCφ ψ̇
No obstante, cabe destacar que, dado que el efecto de la aceleración angular del motor
tiene poca influencia en el torque, puede ser despreciado quedando como se ve en la ecuación
(2.9). Además, desde este punto en adelante se representará el cuadrado de la velocidad
angular de cada motor (ωi2 ) como Mi por simplicidad.
τM i = b ∗ Mi (2.9)
La fuerza producida por todos los rotores girando simultáneamente se puede expresar
como un vector denominado thrust en dirección axial al eje z y que está determinado por la
siguiente matriz (2.10).
0 0
T = P0 = 0 (2.10)
4
k i=1 Mi M 1 + M 2 + M 3 + M 4
La rotación de los motores además produce 3 torques, en los 3 ángulos del sistema defi-
nidos como una matriz τB . Esto se define del siguiente modo en la ecuación (2.11).
τφ lk((M1 + M4 ) − (M2 + M3 ))
τb = τθ = lk((M1 + M2 ) − (M3 + M4 )) (2.11)
τψ M1 − M2 + M3 − M4
En donde l corresponde al brazo de palanca entre la aplicación de la fuerza y el centro de
masa del UAV. Además, se puede apreciar que en el caso del Roll los motores de un costado
aumentan su velocidad angular, mientras que los del costado opuesto la disminuyen. En el
caso del Pitch ocurre lo mismo, pero con los motores delanteros y traseros respectivamente.
Finalmente, en el caso del Yaw son los motores diagonalmente opuestos los que aumentan o
disminuyen su velocidad angular, es decir los motores M1 y M3 la aumentan, mientras que
los motores M2 y M4 la disminuyen.
Puesto que en el sistema inercial la fuerza centrı́fuga se anula, solo la fuerza gravitacional
y el thrust permiten definir la aceleración lineal del drone. Esto se ve como sigue en las
ecuaciones (2.12) y (2.13). Donde se ve que finalmente solo se utiliza la tercera columna de
la matriz de rotación para definir dichas aceleraciones.
ẍ 0 CθCψ CψSθSφ − SψCφ CψSθCφ + SψSφ
ÿ = 0 + T SψCθ SψSθSφ + CψCφ SψSθCφ − SφCψ (2.12)
m
z̈ −g −Sθ CθSφ CθCφ
ẍ 0 CψSθCφ + SψSφ
ÿ = 0 + T SψSθCφ − SφCψ (2.13)
m
z̈ −g CθCφ
Otro punto para tener en cuenta es, dado que el drone se considera un dispositivo simétri-
co con sus cuatro brazos alineados, se tiene que la inercia respecto al eje x (Ixx ) es igual a
la inercia respecto al eje y (Iyy ). Además, en el cuerpo del drone, las aceleraciones angula-
res están definidas por la inercia en este, los torques τb asociados a este y las velocidades
angulares respectivas, quedando una expresión de la forma presentada en la ecuación (2.14).
ω̇φ A1 B1
ω̇θ = A2 + B2 ,
ω̇ψ A3 B3
Con :
(Iyy − Izz )ωθ ωψ
A1 =
Ixx
(Izz − Ixx )ωφ ωψ
A2 =
Iyy (2.14)
(Ixx − Iyy )ωφ ωθ
A3 =
Izz
τφ
B1 =
Ixx
τθ
B2 =
Iyy
τψ
B3 =
Izz
Es por ello que, en resumen, las ecuaciones que definen el comportamiento total del
sistema mediante sus estados constan de las siguientes:
Para la derivada de la posición se tiene las ecuaciones presentadas en (2.15)
x0 = ẋ,
y 0 = ẏ, (2.15)
z 0 = ż
Para la derivada de las velocidades lineales se tiene las ecuaciones presentadas en (2.16),
en donde se agrega el término de la resistencia del aire representado por Ax , Ay y Az .
(CψSθCφ + SψSφ)(M1 + M2 + M3 + M4 ) + Ax ẋ
ẋ0 = ,
m
(SψSθCφ − SφCψ)(M1 + M2 + M3 + M4 ) + Ay ẏ
ẏ 0 = , (2.16)
m
(CθCφ)(M1 + M2 + M3 + M4 ) + Az ż
ż 0 = −g +
m
Para la derivada de las posiciones angulares del prototipo respecto del marco de refe-
rencia inercial o terrestre, se tiene las ecuaciones presentadas en (2.17)
Para la derivada de las velocidades angulares del prototipo respecto del marco de
referencia propio del UAV, se tiene las ecuaciones presentadas en 2.18
2.2. Simulación
Como se señaló anteriormente, en esta sección se presentará en detalle el proceso de simu-
lación en base a las ecuaciones obtenidas en el modelamiento desarrollado con anterioridad.
Para ello se comenzará detallando los parámetros requeridos en su desarrollo, seguidos de
una explicación de las estrategias de control a implementar, y ası́, finalizar con el detalle de
resultados en las diferentes condiciones de trabajo.
2.2.1. Parámetros
Para realizar la simulación los parámetros a considerar son los que se presentan a con-
tinuación. Realizando la salvedad que, debido a que es una etapa previa respecto de las
cotizaciones respecto al diseño del prototipo, se considerarán parámetros tı́picos, tomando
en cuenta el prototipo objetivo que se detallará más adelante. Ası́ se tiene:
Masa del drone: m = 0.468[kg], considerando un estimado en base a la masa tı́pica que
se tiene en los UAV más comunes en el mercado.
Distancia del centro de masa a los motores a lo largo del eje X o Y: l = 0.125cos(45◦ )[m],
dado que se considera un dron de 0.25 [m] de wheelbase, siendo este tamaño uno de los
más tı́picos entre las alternativas existentes.
Constante de levante del prototipo: k = 2.98 ∗ 10−6 , obtenido por las condiciones
estándar en cuanto a geometrı́a y caracterı́sticas de fluido propias del aire en condicio-
nes ideales.
Inercia de los motores: IM = 3.357∗10−5 , considerando valor estándar para los motores
brushless tı́picos utilizados en UAV estándar con textitwheelbase de 0.25 [m].
Inercia del “frame” respecto a los ejes x e y: Ixx = Iyy = 4.856 ∗ 10−3 , estimado por la
geometrı́a (idealmente simétrica en el plano xy) del cuadricóptero.
Inercia del “frame” respecto al eje z: Izz = 8.801 ∗ 10−3 , estimado por la geometrı́a
tı́pica del prototipo.
2.2.2. Controlador
Para el desarrollo del presente diseño, se considera en primera instancia un control de
tipo PD, el cual se utiliza en el control de los ángulos del UAV y la posición en eje Z.
Su funcionamiento se realiza realimentando dichas variables sensadas mediante la Inertial
Measurement Unit (IMU) y el barómetro y comparándolas con la referencia para obtener los
errores respectivos a corregir. El diagrama de bloques que representa este control se puede
apreciar en la figura 2.3.
Control PD
u(t) y(t)
e(t)
Kp
_
Kd d/dt
Además del control anterior, se incluye uno de tipo PID para realizar el control sobre la
posición deseada en ejes X e Y, nuevamente utilizando la comparación de las variables sen-
sadas (esta vez incluyendo la señal obtenida por el sensor Global Positioning System (GPS))
y calculando el error respecto a la referencia. Cuyo diagrama se presenta a continuación en
la figura 2.4.
Control PID
Kd
u(t) y(t)
e(t)
Kp
_
Kd d/dt
Cabe destacar que un punto importante al utilizar estos tipos de control es la necesidad de
realizar la sintonización adecuada de estos, para ası́ evitar la sobre atenuación o la oscilación
excesiva de la salida.
Z ref Thrust
PD
Control M1 a M4
φ θ ψ
Posición angular
MUX C)
Fig. 2.5: Diagrama de bloques sistema con control sobre Roll, Pitch, Yaw y Thrust parte a) (lazos de
control).
A)
Posición lineal
B)
Comportamiento Aceleración lineal Velocidad lineal
del
UAV Velocidad angular Posición angular
Posición angular
C)
Fig. 2.6: Diagrama de bloques sistema con control sobre Roll, Pitch, Yaw y Thrust parte b) (modelamiento
del UAV).
Entradas:
Salidas:
Bloque integrador: Consiste en un bloque que permite integrar los estados para reali-
mentarlos a la etapa de control.
2.5. Finalmente, para el caso del ángulo Yaw, a pesar de tener su entrada en 0, se producen
oscilaciones en su valor, por lo que el controlador ejecuta una acción correctiva para mantener
su magnitud en 0.
Respecto al eje Z y los efectos del control PD sobre el Thrust, se puede señalar que se
puede ver una sintonización más precisa debido al control que se ejerce directamente sobre
la posición respecto al eje Z logrando una oscilación baja y un sobrepaso bastante aceptable,
aunque mejorable en etapas futuras del proyecto.
Señal de referencia vs comportamiento para el ángulo Roll Señal de referencia vs comportamiento para el ángulo Yaw
1 1
0.8 0.8
0.6 0.6
0.4 0.4
Ángulo (rad)
Ángulo (rad)
0.2 0.2
0 0
-0.2 -0.2
-0.4 -0.4
-0.6 -0.6
Señal de control Señal de control
-0.8 -0.8
Respuesta del sistema Respuesta del sistema
-1 -1
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
Tiempo (s) Tiempo (s)
Fig. 2.7: Comparación entre entrada escalón y Fig. 2.9: Comparación entre entrada escalón y
respuesta para el ángulo Roll. respuesta para el ángulo Yaw.
Señal de referencia vs comportamiento para el ángulo Pitch Señal de referencia vs comportamiento para Thrust
1 12
0.8 11
10
0.6
9
0.4 8
Distancia (m)
Ángulo (rad)
0.2 7
6
0
5
-0.2 4
-0.4 3
2
-0.6
1
Señal de control Señal de control
-0.8 0
Respuesta del sistema Respuesta del sistema
-1 -1
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
Tiempo (s) Tiempo (s)
Fig. 2.8: Comparación entre entrada escalón y Fig. 2.10: Comparación entre entrada escalón y
respuesta para el ángulo Pitch. respuesta para el thrust (posición en Z).
prototipo real, considerar las limitaciones que presenta un motor en cuanto su velocidad de
giro.
Respuesta del prototipo en eje X Respuesta del prototipo en eje Y
1000 100
900 0
800 -100
700 -200
Distancia (m)
Distancia (m)
600 -300
500 -400
400 -500
300 -600
200 -700
100 -800
0 -900
-100 -1000
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
Tiempo (s) Tiempo (s)
Fig. 2.11: Posición del drone en el eje X. Fig. 2.12: Posición del drone en el eje Y.
X Y Z
A)
Z ref Thrust
PD
Control M1 a M4
Y relativo PID φ ref PD Roll MMA
Y ref Sat.
Control Control
X ref Rot ψ X relativo PID θ ref PD Pitch B)
Sat.
Control Control
ψ ref PD
Control Yaw
ψ
θ Posición angular
φ MUX
C)
Distancia (m)
7
6
2
5
4
1
3
2
0 1
Señal de control Señal de control
Respuesta del sistema 0 Respuesta del sistema
-1 -1
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
Tiempo (s) Tiempo (s)
Fig. 2.14: Comparación entre posicion deseada en Fig. 2.15: Comparación entre posicion deseada en
eje X y comportamiento del prototipo eje X y comportamiento del prototipo
0.8
0.6
0.4
Ángulo (rad)
0.2
-0.2
-0.4
-0.6
-0.8
-1
0 1 2 3 4 5 6 7 8 9 10
Tiempo (s)
Ángulo (rad)
0.4 0.4
0.2 0.2
0 0
-0.2 -0.2
-0.4 -0.4
-0.6 -0.6
-0.8 -0.8
-1 -1
-1.2 -1.2
-1.4 -1.4
Señal de control Señal de control
-1.6 -1.6
-1.8 Respuesta del sistema -1.8 Respuesta del sistema
-2 -2
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
Tiempo (s) Tiempo (s)
Fig. 2.17: Comparación entre señal de control y Fig. 2.18: Comparación entre señal de control y
comportamiento del ángulo Pitch. comportamiento del ángulo Roll.
3. Etapa de diseño
En el presente capı́tulo se procede a detallar, en base al desarrollo teórico y bibliográfico
previo, el proceso de diseño del prototipo, justificando la necesidad y uso de cada compo-
nente a utilizar. Posteriormente, se detalla las cotizaciones de los componentes requeridos
disponibles en el mercado y sus principales caracterı́sticas.
Además, es importante indicar que dependiendo de la parte móvil del motor brushless,
se pueden obtener dos tipos: los outrunner, en donde la parte móvil o rotor corresponde a la
zona exterior de este; y los inrunner, en los que el rotor corresponde a la parte interior del
motor.
3.1.5. Hélices
Las hélices o propelas corresponden a los elementos que permitan generar la fuerza de
empuje mediante la rotación de los motores.
En el caso de los UAV se pueden encontrar hélices de diversos materiales entre los cuales
se encuentran la fibra de carbono, plástico Acrylonitrile butadiene styrene (ABS), plástico
Polylactic acid (PLA), madera, entre otros, las cuales tendrán distintas caracterı́sticas en
cuanto a su calidad y duración en contraste con su coste.
3.1.7. Frame
En un UAV el frame o marco, es la estructura de soporte para todos los componentes
del sistema, en otras palabras, es como el esqueleto que sustenta mecánicamente todas las
fuerzas involucradas al momento de levantar el drone del suelo.
Existen frames de muchos materiales, desde polı́meros (ya sea ABS, PLA u otro), ası́
como madera, metal, o materiales compuestos como la fibra de carbono. Como se señala en
un artı́culo de la empresa Matmatch, la cual se centra en el estudio de materiales [?], se indica
que, en el caso del frame para términos comerciales se prefieren materiales termoplásticos
como el nylon o el poliestireno que incluyen, entre sus caracterı́sticas, altas resistencias a
la tensión (100[MPa]) y baja densidad (2[g/cm3 ]); no obstante, se señala además, que para
usos industriales, al priorizar el desempeño, se utilizan materiales reforzados, como lo es la
fibra de carbono que a su vez presenta una relación entre resistencia y densidad mucho más
optima, pero a su vez su costo económico resulta mayor. Otro punto que considerar es la
denominada wheelbase que corresponde al diámetro de la circunferencia que conecta el centro
de todos los motores del UAV, y que define parámetros como el tipo de motor, tamaño de
hélice y peso máximo que se puede levantar, entre otros. Esta medida comúnmente se define
3.1.8. IMU
Corresponde a un elemento que posee los elementos necesarios para sensar el movimiento
del prototipo, ya sea en rotación o en traslación mediante acelerómetro y giroscopio, además
de contar con un magnetómetro para determinar la orientación del UAV de modo similar
a una brújula. Este elemento es parte fundamental al momento de realizar la estrategia de
control del UAV ya que permite obtener los inputs requeridos para realizar el ajuste de
posición en los ejes X e Y (en conjunto con el GPS), ası́ como los ángulos Roll, Pitch y Yaw.
3.1.9. Barómetro
Corresponde al dispositivo que permite, en base a la presión atmosférica, estimar la altura
aproximada del prototipo. Este elemento funciona utilizando de setpoint la presión a nivel
del mar para permitir estimar un cambio de presión en términos relativos y convertir dicho
diferencial en la altura efectiva del dispositivo.
3.1.10. GPS
Este elemento permitirá realizar el posicionamiento en coordenadas satelitales del proto-
tipo, esto a fin de permitir realizar un control en las posiciones X e Y. Su funcionamiento se
centra en el enlace del dispositivo con múltiples satélites diseñados para esta función a través
de una antena, permitiendo la triangulación del punto en el que se encuentra el dispositivo
con una tolerancia de alrededor de 5 [m] en promedio.
3.1.12. Cámara
Elemento fundamental para la aplicación del prototipo, corresponde al módulo encargado
de realizar la captura de imágenes para su posterior revisión y análisis.
Este módulo puede ser tanto una cámara convencional, que permita ver fugas, trizaduras,
entre otras evidencias de posible falla, ası́ como una cámara termográfica, la que median-
te el uso del principio de emisividad de los cuerpos permite realizar un análisis sobre las
concentraciones de calor que puede presentar parte de la maquinaria a inspeccionar.
Este módulo sera utilizado para realizar la captura completa de datos que posteriormente
sera exportada para el análisis y toma de decisiones por parte del mantenedor.
Si bien la tarjeta Cora Z7 es la opción idónea para la aplicación buscada, presenta una
limitante, la cual corresponde a la carencia de elementos que permitan realizar comunicación
inalámbrica, considerando la necesidad de desligar de cualquier enlace fı́sico al prototipo
respecto del computador base. No obstante, dicha necesidad puede ser compensada mediante
la inclusión de una tarjeta ESP32.
La tarjeta ESP32 corresponde a un tipo de SoC de bajo y bajo poder, pero que en
compensación incluye de fábrica módulos Wi-Fi y Bluetooth integrados, lo que la hace un
componente ideal para el trabajo en conjunto con la tarjeta Cora Z7 consiguiendo potencia y
capacidad por un lado sumado a la comunicación inalámbrica por otro. Además, es importan-
te destacar la compatibilidad con comunicación I2C permitiendo establecer la comunicación
entre ambas tarjetas SoC. Otro punto favorable de la tarjeta ESP32 es su compatibilidad
con el IDE de arduino, por lo que a su vez, con las bibliotecas correspondientes, permite
integrar y generar un nodo Robot Operating System (ROS) para la comunicación entre el
prototipo y el computador base. Dicha interconexión y la operación de ROS sera detallada
más adelante.
El coste aproximado de este componente es de alrededor de $ 10 USD. En la figura Fig.
3.5 se puede ver la tarjeta ESP32.
además de presentar un tamaño adecuado para la aplicación del presente proyecto. El costo
de este frame oscila en torno a los $ 26 usd.
En la figura Fig. 3.6 se muestra el modelo 3D del frame a utilizar.
Teniendo los parámetros anteriores como punto de partida, se puede realizar la selección
de motores de acuerdo con las recomendaciones presentadas por el fabricante del frame, las
cuales señalan el uso idealmente de motores de 2300 [kV] como actuadores, siendo esta la
velocidad en RPM por cada volt entregado al motor [33]. Por lo cual, considerando la mejor
relación funcionalidad/precio, es que se determina la factibilidad de usar motores Emax
RS2205 2300 kV, los cuales corresponden a motores de tipo outrunner que cumplen con la
caracterı́stica en [kV]. Cuyas dimensiones están determinadas por el numero 2205 (22 [mm]
de diámetro y 05 [mm] de altura permitiendo ser montados en el espacio dedicado para ello
en el frame. El costo de estos motores corresponde alrededor de $ 73 usd en total por el set
de 4 unidades.
Los motores son del tipo mostrado en la figura Fig. 3.7.
de alimentación del prototipo, siendo posible baterı́a de 12 o 16 [V]. Por ello, nuevamente
tomando en cuenta la necesidad de reducir el tamaño del prototipo y además limitar su peso,
es que se considera adecuada una baterı́a LiPo 3S, la cual entrega voltaje nominal de 11.1
[V] y voltaje en carga máxima de 12.4 [V]. Además, tomando en cuenta la corriente máxima
utilizada por los motores a dicho voltaje corresponde a 23 [A], es que se decide utilizar una
baterı́a con C rating mayor a 45 y al menos 1800 [mAh] de capacidad para una autonomı́a de
vuelo aceptable. Tomando en cuenta dichas consideraciones y la disponibilidad de mercado
es que se considera el uso de una baterı́a 3S 70C 2200[mAh], la cual cumple adecuadamente
con la necesidad del sistema. Dicha baterı́a cuyo costo es de alrededor de $ 38 usd, se presenta
en la figura Fig. 3.9.
Posteriormente se define el tipo de hélice a utilizar, siendo opción hélices 5045 tripala
o bipala. Estas hélices poseen un largo de 5 pulgadas con paso de 4.5 pulgadas y su única
diferencia es la cantidad de palas que poseen. Por ello, considerando la disponibilidad de
mercado, se considera recomendable usar la versión de 2 palas fabricadas en ABS, pues
cumplen los requisitos de funcionamiento para el proyecto. Se requiere el uso de 2 hélices de
giro horario y 2 de sentido antihorario, cuyo costo por set es de alrededor de $ 8 usd. En la
figura Fig. 3.10 se presenta las hélices a utilizar.
Otro punto por considerar en la electrónica es la PDB, la cual, dadas las caracterı́sticas
ya descritas del sistema, debe permitir la alimentación de 4 ESC y la tarjeta SoC. Por lo
cual, en base a la disponibilidad de mercado, se considera como mejor opción la PDB Matek
que permite la conexión de hasta 6 ESC y posee además salidas de alimentación de 5 y 12
[V] de hasta 2 [A]. Dicha PDB tiene un costo aproximado de $ 12 usd.
En la figura Fig. 3.12 se puede observar la placa a utilizar.
El siguiente sensor por seleccionar corresponde al módulo GPS, estableciendo el cual, dado
el presupuesto y disponibilidad en el mercado nacional, corresponde al módulo UBLOX-NEO
6M, que, si bien tiene una precisión adecuada, no es la versión mas pequeña en el mercado
para caracterı́sticas similares (MCI Módulo GPS SE868-A el cual se encuentra sin stock
actualmente). No obstante, cumple con los mı́nimos requeridos para la aplicación deseada
contando con comunicación de tipo Universal Asynchronous Receiver-Transmitter (UART)
y una pila de botón para almacenar configuraciones en memoria EEPROM. Su costo se
encuentra alrededor de $ 21 usd. Su PCB se puede apreciar en la Fig. 3.15.
Es ası́, que en resumen el costo total del proyecto suma un total de aproximadamente $
425 usd, considerando los elementos mı́nimos requeridos para el funcionamiento del prototipo
y establecer su funcionamiento.
4. Etapa de construcción
En la presente sección se detallarán los pasos de construcción del prototipo incluyendo el
armado de componentes mecánico/estructurales, el conexionado electrónico y una sección de
programación explicando en detalle el funcionamiento de cada componente de forma aislada y
como se realiza su integración en el prototipo. Esta etapa incluye además pruebas focalizadas
en el funcionamiento de cada uno de los sistemas para tener referencias de su comportamiento
al momento de integrarlos permitiendo un proceso de depuración más sencillo.
Fig. 4.3: Análisis de deflexión sobre la cubierta del UAV con 8 soportes.
Fig. 4.4: Análisis de deflexión sobre la cubierta del UAV con 6 soportes.
A continuación, una vez realizadas las conexiones de ESC’s a la PDB y conector respectivo
para la baterı́a (lo cual se detalla en la siguiente sección), se procede a realizar el montaje de
la PDB en el frame, utilizando para ello tornillos M3 y un separador fabricado en una placa
de ABS (la cual se puede ver en la figura 4.5), a modo de aislante eléctrico, fijando ası́ esta
pieza al frame. Además se realizó la fijación de los ESC’s mediante amarras plásticas para
cables. Esto se puede ver en la figura 4.6.
Ası́, posterior a la programación del PWM requerido para el control de ESC’s y realizado
el conexionado de motores a cada ESC, validando el sentido de giro con las fases de alimen-
Una vez realizado dicho montaje y realizado el shield con las conexiones requeridas para
los sensores, se realiza su montaje en conjunto con la tapa superior del frame. Lo cual se
puede observar en la figura 4.9.
Finalmente, una vez que se tiene construida la placa shield de montaje para los sensores
y tarjeta ESP32, se realiza el montaje de esta en la zona superior mediante velcro adherida
utilizando cinta de doble contacto, finalizando ası́ el montaje del prototipo en el ámbito
mecánico, como se aprecia en la figura 4.10.
Fig. 4.10: Montaje estructural completo del prototipo solo con piezas originales del frame.
relación peso/resistencia, siendo además probada empı́ricamente por el creador del modelo.
Esta pieza impresa se puede observar en la figura Fig. ??.
Ası́, teniendo los nuevos soportes, se puede realizar el cambio de piezas, logrando el
armado final del prototipo a utilizar en el presente proyecto. Los resultados de la modificación
se encuentran evidenciados en las figuras Fig. 4.14 y 4.15, presentando tanto una vista
isométrica, como frontal del prototipo respectivamente. Un punto relevante a señalar es que
el prototipo, en su conjunto, presenta una masa de aproximadamente 695.64 [g].
Fig. 4.14: Montaje estructural completo del prototipo con soportes de aterrizaje altos (vista isométrica).
Fig. 4.15: Montaje estructural completo del prototipo con soportes de aterrizaje altos (vista frontal).
utilizó un cautı́n para soldadura electrónica a 325◦ C, con punta 900M-B y 900M-T para las
secciones de mayor y menor superficie respectivamente. La soldadura utilizada corresponde
a estaño para electrónica 60/40, y en el caso de las soldaduras de montura superficial se
aplicó flux en pasta para mejorar las terminaciones. Los parámetros de soldadura descritos
fueron los utilizados en las conexiones que serán descritas posteriormente. Dichas fijaciones
se aprecian en la figura 4.16.
Posteriormente, se procedió a conectar los motores hacia cada ESC verificando el sentido
de giro de estos utilizando el código de generación de PWM.
Luego, se realizó la soldadura de los pin-headers o conectores de los módulos/sensores a
utilizar. El resultado es el que se puede ver en la siguiente figura Fig. 4.17
Finalmente, una vez teniendo los pines definidos en la tarjeta Cora Z7 se procede a realizar
la soldadura del shield con las salidas correspondientes para la soldadura de la placa con
conectores encargada de soportar los sensores. Para la interconexión entre ambas se utilizó
cable plano AWG28 dado que permite flexibilidad y se puede encontrar en cintas de 10 o 40
vı́as lo que permite dejar la conexión de tipo bus. Esto se puede apreciar en la figura 4.18. Es
importante resaltar el uso de silicona termofusible para realizar el aislamiento de los puntos
en donde se realiza la soldadura y ası́ evitar que se produzcan cortocircuitos por factores
externos o incluso por la caracterı́stica conductora de la fibra de carbono que, si bien el frame
está construido con un compuesto de fibra con resina por lo que no es conductor al ciento por
ciento, aun existe el riesgo de que se generen falsos contactos que pueden desencadenar fallas
en el sistema. También, este adhesivo permite sujeción mecánica de los cables utilizados para
realizar los “puentes” entre los pines asegurando una mayor fiabilidad en su desempeño.
Posteriormente, aun en las configuraciones del programa, se debe definir los datos que se
necesitan leer, en conjunto con su frecuencia de muestreo. Para ello se emplean las funciones
mostradas en el fragmento de código que sigue.
Ası́, el siguiente paso corresponde a realizar las lecturas de la información de forma periódica
dentro de la función loop, como se observa en el fragmento de código a continuación usando
las funciones get*variable*, en donde *variable* corresponde a la variable a obtener.
Posteriormente se realiza la impresión de los valores obtenidos vı́a puerto serial para su
verificación. Los resultados obtenidos se pueden observar en la captura del monitor serial
mostrada en la figura 4.20. Aquı́, se observa una frecuencia de lectura de datos de alrededor
de 0.1 segundo, no obstante ,los datos presentan un cambio efectivo cada 0.4 segundos
aproximadamente dada la configuración de lectura cada 400 [ms].
El último sensor a validar corresponde al GPS, el cual, a diferencia de los anteriores, solo
posee comunicación por protocolo UART. Un punto importante a destacar es el hecho de
que la comunicación de sensores de tipo GPS esta estandarizada por el protocolo National
Marine Electronics Association (NMEA) el cual se puede encontrar en más detalle en la
referencia [38]. Por ello, se requiere realizar una programación un tanto diferente, en la cual,
la biblioteca diseñada por el fabricante [39] funciona sólo como un traductor/intérprete de
los datos recibidos. Esto se puede ver de forma más intuitiva en el ejemplo “simple test.ino”,
el cual, siendo modificado para ser utilizado con el sensor seleccionado, queda de la forma
que se ve en el anexo F. Al igual que los casos anteriores, a continuación, se detallará paso
a paso los puntos más importantes del uso del sensor.
En primer lugar, se debe incluir las bibliotecas de comunicación y del sensor, siendo en
este caso, las librerı́as “SoftwareSerial.h” y “TinyGPS.h” respectivamente. Posteriormente
se crea un objeto con la clase TinyGPS incluida en la biblioteca y se definen los pines para la
comunicación UART. A continuación, dentro de la función setup, se realiza la inicialización
de la comunicación UART, que, dado el sensor a utilizar, se debe configurar a 9600 baudios.
Finalmente, dentro de la función loop, se debe realizar la lectura de la información del
sensor, en este caso, se realizará en intervalos de 1 segundo, interpretando mediante la fun-
ción “encode” cada uno de los caracteres recibidos e identificando si se ha leı́do una nueva
frase del protocolo NMEA como se aprecia en el siguiente fragmento de código.
Ası́, posteriormente, en los casos de lectura de nueva frase, se realiza la obtención de los datos
desde las variables de almacenamiento de la clase TinyGPS como se aprecia en el fragmento
de código presentado a continuación.
1 module design_1_wrapper
2 (DDR_addr,
3 DDR_ba,
4 DDR_cas_n,
5 DDR_ck_n,
6 DDR_ck_p,
7 DDR_cke,
8 DDR_cs_n,
9 DDR_dm,
10 DDR_dq,
11 DDR_dqs_n,
12 DDR_dqs_p,
13 DDR_odt,
14 DDR_ras_n,
15 DDR_reset_n,
16 DDR_we_n,
17 FIXED_IO_ddr_vrn,
18 FIXED_IO_ddr_vrp,
19 FIXED_IO_mio,
20 FIXED_IO_ps_clk,
21 FIXED_IO_ps_porb,
22 FIXED_IO_ps_srstb,
23 shield_i2c_scl_io,
24 shield_i2c_sda_io);
En dicho código se observan 2 señales llamadas shield i2c scl io y shield i2c sda io, las
cuales corresponden a los inout (entradas/salidas) utilizados para las lineas SCL y SDA del
protocolo de comunicación.
Una vez definido aquello, se debe realizar el mapeo de puertos correspondientes con las
salidas del shield de la tarjeta, la cual presenta por diseño 2 pines dedicados a las lineas SDA
y SCL de la comunicación, por lo que se aprovecharan dichas conexiones.
El mapeo de puertos en esta tarjeta se realiza mediante un archivo .xdc, en el cual se
deben relacionar las variables definidas en el Esto se presenta en el código a continuación:
Una vez realizado dicho mapeo, se procede a realizar la transcripción las bibliotecas para
los sensores que utilizan comunicación I2C, cuyos códigos se pueden revisar en los anexos
G y H en el caso del barómetro y los anexos I y J para la IMU, en donde se presentan los
archivos .h y .cc correspondientes a la biblioteca de cada sensor. No obstante, a continuación,
se presentan los fragmentos de código más relevantes que corresponden a las modificaciones
necesarias para realizar cada tipo de comunicación. Esta transcripción, ası́ como toda la
programación en C/C++ del proyecto requerida para la tarjeta Cora Z7, se realiza mediante
el software Xilinx SDK incluido con la suite de Vivado.
En primer lugar, dado que se trabajará con ambos sensores utilizando la comunicación
I2C, hay cambios generales en común que se pueden detallar. El primero de ellos corresponde
a la definición de 2 parámetros referentes al AXI-IIC correspondientes a la identificación del
dispositivo AXI y su dirección base, los cuales son requeridos al momento de utilizar las
funciones de dicho dispositivo. Estos parámetros se muestran a continuación.
1 puertoI2C->beginTransmission(Dirección); //Secuencia de
inicio
2 puertoI2C->write(Dato1); //Dato número 1
3 puertoI2C->write(Dato2); //Dato número 1
4
En donde se envı́a de forma manual la secuencia de inicio en conjunto con la dirección del
esclavo, luego los datos uno a uno y finalmente se envı́a la secuencia de término (STOP). En
cambio, al utilizar un dispositivo AXI en la tarjeta Cora Z7, el algoritmo de envı́o es el que
se muestra a continuación.
En donde se requiere almacenar previamente los datos en un vector de envı́o, para poste-
riormente verificar si el bus de comunicación se encuentra libre y, de ser ası́, realizar el envı́o
de datos mediante una función llamada Xiic Send, cuyos argumentos son la dirección base
del dispositivo AXI, la dirección del esclavo, el buffer de datos a enviar, la cantidad de datos
contenidos en el buffer y finalmente una señal que indique si se desea enviar una señal de
STOP o si se requiere mantener retenido el bus (también llamado estado HOLD) el bus para
evitar se realice envı́o de datos por parte de otros dispositivos conectados a dicho bus. Ası́, un
ejemplo práctico en donde se utiliza este algoritmo corresponde al mostrado a continuación,
extraı́do de la biblioteca BNO080 correspondiente al sensor IMU, en donde es importante
resaltar el uso del protocolo Sensor Hub Transfer Protocol (SHTP). Este protocolo consiste
en un método estándar para realizar la transferencia de datos desde un sensor, ya sea por co-
municación I2C o SPI. Su particularidad radica principalmente en el envı́o de una cabecera o
header de 4 bytes con información de formato respecto al paquete de datos a transferir. Esta
cabecera consiste en utilizar los primeros 2 bytes para indicar el largo del paquete a enviar,
mientras que el tercer byte indica el canal de comunicación, es decir, le da un sentido a la
información a enviar, distinguiendo si es información de configuración o lectura del sensor.
En cuanto al cuarto byte, corresponde al valor de secuencia de la comunicación por el ca-
nal previamente indicado y ası́ permitir la comunicación sincronizada y adecuada del módulo.
12 }
13
3 //contador
4 always @(posedge S_AXI_ACLK) begin
5 if(counter < PWM_COUNTER_MAX-1)
6 counter <= counter + 1;
7 else
8 counter <= 0;
9 end
10
Cabe destacar que el PWM generado posee 4 salidas permitiendo tener una señal indepen-
diente por cada ESC, como se aprecia en la figura 4.23.
Una vez se tiene el mapeo de puertos, es posible realizar el código de control en el software
Xilinx SDK.
Para modificar el valor de los registros para el control del duty cicle mediante código en
Xilinx SDK se utiliza la función Xil Out32(*reg*, *val*) en la cual se indica el registro a
escribir en la variable *reg* y el valor a escribir en la variable *val*, como se presenta en el
anexo M.
Realizando una explicación más en detalle, se distingue, en primer lugar, el uso de uno de
los botones integrados en la tarjeta Cora Z7 para realizar el control de los valores de salida
para el PWM. Aquı́ es importante destacar la necesidad de utilizar valores entre el 6 % y
12 % del duty-cycle total de la señal logrando una señal con tiempo en alto de entre 1 [ms] y
2 [ms], como se indica en el datasheet oficial de los ESC utilizados para los valores de throttle
mı́nimo y máximo respectivamente [40], los cuales, si bien no son los presentados en la coti-
zación, corresponden a una versión alternativa que cumple con los requerimientos necesarios
para el funcionamiento de los motores. En el fragmento de código presentado a continuación
se puede apreciar de mejor modo cómo se realiza el cambio entre los valores mı́nimo y máxi-
mo de PWM mediante la lectura del botón, cambiando el valor de la variable cont entre 0 y 1.
1 switch(cont){
2 case 0:
3 i = min;
4 break;
5 case 1:
6 i = max;
7 break;
8 }
Y, posteriormente, se entrega la misma salida para las 4 señales de PWM mediante la función
“Xil Out32”, como se observa en el fragmento de código presentado a continuación.
1 Xil_Out32(MY_PWM, Full_Val*(i/100.0));
2 Xil_Out32((MY_PWM+4), Full_Val*(i/100.0));
3 Xil_Out32((MY_PWM+8), Full_Val*(i/100.0));
4 Xil_Out32((MY_PWM+12), Full_Val*(i/100.0));
Mediante este Código, además, es posible realizar la configuración de los ESC siguiendo los
pasos descritos en la documentación disponible [40], en donde se indica el utilizar el valor
máximo del duty-cycle previo a realizar la alimentación de poder a los ESC, y posterior a una
alerta entregada mediante sonido generado por el movimiento de los motores, se debe cambiar
al valor mı́nimo del PWM. Con dichos pasos es posible asegurar la definición adecuada de
los lı́mites en la señal de control de giro de los motores, lo que permitirá, posteriormente,
realizar de forma adecuada el control de estos para el posicionamiento de la aeronave.
4.3.4. ROS
ROS corresponde a un entorno de trabajo o conjunto de herramientas orientadas al
desarrollo de sistemas robóticos robustos y complejos, permitiendo además simplificar el
trabajo colaborativo gracias a su forma de operación [41].
La forma de trabajo de ROS se basa en la definición de 2 conceptos principales, los cuales
son publicar y suscribir mientras que dichos conceptos son enlazados mediante los elementos
de tópico y mensaje, pero ¿cómo se realiza aquel enlace?
El centro de la comunicación de ROS se corresponde con la idea de tópico, el cual consiste
en un tema o identificación de la información que se desea publicar en forma de mensaje
(el cual tiene una estructura, datos y formato claramente definidos). Es ası́ que, existiendo
un tópico definido existe la posibilidad de realizar la comunicación utilizando nodos que
puedan publicar y suscribirse a él. Es decir, permite que, una vez se parametriza la forma de
comunicar la información mediante el tipo de mensaje, existan n nodos que publiquen en el
tópico, entregando información (por ejemplo, de sensores) y por otro lado m nodos suscritos
al tópico recibiendo dicha información constantemente para ejecutar alguna acción.
Además de lo anteriormente descrito, existe en ROS el concepto de package o paquete, el
cual corresponde a una pseudo-biblioteca que permite realizar determinadas tareas utilizando
nodos previamente definidos creando una red de publicaciones y suscripciones, simplificando
de este modo la interacción entre los diversos elementos que componen al robot. Un ejemplo
de esto se presenta en el diagrama de la figura Fig. 4.24, en donde se encuentran 3 tipos
de nodos, por un lado, está el nodo 3, el cual solo se encarga de publicar sobre el mensaje
1, por otro se encuentra el nodo 4, el cual solo suscribe sobre el mensaje 1 y finalmente se
encuentran los nodos 1 y 2 que se encargan de publicar y suscribir, en el caso del nodo 1 se
publica sobre el mensaje 1 y se suscribe el mensaje 2; en cambio el nodo 2 realiza el proceso
inverso.
/mensaje2
/nodo3 /nodo4
Fig. 4.24: Diagrama de relaciones en comunicación ROS
Es ası́ que, para el desarrollo del presente proyecto, debido a que se utiliza Ubuntu en su
versión 20.04 [42], la versión de ROS corresponde a su versión de nombre noetic.
En esta versión, y considerando la relevancia para el presente proyecto, se destaca la
existencia de 2 packages principales a utilizar relacionados con la lectura y procesamiento
de información de sensores. Por un lado, se encuentra el de nombre “robot pose ekf”, que
corresponde a la implementación de un filtro Kalman extendido [43]. El que se encarga de
realizar las conversiones necesarias para integrar las señales obtenidas de distintos sensores
en una única salida indicando tanto posición como orientación en el espacio [44]. Por otro
lado, se encuentra el package de nombre “gps common”, que como se verá más adelante,
consiste en un nexo necesario para compartir la información de las variables sensadas por el
gps siendo convertidas de modo que sean compatibles con el package del filtro Kalman [45].
En cuanto al paquete de filtro Kalman, suscribe tópicos de 3 tipos, siendo estos de nombre
textbfimu data, textbfodom y vo, de las cuales la primera corresponde a un elemento con el
tipo de mensaje “sensor msgs/Imu” [46], mientras que las ultimas corresponden a variables
con el tipo de mensaje “nav msgs/Odometry” [?]. De estas 3 suscripciones, las relevantes para
el presente sistema radican en imu data y vo, siendo los tópicos orientados a la transferencia
de información para sensor de tipo IMU y para odometrı́a visual respectivamente, aunque
este último puede ser modificado (al ser una posición en 3 dimensiones) para recibir la
información en conjunto de GPS y barómetro.
Respecto del tópico imu data, analizándolo en más detalle, se puede indicar que está
compuesto de 3 subtópicos y 3 matrices de covarianza de 3x3 asociadas a cada subtópico. Es-
tos subtópicos contienen mensajes con los tipos que corresponden a “geometry msgs/Quaternion”
el cual consiste en el vector de rotación expresado en forma de cuaterniones y 2 subtópicos
con mensajes de tipo “geometry msgs/Vector3” correspondientes a las velocidades angulares
en [rad/s] y las aceleraciones lineales en [m/s2 ].
En cuanto al tópico vo se tiene que contiene 2 subtópicos, por un lado, se tiene uno con
mensaje de tipo “geometry msgs/PoseWithCovariance” que corresponde al almacenamiento
de la posición junto a su matriz de covarianza asociada; este punto corresponde a un men-
saje de tipo “geometry msgs/Pose”, el cual almacena la posición mediante un punto en el
espacio con un mensaje de tipo “geometry msgs/Point” y su orientación en forma de vector
de rotación utilizando cuaterniones. El otro subtópico corresponde a un mensaje de tipo
“geometry msgs/TwistWithCovariance”, que almacena la velocidad junto a su covarianza
asociada; esta velocidad es un mensaje de tipo “geometry msgs/Twist” que contiene las ve-
locidades lineales y angulares en forma de 2 vectores de 3 elementos cuyo tipo corresponde
al presentado anteriormente.
En cuanto a la salida del package, lo que corresponde, en otras palabras, al tópico pu-
blicado, consiste en “geometry msgs/PoseWithCovarianceStamped [47]”, el cual contiene la
información en un subtópico con un mensaje de tipo “geometry msgs/PoseWithCovariance”
el cual ha sido descrito con anterioridad. La importancia de este tópico radica en que, en
base a la integración de distintas variables obtenidas de los diversos sensores, se resume
la posición y orientación del robot (en este caso la aeronave) en un punto en el espacio y
un vector de rotación, los cuales pueden ser utilizados de forma directa en la estrategia de
control que se desee implementar.
Ası́, realizando el análisis del otro package (gps common), se tiene el tópico de nombre fix
como la entrada de éste. Éste tópico, contiene un mensaje de tipo “sensor msgs/NavSatFix”
[48], cuyos subtópicos más relevantes consisten en los de nombre latitude, longitude y
altitude, los cuales consisten en mensajes de tipo float de 64 bits, en conjunto con la matriz
de covarianza asociada a la posición.
Por otro lado, la salida de este package corresponde al tópico de nombre odom el cual
consiste en un mensaje de tipo “nav msgs/Odometry” ya detallado en su uso con el paquete
de filtro Kalman.
Una vez detallados los paquetes orientados al manejo de la información de sensores, es
importante detallar un package adicional de nombre “rosserial server”, el cual permite la
creación de un servidor de enlace para la comunicación entre múltiples dispositivos compati-
bles con este paquete (como lo son Arduino y ESP32 gracias a la existencia de la biblioteca
rosserial arduino). El servidor de enlace puede comunicarse mediante puerto serial utilizando
conexión vı́a cable Universal Serial Bus (USB) o utilizando un puerto glstcp mediante una
conexión a una red WiFi permitiendo una comunicación de tipo inalámbrica.
En cuanto a la implementación de lo anteriormente descrito, se debe señalar la importan-
cia de un tipo de archivo inherente al método de trabajo consistente en ROS, que corresponde
a la extensión “.launch”. Estos archivos corresponden a un texto que contiene (en forma de
código) las instrucciones de los nodos a ejecutar indicando además parámetros relevantes
como lo son la tasa de publicación y el enlace entre los nombres de tópicos publicados y
recibidos. Por ello se tiene en primer lugar el archivo “socket.launch” mostrado a continua-
ción, el cual pertenece al package “rosserial server” e indica los parámetros necesarios para
1 <la unch>
2 <node pkg=” r o s s e r i a l s e r v e r ” type=” s o c k e t n o d e ” name=”
r o s s e r i a l s e r v e r ” />
3 <node pkg=” r o s s e r i a l p y t h o n ” type=” m e s s a g e i n f o s e r v i c e . py”
name=” r o s s e r i a l m e s s a g e i n f o ” />
4 </ l aunch>
Por otro lado, se tiene el archivo “robot pose ekf.launch”, el que, si bien en un comien-
zo inicializa únicamente el nodo “robot pose ekf” correspondiente al filtro Kalman, como
se ve a continuación, se hace la inclusión del nodo “gps conv” perteneciente al paquete
“gps common” que, como se señaló anteriormente, es necesario para interpretar los valores
obtenidos desde el GPS y transformarlos al tipo requerido por el filtro Kalman. Aquı́ se
puede ver además la definición de frecuencia de publicación del filtro Kalman, los tópicos de
entrada a utilizar para dicho filtro y la redefinición de los tópicos de entrada y salida para
ambos nodos permitiendo la interconexión de estos de forma adecuada.
1 <la unch>
2
22 </ l au nch>
Ası́, una vez se encuentra definido el servidor y nodos ROS a ejecutar en el computador
base, es requerido realizar el código para la tarjeta ESP32 correspondiente a la inicialización
de ROS y la conexión a la red WiFi de la placa. Para ello, se realiza la inclusión de las bi-
1 #include <WiFi.h>
2 #include <ros.h>
3 #include <ros/time.h>
4 #include <std_msgs/String.h>
5 #include <sensor_msgs/Imu.h>
6 #include <sensor_msgs/NavSatFix.h>
7 #include <geometry_msgs/PoseWithCovarianceStamped.h>
Por ello, a continuación, se requiere definir los parámetros de la conexión WiFi a utilizar,
siendo estos la SSID y su contraseña. Posteriormente, se definen, como se ve en el código a
continuación, se realiza la definición del handler de los nodos, seguido de la definición de las
publicaciones y suscripciones realizadas por el dispositivo. aquı́ cabe destacar, además, que,
al realizar la suscripción de los mensajes, se realiza la conversión de los cuaterniones, a la
forma Roll, Pitch y Yaw.
1 ros::NodeHandle nh;
2 //Publisher
3 sensor_msgs::Imu imu_msg;
4 ros::Publisher imupub("sensor_msgs/Imu", &imu_msg);
5 sensor_msgs::NavSatFix gps_msg;
6 ros::Publisher gpspub("sensor_msgs/fix", &gps_msg);
7 //Subscriber
8 void messageCb( const geometry_msgs::
PoseWithCovarianceStamped& msg){
9 float qw, qx, qy, qz;
10 float sinr_cosp, cosr_cosp, sinp, siny_cosp, cosy_cosp;
11 TrueZ = msg.pose.pose.position.z;
12 qw = msg.pose.pose.orientation.w;
13 qx = msg.pose.pose.orientation.x;
14 qy = msg.pose.pose.orientation.y;
15 qz = msg.pose.pose.orientation.z;
16 Serial.print("qx es: ");
17 Serial.println(qx);
18 Serial.print("qy es: ");
19 Serial.println(qy);
20 Serial.print("qz es: ");
21 Serial.println(qz);
22 Serial.print("qw es: ");
23 Serial.println(qw);
24 sinr_cosp = 2 * (qw * qx + qy * qz);
25 cosr_cosp = 1 - 2 * (qx * qx + qy * qy);
26 TrueRoll = atan2(sinr_cosp, cosr_cosp);
El siguiente paso corresponde a, dentro de la función setup del IDE Arduino, realizar la
inicialización de la comunicación WiFi, en conjunto con la inicialización de las publicaciones
y suscripciones de ROS, como se muestra a continuación.
1 if (nh.connected()) {
2 Serial.println("Connected");
3 imu_msg.header.seq = seqcount;
4 imu_msg.header.frame_id = "base_footprint";
5 imu_msg.header.stamp = nh.now();
6 imu_msg.orientation.x = RetQuatX;
7 imu_msg.orientation.y = RetQuatY;
8 imu_msg.orientation.z = RetQuatZ;
9 imu_msg.orientation.w = RetQuatR;
10 imu_msg.orientation_covariance[0] = 1;
11 imu_msg.orientation_covariance[1] = 0;
12 imu_msg.orientation_covariance[2] = 0;
13 imu_msg.orientation_covariance[3] = 0;
14 imu_msg.orientation_covariance[4] = 1;
15 imu_msg.orientation_covariance[5] = 0;
16 imu_msg.orientation_covariance[6] = 0;
17 imu_msg.orientation_covariance[7] = 0;
18 imu_msg.orientation_covariance[8] = 1;
19 Serial.println("Published");
20 } else {
21 Serial.println("Not Connected");
22 }
23 nh.spinOnce();
/sensor_msgs/fix /sensor_msgs/Imu
/gps_conv
/robot_pose_ekf
/vo
Fig. 4.25: Diagrama de relaciones simplificado en comunicación ROS para el presente proyecto
1 t
Z
de(t)
u(t) = Kp + Ki e(τ ) dτ + Kd (4.1)
Ti 0 dt
Ki
U (s) = [Kp + + sKd ]E(s) (4.2)
s
Previo a realizar la discretización, se puede realizar un ajuste a la ecuación en el plano
de La Place añadiendo un filtro a la componente derivativa para reducir el ruido en altas
frecuencias [49], obteniendo la ecuación (4.3), en donde τ corresponde a la constante de
tiempo del filtro implementado.
Ki 1
U (s) = [Kp + + sKd ]E(s) (4.3)
s sτ + 1
Ası́, mediante la expresión de discretización por Tustin (presentada en la ecuación (4.4))
se puede realizar el cambio de la expresión del control desde el plano de La Place al plano Z
como se muestra en la ecuación (4.5). En donde T representa el tiempo de muestreo utilizado
en la discretización.
2 z−1
s≈ (4.4)
T z+1
Ki T z + 1 2Kd z − 1 1
U (z) = [Kp + ( )( )+( )( )( 2 z−1 )]E(z) (4.5)
2 z−1 T z + 1 ( T z+1 )τ + 1
Finalmente, realizando el álgebra correspondiente y realizando la transformada Z inversa,
se puede obtener la ecuación de diferencias presentada a continuación en las expresiones (4.7),
(4.8), (4.9) y (4.6).
Ki T
i(k) = (e(k) + e(k − 1)) + i(k − 1) (4.8)
2
2Kd 2τ − T
d(k) = (e(k) − e(k − 1)) + d(k − 1) (4.9)
2τ + T 2τ + T
En base a esto, se puede definir un algoritmo en C/C++ que permita implementar la
estrategia de control en la tarjeta Cora Z7, no obstante, en [49], se encuentra el repositorio
con la biblioteca del control para ser implementada directamente. Ası́, a continuación se
detallarán los puntos más importantes de la biblioteca respecto a su uso. En primera ins-
tancia cabe destacar que esta biblioteca incluye además un algoritmo de tipo Wind-up que
permite evitar la saturación de la señal de control causada por la parte integral, mejorando
su desempeño.
Para simplificar la implementación se define un tipo de estructura, la cual permitirá te-
ner más de un control PID existente a la vez y que almacenará de forma independiente los
parámetros y variables necesarios para su aplicación. Ésta estructura se puede observar en
el listing presentado a continuación.
1 typedef struct{
2 //Constantes de PID
3 float Kp;
4 float Ki;
5 float Kd;
6 //Constante de tiempo para filtro derivativo
7 float tau;
8 //Limites de salida
9 float limMin;
10 float limMax;
11 //Limites de integrador para anti Wind-up
12 float limMinInt;
13 float limMaxInt;
14 //Tiempo de muestreo en segundos
15 float T;
16 //Variables de almacenamiento del controlador
17 float integrator;
18 float prevError;
19 float differentiator;
20 float prevMeasurement;
21 //Salida del controlador
22 float out;
23 } PIDController;
Por otro lado, se definen 2 funciones cuya aplicación consiste en realizar la inicialización
y actualización del controlador en cada muestreo. En cuanto a la función de inicialización
realiza directamente la limpieza de la memoria del control, mientras que la función de actua-
lización, mostrada a continuación, aplica directamente las ecuaciones vistas con anterioridad.
31 return pid->out;
32 }
y método de transferencia de datos puede diferir en algunos aspectos. Es ası́ que, a través de
un análisis de la trama de datos, se identifican 2 incompatibilidades en la transferencia de
datos en dirección desde la tarjeta Cora Z7 hacia la tarjeta ESP32. Estas incompatibilidades
corresponden, por un lado, a la duplicación del primer dato del buffer, problema identificado
como inherente al uso de la tarjeta Cora Z7 como esclavo SPI; por otro, se produce un retraso
en la captura de datos por parte de la tarjeta ESP32 perdiendo el primer bit de la trama,
debido a la forma de realizar la lectura de datos, la cual no es compatible al 100 % con el
envı́o desde la tarjeta SoC. Como solución al problema, se extiende el buffer de comunicación
de 5 a 6 bytes, en donde el ultimo byte se deja sin información, puesto que será reemplazado
por el byte numero 5 después del desplazamiento originado por la duplicidad del primero. Por
otro lado, mediante operaciones de bitwise, se realiza el desplazamiento de los bits requeridos
para reordenar la transmisión de información, logrando ası́ reconstruir el mensaje enviado.
Esto último se realiza mediante el fragmento de código presentado a continuación, extraı́do
del código principal de la placa ESP32. Lo cual consiste en un ciclo que desplaza en 7 bytes
la comunicación, eliminando el dato duplicado y a la vez corrigiendo el desplazamiento de
bits producto del bit no leido.
1 for(int i=0; i<tamano-1;i++){
2 bufferout2[i] = (bufferout[i+1]>>1) | ((bufferout[i]&0x01
)<<7);
3 }
5. Resultados
En el presente capı́tulo se realiza una revisión, de forma secuencial, sobre cada uno de
los ensayos experimentales realizados de forma posterior al montaje completo del prototipo.
Por otro lado, para su funcionamiento, es requerido realizar la ejecución de los archivos
“.launch” de ROS y a su vez mediante el comando rostopic echo, monitorear la información
suscrita y publicada por el filtro Kalman, permitiendo realizar una comparación sobre su
comportamiento. La plantilla de ejecución se puede ver en la figura Fig. 5.1, en donde se
tiene a la izquierda, de arriba hacia abajo, la ejecución de los “.launch” correspondientes a al
servidor ROS y filtro Kalman respectivamente. En la ventana central, por otro lado, se realiza
el monitoreo del tópico “/sensor msgs/fix”, correspondiente al tópico suscrito por el filtro
Kalman y que contiene los valores sensados de altura, con una matriz de covarianza identidad.
Ası́, en el costado derecho se monitorea el tópico “/robot pose ekf/odom combined”, lo que
corresponde al tópico publicado por el filtro Kalman.
Fig. 5.2: Lectura puerto serial Tarjeta Cora Z7 al inicio de la ejecución del código de prueba.
Fig. 5.3: Lectura puerto serial Tarjeta Cora Z7 al estimar la altura base.
Fig. 5.4: Lecturas del terminal Ubuntu para tópicos ROS en instantes iniciales de la prueba experimental.
Fig. 5.5: Lecturas del terminal Ubuntu para tópicos ROS en instantes finales de la prueba experimental.
mediante el uso de sedal de pesca de 0.35[mm] (con una resistencia para 10[kg] según su
empaque) se ató a los brazos del prototipo para tener un ancla de 5[m] para evitar que el
prototipo supere dicha altura.
Al realizar esta prueba, una vez que se inició el prototipo, se esperó a que el prototipo
comience con la secuencia de corrección de altitud, obteniendo un resultado registrado en
video, del cual fue posible extraer las capturas mostradas en la figura Fig. 5.6. Aquı́ se aprecia
como, apenas comienza a elevarse, el prototipo se inclina ampliamente hacia el costado
derecho causando su inestabilidad y posterior caı́da.
00:00:18;15 00:00:19;00
Fig. 5.6: Capturas del resultado de la primera prueba de vuelo del prototipo.
2(qw ∗ qx + qy ∗ qz)
φ = arctan( ) (5.1)
1 − 2(qx2 + qy 2 )
θ = arcsin(2(qw ∗ qy − qz ∗ qx)) (5.2)
2(qw ∗ qz + qx ∗ qy)
ψ = arctan( ) (5.3)
1 − 2(qy 2 + qz 2
Por otro lado, se presenta la opción de realizar una compensación de forma directa sobre
el giro de motores, buscando el valor de PWM (que en código corresponde a un valor de entre
50000 y 100000) permita reducir el efecto de desbalance causado por el centro de gravedad
del UAV.
En base a lo desarrollado, en el presente capı́tulo, se considera que el hecho del retardo que
se produce por el nodo ROS, se considera inviable realizar el control únicamente utilizando
el MMA programado. Es ası́, que se realiza una serie de pruebas de forma empı́rica en una
zona sin peatones y cubierta de pasto para reducir el impacto generado por posibles caı́das.
Además, como se presenta en la figura Fig. 5.7, se utilizó un trozo de madera previamente
nivelado para asegurar que el rotor de los 4 motores apunte directamente en dirección del
eje Z.
Fig. 5.7: Muestra del montaje utilizado para las pruebas con compensación directa sobre el giro de motores.
realizando en primer lugar giro a bajas revoluciones para sacar los motores de inercia, a
continuación se aplica el giro de motores con un thrust base de aproximadamente un 60 % del
máximo posible, siendo modificado utilizando las variables de compensación (siendo comp,
la variable de ajuste del ángulo Roll y comp2 para ajustar el ángulo Pitch) sumándolas y
restándolas según cada motor, como se observa en el fragmento de código a continuación.
1 if (InterruptCounter > 3 && InterruptCounter <= 6){
2 TH1 = (T + comp - comp2);
3 if (TH1 < minThrottle){
4 TH1 = minThrottle;
5 }
6 if (TH1 > maxThrottle-28000){
7 TH1 = maxThrottle-28000;
8 }
9 TH2 = T - comp - comp2;
10 if (TH2 < minThrottle){
11 TH2 = minThrottle;
12 }
13 if (TH2 > maxThrottle-28000){
14 TH2 = maxThrottle-28000;
15 }
16 TH3 = T - comp + comp2;
17 if (TH3 < minThrottle){
18 TH3 = minThrottle;
19 }
20 if (TH3 > maxThrottle-28000){
21 TH3 = maxThrottle-28000;
22 }
23 TH4 = (T + comp + comp2);
24 if (TH4 < minThrottle){
25 TH4 = minThrottle;
26 }
27 if (TH4 > maxThrottle-28000){
28 TH4 = maxThrottle-28000;
29 }
30 }
Posteriormente se disminuye el giro para todos los motores y permitir el descenso del
prototipo, para finalmente dejar los motores con el valor mı́nimo de Thrust, es decir com-
pletamente detenidos.
Al realizar las pruebas con un valor de compensación de 750 para corregir el Roll y 0 en
el caso del Pitch, se observa el resultado mostrado en las capturas presentadas en la figura
Fig. 5.8, en donde se observa que dicho valor sigue sin ser suficiente para evitar la caı́da del
prototipo.
00:00:07;29 00:00:08;12
Fig. 5.8: Capturas del resultado con compensación de valor 750 para corrección de Roll y 0 para corrección
de Pitch.
Dentro de las situaciones que se pueden detallar a momento de realizar los ensayos corres-
pondientes al presente subcapı́tulo, es importante mencionar la alta influencia que presentan
factores externos en su ejecución, en particular, con la influencia del clima que, al reque-
rir realizar la experimentación en un lugar externo, no se puede asegurar en su totalidad
la igualdad de condiciones entre cada ensayo, desencadenando la necesidad de realizar una
mayor cantidad de iteraciones y con ello la rotura o flexión de las hélices, disminuyendo la
posibilidad de perfeccionar de mejor modo el despegue del prototipo.
En cuanto a los resultados del primer ensayo de vuelo, debido a la inclinación que
inestabiliza al prototipo, se puede concluir que el centro de masa de este debe estar
desplazado a causa del montaje de los componentes electrónicos como sensores, ESC
y baterı́a. Resultando en la imposibilidad de realizar un despegue de forma exitosa en
esta experiencia experimental.
A pesar del punto anterior, en cuanto a las capacidades de diseño del prototipo, se
puede señalar que la selección de componentes a utilizar no presenta problemas desde
el punto de vista de la actuación, siendo los motores lo suficientemente potentes para
levantar el prototipo con su masa de alrededor de 700 [g], como se observa en las
capturas previas a la caı́da del UAV.
Pese a que se le entrega la misma señal PWM a los motores no se logra realizar un
despegue exitoso debido a la inclinación del prototipo en el ángulo de Roll, por ello
se hizo necesario realizar, mediante la modificación del duty cycle para cada ESC, una
compensación en las velocidades de estos corrigiendo, de este modo, de forma parcial
dicha inestabilidad.
Es ası́ que, en resumen, se hace posible detallar como conclusión general el cumplimiento
de alrededor de un 80 % de los objetivos especı́ficos, permitiendo realizar un modelo teórico
y diseño general del prototipo de forma adecuada para las necesidades del proyecto. No
obstante, debido a situaciones no consideradas de forma previa es que no se logró implementar
en su totalidad un sistema que permitiera realizar de forma exitosa el control de altitud.
Sin perjuicio de lo anterior, se puede concluir que el presente proyecto permite sentar
las bases para un desarrollo futuro en el uso de la tecnologı́a de drones en el campo del
mantenimiento preventivo realizando tareas de inspección.
Una vez se resuelvan los puntos anteriores, se plantea la tarea de extrapolar el control
sobre altitud a la posición sobre los otros ejes, permitiendo ası́ instalar la cámara para
realizar las rutas de inspección cumpliendo ası́ con el enfoque inicial de la presente
memoria.
Referencias
[1] Inspectioneering, “Visual inspection.” https://inspectioneering.com/tag/
visual+inspection. Accedido 25-07-2021.
[2] Pipemasters, “Visual inspection: Know the advantages and disadvantages.” http://
pipemasters.pt/blog/en/industry/visual-inspection/. Accedido 25-07-
2021.
[3] I. R. de Ingenieria del Mantenimiento, “Técnicas de mantenimiento predictivo en
plantas industriales.” http://www.renovetec.com/irim/131-tecnicas-de-
mantenimiento-predictivo. Accedido 08-12-2020.
[4] J. B. Durán, Gestión de Mantenimiento bajo estándares Internacionales como PAS 55
Asset Management.
[5] V. autores, “Industrial maintenance.” https://www.onupkeep.com/learning/
maintenance-applications/industrial-maintenance. Accedido 25-07-
2020.
[6] D. Chesworth, “Industry 4.0 techniques as a maintenance strategy (a review paper),”
01 2018.
[7] R. K. M. Lindley R. Higgins, MAINTENANCE ENGINEERING HANDBOOK. 2002.
[8] M. R. K. Smith Ricky, Industrial Machinery Repair: Best Maintenance Practices Pocket
Guide. 2003.
[9] hispadrones, “Tipos de drones.” https://www.hispadrones.com/
principiantes/aprendizaje-consejos/tipos-de-drones/, 2019. Accedi-
do 11-08-2021.
[10] M. Guillén, “Tipos de drones aéreos.” https://dronespain.pro/tipos-de-
drones-aereos/, 2020. Accedido 08-12-2020.
[11] N. Zlatanov, “Multirotor aircraft dynamics, simulation, and control,” August 2016.
[12] M. Gonzáles, “Tipos de drones: Los distintos tipos de drones que hay.” https:
//filmora.wondershare.es/drones/types-of-drones.html, 2021. Accedi-
do 11-08-2021.
[13] Ingeoexpert, “Tipos de drones: forma y método de control.”
https://ingeoexpert.com/2018/05/25/tipos-de-drones/
#Tipos de drones segun su metodo de control, 2018. Accedido 08-12-2020.
[14] A. Insights, “¿qué es la fotogrametrı́a con drones?.” https://www.aerial-
insights.co/blog/fotogrametria-con-drones/, 2018. Accedido 16-08-2021.
[15] FENERCOM, “Los drones y sus aplicaciones a la ingenierı́a civil.” https:
//www.fenercom.com/wp-content/uploads/2015/03/Los-Drones-y-
sus-Aplicaciones-a-la-Ingenieria-Civil-fenercom-2015.pdf, 2015.
Accedido 16-08-2021.
[34] EMAX, “Emax rs2205s 2300kv 2600kv racing edition brushless motor for
fpv racing.” https://emaxmodel.com/products/emax-rs2205s-2300kv-
2600kv-racing-edition-brushless-motor-for-fpv-racing. Accedido
09-12-2020.
[35] F. Pierre, “Zmr-250 protection, landing gear, and antenna mount - * revision 3.0 *.”
https://www.thingiverse.com/thing:817622, 2015. Accedido 16-08-2021.
[36] M. Taylor, “sparkfun/sparkfun bme280 arduino library.” https://github.com/
sparkfun/SparkFun BME280 Arduino Library. Accedido 13-06-2021.
[37] N. Seidle, “sparkfun/sparkfun bno080 arduino library.” https://github.com/
sparkfun/SparkFun BNO080 Arduino Library. Accedido 13-06-2021.
[38] sparkfun, “Nmea reference manual.” https://www.sparkfun.com/datasheets/
GPS/NMEA\%20Reference\%20Manual-Rev2.1-Dec07.pdf. Accedido 15-06-
2021.
[39] M. Hart, “mikalhart/tinygpsplus.” https://github.com/mikalhart/
TinyGPSPlus. Accedido 13-06-2021.
[40] “30a bldc esc.” https://usermanual.wiki/Document/
30ABLDCESCProductManual.1338503313/pdf. Accedido 16-06-2021.
[41] “About ros.” https://www.ros.org/about-ros/. Accedido 07-05-2021.
[42] O. Robotics, “Ros noetic ninjemys.” http://wiki.ros.org/noetic, 2020. Accedi-
do 31-08-2021.
[43] A. Pascual, “Ekf y ukf: dos extensiones del filtro de kalman para sistemas no lineales
aplicadas al control de un péndulo invertido,” p. 5, 05 2006.
[44] O. Robotics, “robot pose ekf.” http://wiki.ros.org/
robot pose ekf\#robot pose ekf-1. Accedido 16-06-2021.
[45] O. Robotics, “Adding a gps sensor to the robot pose ekf filter.” http://
wiki.ros.org/robot pose\ ekf/Tutorials/AddingGpsSensor, 2011. Acce-
dido 31-08-2021.
[46] O. Robotics, “sensor msgs/imu message.” http://docs.ros.org/en/api/
sensor\ msgs/html/msg/Imu.html. Accedido 31-08-2021.
[47] O. Robotics, “geometry msgs/posewithcovariancestamped messa-
ge.” http://docs.ros.org/en/api/geometry\ msgs/html/msg/
PoseWithCovarianceStamped.html. Accedido 31-08-2021.
[48] O. Robotics, “sensor msgs/navsatfix message.” http://docs.ros.org/en/api/
sensor\ msgs/html/msg/NavSatFix.html. Accedido 31-08-2021.
[49] P. Salmony, “Pid controller implementation written in c..” https://github.com/
pms67/PID, 2020. Accedido 14-08-2021.
Z ref Thrust
PD
Control Posición lineal
M1 a M4 Velocidad lineal
φ θ ψ
MUX
Diseño y construcción de drone para tareas de inspección en mantenimiento
Fig. A.1: Diagrama de bloques sistema con control sobre Roll, Pitch, Yaw y Thrust.
96
B.
X Y Z
Z ref Thrust
PD
Control Posición lineal
M1 a M4 Velocidad lineal
ψ
θ
φ MUX
Diseño y construcción de drone para tareas de inspección en mantenimiento
97
Diseño y construcción de drone para tareas de inspección en mantenimiento
11 void setup()
12 {
13 Serial.begin(9600); //Se habilita el puerto serial
14 Serial.println("Reading basic values from BME280");
15
27 void loop()
28 {
29 //Se obtiene e imprime la presión en Pa
30 Serial.print(" Pressure: ");
31 Serial.print(mySensor.readFloatPressure(), 0);
32 //Se obtiene e imprime la altura en m
33 Serial.print(" Alt: ");
34 Serial.print(mySensor.readFloatAltitudeMeters(), 1);
35 Serial.println();
36
37 delay(50);
38 }
35 Serial.println(F("Gyro enabled"));
36 Serial.println(F("Output in form x, y, z, in radians per
second"));
37
44 void loop()
45 {
46 //Verifica si hay información desde la IMU
47 if (myIMU.dataAvailable() == true)
48 {
49 //De ser asi almacena los valores recibidos
50 float quatI = myIMU.getQuatI();
51 float quatJ = myIMU.getQuatJ();
52 float quatK = myIMU.getQuatK();
53 float quatReal = myIMU.getQuatReal();
54 float gx = myIMU.getGyroX();
55 float gy = myIMU.getGyroY();
56 float gz = myIMU.getGyroZ();
57 float x = myIMU.getLinAccelX();
58 float y = myIMU.getLinAccelY();
59 float z = myIMU.getLinAccelZ();
60
83 Serial.print(" ");
84 Serial.print(z, 2);
85 Serial.println();
86 }
87 }
12 void setup()
13 {
14 Serial.begin(115200); //Se habilita puerto serial
15 ss.begin(9600); //Se inicia la comunicación UART a 9600
baudios
16
23 void loop()
24 {
25 //Variable para verificar la entrada de nuevos datos
26 bool newData = false;
27 //Variables para almacenar el caracter leı́do, cantidad de
frases y fallas en la comunicación
28 unsigned long chars;
29 unsigned short sentences, failed;
30
31 #ifndef SRC_BME280_IIC_H_
32 #define SRC_BME280_IIC_H_
33 /**********DEFINICIONES GENERALES***************/
34 //Definiciones para axi iic
35 #define IIC_DEVICE_ID XPAR_IIC_0_DEVICE_ID
36 #define IIC_BASE_ADDRESS XPAR_IIC_0_BASEADDR
37 //Dirección I2C por defecto
38 #define BME_I2C_ADDRESS 0x77
39 //Modos de trabajo
40 #define BME_I2C_MODE 0
41 #define BME_MODE_SLEEP 0b00
42 #define BME_MODE_FORCED 0b01
43 #define BME_MODE_NORMAL 0b11
44 //Direcciones de registro
45 #define BME280_DIG_P1_LSB_REG 0x8E
46 #define BME280_DIG_P1_MSB_REG 0x8F
47 #define BME280_DIG_P2_LSB_REG 0x90
48 #define BME280_DIG_P2_MSB_REG 0x91
49 #define BME280_DIG_P3_LSB_REG 0x92
50 #define BME280_DIG_P3_MSB_REG 0x93
51 #define BME280_DIG_P4_LSB_REG 0x94
52 #define BME280_DIG_P4_MSB_REG 0x95
53 #define BME280_DIG_P5_LSB_REG 0x96
54 #define BME280_DIG_P5_MSB_REG 0x97
55 #define BME280_DIG_P6_LSB_REG 0x98
56 #define BME280_DIG_P6_MSB_REG 0x99
57 #define BME280_DIG_P7_LSB_REG 0x9A
58 #define BME280_DIG_P7_MSB_REG 0x9B
59 #define BME280_DIG_P8_LSB_REG 0x9C
60 #define BME280_DIG_P8_MSB_REG 0x9D
61 #define BME280_DIG_P9_LSB_REG 0x9E
62 #define BME280_DIG_P9_MSB_REG 0x9F
63 #define BME280_CHIP_ID_REG 0xD0
64 #define BME280_RST_REG 0xE0
65 #define BME280_STAT_REG 0xF3
66 #define BME280_CTRL_MEAS_REG 0xF4
67 #define BME280_CONFIG_REG 0xF5
68 #define BME280_PRESSURE_MSB_REG 0xF7
69 #define BME280_PRESSURE_LSB_REG 0xF8
70 #define BME280_PRESSURE_XLSB_REG 0xF9
71
72 /****ESTRUCTURAS DE CONFIGURACIÓN**************/
73 struct BME280_settings
74 {
75 public:
76 u8 I2CAddress;
77 u8 runMode;
78 u8 tStandby;
79 u8 filter;
80 u8 pressOverSample;
81 };
82 struct SensorCalibration
83 {
84 public:
85
86 u16 dig_P1;
87 s16 dig_P2;
88 s16 dig_P3;
89 s16 dig_P4;
90 s16 dig_P5;
91 s16 dig_P6;
92 s16 dig_P7;
93 s16 dig_P8;
94 s16 dig_P9;
95 };
96
128
24 #include "BME280_IIC.h"
25
26 /*************************************
27 * CONFIGURACIONES E INICIALIZACIÓN
28 *************************************/
29
30 /*
31 * Constructor--Define las configuraciones por defecto
32 */
33 BME280::BME280(void)
34 {
35 settings.I2CAddress = BME_I2C_ADDRESS;
36
37 settings.runMode = BME_MODE_NORMAL;
38 settings.tStandby = 0;
39 settings.filter = 0;
40 settings.pressOverSample = 1;
41 }
42
43 /*
44 * Realiza el inicio del sensor
45 */
46 bool BME280::inicio()
47 {
48 //settings.commInterface = BME_I2C_MODE;
49 u8 chipID = begin();
50 //printf("chip: %x \n\r",chipID); //Uncomment for
debug
51 //begin() debe retornar 0x60 o 0x58 dependiendo si el
sensor es modelo BME o BMP
52 if(chipID == 0x60){
53 return(true);
54 }
55 if(chipID == 0x58){
56 return(true);
57 }
58 return(false);
59 }
60 u8 BME280::begin()
61 {
62 //sleep(2); //Espera a que el sensor se inicie
63 u8 chipID = readRegister(BME280_CHIP_ID_REG); //Lee
el ID del sensor
64 //printf("read: %d \n \r",chipID); //Uncomment for
debug
65 if (chipID != 0x58 && chipID != 0x60){
66 return(chipID); //Si el ID no corresponde
retorna el valor obtenido
67 }
68 //Si el ID es correcto realiza la calibracion en los
registros correspondientes
69 calibration.dig_P1 =((u16)((readRegister(
BME280_DIG_P1_MSB_REG)<<8)+(readRegister(
BME280_DIG_P1_LSB_REG))));
70 calibration.dig_P2 =((u16)((readRegister(
BME280_DIG_P2_MSB_REG)<<8)+(readRegister(
BME280_DIG_P2_LSB_REG))));
71 calibration.dig_P3 =((u16)((readRegister(
BME280_DIG_P3_MSB_REG)<<8)+(readRegister(
BME280_DIG_P3_LSB_REG))));
72 calibration.dig_P4 =((u16)((readRegister(
BME280_DIG_P4_MSB_REG)<<8)+(readRegister(
BME280_DIG_P4_LSB_REG))));
73 calibration.dig_P5 =((u16)((readRegister(
BME280_DIG_P5_MSB_REG)<<8)+(readRegister(
BME280_DIG_P5_LSB_REG))));
74 calibration.dig_P6 =((u16)((readRegister(
BME280_DIG_P6_MSB_REG)<<8)+(readRegister(
BME280_DIG_P6_LSB_REG))));
75 calibration.dig_P7 =((u16)((readRegister(
BME280_DIG_P7_MSB_REG)<<8)+(readRegister(
BME280_DIG_P7_LSB_REG))));
76 calibration.dig_P8 =((u16)((readRegister(
BME280_DIG_P8_MSB_REG)<<8)+(readRegister(
BME280_DIG_P8_LSB_REG))));
77 calibration.dig_P9 =((u16)((readRegister(
BME280_DIG_P9_MSB_REG)<<8)+(readRegister(
BME280_DIG_P9_LSB_REG))));
78 //Define los valores por defecto para los parametros
de configuracion
79 setStandbyTime(settings.tStandby);
80 setFilter(settings.filter);
81 setPressureOverSample(settings.pressOverSample);
82 setMode(BME_MODE_NORMAL); //Inicia por defecto
en modo normal
83 return(readRegister(BME280_CHIP_ID_REG));
84
85 }
86
87 /*
88 * Define el modo de operacion del módulo (para más detalle
de los modos ver en datasheet).
89 * 00 = Sleep
90 * 01 and 10 = Forced
91 * 11 = Normal mode
92 */
93 void BME280::setMode(u8 mode)
94 {
95 //Si el valor pedido es mayor a 2 bit (error)
mantiene modo normal por defecto
96 if(mode>0b11){
97 mode = 0;
98 }
106 /*
107 * Obtiene el modo actual del módulo
108 */
109 u8 BME280::getMode()
110 {
111 u8 controlData =readRegister(BME280_CTRL_MEAS_REG);
112 return(controlData & 0b00000011);
113 }
114
115 /*
116 * Define los bit de tiempo de espera dependiendo de los
requerimientos. Los modos posibles son:
117 * 0 --> 0.5ms
118 * 1 --> 62.5ms
119 * 2 --> 125ms
120 * 3 --> 250ms
121 * 4 --> 500ms
122 * 5 --> 1000ms
123 * 6 --> 10ms
124 * 7 --> 20ms
125 */
126 void BME280::setStandbyTime(u8 timeSetting)
127 {
128 if(timeSetting>0b111){
129 timeSetting = 0;
130 }
131 u8 controlData = readRegister(BME280_CONFIG_REG);
132 controlData &= ˜((1<<7)|(1<<6)|(1<<5));
133 controlData |= (timeSetting << 5);
134 writeRegister(BME280_CONFIG_REG, controlData);
135 }
136
137 /*
138 * Define los coeficientes para el filtro de respuesta a
impulso (FIR)
139 * 0 --> Filtro desactivado
140 * 1 --> coeficientes = 2
141 * 2 --> coeficientes = 4
156 /*
157 * Define los valores de sobremuestreo 0 es desactivado, los
valores posibles
158 * son 1, 2, 4, 8 o 16
159 */
160 void BME280::setPressureOverSample(u8 overSampleAmount)
161 {
162 overSampleAmount = checkSampleValue(overSampleAmount)
;
163 u8 originalMode = getMode();
164 setMode(BME_MODE_SLEEP);
165
171 setMode(originalMode);
172 }
173
174 /*
175 * Valida el valor de sobremuestreo
176 */
177 u8 BME280::checkSampleValue(u8 userValue)
178 {
179 switch(userValue)
180 {
181 case(0):
182 return 0;
183 break;
184 case(1):
185 return 1;
186 break;
187 case(2):
188 return 2;
189 break;
190 case(4):
191 return 3;
192 break;
193 case(8):
194 return 4;
195 break;
196 case(16):
197 return 5;
198 break;
199 default:
200 return 1; //Si el valor
entregado no es valido por
defecto se deja en modo 1
201 break;
202 }
203 }
204
205 /*
206 * Cambia la dirección I2C para ajustarse al módulo a usar
207 */
208 void BME280::setI2CAddress(u8 address)
209 {
210 settings.I2CAddress = address;
211 }
212
213 /*
214 * Verifica si el sensor esta midiendo y retorna V o F
215 */
216 bool BME280::isMeasuring(void)
217 {
218 u8 stat = readRegister(BME280_STAT_REG);
219 return(stat&(1<<3));
220 }
221
222 /*
223 * Envia la orden de reseteo. Requiere utilizar begin
posteriormente
224 */
225 void BME280::reset(void){
226 writeRegister(BME280_RST_REG, 0xB6);
227 }
228
229 /*****************************************
230 * FUNCIONES PARA TRABAJAR CON BAROMETRO
231 *****************************************/
232
233 /*
234 * Entrega presion en Pa como un entero sin signo de 32 bit
en formato Q24.8
235 * (24 bits enteros y 8 bits fracionarios)
236 * Por ejemplo "24674867" representa 24674867/256 = 96386.2
Pa
237 */
238 float BME280::readFloatPressure(void)
239 {
240 u8 buffer[3];
241 readRegisterRegion(buffer, BME280_PRESSURE_MSB_REG,3)
;
242 s32 adc_P = ((u32)buffer[0]<<12) | ((s32)buffer
[1]<<4) |((buffer[2]>>4)&0x0F);
243 s64 var1, var2, p_acc;
244 var1 = ((s64)t_fine) - 128000;
245 var2 = var1 * var1 * (s64)calibration.dig_P6;
246 var2 = var2 + ((var1 * (s64)calibration.dig_P5)<<17);
247 var2 = var2 + (((s64)calibration.dig_P4)<<35);
248 var1 = ((var1 * var1 * (s64)calibration.dig_P3)>>8) +
((var1 * (s64)calibration.dig_P2)<<12);
249 var1 = (((((s64)1)<<47) + var1))*((s64)calibration.
dig_P1)>>33;
250 if(var1 == 0)
251 {
252 return 0; //Para evitar dividir por 0
253 }
254 p_acc = 1048576 - adc_P;
255 p_acc = (((p_acc<<31)-var2)*3125)/var1;
256 var1 = (((s64)calibration.dig_P9)*(p_acc>>13)*(p_acc
>>13))>>25;
257 var2 = (((s64)calibration.dig_P8)*p_acc)>>19;
258 p_acc = ((p_acc + var1 + var2)>>8) + (((s64)
calibration.dig_P7)<<4);
259
263 /*
264 * Define la presión a nivel del mar para cálculos de altitud
265 */
266 void BME280::setReferencePressure(float refPressure)
267 {
268 _referencePressure = refPressure;
269 }
270
271 /*
272 * Obtiene el valor actual de presión a nivel del mar
configurado
273 */
274 float BME280::getReferencePressure()
275 {
276 return(_referencePressure);
277 }
278
279 /*
280 * Obtiene la altitud en metros mediante la presión
utilizando la
281 * fórmula internacional barométrica.
282 */
283 float BME280::readFloatAltitude(void)
284 {
285 float heightOutput = 0;
286 heightOutput = ((float)-44330.77)*(pow(((float)
readFloatPressure()/(float)_referencePressure)
,0.190263)-(float)1);
287
291 /*****************************
292 * FUNCIONES DE UTILIDAD
293 *****************************/
294
295 /*
296 * Lee una serie de bytes, byte por byte
297 */
298 void BME280::readRegisterRegion(u8 *outputPointer, u8 offset,
u8 length)
299 {
300 //Definición de variables temporales
301 u8 i=0;
302 char c = 0;
303 u8 readBuffer[length]; //Buffer de lectura
304 u8 buffer[1]; //Buffer de escritura, para definir el
registro a leer
305 u16 StatusReg;
319 /*
320 * Para cada elemento del buffer de lectura se
almacena en el puntero de salida
321 * para el programa principal
322 */
323 while(i<length)
324 {
325 c = readBuffer[i];
326 *outputPointer = c;
327 outputPointer++;
328 i++;
329 }
330 }
331
332 /*
333 * Lee un byte único de un registro dado
334 */
335 u8 BME280::readRegister(u8 offset)
336 {
337 //Variables temporales y debug
338 u8 result = 30;
339 u8 numBytes = 1;
340 u8 buffer[1];
341 u16 StatusReg;
342 int checkpoint, check2;
361 /*
362 * Lee 2 bytes y los almacena en una variable de 16 bits
363 */
364 s16 BME280::readRegisterInt16(u8 offset)
365 {
366 //Define un buffer de almacenamiento
367 u8 myBuffer[2];
368 //Llama a la función de lectura solicitando 2 bytes
369 readRegisterRegion(myBuffer, offset, 2);
370 //Concatena ambos bytes en una variable de 16 bit
371 s16 output = (s16)myBuffer[0] | s16(myBuffer[1]<<8);
372 return output;
373 }
374
375 /*
376 * Escribe información en un registro dado
377 */
378 void BME280::writeRegister(u8 offset, u8 dataToWrite)
379 {
391 }
28 #ifndef SRC_BNO080_IIC_H_
29 #define SRC_BNO080_IIC_H_
30 /**********DEFINICIONES GENERALES***************/
31 //Definiciones para axi iic
32 #define IIC_DEVICE_ID XPAR_IIC_0_DEVICE_ID
33 #define IIC_BASE_ADDRESS XPAR_IIC_0_BASEADDR
34 //Dirección I2C por defecto
35 #define BNO_ADDRESS 0x4B
81 #define COMMAND_TARE 3
82 #define COMMAND_INITIALIZE 4
83 #define COMMAND_DCD 6
84 #define COMMAND_ME_CALIBRATE 7
85 #define COMMAND_DCD_PERIOD_SAVE 9
86 #define COMMAND_OSCILLATOR 10
87 #define COMMAND_CLEAR_DCD 11
88 //Comandos de calibración para los sensores
89 #define CALIBRATE_ACCEL 0
90 #define CALIBRATE_GYRO 1
91 #define CALIBRATE_MAG 2
92 #define CALIBRATE_PLANAR_ACCEL 3
93 #define CALIBRATE_ACCEL_GYRO_MAG 4
94 #define CALIBRATE_STOP 5
95 //Tamaño lı́mite de paquete de datos y metadatos
96 #define MAX_PACKET_SIZE 128
97 #define MAX_METADATA_SIZE 9
98
considerar magnetómetro
115 void enableARVRStabilizedRotationVector(u16
timeBetweenReports); //Activa reportes del
vector de rotación con estabilización AR/VR
116 void enableARVRStabilizedGameRotationVector(u16
timeBetweenReports); //Activa reportes del vector
de rotación con estabilización AR/VR sin considerar
magnetómetro
117 void enableAccelerometer(u16 timeBetweenReports);
//Activa lecturas del acelerómetro
118 void enableLinearAccelerometer(u16 timeBetweenReports
); //Activa lecturas del acelerómetro en
componentes lineares
119 void enableGyro(u16 timeBetweenReports); //
Activa reportes del giroscopio
120 void enableMagnetometer(u16 timeBetweenReports);
//Activa reportes del magnetómetro
121 void enableStepCounter(u16 timeBetweenReports); //
Activa función de cuenta de pasos
122 void enableStabilityClassifier(u16 timeBetweenReports
); //Activa el clasificador de estabilidad
123 void enableActivityClassifier(u16 timeBetweenReports,
u32 activitiesToEnable, u8 (&activityConfidences)
[9]); //Activa el clasificador de actividad para
usar en conjunto con el contador de pasos
124 void enableRawAccelerometer(u16 timeBetweenReports);
//Activa la lectura del acelerometro con datos sin
procesar
125 void enableRawGyro(u16 timeBetweenReports); //Activa
la lectura del giroscopio con datos sin procesar
126 void enableRawMagnetometer(u16 timeBetweenReports);
//Activa la lectura del magnetometro con datos sin
procesar
127 void enableGyroIntegratedRotationVector(u16
timeBetweenReports); //Activa el vector de rotacion
de baja latencia mediante predicción
128
133 /*
143 /*
144 * Funciones para obtener las aceleraciones angulares
145 */
146 float getAccelX();
147 float getAccelY();
148 float getAccelZ();
149 u8 getAccelAccuracy();
150
151 /*
152 * Funciones para obtener las aceleraciones lineales
153 */
154 float getLinAccelX();
155 float getLinAccelY();
156 float getLinAccelZ();
157 u8 getLinAccelAccuracy();
158
159 /*
160 * Funciones para obtener las componentes del
giroscopio
161 */
162 float getGyroX();
163 float getGyroY();
164 float getGyroZ();
165 u8 getGyroAccuracy();
166
167 /*
168 * Funciones para obtener las componentes con alta
tasa de refresco para el giroscopio
169 */
170 float getFastGyroX();
171 float getFastGyroY();
172 float getFastGyroZ();
173
174 /*
175 * Funciones para obtener las componentes del
magnetómetro
176 */
177 float getMagX();
178 float getMagY();
179 float getMagZ();
180 u8 getMagAccuracy();
181
182 /*
183 * Funciones de calibración
184 */
185 void calibrateAccelerometer();
186 void calibrateGyro();
187 void calibrateMagnetometer();
188 void calibratePlanarAccelerometer();
189 void calibrateAll();
190 void endCalibration();
191 void saveCalibration();
192 void requestCalibrationStatus();
193 bool calibrationComplete();
194
195 /*
196 * Funciones para obtener valores almacenados
197 */
198 u32 getTimeStamp();
199 u16 getStepCount();
200 u8 getStabilityClassifier();
201 u8 getActivityClassifier();
202 s16 getRawAccelX();
203 s16 getRawAccelY();
204 s16 getRawAccelZ();
205 s16 getRawGyroX();
206 s16 getRawGyroY();
207 s16 getRawGyroZ();
208 s16 getRawMagX();
209 s16 getRawMagY();
210 s16 getRawMagZ();
211
212 /*
213 * Convierte los valores del vector de rotacion en
componentes Roll, Pitch y Yaw
214 */
215 float getRoll();
216 float getPitch();
217 float getYaw();
218
219 /*
220 * Funciones de utilidad para enviar comandos
221 */
222 void setFeatureCommand(u8 reportID, u16
timeBetweenReports);
223 void setFeatureCommand(u8 reportID, u16
timeBetweenReports, u32 specificConfig);
224 void sendCommand(u8 command);
225 void sendCalibrateCommand(u8 thingToCalibrate);
226
244 private:
245 //Variables
246 u8 _deviceAddress; //Almacena la direccion I2C
247 bool _printDebug = false; //Reportes para debug
desactivados por defecto
248
249
270 /*
271 * Valores Q por defecto (ver datasheet)
272 */
273 s16 rotationVector_Q1 = 14;
274 s16 rotationVectorAccuracy_Q1 = 12; //Heading
accuracy estimate in radians. The Q point is 12.
275 s16 accelerometer_Q1 = 8;
276 s16 linear_accelerometer_Q1 = 8;
277 s16 gyro_Q1 = 9;
278 s16 magnetometer_Q1 = 4;
279 s16 angular_velocity_Q1 = 10;
280 };
281
282
283
24 /**************************************
25 * CONFIGURACIONES E INICIALIZACIÓN
26 **************************************/
27 /*
28 * Inicia el módulo
29 */
30 bool BNO080::begin(u8 deviceAddress)
31 {
32 _deviceAddress = deviceAddress; //Se almacena la
dirección I2C entregada
33 //printf("dev_address success"); //Descomentar para
debug
34
38 /*
39 * Verifica la comunicación con el módulo
40 */
41 shtpData[0] = SHTP_REPORT_PRODUCT_ID_REQUEST; //
Solicita el ID del módulo y su informacion de
reinicio
42 shtpData[1] = 0;
//Reservado
43
73 /*
74 * Activa el modo debug
75 */
76 void BNO080::enableDebugging()
77 {
78 _printDebug = true;
79 }
80
81 /*
82 * Verifica si hay datos disponibles
83 */
84 bool BNO080::dataAvailable(void)
85 {
86
87 if (receivePacket() == true)
88 {
89 //Verifica si el paquete es un sensor
reportando datos
90 if (shtpHeader[2] == CHANNEL_REPORTS &&
shtpData[0] == SHTP_REPORT_BASE_TIMESTAMP)
91 {
92 parseInputReport(); //Dependiendo de
la variable, actualiza su
información
93 return (true);
94 }
95 //Si no son datos y es información de control
96 else if (shtpHeader[2] == CHANNEL_CONTROL)
97 {
98 parseCommandReport(); //Actualiza
respuesta a los comandos
99 return (true);
100 }
112 /*
113 * Obtiene la información del report de respuesta a comandos
114 * El packete contiene:
115 * shtpHeader[0:3] --> header de 4 byte
116 * shtpData[0] --> ID de reporte
117 * shtpData[1] --> Numero de secuencia
118 * shtpData[2] --> Comando
119 * shtpData[3] --> Numero de secuencia de comando
120 * shtpData[4] --> Numero de secuencia de respuesta
121 * shtpData[5 + 0] --> R0
122 * shtpData[5 + 1] --> R1
123 * shtpData[5 + 2] --> R2
124 * shtpData[5 + 3] --> R3
125 * shtpData[5 + 4] --> R4
126 * shtpData[5 + 5] --> R5
127 * shtpData[5 + 6] --> R6
128 * shtpData[5 + 7] --> R7
129 * shtpData[5 + 8] --> R8
130 */
131 void BNO080::parseCommandReport(void)
132 {
133 if (shtpData[0] == SHTP_REPORT_COMMAND_RESPONSE)
134 {
135 //El módulo responde con esta funcion a
solicitudes de comando
136 u8 command = shtpData[2]; //Es el byte de
comando para la respuesta
137
, 0=correcto, distinto de 0 =
erroneo
141 }
142 }
143 else
144 {
145 //El reporte no es soportado
146 }
147
148 }
149
150 /*
151 * Obtiene los datos del reporte de entrada, como este
reporte varia en
152 * longitud, esta duncion almacena los valores de 16 bit como
globales
153 * El paquete contiene:
154 * shtpHeader[0:3] --> Header de 4 byte
155 * shtpData[0:4] --> Marca de tiempo de 5 byte en
microsegundos desde la medición
156 * shtpData[5 + 0] --> ID de reporte (0x01 acelerometro, 0x05
vector de rotaación)
157 * shtpData[5 + 1] --> Numero de secuencia
158 * shtpData[5 + 2] --> Estado
159 * shtpData[5 + 3] --> Retraso
160 * shtpData[5 + 4:5] --> Componente i/acelerometro, x/
giroscopio, etc
161 * shtpData[5 + 6:7] --> Componente j/acelerometro, y/
giroscopio, etc
162 * shtpData[5 + 8:9] --> Componente k/acelerometro, z/
giroscopio, etc
163 * shtpData[5 + 10:11] --> Componente real/giroscopio, etc
164 * shtpData[5 + 12:13] --> Estimación de precisión
165 */
166 void BNO080::parseInputReport(void)
167 {
168 //Calcula la cantidad de bytes de dato en el paquete
169 s16 dataLength = ((u16)shtpHeader[1] << 8 |
shtpHeader[0]);
170 dataLength &= ˜(1 << 15); //Limpia el bit más
significativo. Indica que el paquete actual es
continuación del previo
171
172
174
187 return;
188 }
189
312 /************************************************
313 * CONVERSIÓN DE CUATERNIONES A ANGULOS DE EULER
314 ************************************************/
315
316 /*
317 * Entrega el Roll en radianes (rotación en torno al eje x)
318 */
319 float BNO080::getRoll()
320 {
321 float dqw = getQuatReal();
322 float dqx = getQuatI();
323 float dqy = getQuatJ();
324 float dqz = getQuatK();
325
334
342 /*
343 * Entrega el Pitch en radianes (rotación en torno al eje y)
344 */
345 float BNO080::getPitch()
346 {
347 float dqw = getQuatReal();
348 float dqx = getQuatI();
349 float dqy = getQuatJ();
350 float dqz = getQuatK();
351
360
369 /*
370 * Entrega el Yaw en radianes (rotación en torno al eje z)
371 */
372 float BNO080::getYaw()
373 {
374 float dqw = getQuatReal();
375 float dqx = getQuatI();
376 float dqy = getQuatJ();
377 float dqz = getQuatK();
378
395 /************************************************
396 * FUNCIONES DE OBTENCIÓN DE VECTOR DE ROTACIÓN
397 *************************************************/
398
399 /*
400 * Entrega el cuaternion i del vector de rotación
401 */
402 float BNO080::getQuatI()
403 {
404 float quat = qToFloat(rawQuatI, rotationVector_Q1);
405 return (quat);
406 }
407
408 /*
417 /*
418 * Entrega el cuaternion k del vector de rotación
419 */
420 float BNO080::getQuatK()
421 {
422 float quat = qToFloat(rawQuatK, rotationVector_Q1);
423 return (quat);
424 }
425
426 /*
427 * Entrega el cuaternion real del vector de rotación
428 */
429 float BNO080::getQuatReal()
430 {
431 float quat = qToFloat(rawQuatReal, rotationVector_Q1)
;
432 return (quat);
433 }
434
435 /*
436 * Entrega la precisión del vector de rotación en radianes
437 */
438 float BNO080::getQuatRadianAccuracy()
439 {
440 float quat = qToFloat(rawQuatRadianAccuracy,
rotationVectorAccuracy_Q1);
441 return (quat);
442 }
443
444 /*
445 * Entrega la precision del vector de rotación
446 */
447 u8 BNO080::getQuatAccuracy()
448 {
449 return (quatAccuracy);
450 }
451
452 /************************************
456 /*
457 * Entregan componentes x, y, z de la aceleración
458 */
459 float BNO080::getAccelX()
460 {
461 float accel = qToFloat(rawAccelX, accelerometer_Q1);
462 return (accel);
463 }
464 float BNO080::getAccelY()
465 {
466 float accel = qToFloat(rawAccelY, accelerometer_Q1);
467 return (accel);
468 }
469 float BNO080::getAccelZ()
470 {
471 float accel = qToFloat(rawAccelZ, accelerometer_Q1);
472 return (accel);
473 }
474
475 /*
476 * Entrega la precisión de la aceleración
477 */
478 u8 BNO080::getAccelAccuracy()
479 {
480 return (accelAccuracy);
481 }
482
483 /*
484 * Entregan componentes x, y, z de la aceleración lineal. Por
ejemplo, compensando la gravedad
485 */
486 float BNO080::getLinAccelX()
487 {
488 float accel = qToFloat(rawLinAccelX,
linear_accelerometer_Q1);
489 return (accel);
490 }
491 float BNO080::getLinAccelY()
492 {
493 float accel = qToFloat(rawLinAccelY,
linear_accelerometer_Q1);
494 return (accel);
495 }
502 /*
503 * Entrega la precisión de la aceleración lineal
504 */
505 u8 BNO080::getLinAccelAccuracy()
506 {
507 return (accelLinAccuracy);
508 }
509
510 /***************************************
511 * FUNCIONES DE GIROSCOPIO
512 ***************************************/
513
514 /*
515 * Entregan componentes x, y, z del giroscopio
516 */
517 float BNO080::getGyroX()
518 {
519 float gyro = qToFloat(rawGyroX, gyro_Q1);
520 return (gyro);
521 }
522 float BNO080::getGyroY()
523 {
524 float gyro = qToFloat(rawGyroY, gyro_Q1);
525 return (gyro);
526 }
527 float BNO080::getGyroZ()
528 {
529 float gyro = qToFloat(rawGyroZ, gyro_Q1);
530 return (gyro);
531 }
532
533 /*
534 * Entrega la precisión de las lecturas del giroscopio
535 */
536 u8 BNO080::getGyroAccuracy()
537 {
538 return (gyroAccuracy);
539 }
540
541 /*************************************
542 * FUNCIONES DE MAGNETÓMETRO
543 *************************************/
544
545 /*
546 * Entregan componentes x, y, z del magnetómetro
547 */
548 float BNO080::getMagX()
549 {
550 float mag = qToFloat(rawMagX, magnetometer_Q1);
551 return (mag);
552 }
553 float BNO080::getMagY()
554 {
555 float mag = qToFloat(rawMagY, magnetometer_Q1);
556 return (mag);
557 }
558 float BNO080::getMagZ()
559 {
560 float mag = qToFloat(rawMagZ, magnetometer_Q1);
561 return (mag);
562 }
563
564 /*
565 * Entrega la precisión de las lecturas del magnetómetro
566 */
567 u8 BNO080::getMagAccuracy()
568 {
569 return (magAccuracy);
570 }
571
572 /*********************************************************
573 * FUNCIONES DE GIROSCOPIO
574 * CON ALTA TASA DE ACTUALIZACIÓN
575 *********************************************************/
576 /*
577 * Entregan componentes x, y, z del giroscopio con alta tasa
de actualización
578 */
579 float BNO080::getFastGyroX()
580 {
581 float gyro = qToFloat(rawFastGyroX,
angular_velocity_Q1);
582 return (gyro);
583 }
584 float BNO080::getFastGyroY()
585 {
586 float gyro = qToFloat(rawFastGyroY,
angular_velocity_Q1);
587 return (gyro);
588 }
589 float BNO080::getFastGyroZ()
590 {
591 float gyro = qToFloat(rawFastGyroZ,
angular_velocity_Q1);
592 return (gyro);
593 }
594
595 /*******************************
596 * FUNCIONES DE CUENTAPASOS
597 *******************************/
598
599 /*
600 * Entrega la cuenta de pasos
601 */
602 u16 BNO080::getStepCount()
603 {
604 return (stepCount);
605 }
606
607 /*
608 * Entrega el clasificador de estabilidad
609 */
610 u8 BNO080::getStabilityClassifier()
611 {
612 return (stabilityClassifier);
613 }
614
615 /*
616 * Entrega el clasificador de actividad
617 */
618 u8 BNO080::getActivityClassifier()
619 {
620 return (activityClassifier);
621 }
622
623 /*
624 * Entrega la marca de tiempo
625 */
626 u32 BNO080::getTimeStamp()
627 {
628 return (timeStamp);
629 }
630
631 /*****************************************************
632 * FUNCIONES PARA DATOS SIN PROCESAR DE SENSORES MEMS
633 *****************************************************/
634
635 /*
636 * Entregan componentes x, y, z sin procesar para el
acelerometro
637 */
638 s16 BNO080::getRawAccelX()
639 {
640 return (memsRawAccelX);
641 }
642 s16 BNO080::getRawAccelY()
643 {
644 return (memsRawAccelY);
645 }
646 s16 BNO080::getRawAccelZ()
647 {
648 return (memsRawAccelZ);
649 }
650
651 /*
652 * Entregan componentes x, y, z sin porcesar para el
giroscopio
653 */
654 s16 BNO080::getRawGyroX()
655 {
656 return (memsRawGyroX);
657 }
658 s16 BNO080::getRawGyroY()
659 {
660 return (memsRawGyroY);
661 }
662 s16 BNO080::getRawGyroZ()
663 {
664 return (memsRawGyroZ);
665 }
666
667 /*
668 * Entregan componentes x, y, z sin procesar para el
magnetómetro
669 */
670 s16 BNO080::getRawMagX()
671 {
683 /*************************************
684 * FUNCIONES PARA METADATOS
685 *************************************/
686
687 /*
688 * Dado un ID de registro, lee el valor Q1 de los registros
de metadatos en el FRS
689 * Q1 es usado para todos los calculos de datos de sensores
690 */
691 s16 BNO080::getQ1(u16 recordID)
692 {
693 //Q1 siempre son los 16 bits menos significativos de
la palabra 7
694 u16 q = readFRSword(recordID, 7) & 0xFFFF;
695 return (q);
696 }
697
698 /*
699 * Dado un ID de registro, lee el valor Q2 de los registros
de metadatos en el FRS
700 * Q2 es usado para el bias de sensor
701 */
702 s16 BNO080::getQ2(u16 recordID)
703 {
704 //Q2 siempre son los 16 bits más significativos de la
palabra 7
705 u16 q = readFRSword(recordID, 7) >> 16;
706 return (q);
707 }
708
709 /*
710 * Dado un ID de registro, lee el valor Q3 de los registros
de metadatos en el FRS
711 * Q3 es usado para cambiar la sensibilidad de los sensores
712 */
720 /*
721 * Dado un ID de registro, lee el valor de resolución del
registro de metadatos en el FRS para un sensor dado
722 */
723 float BNO080::getResolution(u16 recordID)
724 {
725 /*
726 * Es el mismo valor usado en el reporte de entrada
del sensor
727 * Deberia ser Q1
728 */
729 s16 Q = getQ1(recordID);
730
739 /*
740 * Dado un ID de registro, lee el valor de rango del registro
de metadatos en el FRS para un sensor dado
741 */
742 float BNO080::getRange(u16 recordID)
743 {
744 /*
745 * Es el mismo valor usado en el reporte de entrada
del sensor
746 * Deberia ser Q1
747 */
748 s16 Q = getQ1(recordID);
749
754
758 /*
759 * Dado un ID de registro y un número de palabra, busca la
información de la palabra
760 * Util para obtener un valor Q, rango, etc
761 * Utiliza datos de FRS para obtener objetos multi-palabra de
un sensor
762 */
763 u32 BNO080::readFRSword(u16 recordID, u8 wordNumber)
764 {
765 if (readFRSdata(recordID, wordNumber, 1) == true) //
Obtiene el número de palabras, solo una palabra en
longitud de FRS
766 return (metaData[0]);
//Retorna esta
palabra
767
771 /*
772 * Pide al sensor información del FRS (Flash Record System)
773 */
774 void BNO080::frsReadRequest(u16 recordID, u16 readOffset, u16
blockSize)
775 {
776 shtpData[0] = SHTP_REPORT_FRS_READ_REQUEST; //
Solicitud de lectura FRS
777 shtpData[1] = 0;
//Reservado
778 shtpData[2] = (readOffset >> 0) & 0xFF; //Lee
el bit menos significativo del offset
779 shtpData[3] = (readOffset >> 8) & 0xFF; //Lee
el bit más significativo del offset
780 shtpData[4] = (recordID >> 0) & 0xFF; //Bit
menos significativo del FRS
781 shtpData[5] = (recordID >> 8) & 0xFF; //Bit
más significativo del FRS
782 shtpData[6] = (blockSize >> 0) & 0xFF; //Bit
menos significativo del tamaño de bloque
783 shtpData[7] = (blockSize >> 8) & 0xFF; //Bit
más significativo del tamaño de bloque
784
789 /*
790 * Dado un ID de registro y los bytes de Inicio/Parada, lee
los datos del FRS para el sensor
791 * Retorna verdadero si el arreglo de metadatos carga
exitosamente
792 * Retorna falso si falla
793 */
794 bool BNO080::readFRSdata(u16 recordID, u8 startLocation, u8
wordsToRead)
795 {
796 u8 spot = 0;
797
815 /*
816 * Teniendo el paquete lo inspecciona
buscando contenidos adecuados
817 */
818 if (shtpData[0] ==
SHTP_REPORT_FRS_READ_RESPONSE)
819 if (((u16)shtpData[13] << 8 |
shtpData[12]) == recordID)
820 break; //Es el
paquete buscado
821 }
822
853 /*************************************************
854 * FUNCIONES PARA REINICIO Y CONVERSION DE DATOS
855 *************************************************/
856
857 /*
858 * Envia el comando para reiniciar el módulo
872 usleep(100000);
873 while (receivePacket() == true)
874 //printPacket();
875 ;
876 //printf("receive 1 done\r\n"); //Descomentar para
debug
877 usleep(100000);
878 while (receivePacket() == true)
879 ;
880 //printf("receive 2 done\r\n"); //Descomentar para
debug
881 }
882
883 /*
884 * Obtiene la razón del último reinicio
885 * 1 = PoR (Power on Reset)
886 * 2 = Reinicio interno
887 * 3 = Watchdog (reinicio por error)
888 * 4 = Reinicio externo
889 * 5 = Otro
890 */
891 u8 BNO080::resetReason()
892 {
893 shtpData[0] = SHTP_REPORT_PRODUCT_ID_REQUEST; //
Solicita ID del producto e informacion de reinicio
894 shtpData[1] = 0;
//Reservado
895
898
911 /*
912 * Dado un registro y un valor punto Q, se convierte a punto
float
913 */
914 float BNO080::qToFloat(s16 fixedPointValue, u8 qPoint)
915 {
916 float qFloat = fixedPointValue;
917 qFloat *= pow(2, qPoint * -1);
918 return (qFloat);
919 }
920
921 /***************************************
922 * FUNCIONES DE ACTIVACIÓN
923 ***************************************/
924
925 /*
926 * Envı́a el paquete para activar el vector de rotación
927 */
928 void BNO080::enableRotationVector(u16 timeBetweenReports)
929 {
930 setFeatureCommand(SENSOR_REPORTID_ROTATION_VECTOR,
timeBetweenReports);
931 }
932
933 /*
934 * Envı́a el paquete para activar el vector de rotación con
estabilización AR/VR
935 */
936 void BNO080::enableARVRStabilizedRotationVector(u16
timeBetweenReports)
937 {
938 setFeatureCommand(
SENSOR_REPORTID_AR_VR_STABILIZED_ROTATION_VECTOR,
timeBetweenReports);
939 }
940
941 /*
942 * Envı́a el paquete para activar el vector de rotación sin
magnetómetro
943 */
944 void BNO080::enableGameRotationVector(u16 timeBetweenReports)
945 {
946 setFeatureCommand(
SENSOR_REPORTID_GAME_ROTATION_VECTOR,
timeBetweenReports);
947 }
948
949 /*
950 * Envı́a el paquete para activar el vector de rotación sin
magnetómetro con estabilización AR/VR
951 */
952 void BNO080::enableARVRStabilizedGameRotationVector(u16
timeBetweenReports)
953 {
954 setFeatureCommand(SENSOR_REPORTID_AR_VR_
955 STABILIZED_GAME_ROTATION_VECTOR, timeBetweenReports);
956 }
957
958 /*
959 * Envı́a el paquete para activar el acelerómetro
960 */
961 void BNO080::enableAccelerometer(u16 timeBetweenReports)
962 {
963 setFeatureCommand(SENSOR_REPORTID_ACCELEROMETER,
timeBetweenReports);
964 }
965
966 /*
967 * Envı́a el paquete para activar el acelerómetro lineal
968 */
969 void BNO080::enableLinearAccelerometer(u16 timeBetweenReports
)
970 {
971 setFeatureCommand(SENSOR_REPORTID_LINEAR_ACCELERATION
, timeBetweenReports);
972 }
973
974 /*
975 * Envı́a el paquete para activar el giroscopio
976 */
977 void BNO080::enableGyro(u16 timeBetweenReports)
978 {
979 setFeatureCommand(SENSOR_REPORTID_GYROSCOPE,
timeBetweenReports);
980 }
981
982 /*
983 * Envı́a el paquete para activar el magnetómetro
984 */
985 void BNO080::enableMagnetometer(u16 timeBetweenReports)
986 {
987 setFeatureCommand(SENSOR_REPORTID_MAGNETIC_FIELD,
timeBetweenReports);
988 }
989
990 /*
991 * Envı́a el paquete para activar el vector de rotación con
integración de girocopio de alta actualización
992 */
993 void BNO080::enableGyroIntegratedRotationVector(u16
timeBetweenReports)
994 {
995 setFeatureCommand(
SENSOR_REPORTID_GYRO_INTEGRATED_ROTATION_VECTOR,
timeBetweenReports);
996 }
997
998 /*
999 * Envı́a el paquete para activar el contador de pasos
1000 */
1001 void BNO080::enableStepCounter(u16 timeBetweenReports)
1002 {
1003 setFeatureCommand(SENSOR_REPORTID_STEP_COUNTER,
timeBetweenReports);
1004 }
1005
1006 /*
1007 * Envı́a el paquete para activar el clasificador de
estabilidad
1008 */
1009 void BNO080::enableStabilityClassifier(u16 timeBetweenReports
)
1010 {
1011 setFeatureCommand(
SENSOR_REPORTID_STABILITY_CLASSIFIER,
timeBetweenReports);
1012 }
1013
1014 /*
1015 * Envı́a el paquete para activar las lecturas del
acelerómetro sin procesar
1016 * Es estrictamente necesario además activar los reportes
básicos
1017 */
1018 void BNO080::enableRawAccelerometer(u16 timeBetweenReports)
1019 {
1020 setFeatureCommand(SENSOR_REPORTID_RAW_ACCELEROMETER,
timeBetweenReports);
1021 }
1022
1023 /*
1024 * Envı́a el paquete para activar las lecturas del giroscopio
sin procesar
1025 * Es estrictamente necesario además activar los reportes
básicos
1026 */
1027 void BNO080::enableRawGyro(u16 timeBetweenReports)
1028 {
1029 setFeatureCommand(SENSOR_REPORTID_RAW_GYROSCOPE,
timeBetweenReports);
1030 }
1031
1032 /*
1033 * Envı́a el paquete para activar las lecturas del
magnetómetro sin procesar
1034 * Es estrictamente necesario además activar los reportes
básicos
1035 */
1036 void BNO080::enableRawMagnetometer(u16 timeBetweenReports)
1037 {
1038 setFeatureCommand(SENSOR_REPORTID_RAW_MAGNETOMETER,
timeBetweenReports);
1039 }
1040
1041 /*
1042 * Envı́a el paquete para activar el clasificador de actividad
1043 */
1044 void BNO080::enableActivityClassifier(u16 timeBetweenReports,
u32 activitiesToEnable, u8 (&activityConfidences)[9])
1045 {
1046 _activityConfidences = activityConfidences; //
Almacena el puntero al arreglo
1047
1048 setFeatureCommand(
SENSOR_REPORTID_PERSONAL_ACTIVITY_CLASSIFIER,
timeBetweenReports, activitiesToEnable);
1049 }
1050
1051 /*************************************
1052 * FUNCIONES DE CALIBRACIÓN
1053 *************************************/
1054
1055 /*
1056 * Envı́a los comandos para iniciar la calibración del
acelerómetro
1057 */
1058 void BNO080::calibrateAccelerometer()
1059 {
1060 sendCalibrateCommand(CALIBRATE_ACCEL);
1061 }
1062
1063 /*
1064 * Envı́a los comandos para iniciar la calibración del
giroscopio
1065 */
1066 void BNO080::calibrateGyro()
1067 {
1068 sendCalibrateCommand(CALIBRATE_GYRO);
1069 }
1070
1071 /*
1072 * Envı́a los comandos para iniciar la calibración del
magnetómetro
1073 */
1074 void BNO080::calibrateMagnetometer()
1075 {
1076 sendCalibrateCommand(CALIBRATE_MAG);
1077 }
1078
1079 /*
1080 * Envı́a los comandos para iniciar la calibración del
acelerómetro lineal/planar
1081 */
1082 void BNO080::calibratePlanarAccelerometer()
1083 {
1084 sendCalibrateCommand(CALIBRATE_PLANAR_ACCEL);
1085 }
1086
1087 /*
1088 * Envı́a los comandos para iniciar la calibración de todos
los sensores
1089 * Ver procedimiento de calibracion en documento 1000-4044
punto 2.2
1090 */
1091 void BNO080::calibrateAll()
1092 {
1093 sendCalibrateCommand(CALIBRATE_ACCEL_GYRO_MAG);
1094 }
1095
1096 /*
1097 * Termina la calibración
1098 */
1099 void BNO080::endCalibration()
1100 {
1101 sendCalibrateCommand(CALIBRATE_STOP); //Desactiva
todas las calibraciones
1102 }
1103
1104 /*
1105 * Respuesta de calibración, ver página 51 del manual de
referencias
1106 * El byte 5 es analizado en la lectura de paquete y
almacenada en el estatus de calibración
1107 */
1108 bool BNO080::calibrationComplete()
1109 {
1110 if (calibrationStatus == 0)
1111 return (true);
1112 return (false);
1113 }
1114
1115 /***************************************
1116 * FUNCIONES DE COMUNICACIÓN
1117 ***************************************/
1118
1119 /*
1120 * Dado un ID de reporte, ordena al módulo comenzar a
reportar valores
1121 */
1122 void BNO080::setFeatureCommand(u8 reportID, u16
timeBetweenReports)
1123 {
1124 setFeatureCommand(reportID, timeBetweenReports, 0);
//Sin configuración especı́fica
1125 }
1126
1127 /*
1128 * Dado un ID de reporte de un sensor, le ordena al módulo
comenzar a reportar valores
1129 * Ademas define la palabra especı́fica de configuración. Util
para el clasificador de actividades
1130 */
1131 void BNO080::setFeatureCommand(u8 reportID, u16
timeBetweenReports, u32 specificConfig)
1132 {
1133 long microsBetweenReports = (long)timeBetweenReports
* 1000L;
1134
lotes
1146 shtpData[11] = 0;
//Intervalo de
lotes
1147 shtpData[12] = 0;
//Intervalo de
lotes (MSB)
1148 shtpData[13] = (specificConfig >> 0) & 0xFF; //
Configuración especifica del sensor (LSB)
1149 shtpData[14] = (specificConfig >> 8) & 0xFF; //
Configuración especifica del sensor
1150 shtpData[15] = (specificConfig >> 16) & 0xFF; //
Configuración especifica del sensor
1151 shtpData[16] = (specificConfig >> 24) & 0xFF; //
Configuración especifica del sensor (MSB)
1152
1157 /*
1158 * Le ordena al sensor ejecutar un comando
1159 */
1160 void BNO080::sendCommand(u8 command)
1161 {
1162 shtpData[0] = SHTP_REPORT_COMMAND_REQUEST; //
Solicitud de comando
1163 shtpData[1] = commandSequenceNumber++; //Incrementa
automáticamente en cada ejecución
1164 shtpData[2] = command;
//Comando
1165
1180
1181 /*
1182 * Le indica al módulo iniciar la calibración
1183 * Ver documento de calibración 1000-4044
1184 */
1185 void BNO080::sendCalibrateCommand(u8 thingToCalibrate)
1186 {
1187 /*shtpData[3] = 0; //P0 - Activación de calibración
de acelerómetro
1188 shtpData[4] = 0; //P1 - Activación de calibración de
giroscopio
1189 shtpData[5] = 0; //P2 - Activación de calibracion de
magentómetro
1190 shtpData[6] = 0; //P3 - Subcomando 0x00
1191 shtpData[7] = 0; //P4 - Activación de calibración de
acelerómetro planar
1192 shtpData[8] = 0; //P5 - Reservado
1193 shtpData[9] = 0; //P6 - Reservado
1194 shtpData[10] = 0; //P7 - Reservado
1195 shtpData[11] = 0; //P8 - Reservado*/
1196
1197 for (u8 x = 3; x < 12; x++) //Limpia esta seccion del
arreglo shtpData
1198 shtpData[x] = 0;
1199
mientras se espera
1218 calibrationStatus = 1;
1219
1224 /*
1225 * Solicita el estado de calibración desde el módulo
1226 */
1227 void BNO080::requestCalibrationStatus()
1228 {
1229 /*shtpData[3] = 0; //P0 - Reservado
1230 shtpData[4] = 0; //P1 - Reservado
1231 shtpData[5] = 0; //P2 - Reservado
1232 shtpData[6] = 0; //P3 - 0x01 - Subcomando obtener
calibración
1233 shtpData[7] = 0; //P4 - Reservado
1234 shtpData[8] = 0; //P5 - Reservado
1235 shtpData[9] = 0; //P6 - Reservado
1236 shtpData[10] = 0; //P7 - Reservado
1237 shtpData[11] = 0; //P8 - Reservado*/
1238
1248 /*
1249 * Ordena al módulo guardar los datos dinámicos de
calibración en la memoria flash
1250 */
1251 void BNO080::saveCalibration()
1252 {
1253 /*shtpData[3] = 0; //P0 - Reservado
1254 shtpData[4] = 0; //P1 - Reservado
1255 shtpData[5] = 0; //P2 - Reservado
1256 shtpData[6] = 0; //P3 - Reservado
1257 shtpData[7] = 0; //P4 - Reservado
1258 shtpData[8] = 0; //P5 - Reservado
1263 for (u8 x = 3; x < 12; x++) //Limpia esta sección del
arreglo shtpData
1264 shtpData[x] = 0;
1265
1270 /*
1271 * Verifica si hay nuevos datos disponibles
1272 * Lee los contenidos del paquete entrante en el arreglo
shtpData
1273 */
1274 bool BNO080::receivePacket(void)
1275 {
1276
1301 if (dataLength == 0)
1302 {
1303 //El paquete esta vacı́o
1304 return (false); //Completado
1305 }
1306 dataLength -= 4; //Quita los bytes del header de la
cuenta
1307 //printf(" %d\r\n", dataLength); //Descomentar para
debug
1308 //printf("header got\r\n"); //Descomentar para debug
1309 getData(dataLength);
1310 //printf("data got\r\n");
1311 return (true); //Completado
1312 }
1313
1314 /*
1315 * Envı́a múltiples solicitudes al sensor hasta que todos los
bytes de datos son recibidos
1316 * El buffer shtpData tiene una capacidad máxima de
MAX_PACKET_SIZE. Los bytes extra seran perdidos.
1317 */
1318 bool BNO080::getData(u16 bytesRemaining)
1319 {
1320 u16 dataSpot = 0; //Iicia al comienzo del arreglo
shtpData
1321 int var;
1322 while(bytesRemaining > 0){
1323 u16 numberOfBytesToRead = bytesRemaining;
1324 if(numberOfBytesToRead>(I2C_BUFFER_LENGTH -
4)){
1325 numberOfBytesToRead = (
I2C_BUFFER_LENGTH - 4);
1326 }
1327 //printf("number2read %d\r\n",
numberOfBytesToRead); //Descomentar para
debug
1328 u8 readBuffer[numberOfBytesToRead + 4];
1329 var = XIic_Recv(IIC_BASE_ADDRESS, BNO_ADDRESS
,readBuffer, sizeof(readBuffer), XIIC_STOP)
;
1340 }
1341 //Configura una serie de lecturas de trozos de 32
bytes
1342
1346 /*
1347 * Dado el paquete de datos, envı́a el header y luego los
datos
1348 * Retorna falso si el sensor no retorna ACK
1349 */
1350 bool BNO080::sendPacket(u8 channelNumber, u8 dataLength)
1351 {
1352 u8 packetLength = dataLength + 4; //Añade 4 bytes
para el header
1353 u8 buffer2send[packetLength];
1354 u16 StatusReg;
1355 int checkpoint;
1356 //if(packetLength > I2C_BUFFER_LENGTH) return(false);
//Se estan tratando de enviar muchos datos,
dividir en paquetes más pequeños
1357
1358
1365
1371 }
1372
1385 /***************************************
1386 * FUNCIONES DE DEBUG
1387 ***************************************/
1388
1389 /*
1390 * Imprime los contenidos del paquete actual incluyendo
header y datos
1391 */
1392 void BNO080::printPacket(void)
1393 {
1394 if (_printDebug == true)
1395 {
1396 u16 packetLength = (u16)shtpHeader[1] << 8 |
shtpHeader[0];
1397
1407
1446 printf("\r\n");
1447 }
1448 }
1449
1450 /*
K. Código AXI-PWM.v
1 ‘timescale 1 ns / 1 ps
2
3 module PWM_60hz_v1_0_S00_AXI #
4 (
5 // Users to add parameters here
6 parameter integer PWM_COUNTER_MAX = 1024,
7 // User parameters ends
8 // Do not modify the parameters beyond this line
9
86 // AXI4LITE signals
87 reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
88 reg axi_awready;
89 reg axi_wready;
90 reg [1 : 0] axi_bresp;
91 reg axi_bvalid;
92 reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
93 reg axi_arready;
94 reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
95 reg [1 : 0] axi_rresp;
96 reg axi_rvalid;
97
210 end
211
280 begin
281 if ( S_AXI_ARESETN == 1’b0 )
282 begin
283 axi_bvalid <= 0;
284 axi_bresp <= 2’b0;
285 end
286 else
287 begin
288 if (axi_awready && S_AXI_AWVALID && ˜axi_bvalid &&
axi_wready && S_AXI_WVALID)
289 begin
290 // indicates a valid write response is available
291 axi_bvalid <= 1’b1;
292 axi_bresp <= 2’b0; // ’OKAY’ response
293 end // work error responses in
future
294 else
295 begin
296 if (S_AXI_BREADY && axi_bvalid)
297 //check if bready is asserted while bvalid is
high)
298 //(there is a possibility that bready is always
asserted high)
299 begin
300 axi_bvalid <= 1’b0;
301 end
302 end
303 end
304 end
305
365 end
366
421 endmodule
L. Código PWM.v
1 ‘timescale 1 ns / 1 ps
2
3 module PWM_60hz_v1_0 #
4 (
5 // Users to add parameters here
6 parameter integer PWM_COUNTER_MAX = 128,
7 // User parameters ends
8 // Do not modify the parameters beyond this line
9
10
24
85 endmodule
11 int main(){
12 XGpio_Config *buttonConfig;
13 XGpio button;
14 int Status;
15 int i;
16 int val;
17 int max=12;
18 int min = 6;
19 int cont = 0;
20
21 init_platform();
22 buttonConfig = XGpio_LookupConfig(
XPAR_AXI_GPIO_0_DEVICE_ID);
23
27
28 while(1){
29 val = XGpio_DiscreteRead(&button, 1);
30 printf(" %d \n\r",val);
31 if(val == 1){
32 if(cont = 0){
33 cont = 1;
34 }
35 else{
36 cont = 0;
37 }
38 }
39 printf(" %d \n \r",cont);
40 switch(cont){
41 case 0:
42 i = min;
43 break;
44 case 1:
45 i = max;
46 break;
47 }
48 Xil_Out32(MY_PWM, Full_Val*(i/100.0));
49 Xil_Out32((MY_PWM+4), Full_Val*(i/100.0));
50 Xil_Out32((MY_PWM+8), Full_Val*(i/100.0));
51 Xil_Out32((MY_PWM+12), Full_Val*(i/100.0));
52 usleep(200000);
53 }
54 }
Fig. N.1: Diagrama de relaciones completo en comunicación ROS para el presente proyecto
32 //PWM Setup
33 #define MY_PWM XPAR_PWM_60HZ_0_S00_AXI_BASEADDR
34 #define Full_Val 833334
35
36
37 BME280 Barometer;
38 BNO080 IMU;
39
43 #define BUFFER_SIZE 6
44
52 void PIDforH();
53 /************************** Variable Definitions
*****************************/
54
58 u8 ReadBuffer[BUFFER_SIZE];
59 u8 WriteBuffer[BUFFER_SIZE];
60
77 float TrueZ = 0;
78 float baseZ = 0;
79 float acumZ = 0;
80 float reqZ = 1.5;
81 float T = 0;
82 float P = 0;
83 float R = 0;
84 float Y = 0;
85
86 //PWM limits
87 float maxThrottle = 12*(Full_Val/100.0);
88 float minThrottle = 6*(Full_Val/100.0);
89
90 //Inicializacion pid
91 PIDController pid = { PID_KP, PID_KI, PID_KD,
92 PID_TAU,
93 PID_LIM_MIN,
PID_LIM_MAX,
94 PID_LIM_MIN_INT, PID_LIM_MAX_INT,
95 SAMPLE_TIME_S };
96
97
98
99
112 u8 stat;
113 u8 AuxBuff[sizeof(float)];
114 u8 ReadAuxB[sizeof(float)];
115
131
132
133 PIDController_Init(&pid);
134
174 }
175 printf("begin done\r\n");
176 //Activación lecturas IMU
177 IMU.enableRotationVector(50);
178 printf("Rot. vector enabled\r\n");
179 IMU.enableLinearAccelerometer(50);
180 printf("Lin. Accel enabled\r\n");
181 IMU.enableGyro(50);
182 printf("Ang. Vel. enabled\r\n");
183
252 break;
253 case 0x04:
254 *(float *)(AuxBuff) =
RetQuatZ;
255 //printf("enviando QuatZ: %lf
\r\n", RetQuatZ);
256 break;
257 case 0x05:
258 *(float *)(AuxBuff) =
RetQuatR;
259 //printf("enviando QuatR: %lf
\r\n", RetQuatR);
260 break;
261 case 0x06:
262 *(float *)(AuxBuff) = RetVelX
;
263 //printf("enviando VelX: %lf\
r\n", RetVelX);
264 break;
265 case 0x07:
266 *(float *)(AuxBuff) = RetVelY
;
267 //printf("enviando VelY: %lf\
r\n", RetVelY);
268 break;
269 case 0x08:
270 *(float *)(AuxBuff) = RetVelZ
;
271 //printf("enviando VelZ: %lf\
r\n", RetVelZ);
272 break;
273 case 0x09:
274 *(float *)(AuxBuff) = RetAccX
;
275 //printf("enviando AccX: %lf\
r\n", RetAccX);
276 break;
277 case 0x0A:
278 *(float *)(AuxBuff) = RetAccY
;
279 //printf("enviando AccY: %lf\
r\n", RetAccY);
280 break;
281 case 0x0B:
282 *(float *)(AuxBuff) = RetAccZ
;
de las
alturas
obtenidas
318 baseZ = acumZ
/((
initcount -
1) * 1.0);
319 printf("base
= %lf\n\r",
baseZ);
320 giroinit =
true;
321 setpnt = true
;
322 }
323
324 }
325 //printf("Altura real es: %lf
", TrueZ);
326 //return 0;
327 break;
328
329 default:
330 AuxBuff[0] = 0x00;
331 AuxBuff[1] = 0x00;
332 AuxBuff[2] = 0x00;
333 AuxBuff[3] = 0x00;
334 RetAlt = Alt;
335 RetQuatX = QuatX;
336 RetQuatY = QuatY;
337 RetQuatZ = QuatZ;
338 RetQuatR = QuatR;
339 RetVelX = VelX;
340 RetVelY = VelY;
341 RetVelZ = VelZ;
342 RetAccX = AccX;
343 RetAccY = AccY;
344 RetAccZ = AccZ;
345 //xil_printf("esperando....\
r\n");
346 break;
347 }
348 for(u8 k = 0; k<sizeof(float); k++){
349 WriteBuffer[k+1] =
AuxBuff[k];
350 }
351
355 //MMA
356 TH1 = (T + P + R + Y);
357 if (TH1 < minThrottle+2500){
358 TH1 = minThrottle+2500;
359 }
360 if (TH1 > maxThrottle){
361 TH1 = maxThrottle;
362 }
363 TH2 = (T + P - R - Y);
364 if (TH2 < minThrottle+2500){
365 TH2 = minThrottle+2500;
366 }
367 if (TH2 > maxThrottle){
368 TH2 = maxThrottle;
369 }
370 TH3 = (T - P - R + Y);
371 if (TH3 < minThrottle+2500){
372 TH3 = minThrottle+2500;
373 }
374 if (TH3 > maxThrottle){
375 TH3 = maxThrottle;
376 }
377 TH4 = (T - P + R - Y);
378 if (TH4 < minThrottle+2500){
379 TH4 = minThrottle+2500;
380 }
381 if (TH4 > maxThrottle){
382 TH4 = maxThrottle;
383 }
384 //MOTOR OUTPUTS
385 if (giroinit == false){
386 Xil_Out32(MY_PWM, minThrottle
+2500);
387 Xil_Out32((MY_PWM+4),
minThrottle+2500);
388 Xil_Out32((MY_PWM+8),
minThrottle+2500);
389 Xil_Out32((MY_PWM+12),
minThrottle+2500);
390 }
391 else{
392 Xil_Out32(MY_PWM, TH1);
398
399 //usleep(100);
400
421
422 /*
423 * Initialize the interrupt controller driver so that
it’s ready to use,
424 * specify the device ID that is generated in "
xparameters.h".
425 */
426 Status = XScuGic_CfgInitialize(&IntcInstance,
Gic_Config, Gic_Config->CpuBaseAddress);
427 if (Status != XST_SUCCESS) {
428 return XST_FAILURE;
429 }
430
431 /*
432 * Initialize the exception table.
433 */
434 Xil_ExceptionInit();
435
436 /*
437 * Register the interrupt controller handler with the
exception table.
438 */
439 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT
,
440 (Xil_ExceptionHandler)
XScuGic_InterruptHandler,
441 &IntcInstance);
442
443 /*
444 * Connect a device driver handler that will be
called when an interrupt
445 * for the device occurs, the device driver handler
performs the
446 * specific interrupt processing for the device.
447 */
448 Status = XScuGic_Connect(&IntcInstance,
449 SPI_IRPT_INTR,
450 (XInterruptHandler)
XSpi_InterruptHandler,
451 (void *)SpiInstance);
452 if (Status != XST_SUCCESS) {
453 return XST_FAILURE;
454 }
455
459 /*
460 * Enable non-critical exceptions.
461 */
462 Xil_ExceptionEnable();
463
474 * progress.
475 */
476
22
26
27 // Global variables
28 int InterruptCounter = 0;
29
32
36 int main()
37 {
38 int T;
39 int Status;
40 u8 EN = 0;
41 //Timer init
42 XScuTimer timerDeControl;
43 XScuTimer_Config *Tim_Config;
44 //Timeout interr controller
45 XScuGic timeout_gic;
46 XScuGic_Config *togic_Config;
47
48 togic_Config = XScuGic_LookupConfig(INTC_DEVICE_ID);
49 Status = XScuGic_CfgInitialize(&timeout_gic,
togic_Config, togic_Config->CpuBaseAddress);
50 Tim_Config = XScuTimer_LookupConfig(
XPAR_PS7_SCUTIMER_0_DEVICE_ID);
51 Status = XScuTimer_CfgInitialize(&timerDeControl,
Tim_Config, Tim_Config->BaseAddr);
52 Xil_ExceptionInit();
53 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT
, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &
timeout_gic);
54 Status = XScuGic_Connect(&timeout_gic,
XPAR_SCUTIMER_INTR, (Xil_ExceptionHandler)
my_timer_interrupt_handler, (void *)&timerDeControl
);
55 XScuGic_Enable(&timeout_gic, XPAR_SCUTIMER_INTR);
56 XScuTimer_EnableInterrupt(&timerDeControl);
57 Xil_ExceptionEnable();
58 XScuTimer_LoadTimer(&timerDeControl,
XPAR_PS7_CORTEXA9_0_CPU_CLK_FREQ_HZ / 2);
59 XScuTimer_EnableAutoReload(&timerDeControl);
60
61
62 //Gpio Setup
63 XGpio_Config *ioConfig;
64 XGpio io;
65 float TH1,TH2,TH3,TH4;
66
67 //GPIO config
68 ioConfig = XGpio_LookupConfig(
XPAR_AXI_GPIO_0_DEVICE_ID);
69 Status = XGpio_CfgInitialize(&io, ioConfig, ioConfig
->BaseAddress);
70 XGpio_SetDataDirection(&io, 1, 0b11);
71 XGpio_SetDataDirection(&io, 2, 0b0);
72 XGpio_DiscreteWrite(&io, 2, 0b0);
73
74 //Motors INIT
75 Xil_Out32(MY_PWM, minThrottle);
76 Xil_Out32((MY_PWM+4), minThrottle);
77 Xil_Out32((MY_PWM+8), minThrottle);
78 Xil_Out32((MY_PWM+12), minThrottle);
79 TH1 = minThrottle+2500;
80 TH2 = minThrottle+2500;
81 TH3 = minThrottle+2500;
82 TH4 = minThrottle+2500;
83
84 while(1){
85 if(EN == 0){
86 EN = XGpio_DiscreteRead(&io, 1);
87 EN &= 0b01;
88 }
89 if(EN == 1)
90 {
91 XScuTimer_Start(&timerDeControl);
92 if (InterruptCounter <= 3){
93 T = 53000;
94 }
95 else if (InterruptCounter <= 6){
96 T = 80000;
97 }
98 else if(InterruptCounter <= 9){
99 T = 65000;
100 }
101 else if(InterruptCounter > 9){
102 T = 0;
103 }
104 if (InterruptCounter > 3 &&
InterruptCounter <= 6){
105 TH1 = (T + comp -comp2);
106 if (TH1 < minThrottle){
107 TH1 = minThrottle;
108 }
109 if (TH1 > maxThrottle-28000){
110 TH1 = maxThrottle
-28000;
111 }
112 TH2 = T - comp - comp2;
113 if (TH2 < minThrottle){
114 TH2 = minThrottle;
115 }
116 if (TH2 > maxThrottle-28000){
117 TH2 = maxThrottle
-28000;
118 }
169
170 }
171 }
172 }
173
178 if (XScuTimer_IsExpired(my_Timer_LOCAL))
179 {
180 XScuTimer_ClearInterruptStatus(my_Timer_LOCAL
);
181
182 InterruptCounter++;
183 }
184 }