最大化 Arduino 串行输出
Maximize Arduino serial output
整个过程中穿插了多个问题 post。请仔细阅读并回答您可以投票的部分。
用例
创建压力读数的时间序列 csv 文件。这些读取需要具有最大频率,但我只需要它持续不到 10 秒左右。
Material
- Arduino Uno 克隆(不可更改)
- USB 2.0 串口(可变)
- pyserial (Python 3)
- SSD
问题
识别并修复阻止频率达到最大值的瓶颈 reads/s。
密码
Arduino
void setup() {
//speed over the usb serial interface
Serial.begin(450000);
}
void loop() {
//get sensor data: read the input on analog pin 0:
int sensorValue = analogRead(A0); //returns value 0-1023
//send over serial
Serial.println(sensorValue);
}
Python
...
ser.serial()
ser.baudrate=450000
ser.open()
while True:
try:
serialData = float(ser.readline().decode().replace('\n', ''))
except(ValueError):
continue #I ran the code without this try/except and got 100 reads higher than when this is included in here. Caused no change in order of magnitude.
now = datetime.datetime.now()
serialData2 = serialData * voltageOffset #a couple of these happen
outfile.write(now+', '+str(serialData2)+'\n')
except(KeyboardInterrupt):
...
os.sys.exit(0)
带宽
一些计算来找出瓶颈所在。
1 个数据包 = 10 位
串口线速:450000/10 = 45000包/秒
Arduino samples-into-serial/second 率:
100 µs * 1s/(10^-6 µs) = 1 x 10^4 样本/秒
arduino 硅适配器:便宜的克隆芯片。假设性能不佳,因为它不是 arduino 许可的产品,但这被认为是可以接受的损失,稍后将解释。假定具有无限带宽,尽管这显然会将性能提高一个数量级。
USB 2.0
packets/s = 480 mb/s * 10^6 b/mb * 1 packet/10 b = 48 x 10^6 数据包/秒
PC文件写入速度
400 MB/s = 320 x 10^6 packets/s
PC python 脚本
未知。假设是无限的?已知 datetime.now()
查询大约需要 8 微秒。
结论:瓶颈是串口波特率
调整波特率证实了这一点。我发现 el-cheapo arduino uno 仅支持最大 450000 波特率,而 arduino actually supports 2-7 million 来自我可以在线收集的数据。话虽这么说,还是有些不对劲。
关键问题
回想一下,在这个波特率下,我们应该看到压力读取以理论上的 45000 个数据包/秒生成。
波特率-数据长度-无奇偶校验-1 结束位存在:450000-8-n-1
让sensorValue = 1023
。进入 println
的数据看起来像这样:1023\n
当我在 arduino 中 运行 serial.println(sensorValue)
时,有多少数据包通过线路发送到 PC?这是如何序列化成数据包的?将通过线路发送多少数据包?以下哪一项是正确的:
一个。 16 位 int
需要传输 2 个数据包。 \n
再精确 2 个。总计 = 4
乙。 sensorValue
被转换为字符串并通过单独的数据包通过网络发送:1 个用于 1
,1 个用于 0
,1 个用于 2
,1 个用于 3
,1 个代表 \
,1 个代表 n
。总计 = 6
摄氏度。其他(帮我优化一下??)
测试
我没有看到预期的结果。
让baud = 450000
.
在 arduino 代码 serial.println(value)
中更改 value
在我的 txt 文件中产生不同的 reads/s 第二个:
value = readPin\n
>> 5417压力reads/s
value = 1
>> 10201 reads/s
value = 1023
>> 4279 reads/s
这告诉了我一些事情。读取 analogPin 的值时会产生开销。我还可以得出结论,arduino 也只在必要时发送多个数据包(1 > 8 位数据,1023 > 16 位数据)。 等等,什么? 这没有多大意义。 这是怎么发生的?
调整波特率会导致读取次数发生变化,直到arduino达到最大值。但是让我们找到我应该期望的吞吐量。
450000 b/s *(1 个数据包/10b)* [1 个读取/(4 或 6 个数据包)] = 11250 个读取或 7500 个理论值 reads/s
实际 = 5417 reads/s
2000次阅读消失到哪里去了?
你是对的,瓶颈是串行端口(在你传输数据效率低下的意义上)。但是,您的大多数其他假设都是错误的。
波特率代表什么
波特率是每秒不同符号变化的次数。在这里它相当于每秒位数。波特率将包括传输的所有位,而不仅仅是数据(开始、停止、奇偶校验)。
如何传输 10 位值
因为你有8n1传输,你不能发送正好10位的数据。它必须是 8 的倍数,所以 8、16、24 等。10 位将在两个 8 位部分中发送,就像它们作为 int 存储在 Arduino 内存中一样。
println() 是如何工作的
println()
将数字转换为字符串。您可以为此转换指定基数(DEC、BIN、HEX、OCT)——默认为 DEC,因此 1023 将作为 4 个字节传输 + \n
,这是一个单字节 (ASCII 10) 和 \r
(ASCII 13) 这也是一个单字节。共 6 个字节。 1 将需要 3 个字节 - 1 个用于数据,2 个用于换行符和回车 return.
如何让它更快
- 几乎不做任何更改 - 使用
println(val, HEX)
- 对于大于 255 的数字,您最多需要 5 个字节。
- 使用
Serial.write()
- 此函数将原始二进制数据放入串行,因此如果您要发送值 10,它只会发送值 10 的一个字节。但是,当您要发送 10-位变量,事情变得复杂 - 你需要 2 个字节,PC 需要知道哪个是第一部分,哪个是第二部分才能将它们拼接在一起。您将需要想出某种形式的简单传输协议来处理这个问题,所以可能需要一些 start/stop 个字符。
- 您需要 10 位分辨率吗?如果你能继续使用 8 位,你就可以只传输普通数据,而不需要任何额外的字符。另外,正如评论中所建议的那样,您可以使用更快的 ADC 时钟。
- KIIV 也建议 - 您可以使用 ADC 转换中断,它会在测量完成时立即触发。
如果您仍然使用 10 位,我建议使用 3 字节帧:1 个起始字节和 2 个数据字节。这样你理论上可以达到每秒约 18 000 帧,这高于最大 analogRead()
频率。
一个选项是采样 x 次,然后在发送回 PC 之前取平均值。或者接受压缩 328p 的挑战!如果您的压力读数基本一致,您可能对一些 RLE 没问题。
不要使用 10 位。 ADC 可能具有 10 位分辨率,但精度为 +/- 2 LSB,并且非常依赖于稳定的 vref。因此,降低 2 LSB 并使用前 8 位进行快速 Arduino 到 PC 读数或多次采样并平均噪声。
这是一个嘈杂的、共享的、低分辨率的 ADC。所以不要让其他模拟引脚悬空,因为这会产生噪声(只需将它们接地)。如果可能的话,运行 Arduino 在接地外壳中,因为 ADC 有一个 MUX 导致它有更长的轨迹。切换 ADC 模式或引脚也会产生一些噪音(不是你在这里做的)。如果你能保持稳定的输入电压,电池电量就很棒,否则 AC/DC 噪声可能会导致意外的 vref 波动。
Arduino 上的主循环 运行s 328p 和 8u 之间的串行 IO 以及模拟读取。所以时序瓶颈有点复杂。
UNO 串口到 USB 的转换由 ATMEGA8u2(板上的另一个芯片)处理,它可以 115.2kbaud(不是 450000)通信。
8u 和 328p 上有缓冲区只是为了增加复杂性。并且两个芯片 运行 在不同的时钟上。来自 crystal 的 ATMEGA8u2 运行s 和来自谐振器(均为 16MHz)的 ATMEGA328p(主处理器)运行s。
根据您的需要,Teensy 有一个稍微更准确的 ADC(外部的 ADC 会提供更好的读取)和更快的 CPU、更多的 RAM 等,它可以最大限度地提高USB端口速度。此外,Paul 还提供了一些使事情变得非常快的低级技术的重要信息。
整个过程中穿插了多个问题 post。请仔细阅读并回答您可以投票的部分。
用例
创建压力读数的时间序列 csv 文件。这些读取需要具有最大频率,但我只需要它持续不到 10 秒左右。
Material
- Arduino Uno 克隆(不可更改)
- USB 2.0 串口(可变)
- pyserial (Python 3)
- SSD
问题
识别并修复阻止频率达到最大值的瓶颈 reads/s。
密码
Arduino
void setup() {
//speed over the usb serial interface
Serial.begin(450000);
}
void loop() {
//get sensor data: read the input on analog pin 0:
int sensorValue = analogRead(A0); //returns value 0-1023
//send over serial
Serial.println(sensorValue);
}
Python
...
ser.serial()
ser.baudrate=450000
ser.open()
while True:
try:
serialData = float(ser.readline().decode().replace('\n', ''))
except(ValueError):
continue #I ran the code without this try/except and got 100 reads higher than when this is included in here. Caused no change in order of magnitude.
now = datetime.datetime.now()
serialData2 = serialData * voltageOffset #a couple of these happen
outfile.write(now+', '+str(serialData2)+'\n')
except(KeyboardInterrupt):
...
os.sys.exit(0)
带宽
一些计算来找出瓶颈所在。
1 个数据包 = 10 位
串口线速:450000/10 = 45000包/秒
Arduino samples-into-serial/second 率:
100 µs * 1s/(10^-6 µs) = 1 x 10^4 样本/秒
arduino 硅适配器:便宜的克隆芯片。假设性能不佳,因为它不是 arduino 许可的产品,但这被认为是可以接受的损失,稍后将解释。假定具有无限带宽,尽管这显然会将性能提高一个数量级。
USB 2.0
packets/s = 480 mb/s * 10^6 b/mb * 1 packet/10 b = 48 x 10^6 数据包/秒
PC文件写入速度
400 MB/s = 320 x 10^6 packets/s
PC python 脚本
未知。假设是无限的?已知 datetime.now()
查询大约需要 8 微秒。
结论:瓶颈是串口波特率
调整波特率证实了这一点。我发现 el-cheapo arduino uno 仅支持最大 450000 波特率,而 arduino actually supports 2-7 million 来自我可以在线收集的数据。话虽这么说,还是有些不对劲。
关键问题
回想一下,在这个波特率下,我们应该看到压力读取以理论上的 45000 个数据包/秒生成。
波特率-数据长度-无奇偶校验-1 结束位存在:450000-8-n-1
让sensorValue = 1023
。进入 println
的数据看起来像这样:1023\n
当我在 arduino 中 运行 serial.println(sensorValue)
时,有多少数据包通过线路发送到 PC?这是如何序列化成数据包的?将通过线路发送多少数据包?以下哪一项是正确的:
一个。 16 位 int
需要传输 2 个数据包。 \n
再精确 2 个。总计 = 4
乙。 sensorValue
被转换为字符串并通过单独的数据包通过网络发送:1 个用于 1
,1 个用于 0
,1 个用于 2
,1 个用于 3
,1 个代表 \
,1 个代表 n
。总计 = 6
摄氏度。其他(帮我优化一下??)
测试
我没有看到预期的结果。
让baud = 450000
.
在 arduino 代码 serial.println(value)
中更改 value
在我的 txt 文件中产生不同的 reads/s 第二个:
value = readPin\n
>> 5417压力reads/s
value = 1
>> 10201 reads/s
value = 1023
>> 4279 reads/s
这告诉了我一些事情。读取 analogPin 的值时会产生开销。我还可以得出结论,arduino 也只在必要时发送多个数据包(1 > 8 位数据,1023 > 16 位数据)。 等等,什么? 这没有多大意义。 这是怎么发生的?
调整波特率会导致读取次数发生变化,直到arduino达到最大值。但是让我们找到我应该期望的吞吐量。
450000 b/s *(1 个数据包/10b)* [1 个读取/(4 或 6 个数据包)] = 11250 个读取或 7500 个理论值 reads/s
实际 = 5417 reads/s
2000次阅读消失到哪里去了?
你是对的,瓶颈是串行端口(在你传输数据效率低下的意义上)。但是,您的大多数其他假设都是错误的。
波特率代表什么
波特率是每秒不同符号变化的次数。在这里它相当于每秒位数。波特率将包括传输的所有位,而不仅仅是数据(开始、停止、奇偶校验)。
如何传输 10 位值
因为你有8n1传输,你不能发送正好10位的数据。它必须是 8 的倍数,所以 8、16、24 等。10 位将在两个 8 位部分中发送,就像它们作为 int 存储在 Arduino 内存中一样。
println() 是如何工作的
println()
将数字转换为字符串。您可以为此转换指定基数(DEC、BIN、HEX、OCT)——默认为 DEC,因此 1023 将作为 4 个字节传输 + \n
,这是一个单字节 (ASCII 10) 和 \r
(ASCII 13) 这也是一个单字节。共 6 个字节。 1 将需要 3 个字节 - 1 个用于数据,2 个用于换行符和回车 return.
如何让它更快
- 几乎不做任何更改 - 使用
println(val, HEX)
- 对于大于 255 的数字,您最多需要 5 个字节。 - 使用
Serial.write()
- 此函数将原始二进制数据放入串行,因此如果您要发送值 10,它只会发送值 10 的一个字节。但是,当您要发送 10-位变量,事情变得复杂 - 你需要 2 个字节,PC 需要知道哪个是第一部分,哪个是第二部分才能将它们拼接在一起。您将需要想出某种形式的简单传输协议来处理这个问题,所以可能需要一些 start/stop 个字符。 - 您需要 10 位分辨率吗?如果你能继续使用 8 位,你就可以只传输普通数据,而不需要任何额外的字符。另外,正如评论中所建议的那样,您可以使用更快的 ADC 时钟。
- KIIV 也建议 - 您可以使用 ADC 转换中断,它会在测量完成时立即触发。
如果您仍然使用 10 位,我建议使用 3 字节帧:1 个起始字节和 2 个数据字节。这样你理论上可以达到每秒约 18 000 帧,这高于最大 analogRead()
频率。
一个选项是采样 x 次,然后在发送回 PC 之前取平均值。或者接受压缩 328p 的挑战!如果您的压力读数基本一致,您可能对一些 RLE 没问题。
不要使用 10 位。 ADC 可能具有 10 位分辨率,但精度为 +/- 2 LSB,并且非常依赖于稳定的 vref。因此,降低 2 LSB 并使用前 8 位进行快速 Arduino 到 PC 读数或多次采样并平均噪声。
这是一个嘈杂的、共享的、低分辨率的 ADC。所以不要让其他模拟引脚悬空,因为这会产生噪声(只需将它们接地)。如果可能的话,运行 Arduino 在接地外壳中,因为 ADC 有一个 MUX 导致它有更长的轨迹。切换 ADC 模式或引脚也会产生一些噪音(不是你在这里做的)。如果你能保持稳定的输入电压,电池电量就很棒,否则 AC/DC 噪声可能会导致意外的 vref 波动。
Arduino 上的主循环 运行s 328p 和 8u 之间的串行 IO 以及模拟读取。所以时序瓶颈有点复杂。
UNO 串口到 USB 的转换由 ATMEGA8u2(板上的另一个芯片)处理,它可以 115.2kbaud(不是 450000)通信。
8u 和 328p 上有缓冲区只是为了增加复杂性。并且两个芯片 运行 在不同的时钟上。来自 crystal 的 ATMEGA8u2 运行s 和来自谐振器(均为 16MHz)的 ATMEGA328p(主处理器)运行s。
根据您的需要,Teensy 有一个稍微更准确的 ADC(外部的 ADC 会提供更好的读取)和更快的 CPU、更多的 RAM 等,它可以最大限度地提高USB端口速度。此外,Paul 还提供了一些使事情变得非常快的低级技术的重要信息。