前言
在机器人的运动控制过程中,关于多机通信控制,除了用ros外,还可以进行网络编程,这里就用的是UDP编程实现两台电脑间的通信
两台电脑信息
A电脑用于服务端,B电脑用于客户端
- A电脑:10.10.100.56
- B电脑:10.10.100.54
注:两台电脑必须在同一个局域网内
一、UDP简单样例
1.1 本地测试(服务端单向接收客户端数据)
server.py
服务端,用于接收客户端传来的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#-*- coding: utf-8 -*-
from socket import *
udp_server = socket(AF_INET, SOCK_DGRAM) # 创建UDP套接字(SOCK_DGRAM为UDP)
local_addr = ('127.0.0.1', 9999) #由于是本地,ip可以设置为127.0.0.1,并且自定义设置端口
udp_server.bind(local_addr) # 绑定ip和端口
while True:
client_data,addr = udp_server.recvfrom(1024) #1024表示本次接收的最大字节数
client_data = client_data.decode('utf-8') #对client传过来的数据进行解码
print(client_data) #client_data 为client传过来的数据信息
print(addr) #addr为client的ip和端口
if client_data == "stop":
break
udp_server.close() #关闭套接字client.py
客户端,用于发送数据给服务端
1
2
3
4
5
6
7
8
9
10
11
12
13#-*- coding: utf-8 -*-
from socket import *
udp_client = socket(AF_INET, SOCK_DGRAM) # 创建udp套接字(SOCK_DGRAM为UDP)
#设置服务端的IP地址和端口
dest_addr = ('127.0.0.1', 9999) # 注意 是元组,ip是字符串,端口是数字
while True:
send_data = input(">>") #待发送的数据,从键盘获取
udp_client.sendto(send_data.encode('utf-8'), dest_addr) # 发送数据到指定的电脑上的指定程序中
if send_data == "stop":
break
udp_client.close() #关闭套接字
1.2 两台电脑间通信(同时发送与接收)
server.py
使用A电脑作为服务端,服务端ip为10.10.100.56
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#-*- coding: utf-8 -*-
from socket import *
udp_server = socket(AF_INET, SOCK_DGRAM)
local_addr = ('10.10.100.56', 7996)
udp_server.bind(local_addr)
while True:
client_data,addr = udp_server.recvfrom(1024) #接收来自client的数据
client_data = client_data.decode('utf-8')
print(client_data)
print(addr)
udp_server.sendto(client_data,addr) #数据原样返回给客户端
if recv_result == "stop":
break
udp_server.close()
client.py
B电脑作为客户端,客户端ip地址为10.10.100.54
1 | #-*- coding: utf-8 -*- |
运行结果显示
1
2
3
4
5
6
7
8
9
10
11>>state
server data state
>>takeoff
server data takeoff
>>move
server data move
>>state
server data state
>>land
server data land
>>
二、UDP发送列表数据(json)
在第一步的样例中,UDP发送和接收的都是一串字符串数据,而有时候服务端返回的数据是一个列表信息,如坐标(x,y,z),此时用字符串信息返回就比较麻烦,需要在客户端和服务端都做一些相应的转化。因此,为了解决该问题,可以使用python的json库
2.1 python对象转换为json字符串(json.dumps函数)
下面的是自己的电脑与B电脑进行通信测试的,不是A与B之间的通信
将B电脑作为服务端:10.10.100.54,进行测试
- server.py
1 | #!/usr/bin/env python |
- client.py不变
结果显示
2.2 json字符串转换为python对象(json.loads函数)
这里将只给出核心部分
为什么要做json字符串转python对象?因为有时候从客户端发送过去的数据也是一个列表信息,如发送xyz的速度控制,要根据xyz值的不同进行相应的操作,此时就最好是传输python对象比较好
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import json
while True:
client_data,addr = udp_server.recvfrom(1024)
client_data = client_data.decode('utf-8')
client_data = json.loads(client_data)
print('client_message',client_data)
if client_data[0] == 'state':
quad_pos = ...
print('quad_pos',quad_pos)
json_string = json.dumps(quad_pos) #python对象转json字符串
udp_server.sendto(json_string,addr)
elif client_data[0] == 'takeoff':
...
elif client_data[0] == 'move' and len(client_data) == 5:
vx,vy,vz,t = int(client_data[1]),int(client_data[2]),int(client_data[3]),int(client_data[4])
...client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import json
while True:
send_data = input(">>")
msg = send_data.split(' ')
print('msg',msg)
json_string = json.dumps(msg) #转换成json字符串
udp_client.sendto(json_string, dest_addr)
if send_data == 'state':
server_data,addr = udp_client.recvfrom(1024)
server_data = server_data.decode("utf-8")
print('server data',server_data)
if send_data == "stop":
break结果显示:
客户端的输入输出显示
服务端的输出显示
三、UDP传输图像
用opencv来读取和显示,因此需要先安装opencv
1 | sudo proxychains4 pip install opencv-python |
3.1 Python获取本地摄像头并显示
1 | import cv2 |
3.2 UDP传输图片并显示(小尺寸图片)
如果图片太大,则下面的代码将会报错
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26# -*- coding: utf-8 -*-
import socket
import cv2
import numpy as np
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口:
s.bind(('10.10.100.54', 9999))
print('Bind UDP on 9999...')
while True:
# 接收数据:
data, addr = s.recvfrom(400000)
print('Received from %s:%s.' % addr)
#解码
nparr = np.fromstring(data, np.uint8)
#解码成图片numpy
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
cv2.imshow('result',img_decode)
cv2.waitKey()
reply = "get message!!!"
s.sendto(reply.encode('utf-8'), addr)
cv2.destroyAllWindows()client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# -*- coding: utf-8 -*-
import socket
import cv2
import numpy as np
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
img = cv2.imread('view.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()
# 发送数据:
s.sendto(data, ('10.10.100.54', 9999))
# 接收数据:
print(s.recv(1024).decode('utf-8'))
s.close()
3.3 UDP传输视频流信息
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34# -*- coding: utf-8 -*-
import cv2
import numpy
import socket
import struct
HOST='10.10.100.54'
PORT=9999
buffSize=65535
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建socket对象
server.bind((HOST,PORT))
print('now waiting for frames...')
while True:
data,address=server.recvfrom(buffSize) #先接收的是字节长度
if len(data)==1 and data[0]==1: #如果收到关闭消息则停止程序
server.close()
cv2.destroyAllWindows()
exit()
if len(data)!=4: #进行简单的校验,长度值是int类型,占四个字节
length=0
else:
length=struct.unpack('i',data)[0] #长度值
data,address=server.recvfrom(buffSize) #接收编码图像数据
if length!=len(data): #进行简单的校验
continue
data=numpy.array(bytearray(data)) #格式转换
imgdecode=cv2.imdecode(data,1) #解码
print('have received one frame')
cv2.imshow('frames',imgdecode) #窗口显示
if cv2.waitKey(1)==27: #按下“ESC”退出
break
server.close()
cv2.destroyAllWindows()client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# -*- coding: utf-8 -*-
import cv2
import numpy
import socket
import struct
HOST='10.10.100.54'
PORT=9999
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #socket对象
server.connect((HOST,PORT))
print('now starting to send frames...')
capture=cv2.VideoCapture(0) #VideoCapture对象,可获取摄像头设备的数据
try:
while True:
if capture.isOpened():
success,frame=capture.read()
#frame = cv2.imread('normal.jpg) 以图片形式发送
result,imgencode=cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,50]) #编码
server.sendall(struct.pack('i',imgencode.shape[0])) #发送编码后的字节长度,这个值不是固定的
server.sendall(imgencode) #发送视频帧数据
print('have sent one frame')
except Exception as e:
print(e)
server.sendall(struct.pack('c',1)) #发送关闭消息
capture.release()
server.close()
四、Arisim的UDP通信代码实例
4.1 服务端
connect_simulator.py
用于连接Airsim
1
2
3
4
5
6
7
8
9
10
11
12#!/usr/bin/env python
#-*- coding: utf-8 -*-
import airsim
class Connect(object):
def __init__(self):
self.client = airsim.MultirotorClient()
self.client.confirmConnection()
self.client.enableApiControl(True)
self.client.armDisarm(True)
msg_server.py
接收client传来的特定的消息,并根据适当的消息返回姿态数据和图像数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81#!/usr/bin/env python
#-*- coding: utf-8 -*-
# python library
from socket import *
import json
from PIL import Image
import numpy as np
import cv2
import threading
# airsim
from connect_simulator import Connect
import airsim
# udp
dest_ip = '10.10.100.56'
msg_port = 9999
# connect the airsim simulator
client = Connect().client
# image transform
def transform_input(responses, img_height, img_width):
img1d = np.array(responses[0].image_data_float, dtype=np.float)
img1d = np.array(np.clip(255 * 3 * img1d, 0, 255), dtype=np.uint8)
img2d = np.reshape(img1d, (responses[0].height, responses[0].width))
image = Image.fromarray(img2d)
image = np.array(image.resize((img_width, img_height)).convert('L'))
#cv2.imwrite('b.png', image)
# image = np.float32(image.reshape(1, img_height, img_width, 1))
# image /= 255.0
return image
def receive_msg(recv_msg):
while True:
client_data,addr = recv_msg.recvfrom(1024)
client_data = client_data.decode('utf-8')
client_data = json.loads(client_data)
print('client_message',client_data)
if client_data[0] == 'state':
quad_pos = client.getMultirotorState().kinematics_estimated.position
quad_pos = [quad_pos.x_val,quad_pos.y_val,quad_pos.z_val]
print('quad_pos',quad_pos)
json_string = json.dumps(quad_pos)
recv_msg.sendto(json_string,addr)
elif client_data[0] == 'takeoff':
client.takeoffAsync().join()
elif client_data[0] == 'land':
client.landAsync().join()
elif client_data[0] == 'move' and len(client_data) == 5:
vx,vy,vz,t = int(client_data[1]),int(client_data[2]),int(client_data[3]),int(client_data[4])
#client.moveByVelocityAsync(1,0,0,1).join()
client.moveByVelocityAsync(vx,vy,vz,t).join()
client.moveByVelocityAsync(0,0,0,0.1).join()
elif client_data[0] == 'image':
responses = client.simGetImages([airsim.ImageRequest(1,airsim.ImageType.DepthVis,True)]) #image
observation = transform_input(responses,144,256)
img = observation
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()
recv_msg.sendto(data,addr)
elif client_data[0] == 'stop':
break
recv_msg.close()
def main():
recv_msg = socket(AF_INET,SOCK_DGRAM)
recv_msg.bind((dest_ip,msg_port))
receive_msg(recv_msg)
if __name__ == '__main__':
main()img_server.py
启动这个py,在客户端启动相应的py,则可以观看Airsim的图像流信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58#!/usr/bin/env python
#-*- coding: utf-8 -*-
# python library
from socket import *
import json
from PIL import Image
import numpy as np
import cv2
import threading
# airsim
from connect_simulator import Connect
import airsim
# udp
dest_ip = '10.10.100.56'
img_port = 7996
# connect the airsim simulator
client = Connect().client
# image transform
def transform_input(responses, img_height, img_width):
img1d = np.array(responses[0].image_data_float, dtype=np.float)
img1d = np.array(np.clip(255 * 3 * img1d, 0, 255), dtype=np.uint8)
img2d = np.reshape(img1d, (responses[0].height, responses[0].width))
image = Image.fromarray(img2d)
image = np.array(image.resize((img_width, img_height)).convert('L'))
#cv2.imwrite('b.png', image)
# image = np.float32(image.reshape(1, img_height, img_width, 1))
# image /= 255.0
return image
def send_img(recv_img):
while True:
client_data,addr = recv_img.recvfrom(1024)
client_data = client_data.decode('utf-8')
responses = client.simGetImages([airsim.ImageRequest(1,airsim.ImageType.DepthVis,True)]) #image
observation = transform_input(responses,144,256)
img = observation
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()
recv_img.sendto(data,addr)
recv_img.close()
def main():
recv_img = socket(AF_INET,SOCK_DGRAM)
recv_img.bind((dest_ip,img_port))
send_img(recv_img)
if __name__ == '__main__':
main()
4.2 客户端
client.py
在这个py程序中,输入相应的指令,然后服务端相应,目前是手动的,后面会改成自动的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38#!/usr/bin/env python
#-*- coding: utf-8 -*-
from socket import *
import numpy as np
import cv2
import json
from recv_img import Img
udp_client = socket(AF_INET, SOCK_DGRAM)
dest_addr = ('10.10.100.56', 9999)
while True:
send_data = raw_input(">>")
msg = send_data.split(' ')
print('msg',msg)
json_string = json.dumps(msg)
#udp_client.sendto(send_data.encode('utf-8'), dest_addr)
udp_client.sendto(json_string, dest_addr)
if send_data == 'state':
server_data,addr = udp_client.recvfrom(1024)
server_data = server_data.decode("utf-8")
print('server data',server_data)
if send_data == 'image':
data, addr = udp_client.recvfrom(400000)
#解码
nparr = np.fromstring(data, np.uint8)
#解码成图片numpy
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
print('img',img_decode)
if send_data == "stop":
break
udp_client.close()recv_img.py
启动该py程序,并在服务端启动了对应的py程序后,则能看到窗口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35#!/usr/bin/env python
#-*- coding: utf-8 -*-
from socket import *
import numpy as np
import cv2
udp_client = socket(AF_INET, SOCK_DGRAM)
dest_addr = ('10.10.100.56', 7996)
class Img():
img = None
def main():
img = Img().img
while True:
udp_client.sendto('image'.encode('utf-8'), dest_addr)
data, addr = udp_client.recvfrom(400000)
#解码
nparr = np.fromstring(data, np.uint8)
#解码成图片numpy
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
img = img_decode
print('img',img)
img_decode = cv2.resize(img_decode,(640,480),interpolation=cv2.INTER_CUBIC)
cv2.imshow('result',img_decode)
cv2.waitKey(1)
#cv2.destroyAllWindows()
udp_client.close()
if __name__ == '__main__':
main()
4.3 结果显示
总结
基于UDP传输的基本样例,并且用到的暂时只有这么多,在机器人控制中,服务端主要返回机器人的图像信息和姿态信息,而客户端要发送的主要是机器人的控制信息,如何控制等等,但是我在测试的过程中,如果要在客户端时刻显示机器人的相机信息,则需要新建一个server.py程序,设置一个端口,然后在客户端也新建一个client.py信息,通过这个client来时刻显示视频。当然,如果不需要显示画面的话,则不需要新建
参考链接
python对象转换为json字符串:python socket编程传输列表数据
json字符串转换为python对象:python对象和json互相转换
- UDP传输图片并显示
- UDP传输视频流信息