【问题标题】:Reducing Lag between Arduino and Python减少 Arduino 和 Python 之间的延迟
【发布时间】:2018-03-17 01:18:46
【问题描述】:

我有 3 个 Arduino 传感器节点连接到运行 Python 的 PC,XBee 系列 1 无线电作为无线通信工具。波特率设置为 9600,并且所有地址(ATDL、ATDH、ATMY)都设置正确,因为所有 Arduino 传感器节点都能够正确地将数据发送到连接到我在 Python 上运行的 PC 的 XBee 协调器。连接到各自 Arduino 的 Xbee 无线电(有 2 个 Arduino Unos 和 1 个 Arduino Nano)被配置为终端设备。

我最近发现了一个问题,即 Arduino 上的任何更改在到达 PC 时都会延迟 5 秒,然后写入 CSV 文件。例如,我正在读取其中一个 Arduino 上的引脚状态,并在通过 XBee 将其传输到我的计算机后将此状态写入 CSV 文件。但是,我意识到当我在 08:30:30 (HH:MM:SS) 进行状态更改时,更改仅在 08:30:35 (HH:MM:SS) 时反映在 CSV 文件中。

请问为什么会出现这种情况,我应该如何解决?我分别有以下 Arduino 和 Python 代码。

Arduino(这些代码在 3 个 Arduino 节点上大致相同):

#include <SoftwareSerial.h>
#define IR 10 // IR sensor at D10 position

#define pirPin 9 // Input for HC-S501
#define LEDPinPIR 12 // LED at Pin 12 (PIR)
#define lightLED 11 // LED at Pin 11 (Relay, Neg.Logic - ON = Relay off)

SoftwareSerial xbee(2, 3); // RX, TX

int pirValue; // Place to store read PIR Value
int pirNum = 0;
int pirNumyes = 0;
int pirNumno = 0;

int sw_door = 0; //sw_door has been updated to "sw_relay" w.e.f 27-Feb-2018
int IR_val = 0;

char incomingByte;
unsigned long prevMillis = 0;

void setup() {  
  Serial.begin(9600);
  xbee.begin(9600);
  pinMode(pirPin, INPUT); // PIR sensor
  pinMode(LEDPinPIR, OUTPUT); // Ultrasound sensor indicator
  pinMode(lightLED, OUTPUT); // LED at Pin 11 (Relay, Neg.Logic - ON = Relay off)
  pinMode(SW, INPUT); // Switch
  digitalWrite(SW, LOW);
  digitalWrite(LEDPinPIR, LOW);
}

void loop() {

  unsigned long currentMillis = millis();

  if((unsigned long)currentMillis - prevMillis == 1000){

    //IR sensor "d" refers to door

    if (digitalRead(IR) == LOW){
      IR_val = 1;
      String ID = "d";
      String IRID = ID + IR_val;
      Serial.print(IRID);
      Serial.print(',');
      xbee.print(IRID);
      xbee.print(',');
    }
    else{
      IR_val = 0;
      String ID = "d";
      String IRID = ID + IR_val;
      Serial.print(IRID);
      Serial.print(',');
      xbee.print(IRID);
      xbee.print(',');
    }

    // Motion sensor
    pirValue = digitalRead(pirPin);
    if (pirValue == HIGH) {
      pirNumyes = 1;
      Serial.print(pirNumyes);
      Serial.print(',');
      xbee.print(pirNumyes);
      xbee.print(',');
      digitalWrite(LEDPinPIR, HIGH);
    }
    else {
      pirNumno = 0;
      Serial.print(pirNumno);
      Serial.print(',');
      xbee.print(pirNumno);
      xbee.print(',');
      digitalWrite(LEDPinPIR, LOW);
    }

    // Switch 
    if(digitalRead(lightLED)== HIGH){
      sw_door = 0;
      Serial.print(sw_door);
      Serial.println(',');
      xbee.print(sw_door);
      xbee.println(',');
    }
    else{
      sw_door = 1;
      Serial.print(sw_door);
      Serial.println(',');
      xbee.print(sw_door);
      xbee.println(',');
    }
    prevMillis = currentMillis;
  }

  // Xbee to Arduino Added: 18-Feb-2018
  if (xbee.available()){
    incomingByte = xbee.read();

    if(incomingByte == '1'){
      digitalWrite(lightLED, HIGH);
      //xbee.println("OK");
    }
    else if(incomingByte == '0'){
      digitalWrite(lightLED, LOW);
      //xbee.println("OK");
    }
  }
}

Python:

import threading
import time
import serial
import csv

# Arduino; Arduino is now replaced by XBee modules
arduino = serial.Serial('COM18', 9600, timeout=1)  # Open serial port.

