用于远程控制PiBot的SocketServer(python)

用于远程控制PiBot的SocketServer(python),python,raspberry-pi,robot,socketserver,Python,Raspberry Pi,Robot,Socketserver,这是我的第一个问题!(尽管使用该网站可以找到我所遇到的编程问题的大部分答案) 我已经创建了一个PiBotController,我计划在我的笔记本电脑上运行,我想把控制(来自箭头键的输入)传递给控制我的机器人的raspberry pi。现在,硬件方面不是问题所在,我已经创建了一个响应箭头键输入的程序,我可以通过ssh连接控制pi上的电机 在网上搜索时,我发现了以下使用socketserver的基本服务器和客户端代码,我可以通过它发送一个简单的字符串 服务器: import socketserver

这是我的第一个问题!(尽管使用该网站可以找到我所遇到的编程问题的大部分答案)

我已经创建了一个PiBotController,我计划在我的笔记本电脑上运行,我想把控制(来自箭头键的输入)传递给控制我的机器人的raspberry pi。现在,硬件方面不是问题所在,我已经创建了一个响应箭头键输入的程序,我可以通过ssh连接控制pi上的电机

在网上搜索时,我发现了以下使用socketserver的基本服务器和客户端代码,我可以通过它发送一个简单的字符串

服务器:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The RequestHandler class for our server.

It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

# Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)

# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
客户:

import socket
import sys

HOST, PORT = "192.168.2.12", 9999
data = "this here data wont send!! "


# Create a socket (SOCK_STREAM means a TCP socket)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    # Connect to server and send data
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data + "\n", "utf-8"))

    # Receive data from the server and shut down
    received = str(sock.recv(1024), "utf-8")
finally:
    sock.close()

print("Sent:     {}".format(data))
print("Received: {}".format(received))
现在,它可以很好地工作,并在我的Raspberry Pi(服务器)和笔记本电脑(客户端)上打印结果,但是我已经多次尝试将其组合成一个功能,与我的控制器中的“按键”和“释放”一起激活

皮博特控制器

#import the tkinter module for the GUI and input control
try:
    # for Python2
    import Tkinter as tk
    from Tkinter import *
except ImportError:
    # for Python3
    import tkinter as tk
    from tkinter import *

import socket
import sys

#variables
Drive = 'idle'
Steering = 'idle'




#setting up the functions to deal with key presses
def KeyUp(event):
    Drive = 'forward'
    drivelabel.set(Drive)
    labeldown.grid_remove()
    labelup.grid(row=2, column=2)
def KeyDown(event):
    Drive = 'reverse'
    drivelabel.set(Drive)
    labelup.grid_remove()
    labeldown.grid(row=4, column=2)
def KeyLeft(event):
    Steering = 'left'
    steeringlabel.set(Steering)
    labelright.grid_remove()
    labelleft.grid(row=3, column=1)
def KeyRight(event):
    Steering = 'right'
    steeringlabel.set(Steering)
    labelleft.grid_remove()
    labelright.grid(row=3, column=3)
def key(event):
    if event.keysym == 'Escape':
        root.destroy()

#setting up the functions to deal with key releases
def KeyReleaseUp(event):
    Drive = 'idle'
    drivelabel.set(Drive)
    labelup.grid_remove()
def KeyReleaseDown(event):
    Drive = 'idle'
    drivelabel.set(Drive)
    labeldown.grid_remove()
def KeyReleaseLeft(event):
    Steering = 'idle'
    steeringlabel.set(Steering)
    labelleft.grid_remove()
def KeyReleaseRight(event):
    Steering = 'idle'
    steeringlabel.set(Steering)
    labelright.grid_remove()

#connection functions
def AttemptConnection():
    connectionmessagetempvar = connectionmessagevar.get()
    connectionmessagevar.set(connectionmessagetempvar + "\n" + "Attempting to        connect...") 

def transmit(event):
    HOST, PORT = "192.168.2.12", 9999
    data = "this here data wont send!! "


    # Create a socket (SOCK_STREAM means a TCP socket)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
         # Connect to server and send data
         sock.connect((HOST, PORT))
         sock.sendall(bytes(data + "\n", "utf-8"))

         # Receive data from the server and shut down
         received = str(sock.recv(1024), "utf-8")
    finally:
         sock.close()

    print("Sent:     {}".format(data))
    print("Received: {}".format(received))





