Pregunta:
Orden de transmisión de datos en serie binaria
mike
2014-06-23 13:38:35 UTC
view on stackexchange narkive permalink

Estoy implementando un filtro para mi sensor IMU y, por lo tanto, quiero visualizar datos casi en tiempo real en la computadora. Utilizo la comunicación en serie binaria para facilitar la parte de envío del arduino (hasta donde yo sé, el serial.print es bastante lento). Así que divido mi int16_t en dos bytes y lo envío, como:

  Serial.write ((uint8_t) (gx >> 8)); Serial.write ((uint8_t) (gx & 0xFF));  

Después de eso, envío directamente el siguiente número (3 en total por ahora, tal vez hasta 7 números de 2 bytes en el futuro) Leí la cosa en matlab con:

  dt (k) = toc; tic; bindata ([1: 6], k) = fread (s, [6,1], ' int8 '); time = cumsum (dt (1: k));  

Que lee 6 bytes (3 números) y luego recalculo la representación binaria, los concateno y obtengo el número original (si alguien puede sugerir una manera más fácil ... Encontré matlab bastante poco práctico aquí).

El problema es que los números se mezclan con el tiempo. En ocasiones, cuando no se lee un byte o algo así, los bytes se estropean y se produce un número sin sentido. Se omite un número completo (2 bytes) para una muestra exactamente. En lugar de este número uno, hay dos veces. La siguiente muestra, el orden está desordenado (desplazado, de modo que el primer número sea el segundo). Este proceso aparece después de unos 30 segundos, a veces unos minutos. Después de la primera vez, sigue cambiando y saltando.

¿Alguien puede decirme qué hacer aquí? ¿Puedo incluir algún 'punto de interrupción' / terminador de línea, donde el lector (matlab) sepa, que estamos al comienzo del primer número? ¿O cómo se hace esto en realidad?

Supongo que debo agregar mi objetivo principal: quiero hacer que el envío del arduino sea lo más rápido posible. No deberían ser necesarios cálculos adicionales (si es posible). Y: la razón de estos cambios parece ser la demora (lentitud). Sospecho que es una lectura lenta de matlab, ya que vi scripts de procesamiento fluido en la lectura de HIL. Sin embargo, los errores se han detenido desde que reduje la tasa de baude. Solo los números incorrectos siguen siendo el problema.

¿Puede existir la posibilidad de recorrer el fread y leer y almacenar los valores después de un 'encabezado' agregado? Entonces, digamos que ocurre un pedido incorrecto. Luego descarto todo hasta el siguiente carácter / byte 'a' y uso los siguientes 6 bytes para producir mis 3 valores. Luego espero una 'a' de nuevo. Para eso tendría que hacer un bucle fread (s, [1,1], 'int8'); y busque el encabezado.

Código arduino completo:

  // Programa para enviar los datos giroscópicos / acel mediante puerto serie // programas matlab correspondientes: sensing.m y sensing_binary .m // 2 bucles de seguridad para garantizar un tiempo de muestreo constante // #define DEBUG # include "GY86.h" #include "Wire.h" GY86 gy86; int16_t ax, ay, az; int16_t gx, gy, gz; uint32_t currenttime = 0; uint32_t starttime = 0; uint32_t starttime2 = 0; // #define OUTPUT_ACCEL_COUNTS # define OUTPUT_GYRO_COUNTS // #define OUTPUT_ACCEL_BINARY // #define OUTPUT_GYRO_BINARYvoid setup () {9600 ).begin gy86.setup ();} bucle vacío () {tiempo actual = milis (); if (hora actual-hora de inicio > 9) {while (micros () - hora de inicio2 < 9000) {} hora de inicio2 = micros (); // leer las medidas de aceleración / giro sin procesar del dispositivo gy86.getSensorValues ​​(&ax, &ay, &az, &gx, &gy, &gz); // comprobando constantes // gx = -29; // gy = 245; // gz = 17; #ifdef OUTPUT_GYRO_COUNTS Serial.print ((int) gx); Serial.print (F ("\ t")); Serial.print ((int) gy); Serial.print (F ("\ t")); Serial.print ((int) gz); Serial.print (F ("\ t")); # endif # ifdef OUTPUT_ACCEL_COUNTS Serial.print (ax); Serial.print (F ("\ t")); Serial.print (ay); Serial.print (F ("\ t")); Serial.println (az); Serial.print (F ("\ t")); # endif
