使用串行通信在 python 和 arduino 之间进行同步

Posted

技术标签:

【中文标题】使用串行通信在 python 和 arduino 之间进行同步【英文标题】:Synchronization between python and arduino using serial communication 【发布时间】:2016-11-23 21:39:36 【问题描述】:

我有一个应用程序需要:

    使用 arduino 将伺服电机移动到某个位置并停在该位置 让由 python 控制的相机在该位置获取图像 获取图像后,舵机应移动到对称位置

序列重复N次

所以我尝试使用串行通信来同步 arduino 和 python 代码。在 arduino 方面,当伺服器到达某个位置时,它使用串行通信向 python 代码发送一个字符串。该字符串是“Cross”或“Co”,具体取决于到达的位置。 arduino 应等待 python 代码通过串行通信发送字符串“Ok”(图像采集后)。收到此字符串后,arduino 应启动伺服使其移动到另一个位置。

在 python 代码方面,我读取串行数据并根据接收到的字符串(Cross 或 Co):

    定义了一个字符串名称 使用相机获取图像 图像已保存或附加到列表中 字符串“Ok”被发送到arduino。

代码附在下面。

问题是我无法正确同步伺服位置和图像采集。伺服器只是在两个位置之间来回运行,似乎没有从串行通信中读取任何字符串。然后它永远不会真正停止到一个位置。然而,arduino 代码确实将“Cross”和“Co”发送到 python 代码,并且 python 代码确实设法读取它们并获取和保存图像,但通常使用错误的名称。让 arduino 代码在每个位置等待足够长的时间并不是解决方案,因为我需要适当的图像采集频率。 所以我想知道同步这两个代码的最佳方法是什么,并确保我的图像的正确名称将对应伺服的正确位置?

提前感谢您提供任何链接或想法。

格雷格

arduino 代码 `

#include <Servo.h>

//servo
Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards
int pos = 0;    // variable to store the servo position

//camera
const int CameraPin =  53;      // the number of the camera trigg
int CameraState = LOW;             // ledState used to set the LED
const int ledPin =  13;      // the number of the LED pin
String Str = "c";

void setup() 
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
  // set the digital LED pin as output:
  pinMode(CameraPin, OUTPUT);
  // set the digital camera pin as output:
  pinMode(ledPin, OUTPUT);
  //serial communication 
  Serial.begin(9600);
  Serial.println("Ready");
      
void loop() 

Serial.flush();
// go to Co position and wait
ServoCo(15); // go to Co position
Serial.println("Co"); //send signal Co to python
while(!Serial.available())  // wait for python to send data acquired
while ((Serial.available()<2)) // Test on the length of the serial string   
 
  delay(1);
  String Str = Serial.readStringUntil('\n');
  Serial.println(Str);


// go to cross position and wait
ServoCross(15); // go to Cross position
Serial.println("Cross");
while(!Serial.available()) 
  while ((Serial.available()<2))

  delay(1);
  String Str = Serial.readStringUntil('\n');
  Serial.println(Str);


delay(100);


void ServoCross(int ServoDelay)

  for (pos = 105; pos >= 75; pos -= 1)  // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(ServoDelay);  
  

  void ServoCo(int ServoDelay)

  for (pos = 75; pos <= 105; pos += 1) 
   // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(ServoDelay);  
  `

Python 代码:

from time import sleep
import serial
import scipy
ser = serial.Serial('COM10', 9600) # Establish the connection on a specific port

Nb_total_image = 100
imagergb_cross = np.zeros((480,640))
imagergb_co = np.zeros((480,640))
counter_image = 0;

ser.write('start')
while counter_image<Nb_total_image:
    ser.flush()
    ReadArduino = ser.readline().split('\r')[0] # Read the newest output from the Arduino

    print(ReadArduino)
    if ReadArduino == 'Cross':
        nameImage = 'CrossImage' + str(counterImageCross)
        cam.reset_frame_ready()                 # reset frame ready flag
        # send hardware trigger OR call cam.send_trigger() here
        cam.send_trigger()
        cam.wait_til_frame_ready(1000)              # wait for frame ready due to trigger        
        imageRawcross = cam.get_image_data()
        ser.write("Ok\n")

    else:
        nameImage = 'CoImage' + str(counterImageCo)
        cam.reset_frame_ready()                 # reset frame ready flag
        # send hardware trigger OR call cam.send_trigger() here
        cam.send_trigger()
        cam.wait_til_frame_ready(1000)              # wait for frame ready due to trigger                
        ImageRawCo = cam.get_image_data()
        ser.write("Ok\n")
    imagergb = imageRawCo-imageRawcross   
    counter_image = counter_image + 1

