最近在做一个小项目,希望实现的功能是:
在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();
}
}