#si está definido (OUTPUT_ACCEL_COUNTS) || definido (OUTPUT_GYRO_COUNTS) Serial.print (F ("\ n")); # endif # ifdef OUTPUT_ACCEL_BINARY Serial.write ((uint8_t) (ax >> 8)); Serial.write ((uint8_t) (ax & 0xFF)); Serial.write ((uint8_t) (ay >> 8)); Serial.write ((uint8_t) (ay & 0xFF)); Serial.write ((uint8_t) (az >> 8)); Serial.write ((uint8_t) (az & 0xFF)); # endif # ifdef OUTPUT_GYRO_BINARY Serial.write ((uint8_t) (gx >> 8)); Serial.write ((uint8_t) (gx & 0xFF)); Serial.write ((uint8_t) (gy >> 8)); Serial.write ((uint8_t) (gy & 0xFF)); Serial.write ((uint8_t) (gz >> 8)); Serial.write ((uint8_t) (gz & 0xFF)); # endif hora de inicio = hora actual; }}  
Tres respuestas:
#1
+3
BrettAM
2014-06-24 03:23:22 UTC
view on stackexchange narkive permalink

Puede agregar algún tipo de encabezado / pie de página de paquete a la salida en serie. Por ejemplo, si envió los caracteres "ab" antes de los bytes de su número, podría rechazar los paquetes que solo tuvieran 2 bytes. la serie se vería así

"ab123ab123ab12ab123"

El código de matlab podría ver entrar otro "ab" antes de que finalice el paquete anterior e ignorar el paquete que terminó como solo "ab12" , y luego volver a encarrilarse. Debe usar 2 bytes para su encabezado para evitar colisiones con los datos reales. Agregar una suma de verificación al final del paquete también le permitiría detectar errores de intercambio de bits. CRC o fletcher16 funcionarían perfectamente en este escenario, pero en su caso, una suma de comprobación puede ser más de lo que necesita.

Entonces necesito agregar Serial.write ('ab') y luego escribir los bits numéricos. Y en matlab fread 2 + number-bits y verifique si los primeros 2 son ab. Si no es así, vuelve a leer. ¿Es esto lo que sugieres? ¿Qué quieres decir con colisión con mis datos? Si solo leo 1 + num-bits, y cambian, todavía obtendría un error si no todos los bits están ahí, o el primero no es "a"?
Por colisión me refiero a que si sus datos fueran los caracteres 'ab', estropearía la lectura. Tienes razón sobre el lado de arduino, simplemente envía "ab" antes de los datos. El código de matlab debería detectar que ocurrió la secuencia "ab" y luego buscar los bits de datos a continuación, pero si ocurre otro "ab" antes de que se terminen los bits de datos, volcar lo que ya ha entrado y empezar de nuevo. De esa manera, se realineará con los datos si se descarta un byte.
Colisión, ¿te refieres a la probabilidad de que en mis valores reales uno de los bytes contenga el encabezado por incidencia? Entonces, un encabezado más largo reduciría la posibilidad de tener datos reales = cadena de terminación. ¿Correcto?
Exactamente. Con dos bytes hay una probabilidad de 1 en 2 ^ 16 de que los datos aleatorios generen el encabezado. Un encabezado más largo de 2 bytes que puede resultar en que se transmita menos información ya que sus paquetes son muy pequeños; Enviar un carácter adicional por paquete probablemente costaría más tiempo que perder uno de cada 65536 paquetes.
Está bien. Implementaría esto como Serial.write (219); Serial.write (128); Dado que lo que se envía es binario (por lo tanto, lo mismo que algunos caracteres), y para matlab es más fácil leer (y comparar) directamente el uint con 8 bits, lo marco como solución, ya que puedo eliminar los cambios. Sin embargo, todo es inútil, ya que hay un extraño retraso de tiempo que se acumula. Ver: http: //stackoverflow.com/questions/24368670/matlab-plot-serial-data-continously
#2
+3
Anonymous Penguin
2014-06-24 04:23:26 UTC
view on stackexchange narkive permalink

