动态窗口法_version1.3
1 """ 2 version1.3 3 Mobile robot motion planning sample with Dynamic Window Approach 4 结合https://blog.csdn.net/heyijia0327/article/details/44983551来看,里面含中文注释 5 符号参考《煤矿救援机器人地图构建与路径规划研究》矿大硕士论文 6 """ 7 8 import math 9 import numpy as np 10 import matplotlib.pyplot as plt 11 12 show_animation = True # 动画 13 14 15 class Config(object): 16 """ 17 用来仿真的参数, 18 """ 19 20 def __init__(self): 21 # robot parameter 22 self.max_speed = 1.4 # [m/s] # 最大速度 23 # self.min_speed = -0.5 # [m/s] # 最小速度,设置为可以倒车 24 self.min_speed = 0 # [m/s] # 最小速度,设置为不倒车 25 self.max_yawrate = 40.0 * math.pi / 180.0 # [rad/s] # 最大角速度 26 self.max_accel = 0.2 # [m/ss] # 最大加速度 27 self.max_dyawrate = 40.0 * math.pi / 180.0 # [rad/ss] # 最大角加速度 28 self.v_reso = 0.01 # [m/s],速度分辨率 29 self.yawrate_reso = 0.1 * math.pi / 180.0 # [rad/s],角速度分辨率 30 self.dt = 0.1 # [s] # 采样周期 31 self.predict_time = 3.0 # [s] # 向前预估三秒 32 self.to_goal_cost_gain = 1.0 # 目标代价增益 33 self.speed_cost_gain = 1.0 # 速度代价增益 34 self.robot_radius = 1.0 # [m] # 机器人半径 35 36 37 def motion(x, u, dt): 38 """ 39 :param x: 位置参数,在此叫做位置空间 40 :param u: 速度和加速度,在此叫做速度空间 41 :param dt: 采样时间 42 :return: 43 """ 44 # 速度更新公式比较简单,在极短时间内,车辆位移也变化较大 45 # 采用圆弧求解如何? 46 x[0] += u[0] * math.cos(x[2]) * dt # x方向位移 47 x[1] += u[0] * math.sin(x[2]) * dt # y 48 x[2] += u[1] * dt # 航向角 49 x[3] = u[0] # 速度v 50 x[4] = u[1] # 角速度w 51 # print(x) 52 53 return x 54 55 56 def calc_dynamic_window(x, config): 57 """ 58 位置空间集合 59 :param x:当前位置空间,符号参考硕士论文 60 :param config: 61 :return:目前是两个速度的交集,还差一个 62 """ 63 64 # 车辆能够达到的最大最小速度 65 vs = [config.min_speed, config.max_speed, 66 -config.max_yawrate, config.max_yawrate] 67 68 # 一个采样周期能够变化的最大最小速度 69 vd = [x[3] - config.max_accel * config.dt, 70 x[3] + config.max_accel * config.dt, 71 x[4] - config.max_dyawrate * config.dt, 72 x[4] + config.max_dyawrate * config.dt] 73 # print(Vs, Vd) 74 75 # 求出两个速度集合的交集 76 vr = [max(vs[0], vd[0]), min(vs[1], vd[1]), 77 max(vs[2], vd[2]), min(vs[3], vd[3])] 78 79 return vr 80 81 82 def calc_trajectory(x_init, v, w, config): 83 """ 84 预测3秒内的轨迹 85 :param x_init:位置空间 86 :param v:速度 87 :param w:角速度 88 :param config: 89 :return: 每一次采样更新的轨迹,位置空间垂直堆叠 90 """ 91 x = np.array(x_init) 92 trajectory = np.array(x) 93 time = 0 94 while time <= config.predict_time: 95 x = motion(x, [v, w], config.dt) 96 trajectory = np.vstack((trajectory, x)) # 垂直堆叠,vertical 97 time += config.dt 98 99 # print(trajectory) 100 return trajectory 101 102 103 def calc_to_goal_cost(trajectory, goal, config): 104 """ 105 计算轨迹到目标点的代价 106 :param trajectory:轨迹搜索空间 107 :param goal: 108 :param config: 109 :return: 轨迹到目标点欧式距离 110 """ 111 # calc to goal cost. It is 2D norm. 112 113 dx = goal[0] - trajectory[-1, 0] 114 dy = goal[1] - trajectory[-1, 1] 115 goal_dis = math.sqrt(dx ** 2 + dy ** 2) 116 cost = config.to_goal_cost_gain * goal_dis 117 118 return cost 119 120 121 def calc_obstacle_cost(traj, ob, config): 122 """ 123 计算预测轨迹和障碍物的最小距离,dist(v,w) 124 :param traj: 125 :param ob: 126 :param config: 127 :return: 128 """ 129 # calc obstacle cost inf: collision, 0:free 130 131 min_r = float("inf") # 距离初始化为无穷大 132 133 for ii in range(0, len(traj[:, 1])): 134 for i in range(len(ob[:, 0])): 135 ox = ob[i, 0] 136 oy = ob[i, 1] 137 dx = traj[ii, 0] - ox 138 dy = traj[ii, 1] - oy 139 140 r = math.sqrt(dx ** 2 + dy ** 2) 141 if r <= config.robot_radius: 142 return float("Inf") # collision 143 144 if min_r >= r: 145 min_r = r 146 147 return 1.0 / min_r # 越小越好 148 149 150 def calc_final_input(x, u, vr, config, goal, ob): 151 """ 152 计算采样空间的评价函数,选择最合适的那一个作为最终输入 153 :param x:位置空间 154 :param u:速度空间 155 :param vr:速度空间交集 156 :param config: 157 :param goal:目标位置 158 :param ob:障碍物 159 :return: 160 """ 161 x_init = x[:] 162 min_cost = 10000.0 163 min_u = u 164 165 best_trajectory = np.array([x]) 166 167 trajectory_space = np.array([x]) # 记录搜索所有采样的轨迹,用来画图 168 169 # evaluate all trajectory with sampled input in dynamic window 170 # v,生成一系列速度,w,生成一系列角速度 171 for v in np.arange(vr[0], vr[1], config.v_reso): 172 for w in np.arange(vr[2], vr[3], config.yawrate_reso): 173 174 trajectory = calc_trajectory(x_init, v, w, config) 175 176 trajectory_space = np.vstack((trajectory_space, trajectory)) 177 178 # calc cost 179 to_goal_cost = calc_to_goal_cost(trajectory, goal, config) 180 speed_cost = config.speed_cost_gain * (config.max_speed - trajectory[-1, 3]) 181 ob_cost = calc_obstacle_cost(trajectory, ob, config) 182 # print(ob_cost) 183 184 # 评价函数多种多样,看自己选择 185 # 本文构造的是越小越好 186 final_cost = to_goal_cost + speed_cost + ob_cost 187 188 # search minimum trajectory 189 if min_cost >= final_cost: 190 min_cost = final_cost 191 min_u = [v, w] 192 best_trajectory = trajectory 193 194 # print(min_u) 195 # input() 196 197 return min_u, best_trajectory, trajectory_space 198 199 200 def dwa_control(x, u, config, goal, ob): 201 """ 202 调用前面的几个函数,生成最合适的速度空间和轨迹搜索空间 203 :param x: 204 :param u: 205 :param config: 206 :param goal: 207 :param ob: 208 :return: 209 """ 210 # Dynamic Window control 211 212 vr = calc_dynamic_window(x, config) 213 214 u, trajectory, trajectory_space = calc_final_input(x, u, vr, config, goal, ob) 215 216 return u, trajectory, trajectory_space 217 218 219 def plot_arrow(x, y, yaw, length=0.5, width=0.1): 220 """ 221 arrow函数绘制箭头,表示搜索过程中选择的航向角 222 :param x: 223 :param y: 224 :param yaw:航向角 225 :param length: 226 :param width:参数值为浮点数,代表箭头尾部的宽度,默认值为0.001 227 :return: 228 length_includes_head:代表箭头整体长度是否包含箭头头部的长度,默认值为False 229 head_width:代表箭头头部的宽度,默认值为3*width,即尾部宽度的3倍 230 head_length:代表箭头头部的长度度,默认值为1.5*head_width,即头部宽度的1.5倍 231 shape:参数值为'full'、'left'、'right',表示箭头的形状,默认值为'full' 232 overhang:代表箭头头部三角形底边与箭头尾部直接的夹角关系,通过该参数可改变箭头的形状。 233 默认值为0,即头部为三角形,当该值小于0时,头部为菱形,当值大于0时,头部为鱼尾状 234 """ 235 plt.arrow(x, y, length * math.cos(yaw), length * math.sin(yaw), 236 head_length=1.5 * width, head_width=width) 237 plt.plot(x, y) 238 239 240 def main(): 241 """ 242 主函数 243 :return: 244 """ 245 # print(__file__ + " start!!") 246 # 初始化位置空间 247 x = np.array([0.0, 0.0, math.pi / 2.0, 0.2, 0.0]) 248 249 goal = np.array([10, 10]) 250 251 # matrix二维矩阵 252 ob = np.matrix([[-1, -1], 253 [0, 2], 254 [4.0, 2.0], 255 [5.0, 4.0], 256 [5.0, 5.0], 257 [5.0, 6.0], 258 [5.0, 9.0], 259 [8.0, 9.0], 260 [7.0, 9.0], 261 [12.0, 12.0] 262 ]) 263 # ob = np.matrix([[0, 2]]) 264 u = np.array([0.2, 0.0]) 265 config = Config() 266 trajectory = np.array(x) 267 268 for i in range(1000): 269 270 u, best_trajectory, trajectory_space = dwa_control(x, u, config, goal, ob) 271 272 x = motion(x, u, config.dt) 273 # print(x) 274 275 trajectory = np.vstack((trajectory, x)) # store state history 276 277 if show_animation: 278 draw_dynamic_search(best_trajectory, x, goal, ob, trajectory_space) 279 280 # check goal 281 if math.sqrt((x[0] - goal[0]) ** 2 + (x[1] - goal[1]) ** 2) <= config.robot_radius: 282 print("Goal!!") 283 284 break 285 286 print("Done") 287 288 # draw_path(trajectory, goal, ob, x) 289 290 291 def draw_dynamic_search(best_trajectory, x, goal, ob, trajectory_space): 292 """ 293 画出动态搜索过程图 294 :return: 295 """ 296 plt.cla() # 清除上次绘制图像 297 plt.plot(best_trajectory[:, 0], best_trajectory[:, 1], "-g") 298 299 plt.plot(trajectory_space[:, 0], trajectory_space[:, 1], '-g') 300 301 plt.plot(x[0], x[1], "xr") 302 plt.plot(0, 0, "og") 303 plt.plot(goal[0], goal[1], "ro") 304 plt.plot(ob[:, 0], ob[:, 1], "bs") 305 plot_arrow(x[0], x[1], x[2]) 306 plt.axis("equal") 307 plt.grid(True) 308 plt.pause(0.0001) 309 310 311 def draw_path(trajectory, goal, ob, x): 312 """ 313 画图函数 314 :return: 315 """ 316 plt.cla() # 清除上次绘制图像 317 318 plt.plot(x[0], x[1], "xr") 319 plt.plot(0, 0, "og") 320 plt.plot(goal[0], goal[1], "ro") 321 plt.plot(ob[:, 0], ob[:, 1], "bs") 322 plot_arrow(x[0], x[1], x[2]) 323 plt.axis("equal") 324 plt.grid(True) 325 plt.plot(trajectory[:, 0], trajectory[:, 1], 'r') 326 plt.show() 327 328 329 if __name__ == '__main__': 330 main()