使用串行通信在 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()<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 (3.x) 之间的 Serial.read() 和 Struct.pack / 串行通信问题
arduino 和 Visual Studio C++,2 路串行通信