Editar: parece que el problema se desborda. Eso significa que la conexión USB no se mantiene al día con los datos que se intentan enviar. Para solucionar este problema, debe hacer una de las siguientes cosas (o ambas):

  • Mayor velocidad en baudios. La velocidad en baudios es la frecuencia con la que se envían los datos. Por lo que he oído, cualquier cosa por encima de 500.000 como velocidad en baudios no es útil con las bibliotecas de Arduino.
    • El IDE de Arduino solo sube demasiado. Pruebe una aplicación como PuTTY para obtener velocidades en baudios más altas en el monitor en serie.
    • Hacer velocidades muy altas como esta son más adecuadas con un cable tan corto como sea posible. Yo diría que máx. de 4 pies, aunque depende de muchos factores, incluida la calidad del cable. Un cable más corto tiene menos resistencia (por lo tanto, menos errores).
    • El resto de la respuesta aún se aplica. Es posible que desee agregar un bit de paridad simple para asegurarse de que los datos no se corrompan mientras se envían. Agregar dos o tres caracteres reduce en gran medida el riesgo de corrupción, pero a costa de reducir la frecuencia de muestreo a la mitad y no verifica la integridad de los datos. No conozco su situación exacta, por lo que es posible que no pueda agregar un poco.
  • Tasa de muestreo más baja: está enviando demasiado , por lo que una solución simple es simplemente agregar delay (250); al final del ciclo para no sobrecargar el puerto.

Respuesta original:

Lo único que me viene a la mente que es muy eficiente es un bit de paridad con otro bit que siempre es lo opuesto al bit de paridad. ¿Por qué? Tener algo accidental donde los dos últimos bits son opuestos entre sí y todos suman un número par (ignorando el último bit) sería realmente extraño.

Un bit de paridad es un bit adicional, por lo que todos los bits sumados con el bit de paridad son iguales a un número par. Si no es del todo correcto, entonces sabe que hay un problema. Funciona solo para números impares de bits cambiados, por lo que no es infalible. Un ejemplo es que tiene los bits 10010110 . Hay cuatro 1 s, por lo que es un número par, por lo que el bit de paridad será 0 . Si fuera un número impar, sería 1 para que el recuento total sea un número par. Si la computadora calcula que no cuadra correctamente (disculpe mi tonto juego de palabras), entonces está dañado y la computadora puede descartarlo.

Para implementar esto, necesitaría convertir los números y el bit de paridad a ASCII y luego cuente los 0 y 1. Puede usar una función de resto y dividir por dos para que haya un resto de 1 si es impar, por lo que está dañado. Personalmente, tomaría el último bit y el número x antes, y seguiré repitiendo hasta que encuentre una combinación que satisfaga todo el bit de paridad y esté dentro de un rango razonable que haya especificado en el código.

Un salto de línea sería suficiente, pero ocupa más bits y solo descubre datos faltantes, no datos corruptos.

Quizás debería investigar por qué hay un problema . ¿Podría reducir la longitud del cable USB? ¿Actualice su cable / pruebe con uno diferente? ¿Reducir ligeramente la velocidad en baudios?

El cableado tiene tal vez 2 m de largo. La tasa de baude es 115200 ... ¿cuál sería el beneficio de una tasa de baude más baja? ¿Y cómo sé cuánto necesito?
Como pensé que el error está en mi código de matlab, ya abro una pregunta SE para eso, ya que esto ahora es más relacionado con el retraso, en lugar de los intercambios en bits, continuemos allí. ¡El tiempo de muestra de 1/3 s proporciona valores de tiempo válidos / actuales! Por favor, ayúdenme a comprender completamente de dónde podría haber venido esto: http: //stackoverflow.com/questions/24368670/matlab-plot-serial-data-continously
@mike no es un problema del código Matlab, es el Arduino entonces. Está enviando más datos de los que el chip serial puede manejar a la velocidad en baudios que especificó. Sé que dije que lo bajes (pensé que la información se estaba corrompiendo), pero necesitas aumentar la velocidad en baudios y bajar la frecuencia de muestreo.
25 Hz es posible. Eso es lo más alto posible. Sin embargo, quería depurar un filtrado de quadcopter, ... que no tiene mucho sentido si ejecuto 1/2 o incluso 1/4 de la frecuencia de muestreo planificada. (Intento obtener mi 100Hz, o al menos 50). Además, si agrego más números, este fenómeno ocurre con más frecuencia, supongo. Porque se desborda, ¿verdad? Intentaré conseguir un cableado más corto, ¡gracias por el consejo!
@mike ¡Me alegro de haber podido ayudar! Edité mi pregunta y limpié los comentarios aquí.
#3
  0
