无人机群模型
该示例考虑了用于无人驾驶飞行器(Uav)的基于智能体的集成群控制模型,以执行在有限范围内搜索和摧毁移动目标的任务。 该代码封装了群体行为的关键方面:分散管理,情况的联合映射,在有限范围内的代理之间的通信,以及合作决策以攻击需要同时使用多个无人机来中和
本项目中使用了以下库和工具:
using Random-提供用于生成伪随机数的函数,这对于模拟无人机和目标的随机行为至关重要。
using LinearAlgebra-提供线性代数运算的工具,例如计算向量的范数(norm),用于计算代理之间的距离。
using Statistics-包含基本的统计功能,包括计算平均值(mean)来分析仿真结果。
gr()-激活绘图的GR后端。jl库,它提供了swarm动画的高速和高质量可视化。
using Random
using LinearAlgebra
using Statistics
gr()
现在让我们来看看项目中使用的对象的结构。
结构:SimulationConfig
用途:存储所有仿真配置参数,确保实验管理的灵活性和易用性。
world_size::Tuple{Int, Int}-虚拟多边形的尺寸(宽度和高度)在常规单位,限制代理的运动面积。n_drones::Int-群中无人机的总数。n_targets::Int-范围内的目标总数。- **
dt::Float64**是积分运动方程的时间步长(时间增量)的值,它决定了模拟的离散度。 - **
total_time::Int**是模拟的总持续时间,以时间步数(帧数)衡量。 comm_range::Float64-两架无人机可以交换数据的最大范围(通信半径)。sensor_range::Float64-无人机传感器检测目标的最大范围(观察半径)。drone_speed::Float64-搜索模式下的基本无人机移动速度。target_speed::Float64-基本目标移动速度。- **
seed::Int**是伪随机数发生器的种子,可确保实验结果的重现性。
结构:无人机
目的:描述群中一个无人机代理的状态和行为。 它是可变的,因为它的字段在模拟过程中会主动更新。
- **
id::Int**是无人机的唯一标识符。 pos::Vector{Float64}-矢量与无人机在飞机上的当前坐标[x, y].vel::Vector{Float64}-具有当前无人机速度分量的矢量[vx, vy].state::Symbol-无人机的当前状态(:searching-搜索,:diving-攻击),这决定了他的行为。- **
map::Matrix{Float64}**是已知目标的局部地图,它是一个矩阵,其中1.0标检测到的目标的坐标。 target_id::Union{Int, Nothing}-无人机当前正在攻击的目标的ID;nothing如果无人机处于搜索模式。- **
comm_range::Float64**是特定无人机的通信半径(可用于模拟异构群)。 sensor_range::Float64-特定无人机的观看半径。failed_attack::Bool-指示不成功攻击尝试的标志(需要在视觉上突出显示这样的无人机并更改其逻辑)。target_destroyed::Bool-指示无人机是否已被摧毁的标志。
结构:目标
目的:目标状态的描述。 它也是多变的,因为它的状态和攻击无人机的列表在模拟过程中会发生变化。
- **
id::Int**是目标的唯一标识符。 pos::Vector{Float64}-矢量与目标的当前坐标[x, y].- **
vel::Vector{Float64}**是目标速度的当前分量的矢量[vx, vy]. required_drones::Int-同时攻击摧毁目标所需的无人机数量(合作阈值)。- **
locked_by::Vector{Int}**是包含id当前正在攻击("捕获")给定目标的无人机。 destroyed::Bool-指示目标是否已被摧毁的标志。
struct SimulationConfig
world_size::Tuple{Int, Int}
n_drones::Int
n_targets::Int
dt::Float64
total_time::Int
comm_range::Float64
sensor_range::Float64
drone_speed::Float64
target_speed::Float64
seed::Int
end
mutable struct Drone
id::Int
pos::Vector{Float64}
vel::Vector{Float64}
state::Symbol
map::Matrix{Float64}
target_id::Union{Int, Nothing}
comm_range::Float64
sensor_range::Float64
failed_attack::Bool
target_destroyed::Bool
end
mutable struct Target
id::Int
pos::Vector{Float64}
vel::Vector{Float64}
required_drones::Int
locked_by::Vector{Int}
destroyed::Bool
end
下面的代码使用模拟参数初始化全局常量,创建一个空的映射,并将基本的实验设置输出到控制台以进行视觉检查。
const config = SimulationConfig((200, 200), 10, 7, 0.5, 700, 100.0, 20.0, 3.0, 0.5, 123)
const empty_map = zeros(config.world_size...)
println("Конфигурация:")
println(" Дроны: $(config.n_drones)")
println(" Цели: $(config.n_targets)")
println(" Размер мира: $(config.world_size)")
println(" Диапазон связи: $(config.comm_range)")
println(" Диапазон сенсора: $(config.sensor_range)")
接下来,我们来看看这个例子中使用的辅助函数。
功能 initialize_drones 创建并返回一个向量 n 无人机,其中每一个随机放置在游戏世界的四个边界之一,并接收指向多边形内的初始速度,以及初始化代理的所有起始参数,包括其唯一标识符,空地图,
function initialize_drones(n, world_size, comm_range, sensor_range)
drones = Vector{Drone}(undef, n)
sides = rand(1:4, n)
for i in 1:n
pos, vel = if sides[i] == 1
([rand() * world_size[1], world_size[2]], [0.0, -rand(1.0:config.drone_speed)])
elseif sides[i] == 2
([world_size[1], rand() * world_size[2]], [-rand(1.0:config.drone_speed), 0.0])
elseif sides[i] == 3
([rand() * world_size[1], 0.0], [0.0, rand(1.0:config.drone_speed)])
else
([0.0, rand() * world_size[2]], [rand(1.0:config.drone_speed), 0.0])
end
drones[i] = Drone(i, pos, vel, :searching, copy(empty_map), nothing, comm_range, sensor_range, false, false)
end
return drones
end
功能 initialize_targets 创建并返回一个向量 n 目标,每个目标被放置在世界中心区域的伪随机位置(与边界偏移)并接收随机速度矢量,以及初始化其参数,包括标识符,需要摧毁的无人机数量(默认为1),
function initialize_targets(n, world_size)
targets = Vector{Target}(undef, n)
for i in 1:n
pos = [rand(10:world_size[1]-10), rand(10:world_size[2]-10)]
vel = [rand(-1.0:0.1:1.0), rand(-1.0:0.1:1.0)] * config.target_speed
targets[i] = Target(i, pos, vel, 1, Int[], false)
end
return targets
end
功能 update_position! 根据代理(无人机或目标)的速度和时间步长更新代理(无人机或目标)的位置,通过将代理放置在多边形内并计算一个新的随机速度矢量,该矢量指向从法线到墙的±60度的角度,确保从边界的正确反射,但前提是代理是一个活动的,而不是被破坏的目标。
function update_position!(agent, dt, world_size)
if !(isa(agent, Target) && agent.destroyed)
agent.pos .+= agent.vel .* dt
hit_wall = false
for i in 1:2
if agent.pos[i] < 0
agent.pos[i] = 0
hit_wall = true
elseif agent.pos[i] > world_size[i]
agent.pos[i] = world_size[i]
hit_wall = true
end
end
if hit_wall
θ = rand(-π/3:0.1:π/3)
speed = max(norm(agent.vel), 1.0)
agent.vel = [cos(θ) -sin(θ); sin(θ) cos(θ)] * [speed, 0.0]
for i in 1:2
if agent.pos[i] == 0
agent.vel[i] = abs(agent.vel[i])
elseif agent.pos[i] == world_size[i]
agent.vel[i] = -abs(agent.vel[i])
end
end
end
end
end
功能 sense_and_act! 实现单个无人机的目标检测和决策逻辑:如果无人机没有处于攻击模式,没有失败,也没有摧毁目标,它会扫描其视野范围内的空间,用找到的目标的坐标更新地图,如果目标需要更多的无人机攻击,则切换到攻击模式(:diving),阻止目标并将您的ID添加到其攻击者列表中。
function sense_and_act!(drone, targets, world_size)
(drone.state == :diving || drone.failed_attack || drone.target_destroyed) && return nothing
for target in targets
target.destroyed && continue
dist = norm(drone.pos - target.pos)
if dist < drone.sensor_range
x_idx = Int(clamp(round(target.pos[1]), 1, world_size[1]))
y_idx = Int(clamp(round(target.pos[2]), 1, world_size[2]))
drone.map[x_idx, y_idx] = 1.0
if length(target.locked_by) < target.required_drones && drone.target_id === nothing
drone.state = :diving
drone.target_id = target.id
push!(target.locked_by, drone.id)
break
end
end
end
end
功能 communicate! 实现了一种在无人机之间交换信息的机制:对于每对无人机,检查它们之间的距离,如果它小于两者的通信半径,则通过组合已知坐标同步它们的目标图(
function communicate!(drones)
n = length(drones)
for i in 1:n
for j in i+1:n
dist = norm(drones[i].pos - drones[j].pos)
if dist < min(drones[i].comm_range, drones[j].comm_range)
for x in 1:size(drones[i].map, 1)
for y in 1:size(drones[i].map, 2)
if drones[i].map[x, y] == 1.0 || drones[j].map[x, y] == 1.0
drones[i].map[x, y] = 1.0
drones[j].map[x, y] = 1.0
end
end
end
end
end
end
end
功能 dive_to_target! 控制无人机攻击过程:如果目标已经被摧毁,无人机切换到搜索模式,否则无人机向目标移动,当接近临界距离时,以80%的概率摧毁目标(如果达到所需数量的
function dive_to_target!(drone, targets, dt, world_size)
if drone.state == :diving && drone.target_id !== nothing
target = targets[drone.target_id]
if target.destroyed
drone.state = :searching
drone.target_id = nothing
drone.target_destroyed = true
return
end
direction = target.pos - drone.pos
norm_dir = norm(direction)
if norm_dir > 1.0
drone.vel = direction / norm_dir * 5.0
else
drone.vel .= 0.0
if rand() < 0.8 && length(target.locked_by) >= target.required_drones
target.destroyed = true
target.vel .= 0.0
drone.target_destroyed = true
else
drone.failed_attack = true
drone.state = :searching
drone.vel .= 0.0
filter!(x -> x != drone.id, target.locked_by)
end
end
update_position!(drone, dt, world_size)
end
end
功能 create_plot 可视化模拟的当前状态:显示目标(红色-活动,灰色-破坏),有关攻击无人机的数量信息,各种状态的无人机(完成任务或失败后蓝色-搜索,绿色-攻击,灰色-不活动),
function create_plot(drones, targets, frame, world_size, destroyed_count, attacking_count)
p = plot(size=(800, 800), xlim=(0, world_size[1]), ylim=(0, world_size[2]), legend=false, title="Рой дронов - Кадр: $frame/$(config.total_time)")
for target in targets
color = target.destroyed ? :gray : :red
scatter!([target.pos[1]], [target.pos[2]], color=color, markersize=6, marker=:square, alpha=0.8)
if !target.destroyed
annotate!(target.pos[1] + 3, target.pos[2] + 3, text("Ц$(target.id)($(length(target.locked_by))/$(target.required_drones))", 8))
end
end
for drone in drones
color = if drone.failed_attack || drone.target_destroyed # внесены изменения
:gray
elseif drone.state == :searching
:blue
elseif drone.state == :diving
if drone.target_id !== nothing && targets[drone.target_id].destroyed
:gray
else
:green
end
else
:purple
end
scatter!([drone.pos[1]], [drone.pos[2]], color=color, markersize=7, marker=:utriangle, alpha=0.9)
if drone.state == :searching && !drone.failed_attack && !drone.target_destroyed # внесены изменения
plot!([drone.pos[1] + drone.sensor_range * cos(θ) for θ in 0:0.2:2π],
[drone.pos[2] + drone.sensor_range * sin(θ) for θ in 0:0.2:2π],
linecolor=color, linealpha=0.1, linewidth=1, seriestype=:path)
end
annotate!(drone.pos[1] - 6, drone.pos[2] - 6, text("Д$(drone.id)", 8, color))
end
annotate!(5, world_size[2] - 5, text("Уничтожено: $(destroyed_count)/$(config.n_targets)", 10, :black))
annotate!(5, world_size[2] - 15, text("Атакующие: $(attacking_count)", 10, :black))
annotate!(5, world_size[2] - 25, text("Кадр: $frame/$(config.total_time)", 10, :black))
return p
end
功能 run_simulation 它是主要的仿真周期:初始化无人机和目标,然后在每个时间步更新目标位置,通过无人机进行目标检测,组织无人机之间的通信,更新主动无人机的位置,可视化系统状态,跟踪目标破坏时间,并在仿真完成后输出最终统计信息。
function run_simulation()
Random.seed!(config.seed)
drones = initialize_drones(config.n_drones, config.world_size, config.comm_range, config.sensor_range)
targets = initialize_targets(config.n_targets, config.world_size)
frames_to_destroy = Int[]
target_destroy_times = Dict{Int, Int}()
destroyed_count = 0
anim = @animate for frame in 1:config.total_time
attacking_count = count(d -> d.state == :diving, drones)
current_destroyed = count(t -> t.destroyed, targets)
if current_destroyed > destroyed_count
for target in targets
if target.destroyed && !haskey(target_destroy_times, target.id)
target_destroy_times[target.id] = frame
push!(frames_to_destroy, frame)
end
end
destroyed_count = current_destroyed
end
if destroyed_count == config.n_targets
println("Все цели уничтожены на кадре $frame")
break
end
for target in targets
update_position!(target, config.dt, config.world_size)
end
for drone in drones
sense_and_act!(drone, targets, config.world_size)
end
communicate!(drones)
for drone in drones
if drone.state == :searching && !drone.failed_attack && !drone.target_destroyed # внесены изменения
update_position!(drone, config.dt, config.world_size)
elseif drone.state == :diving
dive_to_target!(drone, targets, config.dt, config.world_size)
end
end
p = create_plot(drones, targets, frame, config.world_size, destroyed_count, attacking_count)
end every 5
println("\n" * "="^50)
println("ИТОГИ СИМУЛЯЦИИ")
println("="^50)
println("Уничтожено целей: $(destroyed_count)/$(config.n_targets)")
if !isempty(frames_to_destroy)
println("Среднее время уничтожения: $(round(mean(frames_to_destroy), digits=1)) кадров")
println("Максимальное время: $(maximum(frames_to_destroy)) кадров")
println("Минимальное время: $(minimum(frames_to_destroy)) кадров")
end
return anim
end
现在让我们运行模拟并查看其结果。
println("Запуск симуляции...")
animation = run_simulation()
gif(animation, "drone_swarm.gif", fps=10)
结论
这个项目是一个很好的例子模拟复杂系统与分散管理。 它教授基于代理的建模原则,其中每个无人机都有自己的行为逻辑和本地信息,但能够通过有限的通信进行协作交互。
最有趣的方面:
-在没有中央控制者的情况下实施集体决策机制
-模拟有限的感觉区域和通信半径
-需要多个无人机同时参与的目标的合作攻击
-动态适应不断变化的环境和处理不成功的攻击
-实时可视化与代理状态的颜色指示
该代码演示了单个级别的一组简单规则如何在系统级别生成复杂的组行为,这是群体智能和多代理系统中的关键原则。