#setting up GUI window        
root = tk.Tk()
root.minsize(300,140)
root.maxsize(300,140)
root.title('PiBot Control Centre')
root.grid_columnconfigure(0, minsize=50)
root.grid_columnconfigure(1, minsize=35)
root.grid_columnconfigure(2, minsize=35)
root.grid_columnconfigure(3, minsize=35)
root.grid_rowconfigure(2, minsize=35)
root.grid_rowconfigure(3, minsize=35)
root.grid_rowconfigure(4, minsize=35)
root.configure(background='white')
root.option_add("*background", "white")




#set up the labels to display the current drive states
drivelabel = StringVar()
Label(root, textvariable=drivelabel).grid(row=0, column=1, columnspan=2)
steeringlabel = StringVar()
Label(root, textvariable=steeringlabel).grid(row=1, column=1, columnspan=2)
Label(root, text="Drive: ").grid(row=0, column=0, columnspan=1)
Label(root, text="Steering: ").grid(row=1, column=0, columnspan=1)

#set up the buttons and message for connecting etc..
messages=tk.Frame(root, width=150, height=100)
messages.grid(row=1,column=4, columnspan=2, rowspan=4)


connectionbutton = Button(root, text="Connect", command=AttemptConnection)
connectionbutton.grid(row=0, column=4)
connectionmessagevar = StringVar()
connectionmessage = Message(messages, textvariable=connectionmessagevar, width=100, )
connectionmessage.grid(row=1, column=1, rowspan=1, columnspan=1)
disconnectionbutton = Button(root, text="Disconnect")
disconnectionbutton.grid(row=0, column=5)







#pictures
photodown = PhotoImage(file="down.gif")
labeldown = Label(root, image=photodown)
labeldown.photodown = photodown
#labeldown.grid(row=4, column=1)

photoup = PhotoImage(file="up.gif")
labelup = Label(root, image=photoup)
labelup.photoup = photoup
#labelup.grid(row=2, column=1)

photoleft = PhotoImage(file="left.gif")
labelleft = Label(root, image=photoleft)
labelleft.photoleft = photoleft
#labelleft.grid(row=3, column=0)

photoright = PhotoImage(file="right.gif")
labelright = Label(root, image=photoright)
labelright.photoright = photoright
#labelright.grid(row=3, column=2)

photoupleft = PhotoImage(file="upleft.gif")
labelupleft = Label(root, image=photoupleft)
labelupleft.photoupleft = photoupleft
#labelupleft.grid(row=2, column=0)

photodownleft = PhotoImage(file="downleft.gif")
labeldownleft = Label(root, image=photodownleft)
labeldownleft.photodownleft = photodownleft
#labeldownleft.grid(row=4, column=0)

photoupright = PhotoImage(file="upright.gif")
labelupright = Label(root, image=photoupright)
labelupright.photoupright = photoupright
#labelupright.grid(row=2, column=2)

photodownright = PhotoImage(file="downright.gif")
labeldownright = Label(root, image=photodownright)
labeldownright.photodownright = photodownright
#labeldownright.grid(row=4, column=2)




#bind all key presses and releases to the root window
root.bind_all('<Key-Up>', KeyUp)
root.bind_all('<Key-Down>', KeyDown)
root.bind_all('<Key-Left>', KeyLeft)
root.bind_all('<Key-Right>', KeyRight)

root.bind_all('<KeyRelease-Up>', KeyReleaseUp)
root.bind_all('<KeyRelease-Down>', KeyReleaseDown)
root.bind_all('<KeyRelease-Left>', KeyReleaseLeft)
root.bind_all('<KeyRelease-Right>', KeyReleaseRight)

root.bind_all('<Key>', key)
root.bind_all('<Key>', transmit)




#set the labels to an initial state
steeringlabel.set('idle')
drivelabel.set('idle')
connectionmessagevar.set ('PiBotController Initiated')

#initiate the root window main loop
root.mainloop()

问题在于,当Tkinter捕捉到一个键事件时,它会首先触发更具体的绑定(例如“key Up”),而事件永远不会传递给更一般的绑定(“key”)。因此,当您按下“向上”键时,会调用KeyUp,但不会调用transmit

解决这个问题的一种方法是在所有回调函数(KeyUp、KeyDown等)中调用transmit()