def acquire_data():
    while True:
        try:
            data_in = arduino.readline()  # read serial data from Arduino
        except:
            pass

        data_stripped = data_in.strip()  # Removes spaces and \n

        for data_stripped in arduino:
            if data_stripped.startswith('b') and data_stripped.count(
                    ',') == 3:  # first char identifies where data is coming from; count commas to double-check incoming string
                field = data_stripped.split(',')  # split data to be put into 'boxes'
                bed_sen = field[0] + ',' + field[1] + ',' + field[2]  # We have 3 data sensor fields
                bed_sen_fill = True  # A flag to show that this if-statement has been completed

            if data_stripped.startswith('t') and data_stripped.count(',') == 3:
                field = data_stripped.split(',')
                table_sen = field[0] + ',' + field[1] + ',' + field[2]
                table_sen_fill = True

            if data_stripped.startswith('d') and data_stripped.count(',') == 3:
                field = data_stripped.split(',')
                door_sen = field[0] + ',' + field[1] + ',' + field[2]
                door_sen_fill = True

            try:
                if bed_sen_fill == True and table_sen_fill == True and door_sen_fill == True:
                    data_combi = bed_sen + ',' + table_sen + ',' + door_sen
                    break
            except:
                pass

        if data_combi:
            datasplit = data_combi.split(",")
            field1 = datasplit[0]
            field2 = datasplit[1]
            field3 = datasplit[2]
            field4 = datasplit[3]
            field5 = datasplit[4]
            field6 = datasplit[5]
            field7 = datasplit[6]
            field8 = datasplit[7]
            field9 = datasplit[8]

        with open('abs_testing.csv', 'ab') as csvfile:  # 'ab' to remove newline char after each print
            writer = csv.writer(csvfile)
            sensor_fields = [field1, field2, field3, field4, field5, field6, field7, field8, field9,
                             time.strftime("%H%M%S")]
            writer.writerow(sensor_fields)

        time.sleep(1)

def counting():
     while True:
         sum = 3 + 2
         sum2 = sum*8
         print sum2
         time.sleep(0.2)

def on_light():
    strin = '1'
    arduino.write(strin.encode())
    print "Confirm ON"

def off_light():
    strin = '0'
    arduino.write(strin.encode())
    print "Confirm OFF"

# now threading1 runs regardless of user input
threading1 = threading.Thread(target = acquire_data)
threading2 = threading.Thread(target = counting)
threading1.daemon = False # May remove later. Unsure at the moment.
threading2.daemon = False # May remove later. Unsure at the moment.
threading1.start()
threading2.start()

while True:
    if raw_input() == 't':
        on_light()
        print "ON"
    if raw_input() == 'r':
        off_light()
        print "OFF"

    time.sleep(1)

这里的多线程是通过一个查找 8*5 的愚蠢操作来实现的,因为稍后,这将扩展为一个实时机器学习函数,该函数决定何时打开/关闭灯。 raw_input() 函数证明数据可以传递回 Arduino 传感器节点。

非常感谢您的帮助! :)

【问题讨论】:

  • 在你调用writer.writerow()之后马上试试writer.sync()(我不知道API中是否有这个func调用,但你可以尝试找到类似的),看看是否问题仍然存在。也许它可能是带有缓冲 IO 的东西。
  • @phyloflash 非常感谢您的建议! ^^我会阅读更多关于该功能的内容,然后尝试一下~ ^^

标签: python arduino xbee


【解决方案1】:

关于可能改进的一些想法:

  • 在 Arduino 上将Serial() 波特率提高到 115200。
  • 在 Arduino 上将 XBee 波特率提高到 115200(或使用软件串行尽可能高)。
  • 在运行 Python 的 PC 上将波特率提高到 115200(它绝对可以处理该速率,并且每个设备上的速率不需要匹配)。
  • 删除或减少sleep(1) 中的acquire_data()
  • 检查终端设备上的睡眠配置。他们有可能一次睡几秒钟吗?您能否将它们配置为路由器以消除可能导致延迟的原因?
  • 更改您的发送代码以创建单个字符串(见下文),然后发送。然后您可以轻松地将其先发送到 XBee,然后分别发送到串行输出。如果Serial() 接口没有被缓冲,您可能会将您的XBee 数据包计时(请参阅ATRO 设置)并将您的响应作为多个数据包发送(效率较低)。尝试将 ATRO 从默认值 3 增加到 20,看看是否有帮助。
  • 现在您每秒发送一次。如果您更频繁地检查 I/O 并且仅在输入发生变化时(或者距离上次传输 5 秒)才发送响应,该怎么办?

您每秒发送一次,因此平均而言,您将在 I/O 更改后 0.5 秒发送一次。您可以在 Arduino 上添加一些计时代码,以打印出组装和发送数据所需的毫秒数(这可能会导致一些延迟)。或者试试这个替换代码:

char buffer[16];

pirValue = digitalRead(pirPin);
digitalWrite(LEDPirPIN, pinValue);

sprintf(buffer, "d%u,%u,%u,\n",
    digitalRead(IR) == LOW,
    pirValue == HIGH,
    digitalRead(lightLED) == LOW);
xbee.print(buffer);
Serial.print(buffer);

【讨论】:

  • 非常感谢您的详细解释。请问,关于将 XBee 配置为路由器的部分,因为我目前使用 AT 模式,其中一个 Xbee 作为协调器,另外 3 个 Xbee 作为终端设备,这算作配置为路由器吗?谢谢你。 (:让我继续消化您的回答,因为我是新手。非常感谢您的建议。(:
  • 我在 Xbee 设备(无论协调器或终端设备)上禁用了睡眠,并在我的 Arduino 上实现了 1 秒延迟(通过 delay(1000) 和其中一个,通过 millis()功能)。使用 Python 上的 time.sleep(1),以便 Python 和 Arduinos 中的时间延迟可以同步。
猜你喜欢
  • 2016-05-06
  • 1970-01-01
  • 1970-01-01
  • 2021-11-03
  • 2018-03-01
  • 2018-04-10
  • 2018-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多