剩余任务功能分解
航弹院项目:
-
解决移动问题
-
界面-图表-指令发送界面-配置等等
-
使用套接字发送指令与图片
-
功能分解:
-
指令控制功能:
- 设计指令数据包.
- 写到配置文件里并实时读取配置
- 在飞机里增加套接字,用于针对性的发送指令
- 写一个下拉菜单调用发送指令的功能
-
实验科目功能
- 读取配置文件,初始化实验场地
- 在gamemode蓝图中?进行读取并放置的操作
-
-
通信配置界面功能
- 配置基础通信(基本实现)
-
数据显示界面
- 接受外部信息 动力学 节点数量 需要单开一个套接字
- 读取图片并显示
-
一期未完成功能
- 地图问题(增加一个海洋,已有解决方案)
1.三维仿真运行是实时的,和飞机导弹等同步展示
2.三维场景下的飞机只需要展示飞机的状态轨迹携带两种弹药的数量 没写这个飞机携带的弹药数量
手搓激光雷达
手搓照相
应该是64线120°,角分辨利率0.18°,具体可以查查RSM1激光雷达的参数
动态导入:
RuntimeMeshLoader
gITFRuntime
下一步任务:
实现这个实验科目功能.
-
设计配置文件内的保存结构1
-
配置文件:读取实验科目的数量,然后生成对应数量的实验科目列表项.选第几个,就读取几号的配置.默认去读一号的位置1
-
发现问题:在选择科目的时候,不同科目之间的飞机数量可能不同,而如果飞机是成组接收的话这个值也要变化。1
-
逻辑:选择科目(默认科目1)-》读取场地中的无人机位置,清空原本的场地中的飞机导弹等-》根据读取到的科目里的飞机数量,修改一下套接字里的飞机数量1
实现这个控制指令功能
-
创建一个发送的udp,用于发送指令1
-
思考指令应该是什么,问gpt
-
在udp套接字里留一个发送指令的接口1
-
**指令设计**
数据包格式:
[数据包标识 (1 byte), 目标无人机 (1 byte), 指令类型 (1 byte), 指令参数 (1 byte)]
1. 起飞指令
- 数据包标识:
0x05
- 目标无人机: 例如
0x01
(表示第1架无人机) - 指令类型:
0x01
(起飞指令) - 指令参数:
0x00
(起飞指令无额外参数,填充为0x00
) - 总长度: 4 bytes
- 格式:
[0x05, 0x01, 0x01, 0x00]
- 解释:
0x05
表示指令数据包,0x01
表示目标无人机,0x01
表示起飞,0x00
表示无额外参数。
2. 模式选择指令
- 数据包标识:
0x05
- 目标无人机: 例如
0x01
(表示第1架无人机) - 指令类型:
0x04
(模式选择指令) - 指令参数: 模式编号
0x01
姿态模式(Stabilize Mode)0x02
高度保持模式(AltHold Mode)0x03
自动模式(Auto Mode)0x04
悬停模式(Loiter Mode)0x05
引导模式(Guided Mode)
- 示例格式(高度保持模式):
[0x05, 0x01, 0x04, 0x02]
- 解释:
0x05
表示指令数据包,0x01
表示目标无人机,0x04
表示模式选择,0x02
表示高度保持模式。
3. 返航指令
- 数据包标识:
0x05
- 目标无人机: 例如
0x01
(表示第1架无人机) - 指令类型:
0x02
(返航指令) - 指令参数:
0x00
(返航指令无额外参数,填充为0x00
) - 总长度: 4 bytes
- 格式:
[0x05, 0x01, 0x02, 0x00]
- 解释:
0x05
表示指令数据包,0x01
表示目标无人机,0x02
表示返航,0x00
表示无额外参数。
4. 降落指令
- 数据包标识:
0x05
- 目标无人机: 例如
0x01
(表示第1架无人机) - 指令类型:
0x03
(降落指令) - 指令参数:
0x00
(降落指令无额外参数,填充为0x00
) - 总长度: 4 bytes
- 格式:
[0x05, 0x01, 0x03, 0x00]
- 解释:
0x05
表示指令数据包,0x01
表示目标无人机,0x03
表示降落,0x00
表示无额外参数。
5. 队形切换指令
- 数据包标识:
0x05
- 目标无人机: 例如
0x01
(表示第1架无人机) - 指令类型:
0x05
(队形切换指令) - 指令参数: 队形编号
0x01
队形10x02
队形20x03
队形3
- 示例格式(切换到队形2):
[0x05, 0x01, 0x05, 0x02]
- 解释:
0x05
表示指令数据包,0x01
表示目标无人机,0x05
表示队形切换,0x02
表示切换到队形2。
···
bool SendCommand(uint8 TargetDrone, uint8 CommandType, const TArray& Params)
{
TArrayDataToSend;
DataToSend.Add(0x05); // 数据包标识
DataToSend.Add(TargetDrone); // 目标无人机
DataToSend.Add(CommandType); // 指令类型
DataToSend.Append(Params); // 指令参数return SendBytes(DataToSend); // 通过UDP发送
}
</details>
- 数据包标识:
实现载具移动功能
场景中事先有载具 /动态生成 通信部分增加接收载具的部分具体留白 目前复用了导弹的信息
游戏开始创建一个套接字并绑定载具管理对象 1
实现能够接受多个套接字不同ip和端口接受数据的功能1
多创建几个套接字并命名,然后管理类绑定多个套接字1
设置界面对应更新,配置功能对应更新1
统计信息显示:通过UDP通信的方式接收来自外部的实验参数信息,如动力学数据(质量、转动惯量等),飞行状态(位置、姿态、速度等),不同节点数量(数字、半实物、实飞); 设计一个包1
区域删除功能1
这个统计信息展示部分,发来的动力学数据、飞行姿态等是和无人机
更新飞机收到的信息解析与委托。1
更新区域删除的信息 1
导弹和载具更新后 他们的删除操作重写1
通信协议
udp
飞机 包标志位uint8 1 飞机id uint8 飞机类型(侦察机/载机/布撒器) uint8 节点类型(数组/半实物/实飞)uint8 动力学数据(质量float、转动惯量float) 飞行状态(位置、姿态、速度 double9)
导弹 包标志位uint8 2 导弹id uint8 导弹类型uint8 导弹状态uint8 导弹位置double3
载具 包标志位uint8 3 载具id uint8 载具类型uint8 载具状态uint8 载具位置double*3
mavlink
目标区域
目标点
GPS干扰区域
通信干扰区域
高威胁区
import socket
import struct
import math
import time
from threading import Timer
UDP 目标 IP 和端口
TARGET_IP = "192.168.1.105"
TARGET_PORT = 12345
创建 UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
配置部分
config = {
'aircrafts': [{'id': 1, 'type': 1, 'node_type': 1, 'position': (0, 0, 10), 'speed': 10},
{'id': 2, 'type': 1, 'node_type': 1, 'position': (2, 0, 10), 'speed': 10},
{'id': 3, 'type': 1, 'node_type': 1, 'position': (4, 0, 10), 'speed': 10},
{'id': 4, 'type': 1, 'node_type': 1, 'position': (6, 0, 10), 'speed': 10},
{'id': 5, 'type': 1, 'node_type': 1, 'position': (8, 0, 10), 'speed': 10},
{'id': 6, 'type': 1, 'node_type': 1, 'position': (10, 0, 10), 'speed': 10},
{'id': 7, 'type': 1, 'node_type': 1, 'position': (12, 0, 10), 'speed': 10},
{'id': 8, 'type': 1, 'node_type': 1, 'position': (14, 0, 10), 'speed': 10},
{'id': 9, 'type': 1, 'node_type': 1, 'position': (16, 0, 10), 'speed': 10},
{'id': 10, 'type': 1, 'node_type': 1, 'position': (18, 0, 10), 'speed': 10}]
,
'missiles': [
{'id': 1, 'type': 1, 'position': (-8, 0, 20), 'speed': -10, 'state': 1}, # 最左
{'id': 2, 'type': 1, 'position': (-4, 0, 20), 'speed': -10, 'state': 1}, # 左
{'id': 3, 'type': 1, 'position': (0, 0, 20), 'speed': -10, 'state': 1}, # 中
{'id': 4, 'type': 1, 'position': (4, 0, 20), 'speed': -10, 'state': 1}, # 右
{'id': 5, 'type': 1, 'position': (8, 0, 20), 'speed': -10, 'state': 1}, # 最右
],
'vehicles': [
{'id': 1, 'type': 1, 'position': (-10, 0, 0), 'speed': -4, 'state': 1}, # 最左
{'id': 2, 'type': 2, 'position': (-5, 0, 0), 'speed': -4, 'state': 1}, # 左
{'id': 3, 'type': 3, 'position': (0, 0, 0), 'speed': -4, 'state': 1}, # 中
{'id': 4, 'type': 4, 'position': (5, 0, 0), 'speed': -4, 'state': 1}, # 右
{'id': 5, 'type': 5, 'position': (10, 0, 0), 'speed': -4, 'state': 1}, # 最右
],
'counts': {
'aircraft': 10, # 一个包发送3个飞机
'missile': 5, # 一个包发送5个导弹
'vehicle': 5 # 一个包发送5个载具
}
}
保存先前位置,用于计算偏航角
previous_positions = {ac['id']: (ac['position'][0], ac['position'][1]) for ac in config['aircrafts']}
def calculate_yaw(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
return math.degrees(math.atan2(dy, dx))
def send_aircraft_data(aircrafts, t):
# 包标志位uint8 1 + 飞机数据
packet_data = struct.pack('=B', 1) # 包标志位1表示飞机数据包
for i in range(config["counts"]["aircraft"]):
# 如果索引在飞机列表范围内,使用实际的飞机数据
if i < len(aircrafts):
aircraft=aircrafts[i]
# for aircraft in aircrafts:
# 计算新位置(直线向前飞行)
x = aircraft['position'][0] + t * aircraft['speed']# X保持不变
y = aircraft['position'][1] # Y随时间向前
z = aircraft['position'][2] # Z保持不变
# 计算偏航角
pre_x, pre_y = previous_positions[aircraft['id']]
yaw = calculate_yaw(pre_x, pre_y, x, y)
pitch = 0.0
roll = 0.0
# 计算速度分量
vel_x = 0.0
vel_y = aircraft['speed']
vel_z = 0.0
# 更新先前位置
previous_positions[aircraft['id']] = (x, y)
else:
x, y, z = 0, 0, 0
yaw, pitch, roll = 0.0, 0.0, 0.0
vel_x, vel_y, vel_z = 0.0, 0.0, 0.0
# 打包单个飞机数据
aircraft_data = struct.pack('=BBBff' + 'd' * 9,
aircraft['id'] if i < len(aircrafts) else 0, # 飞机ID uint8
aircraft['type'] if i < len(aircrafts) else 0, # 飞机类型 uint8
aircraft['node_type'] if i < len(aircrafts) else 0, # 节点类型 uint8
1000.0, # 质量 float
100.0, # 转动惯量 float
x, y, z, # 位置 double*3
roll, pitch, yaw, # 姿态 double*3
vel_x, vel_y, vel_z # 速度 double*3
)
packet_data += aircraft_data
sock.sendto(packet_data, (TARGET_IP, TARGET_PORT))
def send_missile_data(missiles, t):
# 包标志位uint8 2 + 导弹数据
packet_data = struct.pack('=B', 2) # 包标志位2表示导弹数据包
for missile in missiles:
# 计算新位置
x = missile['position'][0]
y = missile['position'][1] + t * missile['speed']
z = missile['position'][2]
# 打包单个导弹数据
missile_data = struct.pack('=BBBddd',
missile['id'], # 导弹ID uint8
missile['type'], # 导弹类型 uint8
missile['state'], # 导弹状态 uint8
x, y, z # 位置 double*3
)
packet_data += missile_data
sock.sendto(packet_data, (TARGET_IP, TARGET_PORT))
def send_vehicle_data(vehicles, t):
# 包标志位uint8 3 + 载具数据
packet_data = struct.pack('=B', 3) # 包标志位3表示载具数据包
for vehicle in vehicles:
# 计算新位置
x = vehicle['position'][0]
y = vehicle['position'][1] + t * vehicle['speed']
z = vehicle['position'][2]
# 打包单个载具数据
vehicle_data = struct.pack('=BBBddd',
vehicle['id'], # 载具ID uint8
vehicle['type'], # 载具类型 uint8
vehicle['state'], # 载具状态 uint8
x, y, z # 位置 double*3
)
packet_data += vehicle_data
sock.sendto(packet_data, (TARGET_IP, TARGET_PORT))
def main():
start_time = time.time()
while True:
t = time.time() - start_time
# 发送飞机数据
send_aircraft_data(config['aircrafts'], t)
# 发送导弹数据
# send_missile_data(config['missiles'], t)
# 发送载具数据
# send_vehicle_data(config['vehicles'], t)
# 控制发送频率
time.sleep(0.02) # 20Hz的更新频率
if name == "main":
main()