Python中实现矩形螺旋扫描
在许多的实际生活与工程案例中都需要我们在给定的样本中寻找到一个特定的目标,最常用的方法就是对待测的样本空间进行扫描。扫描的方法有很多,其中,矩形螺旋扫描能够兼顾矩形扫描和螺旋扫描这两者的优点,既实现起来相对简单,同时又能确保扫描的覆盖率,从而提高整体的捕获效率,是一种常用的扫描方法。下面我将分享一种在Python中实现矩形螺旋扫描的方法。
设计目标
在一个不确定区域边长大小为6的空间中,如果考虑每个扫描点的扫描覆盖空间为直径为0.3或者0.6的圆形区域,那么设计合理的步长,在Python中实现矩形螺旋扫描从而寻找到特定的目标。
步长选取
我们希望选择这样一个步长,从而既能够实现不确定区域的全覆盖,又能尽可能的减少重复扫描的区域,这里我考虑这样一种特殊的情况:

我们可以看到,对于给定的单个扫描点的扫描直径(图中红线),我们可以选择相应的一个特定步长(绿线)使点的扫描区域与
点的扫描区域相切,从而实现尽可能小的重复扫描区域。易知此时的扫描步长为扫描半径的
倍。
Python实现
编程的平台我用的是ANACONDA,编辑器用的是PyCharm。所用到的OpenCV等等相关库都已下载好。
一、导入相关库与基本配置
import math
import numpy as np
import matplotlib.pyplot as plt
import random
from matplotlib import font_manager
# 指定Matplotlib使用支持中文的字体
plt.rcParams['font.sans-serif'] = ['KaiTi'] # 使用黑体
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
二、初始化扫描环境与参数
这里我们在给定的不确定区域中随机生成一个待测点,并且基于先前的推导设置相应的扫描步长。
# 初始条件
# 设置扫描初始点为原点
origin_point = (0, 0)
# 扫描的不确定区域
rect_size = 6
# 设置待测点,可以替换为任意点,这里采用随机数生成
x0 = random.uniform(-rect_size/2, rect_size/2)
y0 = random.uniform(-rect_size/2, rect_size/2)
target_point = (x0, y0)
# 设置光斑发散角的大小,此处为半径
detection_radius = 0.15
# detection_radius = 0.3
# 设置扫描步长以实现全区域的覆盖
step_length = detection_radius * math.sqrt(2)
print(f"origin_point: {origin_point}")
print(f"target_point: ({target_point[0]:.4f}, {target_point[1]:.4f})")
三、编写矩形螺旋扫描函数并执行
对于这样一个矩形螺旋扫描函数,我们对它输入扫描的初始点,待测点,步长以及扫描半径,然后我们可以获得相应的按顺序存放的所以扫描遍历点的坐标位置。函数的基本思想是根据矩形螺旋扫描的规律不断改变扫描点的位置,并且在每个点的位置处检验是否能够覆盖到待测点,如果不行,则继续执行循环;如果可以,则选择跳出循环。至于矩形螺旋扫描的实现,我们可以设置四个单位方向向量,每次更新时即将这些单位方向向量乘以步长加在之前的扫描点坐标上。我们还可以发现,要实现矩形螺旋扫描的功能,每一个方向上扫描的次数以
的规律进行变化,那么我们可以额外设置一个变量来控制每一个方向上扫描的次数,当发生
或者
这两个方向变化时就增加一次次数。
# 矩形螺旋扫描函数
def rectangular_spiral_scan(origin_point, target_point, step_length, detection_radius):
# 存储遍历点的列表
scanned_points = []
# 设置初始遍历点为原点
x, y = origin_point
# 设置扫描的方向,此时即代表初始方向为水平向x轴正方向
direction_x = 1
direction_y = 0
# 定义一个变量,用于判断每次移动时是否需要改变方向
judge_num = 1
judge_num_temp = judge_num
while True:
# 存储遍历点的坐标
scanned_points.append((x, y))
# 尝试在当前方向上移动
new_x = x + direction_x * step_length
new_y = y + direction_y * step_length
# 检查检测区域是否覆盖目标点
if math.hypot(new_x - target_point[0], new_y - target_point[1]) <= detection_radius:
scanned_points.append((new_x, new_y))
return scanned_points
# 更新扫描点的位置
x, y = new_x, new_y
judge_num_temp = judge_num_temp - 1
# 改变方向
if direction_x == 1 and judge_num_temp == 0:
direction_x = 0
direction_y = 1
judge_num_temp = judge_num
elif direction_y == 1 and judge_num_temp == 0:
direction_y = 0
direction_x = -1
# 在这个拐角处需要增加一次下一方向的遍历次数
judge_num = judge_num + 1
judge_num_temp = judge_num
elif direction_x == -1 and judge_num_temp == 0:
direction_x = 0
direction_y = -1
judge_num_temp = judge_num
elif direction_y == -1 and judge_num_temp == 0:
direction_y = 0
direction_x = 1
# 在这个拐角处也需要增加一次下一方向的遍历次数
judge_num = judge_num + 1
judge_num_temp = judge_num
# 执行扫描并获取覆盖目标点的扫描点
scanned_points = rectangular_spiral_scan(origin_point, target_point, step_length, detection_radius)
四、绘图与可视化展示
在我们编写了矩形螺旋扫描函数并执行后,我们可以得到所有扫描点的坐标数据,然后我们就可以调用相关库里面的函数进行绘图与可视化展示。
# 输出最后一个扫描点的位置
last_point = scanned_points[-1]
print(f"last_point: ({last_point[0]:.4f}, {last_point[1]:.4f})")
# 绘制遍历图样
# 读取所有遍历点的数据
x_vals, y_vals = zip(*scanned_points)
# 绘制待测点
plt.scatter(target_point[0], target_point[1], s=2, color='red', label='target_point', zorder=5)
# 绘制所有的遍历点,除了最后一个点
plt.plot(x_vals, y_vals, marker='o', markersize=2, linestyle='-', color='blue', label='scan_path')
# 获取scanned_points的长度
num_points = len(scanned_points)
# 绘制所有遍历点的扫描区域
for i in range(num_points - 1):
point = scanned_points[i]
circle = plt.Circle(point, detection_radius, color='pink', fill=False)
plt.gca().add_patch(circle)
# 突出绘制最后一个遍历点
plt.scatter(last_point[0], last_point[1], s=2, color='green', label='last_point', zorder=5)
circle = plt.Circle(last_point, detection_radius, color='green', linewidth=3, fill=False, label='scan_zone(last_point)')
plt.gca().add_patch(circle)
plt.gca().set_aspect('equal', adjustable='box')
# 设置图像的基本参数
plt.xlim(-rect_size/2, rect_size/2)
plt.ylim(-rect_size/2, rect_size/2)
plt.legend()
plt.title('矩形螺旋扫描图样')
plt.xlabel('X坐标')
plt.ylabel('Y坐标')
plt.grid(True)
# 保存图像到文件
plt.savefig("E:\\result.png")
# 绘制图像
plt.show()
结果展示
由于我们的待测点是通过随机数来随机生成的,那么我们也就可以通过多次运行来验证代码的功能,结果如下:




作者:HUSToei小白