随着之前博客的介绍,关于飞行器多阵形仿真tsp问题已基本结束,本博客将整合之前多种阵形并优化为导入路径信息,方便读者自行选择tsp算法,只需要导入城市遍历顺序即可,将其打包设计为一个可切换运行的完整程序,包括方阵,三角阵,一字阵,话不多说直接进入正题
一.导入路径方式
自行设置城市数量,坐标,导入tsp将结果(城市运行顺序)
这段代码用于创建随机的城市和导入路径信息,并将起始城市添加到路径中。
- 首先,使用 np.random.rand(num_cities, 2) 创建了一个形状为 (num_cities, 2) 的二维数组 cities。这里的 num_cities 表示城市的数量,每个城市有两个坐标值 (x, y)。np.random.rand() 函数返回一个由 [0, 1) 范围内的随机数组成的数组。
- 定义一个名为 path 的 NumPy 数组,它包含了城市在 cities 数组中的索引,并按照访问顺序排列。在这个例子中,path 是一个长度为 5 的一维数组,表示红点按照索引 0、1、2、3、4 的顺序依次访问城市。
- 最后,通过 np.append(path, path[0]) 将起始城市的索引添加到路径的末尾,以形成闭环路径。这样做是为了确保红点可以从最后一个城市回到起始城市,完成整个循环。
总结来说,以上代码片段创建了一个包含随机生成城市和预定义路径信息的模拟环境,红点将按照路径依次访问这些城市。
# 设置城市数量及坐标
num_cities = 5
cities = np.random.rand(num_cities, 2)
# 导入路径信息(本质为遍历城市顺序,下面点阵将根据这个顺序进行仿真运行)
path = np.array([0, 1, 2, 3, 4])
# 添加起始城市到路径
tour = np.append(path, path[0])
二.不同阵形处理
首先将总体红点隐藏,后续通过按键进行选择为那种阵形显示,初始阵形设计均采用中心偏移设定,根据偏移不同设计不同的 offset 信息,进而显示为不同阵形,具体计算详情请参考之前博客。
def update_dots(formation, center_x, center_y, dx, dy):
# 隐藏所有红点
for dot in red_dots:
dot.set_visible(False)
if formation == 'square': # 方阵
offsets = [(-0.02, -0.02), (0.02, -0.02), (-0.02, 0.02), (0.02, 0.02)]
elif formation == 'line': # 一字阵形
offsets = [(0, -0.03), (0, 0), (0, 0.03)]
elif formation == 'triangle': # 三角形阵形
offsets = [(0, 0), (-0.02, -0.03), (0.02, -0.03)] # 修改点阵形状 设置不同的中心偏移
for k, (offset_x, offset_y) in enumerate(offsets):
rotated_x = offset_x * dx - offset_y * dy
rotated_y = offset_x * dy + offset_y * dx
dot_x = center_x + rotated_x
dot_y = center_y + rotated_y
red_dots[k].set_data([dot_x], [dot_y])
red_dots[k].set_visible(True)
三,按钮设置
1.切换阵形按钮
如果检测到按钮按下,则切换阵形
# 切换阵形按钮
def change_formation(event):
global current_formation
current_formation = (current_formation + 1) % len(formations)
ani.event_source.stop()
ani.event_source.start()
ax_button = plt.axes([0.35, 0.05, 0.3, 0.075])
btn = Button(ax_button, "change formation")
btn.on_clicked(change_formation)
2.重置运行按钮
在遍历完一次路径后,按下按钮可再次运行,可用于切换阵形后运行,当然本设计支持,运行途中进行按键切换阵形
# 再次运行按钮
def run_animation(event):
global ani
ani = FuncAnimation(
fig, update_frame,
frames=100 * (num_cities + 1),
repeat=False, blit=True,
interval=10
)
plt.draw()
ax_button_run = plt.axes([0.6, 0.05, 0.3, 0.075])
btn_run = Button(ax_button_run, "Run")
btn_run.on_clicked(run_animation)
四.保持各阵形稳定运行不会随路径转折变化
这里定义一个名为update_frame的函数。该函数接受一个参数frame,用于更新动画的帧。
首先,通过整除和取余运算,将frame分解为两个变量current_i和current_j。current_i表示当前城市的索引,current_j表示当前城市之间的线上点的位置。然后通过判断current_i是否超出城市数量num_cities,来确定是否需要重置动画。如果超出城市数量,则直接返回红点的列表red_dots。
运行过程:
1.根据当前城市的索引,从城市列表cities中获取当前城市和下一个城市的坐标。
2.通过线性插值计算出线上当前点的坐标。使用current_j / 100.0来确定当前点在城市之间的位置。
然后,计算出城市之间的差值dx和dy,并计算出其模长norm。
3.调用update_dots函数,传入当前形状、当前点的坐标以及dy和-dx,来更新红点的位置。并返回红点的列表red_dots。
def update_frame(frame):
current_i = frame // 100
current_j = frame % 100
# 如果超出城市数, 重置
if current_i >= num_cities:
return red_dots
# 获取当前和下一个城市坐标
city1 = cities[tour[current_i]]
city2 = cities[tour[current_i + 1]]
# 计算线上当前的点
current_point = city1 + (city2 - city1) * (current_j / 100.0)
dx = city2[0] - city1[0]
dy = city2[1] - city1[1]
norm = np.sqrt(dx*dx + dy*dy) + 1e-8
dx /= norm
dy /= norm
update_dots(formations[current_formation], current_point[0], current_point[1], dy, -dx)
return red_dots
到这里就完成了不同阵形的切换运行,显示出可视化图就好了
ani = FuncAnimation(
fig,
update_frame,
frames=100 * (num_cities + 1),
repeat=False, blit=True, interval=10
)
plt.show()
五.效果展示
点阵模拟——切换阵形
至此关于tsp飞行器多阵形仿真就告一段落了,回顾总结来看,这个项目其实并不困难,主要是多阵形的设计,维持阵形随着路径变化,以及路径的规划运行三大部分,总结我的开发过程来看,感觉最有收获的是将数学知识与代码设计精密结合起来,学会用数学解决算法,代码实现算法是我们开发的核心要点。以后我还会继续分享各种不同的项目设计及突然心血来潮的代码想法,大家共勉。
点赞三连呀
评论区踢,完整代码