【问题讨论】:

Arduino 是否将收到的Ok 发回? Serial.available()&lt;2 只有在缓冲区中有一个字节时才会为真。 Ok\n 是三个字节。而且你永远不会通过读取缓冲区来清除它。 是的,我希望代码在只有 2 个字节的情况下在 while 循环中等待,并在有 3 个字节时继续执行(如“Ok\n”)。当您说清除缓冲区时,您的意思是刷新它吗? 您的 Arduino loop() 函数的架构不符合该函数的定期调用。注意:似乎开括号的数量不等于该函数中的右括号。 Arduino源代码编译没有错误?如果不是,则在 ServoCo() 中缺少最后一个右括号。 【参考方案1】:

在您的loop() 函数中,您已经实现了您的软件,因为该函数将在控制台软件中作为main() 执行一次。

由于 Arduino(C 语言)必须通过串行链路与您的计算机(Python 语言)进行通信,因此一种有效的解决方案是使用 状态机 原则。

第 1 步 - 定义所需的状态列表以及要达到的位置数。

一个简单的enum e_State 允许定义列表。

typedef enum e_State 
    STATE_START = 0, // first state to initialize
    STATE_MOVE_POS,  // moving Servo to the selected position
    STATE_SEND_CMD,  // sending message when position reached
    STATE_WAIT_ACK   // waiting acknowledge from message
 eState;

对于要到达的 2 个位置,使用 enum e_Pos

typedef enum e_Pos 
    FIRST_POS = 0,
    SECOND_POS
 ePos;

第 2 步 - 定义起始参数

loop() 调用之间存储持久数据,static 变量 使用:

static eState LoopState = STATE_START; // to store the current state
static ePos ServoPos = FIRST_POS;  // to store the selected position

要暂时存储下一个状态,请添加eState NextState

loop()函数的入口处,NextState = LoopState;到 默认保持相同的状态。

第 3 步 - 定义状态机算法。

void loop() 

static eState LoopState = STATE_START; // to store the current state
static ePos ServoPos = FIRST_POS;  // to store the selected position

    eState NextState = LoopState;

    switch (LoopState) 
    case STATE_START:
        ServoPos = FIRST_POS;
        NextState = STATE_MOVE_POS;
        break;
    case STATE_MOVE_POS:
        NextState = STATE_SEND_CMD;
        break;
    case STATE_SEND_CMD:
        NextState = STATE_WAIT_ACK;
        break;
    case STATE_WAIT_ACK:
        // NextState = STATE_MOVE_POS;
        break;
    default:
        // when undefined state, restart
        NextState = STATE_START;
    
    // define the state for the next loop
    LoopState = NextState;

第 4 步 - 使用选定的 ServoPos 管理 STATE_MOVE_POS 的操作。

switch (ServoPos) 
case FIRST_POS:
    ServoCo(15); // go to 'Co' position
    break;
case SECOND_POS:
    ServoCross(15); // go to 'Cross' position
    break;
;
NextState = STATE_SEND_CMD; // when reached, send serial message

第 5 步 - 根据到达的ServoPos 管理STATE_SEND_CMD 的操作。

Serial.flush();   // clear the serial buffer
if (ServoPos == FIRST_POS) 
    Serial.println("Co"); //send signal 'Co' to python

else 
    Serial.println("Cross"); //send signal 'Cross' to python

NextState = STATE_WAIT_ACK;

第 6 步 - 通过查找 “OK” 确认来管理 STATE_WAIT_ACK 的操作。

(可选)添加比较 if (Str == "OK") 以确保 计算机回答正确。

if (Serial.available())  // no state change while no acknowledge
    String Str = Serial.readStringUntil('\n');
    Serial.println(Str);
    if (Str == "OK") 
        // ack is OK, select the next position and continue
        ServoPos = (ServoPos == FIRST_POS)?(SECOND_POS):(FIRST_POS);
        NextState = STATE_MOVE_POS;
    
    else 
        // ack is KO, restart from first position
        NextState = STATE_START;
    

【讨论】:

以上是关于使用串行通信在 python 和 arduino 之间进行同步的主要内容,如果未能解决你的问题,请参考以下文章

Arduino和Python之间的串行通信发送/接收数据

Arduino 和 Python (3.x) 之间的 Serial.read() 和 Struct.pack / 串行通信问题

串行通信后Arduino一直在重启。

arduino 和 Visual Studio C++,2 路串行通信

Raspberry Pi 和 Arduino 之间的简单 2 路串行通信

从 php 到 arduino 的串行通信