使用串行通信

时间:2016-11-23 21:39:36

标签: python arduino serial-port synchronization

我有一个应用程序,我需要:

  1. 使用arduino将伺服电机移动到一个位置并停在该位置
  2. 让python控制的相机在该位置获取图像
  3. 当获取图像时,伺服应该移动到对称位置
  4. 序列重复N次

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

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

    1. 定义了字符串名称
    2. 使用相机获取图像
    3. 图像已保存或附加到列表
    4. 字符串“Ok”发送给arduino。
    5. 下面附有代码。

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

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

      Greg

      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
      

1 个答案:

答案 0 :(得分:3)

loop()功能中,您已实施了软件,因为该功能将在控制台软件中以main()执行一次。

因为 Arduino (C语言)必须通过串行链接与您的计算机进行通信(Python语言),所以一种有效的解决方案是使用状态机原则。 / p>

第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步 - 使用选定的STATE_MOVE_POS管理ServoPos的操作。

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步 - 根据到达的STATE_SEND_CMD管理ServoPos的操作。

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步 - 通过查找“确定”确认来管理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;
    }
}