最近在做一个小项目,希望实现的功能是:
在PC端跑deepsort等目标识别代码,然后返回目标在视频中的坐标(x,y)。将检测量(x,y)传入PID中得到执行量(do_x,do_y)。这个时候我们遇到了一个问题,如何使用python代码调用串口,并且正确传递(do_x,do_y)给单片机(arduino)。
查了很多资料,发现要么就是只传递单个数字字符串,要么代码不适用arduino端。我写了一个简单的算法,具体原理是:
python端代码
-
获得do_x,do_y(代码中变量名为x,y)。将两个int型变量转换为字符串str_x,str_y
python中的传递函数write(),传递的是字节流。因此我们先要将int数字转换为字符串。之后,通过encode()转换为字节流
x_str = str(x) y_str = str(y)
-
构造传递字符串,设置头和尾
为了保证传递的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就是我们需要传递的变量
-
完整代码
代码效果是不断传递(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(); } }