最近在做一个小项目,希望实现的功能是:

在PC端跑deepsort等目标识别代码,然后返回目标在视频中的坐标(x,y)。将检测量(x,y)传入PID中得到执行量(do_x,do_y)。这个时候我们遇到了一个问题,如何使用python代码调用串口,并且正确传递(do_x,do_y)给单片机(arduino)。

查了很多资料,发现要么就是只传递单个数字字符串,要么代码不适用arduino端。我写了一个简单的算法,具体原理是:

python端代码

  1. 获得do_x,do_y(代码中变量名为x,y)。将两个int型变量转换为字符串str_x,str_y

python中的传递函数write(),传递的是字节流。因此我们先要将int数字转换为字符串。之后,通过encode()转换为字节流

x_str = str(x)
y_str = str(y)
  1. 构造传递字符串,设置头和尾
    为了保证传递的x和y是一组的,我设置了传递数据的头和尾:
start = "b"
end = "e"
x = 1500
x_str = str(x)
y = 1300
y_str = str(y)
dot = ","
str1 = start + x_str + dot + y_str + end

此时str1就是我们需要传递的变量

  1. 完整代码
    代码效果是不断传递(x = 1500,y = 1300)和(x = 1300,y = 1500)。最终让舵机循环往复的在两个角度位置运动
# coding:utf-8
import serial.tools.list_ports
import time
import struct

plist = list(serial.tools.list_ports.comports())# plist 包含的是还有串口一维数组 comports()是返回一串串口对象,这些对象包含三个变量:port name, human readable description and a hardware ID

if len(plist) <= 0: # 在我的电脑上 len(plist)是4
        print("没有发现端口!")
else:
        plist_0 = list(plist[4])# 定义plist[4]三个变量的lsit
        serialName = plist_0[0]# 取出其中的端口名
        serialFd = serial.Serial(serialName, 9600, timeout=60)# timeout单位是秒,设置后,如果没有设置回读的判断条件,就会一直等到timeout后打出返回结果。
        print("可用端口名>>>", serialFd.name)
while 1:
        start = "b"
        end = "e"
        x = 1500
        x_str = str(x)
        y = 1300
        y_str = str(y)
        dot = ","
        str1 = start + x_str + dot + y_str + end
        serialFd.write(str1.encode())# encode()完成的是对原字符串进行编码
        time.sleep(0.5)

        x = 1300
        x_str = str(x)
        y = 1500
        y_str = str(y)
        str1 = start + x_str + dot + y_str + end
        serialFd.write(str1.encode())# encode()完成的是对原字符串进行编码
        time.sleep(0.5)

arduino端代码

/*******************************************************************************             
  * @库文件: <Servo.h>
  * @功能:   实现串口接收500-2500PWM值实现舵机控制
 ******************************************************************************/
/*----------------------------------------------------------------------------
    相关库函数:
    1.servo类成员函数
    attach()              设定舵机的接口,只有9或10接口可利用。
    write()               用于设定舵机旋转角度的语句,可设定的角度范围是0°到180°。
    writeMicroseconds()  用于设定舵机PWM值的语句,直接用微秒作为参数。
    read()                用于读取舵机角度的语句,可理解为读取最后一条write()命令中的值。
    attached()            判断舵机参数是否已发送到舵机所在接口。
    detach()              使舵机与其接口分离,该接口(9或10)可继续被用作PWM接口。12
  ----------------------------------------------------------------------------*/
#include <Servo.h>                // 声明调用Servo.h库
#include <SoftwareSerial.h>//引用串口
Servo myservo_y;                    //竖直方向舵机类
Servo myservo_x;                     //水平方向舵机类
String inStringx = "";             //声明一个字符串
String inStringy = "";             //声明一个字符串
String comdata = "";               //声明一个字符串
#define  PIN_x  7 //           //宏定义舵机控制引脚
#define  PIN_y  A1 //           //宏定义舵机控制引脚
int x = 0,y = 0;

void receive(){
  int tmp = 0;//用于记录逗号所在的位置
  int j = 0;//用于记录是x数据还是y数据。0是x数据,1是y数据
  // 先将一次通信中的字节流数据读入,并将其拼接成字符串
  // comdata的头是字符'b',尾是字符'e'。
  while(Serial.available()>0)
  {
    comdata += char(Serial.read());
    delay(2);
    if(comdata[comdata.length()-1] == 'e')break;
  }
  
  Serial.println(comdata);
  // 对读入的数据comdata进行翻译,获取包含的x和y数据  
  if(comdata[0] == 'b')
  {
    for(int i = 1; i<comdata.length();i++)
    {
      if(comdata[i]==',')
      {
        tmp = i;
        j++;
        continue;
      }
      else if(j==0)
      {
        
        inStringx += comdata[i];
      }
      else
      {
        if(i==comdata.length()-1)
          continue; // 此时到达最后一位,也就是e
        else
          inStringy += comdata[i];
      }
    }
  }
  j = 0;
  tmp = 0;
  comdata = "";
  // 将获得包含有数据的字符串 inStringx 和 inStringy 转换成整型变量。
  if(inStringx.length()>0 && inStringy.length()>0)
  {
    x = inStringx.toInt();
    Serial.println(inStringx);
    y = inStringy.toInt();
    Serial.println(inStringy);
    myservo_x.writeMicroseconds(x); // 控制水平方向舵机运动
    delay(2);
    myservo_y.writeMicroseconds(y); // 控制垂直方向舵机运动
    delay(2);
    inStringx = "";
    inStringy = "";

  }

}

void setup(){ 
    myservo_x.attach(PIN_x);    // 将引脚与声明的舵机对象连接起来
    myservo_y.attach(PIN_y);    // 将引脚与声明的舵机对象连接起来
    Serial.begin(9600);        //初始化波特率为9600
    myservo_x.writeMicroseconds(1500); // 初始化舵机位置
    myservo_y.writeMicroseconds(1500); // 初始化舵机位置

} 

void loop(){
  while(Serial.available())
  {
    receive();
  }
}