轉發腳本的任務需求
仿真采用UDP通信方式,在 wsl 中仿真三臺飛機,項目需要將一臺電腦中的三臺飛機的數據打包發送到另一臺飛機的地面站,但地面站是無法直接訪問另一臺主機的 wsl 中的端口的,wsl 中的端口需要本機才能訪問,接收數據,因此,需要編寫一個腳本在主機運行,接收 wsl 中仿真飛機的數據,通過主機的 ip 與端口發送給 app 的主機 ip 與端口(),并且,接收 app 的命令,轉發給三臺飛機(命令中會包含飛機id,因此腳本不需要做篩選,直接轉發給三個即可)。
目標端口與本機都需要在同一個局域網下(連接同一個wifi),整體框圖如下:
腳本編寫的注意點
仿真飛機的發送端口
仿真飛控在沒有收到地面站心跳包時,是往 14550 端口發送數據的,只有在收到自己預先配置的接收端口接收到數據后,才會往配置的接收端口發送數據,一般配置的端口都是 18570,多臺飛機就往后順延,18571,18572
地面站發送心跳也是必須往飛機配置的端口發送心跳才行,例如飛機是18570,那么必須往這個端口發心跳,飛控才會改為往18570發送數據。
代碼:
#! /usr/bin/env python
# -*-coding:utf-8-*
import socketimport socket
import threading
import time# 監控gcs發送的心跳包并轉發給飛機的心跳端口 14550 (心跳端口為多機共用端口)
def udp_listen_gcs_to_uav(gcs_sock, uav1_sock, uav2_sock, uav3_sock, uav_ip, uav1_port, uav2_port, uav3_port):# print("listen GCS UDP port") while True:# 接收數據 buffer_size = 255 #緩沖區大小 gcs_data, gcs_addr = gcs_sock.recvfrom(buffer_size) #讀取gcs發送過來的數據包,轉發給wsl的飛控 print("addr:", gcs_addr, "data:", gcs_data)#發送數據到wsl的飛機地址與端口 uav1_sock.sendto(gcs_data, (uav_ip, uav1_port))uav2_sock.sendto(gcs_data, (uav_ip, uav2_port))uav3_sock.sendto(gcs_data, (uav_ip, uav3_port))# 監控uav1 18570 端口發送的消息并轉發給gcs端口
def udp_listen_uav_to_gcs(uav_sock, gsc_sock, gcs_ip, gcs_port):# print("listen uav UDP port:") while True:# 接收數據 buffer_size = 255 #緩沖區大小 uav_data, uav_addr = uav_sock.recvfrom(buffer_size) #讀取gcs發送過來的數據包,轉發給wsl的飛控 # print("uav_addr:", uav_addr, "uav_data:", uav_data) # 發送數據到wsl的飛機地址與端口 gsc_sock.sendto(uav_data, (gcs_ip, gcs_port))def main():self_UDP_IP = "172.16.20.86" # 本機IP UDP通信可以不使用此字段 gcs_tx_ip = "172.16.20.16" # gcs發送ip gcs_tx_port = 14650 # gcs發送端口 gcs_rx_ip = "172.16.20.16" # gcs接收ip gcs_rx_port = 14650 # gcs接收端口 uav_ip = "172.27.21.175" # 飛機的ip都是相同的 uav1_port = 18570 # 飛機1的端口號 在發送了心跳包后,需要與飛機在此端口進行通信 uav2_port = 18571 # 飛機1的端口號 在發送了心跳包后,需要與飛機在此端口進行通信 uav3_port = 18572 # 飛機1的端口號 在發送了心跳包后,需要與飛機在此端口進行通信 #創建與地面站以及與飛機通信的sock,之后傳入各個線程 gcs_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)gcs_sock.bind(('', gcs_tx_port))# 綁定使用哪個端口發送 要讓飛機往18570發送數據,因此心跳包也要從18570發送,飛控從18570接收到心跳包后,就會往18570發送自身數據 uav1_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)uav1_sock.bind(('', uav1_port))uav2_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)uav2_sock.bind(('', uav2_port))uav3_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)uav3_sock.bind(('', uav3_port))# 創建線程 監聽gcs的包之后轉發給飛控心跳端口 14550 listen_gcs_to_uav_thread = threading.Thread(target=udp_listen_gcs_to_uav, args=[gcs_sock, uav1_sock, uav2_sock, uav3_sock, uav_ip, uav1_port, uav2_port, uav3_port])# 創建線程 監聽wsl中的uav1,2,3端口消息 18570,18571,18572 并轉發給gcs端口 listen_uav1_to_gcs_thread = threading.Thread(target=udp_listen_uav_to_gcs, args=[uav1_sock, gcs_sock, gcs_rx_ip, gcs_rx_port])listen_uav2_to_gcs_thread = threading.Thread(target=udp_listen_uav_to_gcs, args=[uav2_sock, gcs_sock, gcs_rx_ip, gcs_rx_port])listen_uav3_to_gcs_thread = threading.Thread(target=udp_listen_uav_to_gcs, args=[uav3_sock, gcs_sock, gcs_rx_ip, gcs_rx_port])listen_gcs_to_uav_thread.start()listen_uav1_to_gcs_thread.start()listen_uav2_to_gcs_thread.start()listen_uav3_to_gcs_thread.start()# 等待線程結束 listen_gcs_to_uav_thread.join() # 等待心跳包發送完成后就啟動下一個任務 listen_uav1_to_gcs_thread.join()listen_uav2_to_gcs_thread.join()listen_uav3_to_gcs_thread.join()# Press the green button in the gutter to run the script.
if __name__ == '__main__':main()
在上述代碼中,UDP 在接收時是用不到此ip的,也就是 gcs_sock.bind(('', gcs_tx_port)),函數中的 ip 可以不填,只需要將端口號填對即可,但在發送時,需要指定 ip,而且在給飛控發送心跳時,需要綁定端口號為飛機的端口號