通过串行 (UART) 从 MPU6050 记录数据失败(数据丢失)
Log data from MPU6050 through serial (UART) fails (data loss)
这是我面临的问题。我已将我的 ATmega328P 与 6 轴 IMU(MPU6050 与 GY521 分线板)连接起来。我可以通过 TWI 接口(Atmel 的 I2C)读取数据并通过 UART 将其发送到我的 PC(运行 Ubuntu)。我正在为这两种通信协议使用定制的库,但它们非常标准并且似乎工作得很好。该项目的目标是根据 IMU 读数 实时计算方向数据 ,例如 100 Hz。
主要问题是我无法以 100 Hz(甚至 50 Hz)从设备记录数据。我使用的方向过滤器 (here) 需要相当高的频率,结果证明 100 Hz 可以正常工作(测试离线从另一台设备获取数据)。
现在,我正在使用 ATmega328P 的 16 位定时器以 100 Hz 的频率采样数据,这似乎有效,因为我在 ISR 中添加了一条线来切换内置 LED,在我看来它是以 100 Hz 的频率闪烁(我几乎看不到它打开和关闭)。在同一个 ISR 中,我从惯性传感器读取值,为了记录它们,通过串行端口发送这些值。每 10 毫秒(最大值),我以 115200 的波特率发送 9 个浮点数(36 字节)。如果我使用 Arduino IDE 的串行监视器来可视化此数据流,我注意到一些非常奇怪的东西,如以下截图。
当您注意查看时间戳时,每收到 2 或 3 组样本就有一个常见的 33 毫秒延迟。此外,我得到了大约 60% 的数据。例如,10 秒的采集只能让我得到不到 600 个样本(每个变量)而不是 1000 个。此外,我测试了通过 UART 仅发送一个变量(即只有一个浮点数,4 字节)的相同结果同样的行为!
顺便说一句,我正在利用以下内容通过 UART 接口发送每个字节(字符)。
void writeCharUART(char c) {
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
}
即使我的 ISR 以 100 Hz 运行(LED 闪烁似乎证实了这一点),数据丢失也可能发生在 TWI 传输级别。为了证明这一点,我修改了 ISR 的代码,只发送一个普通字符 (T
) 而不是来自 MPU 的数据,我得到了类似的行为。像这样:
00:10:05.203 -> T
00:10:05.203 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.269 -> T
所以,我猜 UART 库有问题,我实际上以 100 Hz 采样,但记录频率是低得多(而且不是恒定的)。如何解决这个问题 and/or 调试 UART 库?您是否看到其他理由来证明这个问题?
编辑 1
正如评论中所指出的,这似乎是接收软件的问题,它通过某种缓冲将频率限制在 ~30 Hz。为了确认这一点,我使用以下代码对 ATmega328P 进行了编程(这次使用 IDE)。
void loop() {
Serial.println("T");
}
一开始以为这次没有延迟,结果208个sample后才发现。因此,在同一时间戳接收到约 200 个样本,并在 33 毫秒后接收到另一组样本。 这可能是接收软件引入延迟的证据。
我还测试了一个用 C 开发的简单串行监视器,即使没有时间戳功能,如果我将采集采样的持续时间固定为 100 Hz,我也会丢失样本。我的串行监视器基于 termios.h
库,但我找不到任何关于其 缓冲 传入数据方式的文档。
这里有两个问题:
您缺少消息。您仅用眼睛检查了采样率并告诉我们您仍然可以看到非常快速的闪烁。根据 LED 的颜色、环境光、您的身体状态和您的眼睛,这可能意味着 30 Hz 到 100 Hz 之间的任何值。
- 我不相信自己的眼睛可以估计,而是使用示波器或频率计数器测量。
- 你可以通过软件划分,将LED闪烁的频率降低到1Hz甚至更低。如此低的频率可以通过秒表手动测量。例如数 30 次眨眼并检查所需的时间。
- 为消息添加一个计数器,并随着每条消息递增。如果您正在丢失数据,您会立即看到它。
时间戳似乎表明消息 "clustered" 频率约为 30 Hz。
- 我猜测 运行 中的时间戳来源为 30 Hz。所以它不会给你更准确的值。
我有点解决了我的问题!首先,感谢评论,我检查了我的 ISR 在 100 Hz 时是否正确 运行。这样做,我可以确定问题出在其他地方,即在 UART 通信中。
我发现这很有帮助:Linux, serial port, non-buffering mode
显然,Arduino IDE 提供的串行监视器利用了 termios.h
库并使用其默认设置。我还检查了用户手册并切换到轮询读取模式。引用用户手册
If data is available, read(2) returns immediately, with the lesser of the number of bytes available, or the number of bytes requested. If no data is available, read(2) returns 0.
因此,我切换回我的串行监视器代码并更改了 initPort()
函数,添加了以下代码行。
struct termios options;
(...)
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 0;
我立即注意到终端中的数据频率高得多。我让 ISR 中的 1 Hz LED 保持闪烁,并且没有周期延长。此外,这次 10 秒的采集为我提供了每个变量大约 1000 个样本,与 100 Hz 的采样率一致。
在AVR端,我也改变了通过UART发送数据的方式。之前,我像这样发送 9 个花车:
sprintf(buffer, "%f, %f, %f", value1_x, value1_y, value1_z);
serial_print(buffer); // no "\n" sent here
sprintf(buffer, "%f, %f, %f", value2_x, value2_y, value2_z);
serial_print(buffer); // again, no "\n" sent
sprintf(buffer, "%f, %f, %f", roll, pitch, yaw);
serial_println(buffer); // "\n" is sent here once the last data byte is sent
现在,我用对函数的一次调用替换了所有这些 serial_println()
并且我只向缓冲区写入了 6 个浮点数。
这是我面临的问题。我已将我的 ATmega328P 与 6 轴 IMU(MPU6050 与 GY521 分线板)连接起来。我可以通过 TWI 接口(Atmel 的 I2C)读取数据并通过 UART 将其发送到我的 PC(运行 Ubuntu)。我正在为这两种通信协议使用定制的库,但它们非常标准并且似乎工作得很好。该项目的目标是根据 IMU 读数 实时计算方向数据 ,例如 100 Hz。
主要问题是我无法以 100 Hz(甚至 50 Hz)从设备记录数据。我使用的方向过滤器 (here) 需要相当高的频率,结果证明 100 Hz 可以正常工作(测试离线从另一台设备获取数据)。 现在,我正在使用 ATmega328P 的 16 位定时器以 100 Hz 的频率采样数据,这似乎有效,因为我在 ISR 中添加了一条线来切换内置 LED,在我看来它是以 100 Hz 的频率闪烁(我几乎看不到它打开和关闭)。在同一个 ISR 中,我从惯性传感器读取值,为了记录它们,通过串行端口发送这些值。每 10 毫秒(最大值),我以 115200 的波特率发送 9 个浮点数(36 字节)。如果我使用 Arduino IDE 的串行监视器来可视化此数据流,我注意到一些非常奇怪的东西,如以下截图。
当您注意查看时间戳时,每收到 2 或 3 组样本就有一个常见的 33 毫秒延迟。此外,我得到了大约 60% 的数据。例如,10 秒的采集只能让我得到不到 600 个样本(每个变量)而不是 1000 个。此外,我测试了通过 UART 仅发送一个变量(即只有一个浮点数,4 字节)的相同结果同样的行为!
顺便说一句,我正在利用以下内容通过 UART 接口发送每个字节(字符)。
void writeCharUART(char c) {
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
}
即使我的 ISR 以 100 Hz 运行(LED 闪烁似乎证实了这一点),数据丢失也可能发生在 TWI 传输级别。为了证明这一点,我修改了 ISR 的代码,只发送一个普通字符 (T
) 而不是来自 MPU 的数据,我得到了类似的行为。像这样:
00:10:05.203 -> T
00:10:05.203 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.236 -> T
00:10:05.269 -> T
所以,我猜 UART 库有问题,我实际上以 100 Hz 采样,但记录频率是低得多(而且不是恒定的)。如何解决这个问题 and/or 调试 UART 库?您是否看到其他理由来证明这个问题?
编辑 1
正如评论中所指出的,这似乎是接收软件的问题,它通过某种缓冲将频率限制在 ~30 Hz。为了确认这一点,我使用以下代码对 ATmega328P 进行了编程(这次使用 IDE)。
void loop() {
Serial.println("T");
}
一开始以为这次没有延迟,结果208个sample后才发现。因此,在同一时间戳接收到约 200 个样本,并在 33 毫秒后接收到另一组样本。 这可能是接收软件引入延迟的证据。
我还测试了一个用 C 开发的简单串行监视器,即使没有时间戳功能,如果我将采集采样的持续时间固定为 100 Hz,我也会丢失样本。我的串行监视器基于 termios.h
库,但我找不到任何关于其 缓冲 传入数据方式的文档。
这里有两个问题:
您缺少消息。您仅用眼睛检查了采样率并告诉我们您仍然可以看到非常快速的闪烁。根据 LED 的颜色、环境光、您的身体状态和您的眼睛,这可能意味着 30 Hz 到 100 Hz 之间的任何值。
- 我不相信自己的眼睛可以估计,而是使用示波器或频率计数器测量。
- 你可以通过软件划分,将LED闪烁的频率降低到1Hz甚至更低。如此低的频率可以通过秒表手动测量。例如数 30 次眨眼并检查所需的时间。
- 为消息添加一个计数器,并随着每条消息递增。如果您正在丢失数据,您会立即看到它。
时间戳似乎表明消息 "clustered" 频率约为 30 Hz。
- 我猜测 运行 中的时间戳来源为 30 Hz。所以它不会给你更准确的值。
我有点解决了我的问题!首先,感谢评论,我检查了我的 ISR 在 100 Hz 时是否正确 运行。这样做,我可以确定问题出在其他地方,即在 UART 通信中。
我发现这很有帮助:Linux, serial port, non-buffering mode
显然,Arduino IDE 提供的串行监视器利用了 termios.h
库并使用其默认设置。我还检查了用户手册并切换到轮询读取模式。引用用户手册
If data is available, read(2) returns immediately, with the lesser of the number of bytes available, or the number of bytes requested. If no data is available, read(2) returns 0.
因此,我切换回我的串行监视器代码并更改了 initPort()
函数,添加了以下代码行。
struct termios options;
(...)
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 0;
我立即注意到终端中的数据频率高得多。我让 ISR 中的 1 Hz LED 保持闪烁,并且没有周期延长。此外,这次 10 秒的采集为我提供了每个变量大约 1000 个样本,与 100 Hz 的采样率一致。
在AVR端,我也改变了通过UART发送数据的方式。之前,我像这样发送 9 个花车:
sprintf(buffer, "%f, %f, %f", value1_x, value1_y, value1_z);
serial_print(buffer); // no "\n" sent here
sprintf(buffer, "%f, %f, %f", value2_x, value2_y, value2_z);
serial_print(buffer); // again, no "\n" sent
sprintf(buffer, "%f, %f, %f", roll, pitch, yaw);
serial_println(buffer); // "\n" is sent here once the last data byte is sent
现在,我用对函数的一次调用替换了所有这些 serial_println()
并且我只向缓冲区写入了 6 个浮点数。