user2973
2014-06-30 18:56:13 UTC
view on stackexchange narkive permalink

Si lo que desea es alta velocidad, la velocidad en baudios en serie es su principal preocupación. Supongo que está utilizando un Arduino con un chip convertidor de serie a USB, como Uno o Mega.

Transfiriendo a 115200 baudios, envía 11520 bytes / s en la configuración de serie normal (1 inicio + 8 datos + 1 bits de parada = 10 bits para enviar 1 byte de datos). Con un Arduino funcionando a un reloj de 16 MHz, puede ejecutar instrucciones 16M / 11520 = 1389 en el tiempo que lleva transmitir un byte. Por lo tanto, no tiene que preocuparse mucho por las rutinas lentas de la biblioteca, de lo que debe preocuparse es de cuántos bytes envía. De esa manera, tiene razón en no usar Serial.print, ya que convertirá su int a ascii, haciendo que todos los números mayores a 99 sean más lentos para transmitir (para un uint16).

Cuando envía datos con Serial .write se almacena primero en un búfer de anillo y luego se transmite en segundo plano mediante una rutina de servicio de interrupción. El búfer de anillo suele ser de 64 bytes. Una vez que el búfer está lleno, una llamada a Serial.write se bloqueará hasta que se haya transmitido un byte, por lo que hay espacio en el búfer nuevamente. Por lo tanto, la sincronización en su bucle principal no es realmente necesaria, puede eliminarla y su frecuencia de muestreo se adaptará a su velocidad en baudios en serie.

Una vez que los datos hayan llegado al chip convertidor de serie a USB, transmitirse en el bus USB a 12 Mbit / s. No sé la velocidad exacta de transferencia de datos, ya que hay mucha más sobrecarga en el USB que en la serie, pero puede estar seguro de que el cuello de botella no está aquí. No creo que tengas que preocuparte por la longitud de los cables USB, ya que hay detección de errores incorporada y retransmisión en el protocolo USB, y la velocidad es mucho más rápida que la del enlace en serie.

En la computadora, los datos se pueden almacenar en búfer en varios lugares, todo depende de los controladores de dispositivo y las bibliotecas de serie utilizadas. No soy un experto aquí. Sin control de flujo, los controladores / bibliotecas probablemente no tengan más remedio que descartar datos si están recibiendo más de lo que la aplicación final o la capa posterior pueden procesar. Tal vez matlab o su implementación en serie o su computadora sea lenta de alguna manera (los datos realmente no llegan muy rápido para una computadora moderna). De modo que podría reducir la velocidad en baudios o tratar de lidiar con los paquetes perdidos.

Tengo una sugerencia para lidiar con los paquetes perdidos: Reserve los x bits superiores en cada byte que transmita para mantener el número de ese byte en su secuencia. Podrías p. Ej. reserve 2 bits, lo que le permite enviar paquetes de hasta 4 bytes que pertenecen juntos. Cada paquete de bytes tendría 6 bits de datos, lo que le permitirá enviar números con 24 bits de precisión. Para enviar un uint16:

  void send_int16 (uint16_t data) {Serial.write ((0 << 6) | (datos & 0b00111111)); datos >> = 6; Serial.write ((1 << 6) | (datos & 0b00111111)); datos >> = 6; Serial.write ((2 << 6) | (data & 0b00111111));}  

Luego, en matlab, debe esperar una secuencia de paquetes numerados 0, 1, 2 en los dos primeros bits, elimine la numeración y coloque los paquetes junto con operadores bit a bit. Si obtiene una secuencia mal formada, por ejemplo. 0, 2, puede descartarlo y esperar al siguiente byte que comience con cero.

Realmente no he usado matlab, así que está solo.



Esta pregunta y respuesta fue traducida automáticamente del idioma inglés.El contenido original está disponible en stackexchange, a quien agradecemos la licencia cc by-sa 3.0 bajo la que se distribuye.
Loading...