问题描述
|
我正在基于Python的GUI界面上与运行Python的机器人以及作为电机控制器和传感器控制器的Arduino Mega进行交互。
最初,我打算使用远程桌面从机器人加载GUI。事实证明,由于使用远程桌面,这非常慢。我认为服务器和客户端会更好。
我有一个在我的Arduino上运行的草图,它可以捕获电动机命令并执行它们。它还等待“ Ping”命令通过,这时应该检查超声波传感器在三个不同位置,然后将此信息写回到服务器,服务器应捕获此数据并将其传递给客户端GUI。我已经将其全部工作了,但似乎无法将数据从服务器返回到客户端。我曾以为,简单的\“ client.recv()\”可以完成此操作,但事实并非如此。
如果我不知道到底要返回多少数据,我该如何接收这些数据?
Arduino将数据发送为\“ dist1,dist2,dist3 \\ n \”。
这是我的Arduino代码:
#include <LiquidCrystal.h>
#include <Ping.h>
#include <Servo.h>
// Parallax Ping Unit for distance sensing.
Ping sonic(22);
// Setup LCD pins.
LiquidCrystal lcd(7,8,9,10,11,12);
// Servo for Ping unit sweeping.
Servo PingServo;
// Setup Ping distance setting variables.
int pingdisCent;
int pingdisLeft;
int pingdisRight;
// Variable to keep commands in.
char MsgRcvd;
// Motor setup information.
int LF[] = {23,24};
int LR[] = {25,26};
int RF[] = {27,28};
int RR[] = {29,30};
// Set Debugging here
// 1 - Debug on - Motors don\'t turn when commands are sent.
// 0 - Debug off - Motors turn when commands are sent.
int debug = 1;
//Variables for speed
int SpdPin = 22;
int Speed = 255;
void setup()
{
Serial.begin(9600); // start serial communications
// Setup motors for output.
int i;
for(i = 0; i < 2; i++){
pinMode(LF[i],OUTPUT);
pinMode(LR[i],OUTPUT);
pinMode(RF[i],OUTPUT);
pinMode(RR[i],OUTPUT);
}
// Setup servo to sweep.
PingServo.attach(6);
PingServo.write(90);
// Set up the LCD\'s number of rows and columns:
lcd.begin(16,2);
// Print a message to the LCD.
lcd.print(\"Waiting...\");
// Setup speed pin.
pinMode(SpdPin,OUTPUT);
}
void loop()
{
if (Serial.available() > 0) //Check to see if a command is available.
{
MsgRcvd = Serial.read(); // If a command is there,see what it is.
switch (MsgRcvd)
{
case \'0\':
Stop();
break;
case \'1\':
MoveForward();
break;
case \'2\':
MoveLeft();
break;
case \'3\':
MoveRight();
break;
case \'4\':
MoveBackward();
break;
case \'~\':
active_ir();
break;
case \'M\': // Check to see if we have a connection from the GUI - if so spit out information to the LCD.
lcd.clear();
lcd.print(\"Connected\");
lcd.setCursor(0,1);
lcd.print(\"waiting..\");
break;
case \'D\':
lcd.setCursor(0,1);
lcd.print(\"disconnected\"); // Client disconnected - spit out a disconnect to the LCD.
break;
}
}
delay(100);
}
// ===================================
// ===== Ping Ultrasonic =====
// ===================================
void active_ir()
{
// Read to the right.
PingServo.write(30);
delay(300);
pingdisRight = sonic.inch();
delay(500);
// Read to the front.
PingServo.write(90);
delay(300);
pingdisCent = sonic.inch();
delay(500);
// Read to the left.
PingServo.write(150);
delay(300);
pingdisLeft = sonic.inch();
delay(500);
Serial.print(pingdisLeft);
Serial.print(\',\');
Serial.print(pingdisCent);
Serial.print(\',\');
Serial.println(pingdisRight);
return;
}
// ==========================================
// ====== MOTOR CONTROL =========
// ==========================================
void MoveForward()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(\"Forward\");
if (debug == 0){
digitalWrite(LF[0],HIGH);
digitalWrite(LF[1],LOW);
digitalWrite(LR[0],HIGH);
digitalWrite(LR[1],LOW);
digitalWrite(RF[0],HIGH);
digitalWrite(RF[1],LOW);
digitalWrite(RR[0],HIGH);
digitalWrite(RR[1],LOW);
}
}
void MoveBackward()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(\"Reverse\");
if (debug == 0){
analogWrite(SpdPin,Speed);
digitalWrite(LF[0],LOW);
digitalWrite(LF[1],HIGH);
digitalWrite(LR[0],LOW);
digitalWrite(LR[1],HIGH);
digitalWrite(RF[0],LOW);
digitalWrite(RF[1],HIGH);
digitalWrite(RR[0],LOW);
digitalWrite(RR[1],HIGH);
}
}
void MoveLeft()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(\"Left\");
if (debug == 0){
analogWrite(SpdPin,LOW);
}
}
void MoveRight()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(\"Right\");
if (debug == 0) {
analogWrite(SpdPin,HIGH);
}
}
void Stop()
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(\"Stopping\");
if (debug == 0){
digitalWrite(LF[0],LOW);
}
}
这是我的Python服务器代码:
import serial
import socket
Serv = socket.socket(socket.AF_INET,socket.soCK_STREAM)
Serv.bind((\'\',9000))
Serv.listen(1)
print \"Listening on TCP 9000\"
motor = serial.Serial(\'/dev/ttyUSB0\',9600,timeout=1)
print \"Connected to Motor Controller: /dev/ttyUSB0\"
while(1):
print \"Waiting For Connection...\"
connection,addr = Serv.accept()
connection.setblocking(0)
print \"Connected by\",addr[0]
while(1):
try:
Servdata = connection.recv(1)
break
except:
pass
if (Servdata == \'M\'):
print \"Entering Manual Mode\"
motor.write(Servdata)
while(Servdata != \'X\'):
Servdata = \'9\'
try:
Servdata = connection.recv(1)
except:
pass
if Servdata == \'X\':
print \"Exiting\"
break
if Servdata == \'0\':
print \"Stopping\"
motor.write(Servdata)
if Servdata == \'1\':
print \"Forward\"
motor.write(Servdata)
if Servdata == \'2\':
print \"Left\"
motor.write(Servdata)
if Servdata == \'3\':
print \"Right\"
motor.write(Servdata)
if Servdata == \'4\':
motor.write(Servdata)
print \"Backwards\"
if Servdata == \'~\':
motor.write(Servdata)
retval = motor.readline()
Serv.send(retval)
else:
pass
motor.write(\'0\')
connection.close()
print addr[0],\"Closed Manual Mode\"
最后但并非最不重要的一点是,客户端GUI代码(这也是我认为问题所在的地方...):
from socket import *
from PythonCard import model
HOST = \'\'
PORT = 9000
ADDR = (HOST,PORT)
BUFSIZE = 4096
Client = socket (AF_INET,SOCK_STREAM)
Client.connect((ADDR))
Client.send(\'M\')
class MainWindow(model.Background):
def on_SetSpdBtn_mouseClick(self,event):
spd = self.components.SpdSpn.value
def on_FwdBtn_mouseClick(self,event):
spd = self.components.SpdSpn.value
Client.send(\'1\')
def on_LftBtn_mouseClick(self,event):
spd = self.components.SpdSpn.value
Client.send(\'2\')
def on_RitBtn_mouseClick(self,event):
spd = self.components.SpdSpn.value
Client.send(\'3\')
def on_RevBtn_mouseClick(self,event):
spd = self.components.SpdSpn.value
Client.send(\'4\')
def on_StpBtn_mouseClick(self,event):
spd = self.components.SpdSpn.value
Client.send(\'0\')
def on_GetPing_mouseClick(self,event):
Client.send(\'~\')
retval = Client.recv()
ping_data = retval.strip() # Strip out the newline,if you read an entire line.
split_data = ping_data.split(\',\')
L_Ping = split_data[0]
R_Ping = split_data[1]
self.components.PingLeft.text = str(L_Ping)
self.components.PingRight.text = str(R_Ping)
app = model.Application(MainWindow)
app.MainLoop()
解决方法
我想我在这段代码中发现了三个问题;第一个是浪费,第二个可能是您今天来这里的原因,第三个是您认为今天来这里的原因。 :)
忙碌中
此代码正忙于等待数据进入连接:
connection.setblocking(0)
print \"Connected by\",addr[0]
while(1):
try:
Servdata = connection.recv(1)
break
except:
pass
再次在这里:
while(Servdata != \'X\'):
Servdata = \'9\'
try:
Servdata = connection.recv(1)
except:
pass
# ...
else:
pass
如此疯狂地消耗CPU周期。希望您不要依靠电池供电。它也不会买任何东西。您最好将阻塞叫recv()
。等待下一个输入字节时让CPU进入睡眠状态。 (如果您实际上在对任何内容使用非阻塞,那么忙碌的等待会更有意义,但是您没有。如果您想限制服务器阻塞输入的时间,总会有settimeout()
。但是也不要盲目地使用它,因为仅从切换到阻止recv()
,此代码将受益最大。)
不向客户端发送数据
if Servdata == \'~\':
motor.write(Servdata)
retval = motor.readline()
Serv.send(retval)
我认为此代码块尚未执行:)Serv
未连接任何东西,它是一个监听套接字。您在这里的意思是10英镑。我在服务器中找不到任何其他行,这些行实际上会将数据发送到客户端,因此大概应该是这样。
一次全部
客户端中的这段代码有些脆弱,但可能永远不会中断:
def on_GetPing_mouseClick(self,event):
Client.send(\'~\')
retval = Client.recv()
ping_data = retval.strip() # strip out the newline,if you read an entire line
split_data = ping_data.split(\',\')
L_Ping = split_data[0]
R_Ping = split_data[1]
self.components.PingLeft.text = str(L_Ping)
self.components.PingRight.text = str(R_Ping)
该代码假定“ 5”调用将恰好返回一个协议消息。 TCP流无法正常工作,对等方可以随意发送任意大小的传出数据。 (TCP / IP堆栈始终将多个应用程序级别的消息组合到一个TCP数据包中。它们还会发送小于请求的数据包,以避免分段。)
更安全的方法是使用从远程对等方接收的内容填充队列,然后解析该队列以获取命令/消息。您可能在队列中找到十个命令,可能只找到一部分命令-但是您的代码需要准备好将部分消息推送到队列中,并在可用时使用队列中的完整消息。
这是一些额外的工作,但是在不太理想的情况下安全操作是必需的。您可能永远不会在LAN上遇到问题,但是当您进行无线连接或通过较大的网络路由时会遇到麻烦。