例如,KeyUp将成为

def KeyUp(event):
    Drive = 'forward'
    drivelabel.set(Drive)
    labeldown.grid_remove()
    labelup.grid(row=2, column=2)
    transmit()
然后,您可以摆脱与“Key”的事件绑定

另一个选项是将“驱动”和“转向”设置为Tkinter.StringVar对象,然后使用“跟踪”绑定到写入事件,如下所示:

Drive = tk.StringVar()
Drive.set('idle')
Drive.trace('w', transmit)
请注意,
trace
向回调发送一组参数,因此您必须编辑
transmit
以接受它们

编辑

好的,我看到问题了——有三个

一,。当你写作时

Drive = 'forward'
DriveSend = drivelabel.get
SteeringSend = steeringlabel.get
在回调函数中,您在模块名称空间中设置变量
Drive
,在本地函数名称空间中设置
Drive
,因此模块名称空间
Drive
从不更改,因此当
传输
访问它时,它总是相同的

二,。在
传输中
,您可以编写

Drive = 'forward'
DriveSend = drivelabel.get
SteeringSend = steeringlabel.get
这是一个好主意,但您只是引用函数,而不是调用它们。你需要

DriveSend = drivelabel.get()
SteeringSend = steeringlabel.get()
三,。在
transmit
中,通过套接字发送的值是模块级变量
Drive
Steering
(它们不会根据问题1而改变),而不是
DriveSend
SteeringSend

解决方案:

我建议完全取消所有
Drive
Steering
变量,只使用
StringVars
“drivellabel
steeringlab”。因此,您的回调可能会变成:

def KeyUp(event):
#    Drive = 'forward'   (this doesn't actually do any harm, but to avoid confusion I'd just get rid of the Drive variables altogether)
    drivelabel.set('forward')
    labeldown.grid_remove()
    labelup.grid(row=2, column=2)
(对于其余的回调,依此类推),您的传输函数将变为

def transmit():
    HOST, PORT = "192.168.2.12", 9999
    DriveSend = drivelabel.get()        # Note the ()
    SteeringSend = steeringlabel.get()

    # Create a socket (SOCK_STREAM means a TCP socket)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        # Connect to server and send data
        sock.connect((HOST, PORT))
        sock.sendall(bytes(DriveSend + "\n", "utf-8"))    # Note Drive ==> DriveSend
        sock.sendall(bytes(SteeringSend + "\n", "utf-8")) # Note Steering ==> SteeringSend

        # Receive data from the server and shut down
        received = str(sock.recv(1024), "utf-8")
    finally:
        sock.close()

    print("Sent:     {}".format(SteeringSend))   # Note Steering ==> SteeringSend
    print("Sent:     {}".format(DriveSend))      # Note Drive ==> DriveSend
    print("Received: {}".format(received))
修改后的解决方案(来自OP):

由于使用了这种方法一段时间,我发现,由于按键被按下,每100毫秒不断地更改变量是很麻烦的,并且会导致在我(例如)正向前行驶时,电机控制的平稳性出现问题。为了解决这个问题,我在每个函数中使用了以下编辑

def KeyUp(event):
    if drivelabel.get() == "forward":
        pass
    else:
        drivelabel.set("forward")
        labeldown.grid_remove()
        labelup.grid(row=2, column=2)
        transmit()
        print (drivelabel.get())

该代码现在检查varibale是否已设置为Relevant(相关)方向,如果已设置,则不执行任何操作,否则将对其进行修改。打印行就在那里,我可以检查它是否正常工作,是否可以删除或注释掉。

这是一个非常简单的修复!现在来整理一些有用的东西:PGot它发送我的变量。。。我让它发送变量最初设置的值。我似乎无法让它发送更新的值,尽管事实上我有标签,可以使用我认为相同的方法进行良好更新:SSORY-很难帮助您诊断,除非我能看到您如何构建
传输
功能。如果您想使用新的
传输
功能在问题末尾添加一个编辑,我来看看。@DMason在我的答案中添加了一个编辑。希望这会有帮助。另外,关于:每次传输时打开和关闭插座-我不知道这样做与在整个机器人控制会话中打开一次插座的利弊。我想说,如果它工作正常,并且没有造成任何性能问题(比如控制响应缓慢?),请保留它。