【路径规划】局部路径规划算法——DWA算法(动态窗口法)|(含python实现)

news2024/11/25 6:57:22

文章目录

  • 参考资料
  • 1. DWA算法原理
    • 1.1 简介
    • 1.2 算法原理
      • 1. 速度采样
      • 2. 轨迹预测(轨迹推算)
      • 3. 轨迹评价
  • 2. Python实现
    • 2.1 参数配置
    • 2.2 机器人运动学模型
    • 2.3 DWA算法类实现
    • 2.4 画图
    • 2.5 主函数
  • 3. 总结

参考资料

  • The Dynamic Window Approach to Collision Avoidance
  • 基于室内多障碍物复杂环境的路径规划方法研究
  • 基于改进A*算法与改进DWA算法的无人驾驶汽车路径规划研究
  • 室内移动机器人路径规划方法研究
  • 机器人局部避障的动态窗口法

1. DWA算法原理

1.1 简介

  • 动态窗口算法(Dynamic Window Approaches, DWA) 是基于预测控制理论的一种次优方法,因其在未知环境下能够安全、有效的避开障碍物, 同时具有计算量小, 反应迅速、可操作性强等特点。

  • DWA算法属于局部路径规划算法。

  • DWA算法的核心思想是根据移动机器人当前的位置状态和速度状态在速度空间 ( v , ω ) (v, \omega) (v,ω) 中确定一个满足移动机器人硬件约束的采样速度空间,然后计算移动机器人在这些速度情况下移动一定时间内的轨迹, 并通过评价函数对该轨迹进行评价,最后选出评价最优的轨迹所对应的速度来作为移动机器人运动速度, 如此循环直至移动机器人到达目标点。

  • 对于无人驾驶汽车而言,情况类似,将车辆的位置变化转化为线速度和角速度控制,避障问题转变成空间中的运动约束问题,这样可以通过运动约束条件选择局部最优的路径。

1.2 算法原理

  • DWA算法的思路是:先通过机器人数学模型采集机器人速度样本,并预测模拟出在样本速度下一段时间内生成的运动轨迹, 并对这些运动轨迹进行标准评价, 选择出一组最优轨迹,机器人按照最优轨迹运动。机器人的运动姿态和方向是由机器人当前的线速度及角速度 (转向速度) 共同决定的。

  • DWA算法主要包括3个步骤:速度采样、轨迹预测(推算)、轨迹评价。

1. 速度采样

由于移动机器人硬件、结构和环境等限制条件,移动机器人的速度采样空间 V s \mathbf{V}_{\mathrm{s}} Vs ( v , ω ) (v, \omega) (v,ω) 有一定的范围限制。该限制主要分为三大类:

  • 速度边界限制

    根据移动机器人的硬件条件和环境限制, 移动机器人的速度存在的边界限制, 此时可采样的速度空间 V m \mathbf{V}_m Vm
    V m = { ( v , ω ) ∣ v ∈ [ v min  , v max  ] , ω ∈ [ ω min  , ω max  ] } (1) \tag{1} \mathbf{V}_m=\left\{(v, \omega) \mid v \in\left[v_{\text {min }}, v_{\text {max }}\right], \omega \in\left[\omega_{\text {min }}, \omega_{\text {max }}\right]\right\} Vm={(v,ω)v[vmin ,vmax ],ω[ωmin ,ωmax ]}(1)
    式中 v min  、 v max  v_{\text {min }} 、 v_{\text {max }} vmin vmax  分别为移动机器人最小线速度和最大线速度, ω min  、 ω max ⁡ \omega_{\text {min }} 、 \omega_{\max } ωmin ωmax 分别 为移动机器人最小角速度和最大角速度。

  • 加速度限制

    由于移动机器人的驱动电机的限制, 故移动机器人的线加速度和角加速度均存在边界限制,假设最大加速度和减速度大小一样,故考虑加速度时可采样的速度空间 V d \mathbf{V}_d Vd
    V d = { ( v , ω ) ∣ v ∈ [ v c − a v max ⁡ ⋅ Δ t , v c + a v max ⁡ ∙ Δ t ] ω ∈ [ ω c − a w max ⁡ ∙ Δ t , ω c + a w max ⁡ ∙ Δ t ] } (2) \tag{2} \mathbf{V}_d=\left\{(v, \omega) \mid \begin{array}{l} v \in\left[v_c-a_{v \max } \cdot \Delta t, v_c+a_{v \max } \bullet \Delta t\right] \\ \omega \in\left[\omega_c-a_{w \max } \bullet \Delta t, \omega_c+a_{w \max } \bullet \Delta t\right] \end{array}\right\} Vd={(v,ω)v[vcavmaxΔt,vc+avmaxΔt]ω[ωcawmaxΔt,ωc+awmaxΔt]}(2)
    式中 v c 、 ω c v_c 、 \omega_c vcωc 分别为移动机器人当前时刻的线速度和角速度, a v max ⁡ 、 a w  max  a_{v \max } 、 a_{w \text { max }} avmaxaw max  分别为 移动机器人最大线加速度和最大角加速度。

  • 环境障碍物限制

    局部规划还需要有动态实时的避障功能。考虑移动机器人的周围的障碍物因素,某一时刻移动机器人不与周围障碍物发生碰撞的可约束条件为
    V a = { ( v , ω ) ∣ v ∈ [ v min ⁡ , 2 ⋅ d i s t ( v , ω ) ⋅ a v max ⁡ ] ω ∈ [ ω min ⁡ , 2 ⋅ d i s t ( v , ω ) ⋅ a ω max ⁡ ] } (3) \tag{3} \mathbf{V}_a=\left\{(v, \omega) \mid \begin{array}{l} v \in\left[v_{\min}, \sqrt{2\cdot dist(v,\omega)\cdot a_{v \max}}\right] \\ \omega \in\left[\omega_{\min}, \sqrt{2\cdot dist(v,\omega)\cdot a_{\omega \max}}\right] \end{array}\right\} Va= (v,ω)v[vmin,2dist(v,ω)avmax ]ω[ωmin,2dist(v,ω)aωmax ] (3)

    式中 dist ⁡ ( v , ω ) \operatorname{dist}(v, \omega) dist(v,ω)表示当前速度下对应模拟轨迹与障碍物之间的最近距离。 在无障碍物的情况下 dist ⁡ ( v , ω ) \operatorname{dist}(v, \omega) dist(v,ω) 会是一个很大的常数值。当机器人运行采样速度在公式 (3) 范围时, 能够以最大减速度的约束实现安全减速直至避开障碍物。

    注意: 这个限制条件在采样初期是得不到的,需要我们先使用 V m ∩ V d \mathbf{V}_m \cap \mathbf{V}_d VmVd的速度组合采样模拟出轨迹后, 计算当前速度下对应模拟轨迹与障碍物之间的最近距离, 然后看当前采样的这对速度能否在碰到障碍物之前停下来, 如果能够停下来, 那这对速度就是可接收的。如果不能停下来, 这对速度就得抛弃掉。

    我在代码中并没有采用这种做法,而是直接计算机器人当前位置(并不是模拟轨迹)与障碍物的最近距离来得到 V a \mathbf{V}_a Va,算是一种比较投机的做法。

结合上述三类速度限制, 最终的移动机器人速度采样空间是三个速度空间的交集,即
V s = V m ∩ V d ∩ V a (4) \tag{4} \mathbf{V}_s=\mathbf{V}_m \cap \mathbf{V}_d \cap \mathbf{V}_a Vs=VmVdVa(4)

2. 轨迹预测(轨迹推算)

  • 在确定速度采样空间 V s \mathbf{V}_s Vs 后,DWA算法以一定的采样间距(分辨率)在该空间均匀采样。

  • 在速度空间中, 分别对线速度和角速度设置分辨率, 分别用 E w 、 E v E_w 、 E_v EwEv 表示采样分辨率,那么采样速度组的个数就可以确定下来, 如下式所示。
    n = [ ( v h i g h − v l o w ) / E v ] ⋅ [ ( w h i g h − w l o w ) / E w ] n=\left[\left(v_{high}-v_{low }\right) / E_v\right]\cdot\left[\left(w_{high }-w_{low }\right) / E_w\right] n=[(vhighvlow)/Ev][(whighwlow)/Ew]
    式中的 v h i g h , v l o w , w h i g h , w l o w v_{high},v_{low },w_{high },w_{low } vhigh,vlow,whigh,wlow是速度空间的上下限。

    上式说明线速度每间隔一个 E v E_v Ev 大小取一个值, 角速度每间隔一个 E w E_w Ew 大小取一个值,由此组成了一系列的速度组。

  • 当采样了一组 ( v , ω ) (v, \omega) (v,ω) 后, 通过移动机器人(无人车辆)的运动学模型进行轨迹预测(即位置更新)。

  • 因此,在轨迹预测阶段,只需要知道机器人(无人车辆)的运动学模型即可,然后在预测时间内保存预测轨迹便于后面处理。有关无人车的运动学模型可参考这篇博客。

  • 这边不妨使用差分驱动移动机器人的运动学模型,那么轨迹预测就是计算下式:
    x k = x k − 1 + v ⋅ cos ⁡ ( θ k − 1 ) Δ t y k = y k − 1 + v ⋅ sin ⁡ ( θ k − 1 ) Δ t θ k = θ k − 1 + ω Δ t \begin{aligned} x_k&=x_{k-1}+v\cdot \cos{(\theta_{k-1})}\Delta t\\ y_k&=y_{k-1}+v\cdot \sin{(\theta_{k-1})}\Delta t\\ \theta_k &= \theta_{k-1}+\omega \Delta t \end{aligned} xkykθk=xk1+vcos(θk1)Δt=yk1+vsin(θk1)Δt=θk1+ωΔt
    式中, ( x , y , θ ) (x,y,\theta) (x,y,θ)代表机器人的位姿, k k k代表采样时刻, Δ t \Delta t Δt表示采样间隔。

上面轨迹推算这块是我比较肤浅的理解,因为按照原论文是需要假设相邻时间段内机器人的轨迹是圆弧,然后再进行轨迹推算,感兴趣的朋友可以直接查看原论文。

3. 轨迹评价

确定了机器人约束速度范围后,有一些速度模拟的轨迹是可行的, 但是还有不达标的轨迹, 这需要对采样得到的多组轨迹进行评价择优。通过标准评价轨迹, 比较评分来选出最优轨迹, 然后选取最优轨迹对应的速度作为驱动速度。对每条轨迹进行评估 的评价函数如公式 ( 5 ) (5) (5) 所示。
G ( v , ω ) = σ ( α ⋅ heading ⁡ ( v , ω ) ) + σ ( β ⋅ dist ⁡ ( v , ω ) ) + σ ( γ ⋅ velocity ⁡ ( v , ω ) ) (5) \tag{5} G(v, \omega)=\sigma(\alpha \cdot \operatorname{heading}(v, \omega))+\sigma(\beta \cdot \operatorname{dist}(v, \omega))+\sigma(\gamma \cdot \operatorname{velocity}(v, \omega)) G(v,ω)=σ(αheading(v,ω))+σ(βdist(v,ω))+σ(γvelocity(v,ω))(5)

  • heading ( v , ω ) (v, \omega) (v,ω) 是方位角评价函数, 用作评估在当前采样速度下产生的轨迹终点位置方向与目标点连线的夹角的误差 Δ θ \Delta\theta Δθ;由于我们想要用评价函数越大表示越优,所以用 π − Δ θ \pi-\Delta\theta πΔθ来参与评价,即 h e a d i n g ( v , ω ) = π − Δ θ heading (v, \omega)= \pi-\Delta\theta heading(v,ω)=πΔθ

  • dist ⁡ ( v , ω ) \operatorname{dist}(v, \omega) dist(v,ω) 是距离评价函数, 表示当前速度下对应模拟轨迹与障碍物之间的最近距离;如果没有障碍物或者最近距离大于设定的阈值,那么就将其值设为一个较大的常数值。

  • velocity ( v , ω ) (v, \omega) (v,ω) 是速度评价函数, 表示当前的速度大小,可以直接用当前线速度的大小来表示。它越大,表示规划轨迹上的速度越快,评价得分越高。

以上三种评价函数只是给了个大体的意思,并不绝对,例如有的人是把评价函数作为代价,代价越小,轨迹越优。可根据自己的想法进行评价函数的设置,但无论怎么变,这三种评价函数都是需要的。

α 、 β \alpha 、 \beta αβ γ \gamma γ 均为评价函数的系数。由于局部路径规划的过程需要多传感器的采集, 采集信息无法做到连续, 这样也会使得评价后差别较大, 所以可以进行归一化处理(平滑处理), 其中 σ \sigma σ 表示 归一化。

  • 归一化处理过程如下式 (6) 所示:
    σ ⋅  heading  ( v , ω ) = normalize-heading ( i ) = heading ⁡ ( i ) ∑ i = 1 n heading ⁡ ( i ) σ ⋅ dist ⁡ ( v , ω ) =  normalize-dist  ( i ) = dist ⁡ ( i ) ∑ i = 1 n dist ⁡ ( i ) σ ⋅ velocity ⁡ ( v , ω ) =  normalize-velocity  ( i ) =  velocity  ( i ) ∑ i = 1 n  velocity  ( i ) (6) \tag{6} \begin{aligned} &\sigma \cdot \text { heading }(v, \omega)=\text {normalize-heading}(i)=\frac{\operatorname{heading}(i)}{\sum_{i=1}^n \operatorname{heading}(i)} \\ &\sigma \cdot \operatorname{dist}(v, \omega)=\text { normalize-dist }(i)=\frac{\operatorname{dist}(i)}{\sum_{i=1}^n \operatorname{dist}(i)}\\ &\sigma \cdot \operatorname{velocity}(v, \omega)=\text { normalize-velocity }(i)=\frac{\text { velocity }(i)}{\sum_{i=1}^n \text { velocity }(i)} \end{aligned} σ heading (v,ω)=normalize-heading(i)=i=1nheading(i)heading(i)σdist(v,ω)= normalize-dist (i)=i=1ndist(i)dist(i)σvelocity(v,ω)= normalize-velocity (i)=i=1n velocity (i) velocity (i)(6)

    其中, i i i 代表第 i i i条模拟轨迹, n n n 为约束条件下的全部采样轨迹总数。由上述公式 (5) 和公式 (6) 可以得出一条满足避开障碍物并朝着目标点快速行进的路径, 使得机器人完成局部路径最优规划。

DWA算法的流程如下图所示。

2. Python实现

完整程序请移步github仓库。
本次代码的参数配置画图代码参考了AtsushiSakai/PythonRobotics 。

2.1 参数配置

使用一个类来保存需要设置的参数。

import numpy as np
import matplotlib.pyplot as plt
import copy
from celluloid import Camera  # 保存动图时用,pip install celluloid
import math

class Config:
    """
    simulation parameter class
    """

    def __init__(self):
        # robot parameter
        # 线速度边界
        self.v_max = 1.0  # [m/s]
        self.v_min = -0.5  # [m/s]
        # 角速度边界
        self.w_max = 40.0 * math.pi / 180.0  # [rad/s]
        self.w_min = -40.0 * math.pi / 180.0  # [rad/s]
        # 线加速度和角加速度最大值
        self.a_vmax = 0.2  # [m/ss]
        self.a_wmax = 40.0 * math.pi / 180.0  # [rad/ss]
        # 采样分辨率 
        self.v_sample = 0.01  # [m/s]
        self.w_sample = 0.1 * math.pi / 180.0  # [rad/s]
        # 离散时间间隔
        self.dt = 0.1  # [s] Time tick for motion prediction
        # 轨迹推算时间长度
        self.predict_time = 3.0  # [s]
        # 轨迹评价函数系数
        self.alpha = 0.15
        self.beta = 1.0
        self.gamma = 1.0

        # Also used to check if goal is reached in both types
        self.robot_radius = 1.0  # [m] for collision check
        
        self.judge_distance = 10 # 若与障碍物的最小距离大于阈值(例如这里设置的阈值为robot_radius+0.2),则设为一个较大的常值

        # 障碍物位置 [x(m) y(m), ....]
        self.ob = np.array([[-1, -1],
                    [0, 2],
                    [4.0, 2.0],
                    [5.0, 4.0],
                    [5.0, 5.0],
                    [5.0, 6.0],
                    [5.0, 9.0],
                    [8.0, 9.0],
                    [7.0, 9.0],
                    [8.0, 10.0],
                    [9.0, 11.0],
                    [12.0, 13.0],
                    [12.0, 12.0],
                    [15.0, 15.0],
                    [13.0, 13.0]
                    ])
        # 目标点位置
        self.target = np.array([10,10])

值得注意的是,这边障碍物只给了位置,并没有给定大小,因为这边相当于把障碍物的大小合并到了机器人本体大小上,也即代码中的robot_radius上。

2.2 机器人运动学模型

主要用于轨迹推算。

def KinematicModel(state,control,dt):
  """机器人运动学模型

  Args:
      state (_type_): 状态量---x,y,yaw,v,w
      control (_type_): 控制量---v,w,线速度和角速度
      dt (_type_): 离散时间

  Returns:
      _type_: 下一步的状态
  """
  state[0] += control[0] * math.cos(state[2]) * dt
  state[1] += control[0] * math.sin(state[2]) * dt
  state[2] += control[1] * dt
  state[3] = control[0]
  state[4] = control[1]

  return state

在这里顺便保存了线速度和角速度作为状态分量,便于后面轨迹计算。

2.3 DWA算法类实现

通过一个类来实现DWA算法的速度采样、轨迹预测、轨迹评价三个主要步骤。

class DWA:
    def __init__(self,config) -> None:
        """初始化

        Args:
            config (_type_): 参数类
        """
        self.dt=config.dt
        self.v_min=config.v_min
        self.w_min=config.w_min
        self.v_max=config.v_max
        self.w_max=config.w_max
        self.predict_time = config.predict_time
        self.a_vmax = config.a_vmax
        self.a_wmax = config.a_wmax
        self.v_sample = config.v_sample # 线速度采样分辨率
        self.w_sample = config.w_sample # 角速度采样分辨率
        self.alpha = config.alpha
        self.beta = config.beta
        self.gamma = config.gamma
        self.radius = config.robot_radius
        self.judge_distance = config.judge_distance

    def dwa_control(self,state,goal,obstacle):
        """滚动窗口算法入口

        Args:
            state (_type_): 机器人当前状态--[x,y,yaw,v,w]
            goal (_type_): 目标点位置,[x,y]

            obstacle (_type_): 障碍物位置,dim:[num_ob,2]

        Returns:
            _type_: 控制量、轨迹(便于绘画)
        """
        control,trajectory = self.trajectory_evaluation(state,goal,obstacle)
        return control,trajectory


    def cal_dynamic_window_vel(self,v,w,state,obstacle):
        """速度采样,得到速度空间窗口

        Args:
            v (_type_): 当前时刻线速度
            w (_type_): 当前时刻角速度
            state (_type_): 当前机器人状态
            obstacle (_type_): 障碍物位置
        Returns:
            [v_low,v_high,w_low,w_high]: 最终采样后的速度空间
        """
        Vm = self.__cal_vel_limit()
        Vd = self.__cal_accel_limit(v,w)
        Va = self.__cal_obstacle_limit(state,obstacle)
        a = max([Vm[0],Vd[0],Va[0]])
        b = min([Vm[1],Vd[1],Va[1]])
        c = max([Vm[2], Vd[2],Va[2]])
        d = min([Vm[3], Vd[3],Va[3]])
        return [a,b,c,d]

    def __cal_vel_limit(self):
        """计算速度边界限制Vm

        Returns:
            _type_: 速度边界限制后的速度空间Vm
        """
        return [self.v_min,self.v_max,self.w_min,self.w_max]
    
    def __cal_accel_limit(self,v,w):
        """计算加速度限制Vd

        Args:
            v (_type_): 当前时刻线速度
            w (_type_): 当前时刻角速度
        Returns: 
            _type_:考虑加速度时的速度空间Vd
        """
        v_low = v-self.a_vmax*self.dt
        v_high = v+self.a_vmax*self.dt
        w_low = w-self.a_wmax*self.dt
        w_high = w+self.a_wmax*self.dt
        return [v_low, v_high,w_low, w_high]
    
    def __cal_obstacle_limit(self,state,obstacle):
        """环境障碍物限制Va

        Args:
            state (_type_): 当前机器人状态
            obstacle (_type_): 障碍物位置

        Returns:
            _type_: 某一时刻移动机器人不与周围障碍物发生碰撞的速度空间Va
        """
        v_low=self.v_min
        v_high = np.sqrt(2*self._dist(state,obstacle)*self.a_vmax)
        w_low =self.w_min
        w_high = np.sqrt(2*self._dist(state,obstacle)*self.a_wmax)
        return [v_low,v_high,w_low,w_high]

    def trajectory_predict(self,state_init, v,w):
        """轨迹推算

        Args:
            state_init (_type_): 当前状态---x,y,yaw,v,w
            v (_type_): 当前时刻线速度
            w (_type_): 当前时刻线速度

        Returns:
            _type_: _description_
        """
        state = np.array(state_init)
        trajectory = state
        time = 0
        # 在预测时间段内
        while time <= self.predict_time:
            x = KinematicModel(state, [v,w], self.dt) # 运动学模型
            trajectory = np.vstack((trajectory, x))
            time += self.dt

        return trajectory

    def trajectory_evaluation(self,state,goal,obstacle):
        """轨迹评价函数,评价越高,轨迹越优

        Args:
            state (_type_): 当前状态---x,y,yaw,v,w
            dynamic_window_vel (_type_): 采样的速度空间窗口---[v_low,v_high,w_low,w_high]
            goal (_type_): 目标点位置,[x,y]
            obstacle (_type_): 障碍物位置,dim:[num_ob,2]

        Returns:
            _type_: 最优控制量、最优轨迹
        """
        G_max = -float('inf') # 最优评价
        trajectory_opt = state # 最优轨迹
        control_opt = [0.,0.] # 最优控制
        dynamic_window_vel = self.cal_dynamic_window_vel(state[3], state[4],state,obstacle) # 第1步--计算速度空间
        
        # sum_heading,sum_dist,sum_vel = 0,0,0 # 统计全部采样轨迹的各个评价之和,便于评价的归一化
        # # 在本次实验中,不进行归一化也可实现该有的效果。
        # for v in np.arange(dynamic_window_vel[0],dynamic_window_vel[1],self.v_sample):
        #     for w in np.arange(dynamic_window_vel[2], dynamic_window_vel[3], self.w_sample):   
        #         trajectory = self.trajectory_predict(state, v, w)  

        #         heading_eval = self.alpha*self.__heading(trajectory,goal)
        #         dist_eval = self.beta*self.__dist(trajectory,obstacle)
        #         vel_eval = self.gamma*self.__velocity(trajectory)
        #         sum_vel+=vel_eval
        #         sum_dist+=dist_eval
        #         sum_heading +=heading_eval

        sum_heading,sum_dist,sum_vel = 1,1,1 # 不进行归一化
        # 在速度空间中按照预先设定的分辨率采样
        for v in np.arange(dynamic_window_vel[0],dynamic_window_vel[1],self.v_sample):
            for w in np.arange(dynamic_window_vel[2], dynamic_window_vel[3], self.w_sample):

                trajectory = self.trajectory_predict(state, v, w)  # 第2步--轨迹推算

                heading_eval = self.alpha*self.__heading(trajectory,goal)/sum_heading
                dist_eval = self.beta*self.__dist(trajectory,obstacle)/sum_dist
                vel_eval = self.gamma*self.__velocity(trajectory)/sum_vel
                G = heading_eval+dist_eval+vel_eval # 第3步--轨迹评价

                if G_max<=G:
                    G_max = G
                    trajectory_opt = trajectory
                    control_opt = [v,w]

        return control_opt, trajectory_opt

    def _dist(self,state,obstacle):
        """计算当前移动机器人距离障碍物最近的几何距离

        Args:
            state (_type_): 当前机器人状态
            obstacle (_type_): 障碍物位置

        Returns:
            _type_: 移动机器人距离障碍物最近的几何距离
        """
        ox = obstacle[:, 0]
        oy = obstacle[:, 1]
        dx = state[0,None] - ox[:, None]
        dy = state[1,None] - oy[:, None]
        r = np.hypot(dx, dy)
        return np.min(r)

    def __dist(self,trajectory,obstacle):
        """距离评价函数
        表示当前速度下对应模拟轨迹与障碍物之间的最近距离;
        如果没有障碍物或者最近距离大于设定的阈值,那么就将其值设为一个较大的常数值。
        Args:
            trajectory (_type_): 轨迹,dim:[n,5]
            
            obstacle (_type_): 障碍物位置,dim:[num_ob,2]

        Returns:
            _type_: _description_
        """
        ox = obstacle[:, 0]
        oy = obstacle[:, 1]
        dx = trajectory[:, 0] - ox[:, None]
        dy = trajectory[:, 1] - oy[:, None]
        r = np.hypot(dx, dy)
        return np.min(r) if np.array(r <self.radius+0.2).any() else self.judge_distance

    def __heading(self,trajectory, goal):
        """方位角评价函数
        评估在当前采样速度下产生的轨迹终点位置方向与目标点连线的夹角的误差

        Args:
            trajectory (_type_): 轨迹,dim:[n,5]
            goal (_type_): 目标点位置[x,y]

        Returns:
            _type_: 方位角评价数值
        """
        dx = goal[0] - trajectory[-1, 0]
        dy = goal[1] - trajectory[-1, 1]
        error_angle = math.atan2(dy, dx)
        cost_angle = error_angle - trajectory[-1, 2]
        cost = math.pi-abs(cost_angle)

        return cost

    def __velocity(self,trajectory):
        """速度评价函数, 表示当前的速度大小,可以用模拟轨迹末端位置的线速度的大小来表示

        Args:
            trajectory (_type_): 轨迹,dim:[n,5]

        Returns:
            _type_: 速度评价
        """
        return trajectory[-1,3]

从后面的实验效果上看,这边没有进行归一化也是可以的。

2.4 画图

主要用于机器人和方向箭头的绘制。

def plot_arrow(x, y, yaw, length=0.5, width=0.1):  # pragma: no cover
    plt.arrow(x, y, length * math.cos(yaw), length * math.sin(yaw),
              head_length=width, head_width=width)
    plt.plot(x, y)


def plot_robot(x, y, yaw, config):  # pragma: no cover
        circle = plt.Circle((x, y), config.robot_radius, color="b")
        plt.gcf().gca().add_artist(circle)
        out_x, out_y = (np.array([x, y]) +
                        np.array([np.cos(yaw), np.sin(yaw)]) * config.robot_radius)
        plt.plot([x, out_x], [y, out_y], "-k")

2.5 主函数

def main(config):
    # initial state [x(m), y(m), yaw(rad), v(m/s), omega(rad/s)]
    x = np.array([0.0, 0.0, math.pi / 8.0, 0.0, 0.0])
    # goal position [x(m), y(m)]
    goal = config.target

    # input [forward speed, yaw_rate]

    trajectory = np.array(x)
    ob = config.ob
    dwa = DWA(config)
    fig=plt.figure(1)
    camera = Camera(fig)
    while True:
        u, predicted_trajectory = dwa.dwa_control(x,goal, ob)

        x = KinematicModel(x, u, config.dt)  # simulate robot
        trajectory = np.vstack((trajectory, x))  # store state history
        plt.cla()
        # for stopping simulation with the esc key.
        plt.gcf().canvas.mpl_connect(
            'key_release_event',
            lambda event: [exit(0) if event.key == 'escape' else None])
        plt.plot(predicted_trajectory[:, 0], predicted_trajectory[:, 1], "-g")
        plt.plot(x[0], x[1], "xr")
        plt.plot(goal[0], goal[1], "xb")
        plt.plot(ob[:, 0], ob[:, 1], "ok")
        plot_robot(x[0], x[1], x[2], config)
        plot_arrow(x[0], x[1], x[2])
        plt.axis("equal")
        plt.grid(True)
        plt.pause(0.001)

        # check reaching goal
        dist_to_goal = math.hypot(x[0] - goal[0], x[1] - goal[1])
        if dist_to_goal <= config.robot_radius:
            print("Goal!!")
            break
        # camera.snap()
        # print(x)
        # print(u)

    print("Done")
    plt.plot(trajectory[:, 0], trajectory[:, 1], "-r")
    plt.pause(0.001)
    # camera.snap()
    # animation = camera.animate()
    # animation.save('trajectory.gif')
    plt.show()


main(Config())

运行主函数,即可得到下图所示的效果:

在这里插入图片描述

3. 总结

起初在不了解之前我以为DWA算法是一种很难实现的算法,但最近因为毕设想要用到这个传统方法进行对比,所以花了点时间去看别人写的论文,发现DWA算法的原理比较简单,代码实现也比较简单,并没有我想象中得那么困难。所以还是不能有畏难心理

在实际调试过程中发现评价函数的设置非常重要,设置的不好,以及参数设置的不好(例如评价函数的系数),那么就极有可能算法失效。也看了一些别人写的论文,针对DWA算法的改进基本也是集中在评价函数上,所以如果需要改进效果的话不妨从这上面入手。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/4633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二分查找的模板

这篇博客的二分用的都是左闭右闭的区间&#xff0c;对于二分来说还是我还是习惯这样写 最传统的二分查找&#xff0c;用左闭右闭写 int search(vector<int>& nums, int target) {int left 0;int right nums.size() - 1; // 定义target在左闭右闭的区间里&#xff0…

Mybatis学习之动态Sql

目录 1. 什么是动态Sql 2. 动态Sql需要学习什么 3. 动态Sql之《if》 4. 动态Sql之《where》 5. 动态Sql之《foreach》 6. 动态Sql之《sql》 7. PageHelper分页插件的使用 1. 什么是动态Sql 答案&#xff1a;动态Sql指的是&#xff0c;Sql语句是变化的&#xff0c;不是固…

Allegro原理图反标教程

Allegro原理图反标教程 Logic→Auto Rename Refdes→Rename 点击More进行详细设置 按照下图设置 点击Rename 打开刚刚rename时生成的rename.log文件,需要提取一些数据,如下图 将上图所有带有OLD和NEW的行提取出来,再将OLD,NEW删除 打开Capture,点击Tools→Back Annota…

C++中运行一个程序的内存分配情况及qt中的内存管理机制

一个由c/C编译的程序占用的内存分为以下几个部分 1、栈区&#xff08;stack&#xff09;— 由编译器自动分配释放 &#xff0c;存放函数的参数值&#xff0c;局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区&#xff08;heap&#xff09; — 一般由程序员分配释放&a…

【springboot】你了解@Autowired 和 @Resource吗?@Autowired 和 @Resource深入分析

Autowired 和 Resource深入分析“认祖归宗”--Autowired 和 Resource来源“通过现象看本质”--Autowired 和 Resource作用和区别1.现象一&#xff1a;一个业务接口只对应一个业务实现类2.现象二&#xff1a;一个业务接口 对应 两个或多个业务实现类我们在开发中&#xff0c;一直…

【数据结构】链表其实并不难 —— 手把手带你实现双向链表

文章目录0. 前言1. 双向链表的概念2. 双向链表的实现2.1 结构设计2.2 接口总览2.3 初始化2.4 创建新节点2.5 尾插2.6 头插2.7 尾删2.8 头删2.9 查找2.10 在pos位置之前插入2.11 在pos位置删除2.12 打印2.13 销毁3. 完整代码List.hList.ctest.c4. 结语0. 前言 之前&#xff0c;…

【Python百日进阶-WEB开发】Day175 - Django案例:07状态保持

文章目录五、状态保持5.1 Django中状态保持5.1.1 状态保持概述5.1.2 Cookie5.1.2.1 Cookie的用处&#xff1a;5.1.2.1 Cookie的特点&#xff1a;5.1.2.1 Cookie的操作&#xff1a;5.1.3 session5.1.3.1 Session的特点&#xff1a;5.1.3.2 Session依赖于Cookie5.1.3.3 存储方式5…

网页数据抓取-网页实时数据抓取软件

网页数据抓取&#xff0c;随着社会的发展&#xff0c;互联网的普及&#xff0c;不管是企业还是个人都意识到数据的重要性。今天给大家分享一款免费的网页数据抓取软件。只要点点鼠标就能轻松采集你想要的内容不管是导出还是自动发布都支持&#xff01;详细参考图片&#xff01;…

Qlib股票数据获取与查看(Qlib学习1)

文章目录Qlib基本信息数据使用方法1. 借助Qlib下载数据2. 查看相关数据参考链接Qlib基本信息 Qlib Github主页&#xff1a;https://github.com/microsoft/qlib Qlib quickstart&#xff1a;https://qlib.readthedocs.io/en/latest/introduction/quick.html#introduction 基本…

LeetCode刷题---142. 环形链表 II(双指针-快慢指针)

文章目录一、编程题&#xff1a;142. 环形链表 II&#xff08;双指针-快慢指针&#xff09;1.题目描述2.示例1&#xff1a;3.示例2&#xff1a;4.示例3&#xff1a;5.提示&#xff1a;6.提示&#xff1a;二、解题思路1.思路2.复杂度分析&#xff1a;3.算法图解三、代码实现总结…

如何理解Linux下一切皆文件

文章目录一、问题抛出二、如何理解三、Linux源码验证一、问题抛出 Linux中普通文件、目录、字符设备、块设备、网络设备等都被当做文件来对待。虽然他们的类型不同&#xff0c;但是Linux中提供了统一的操作接口。  普通文件、目录文件显然非常好理解&#xff0c;因此在本文中&…

今日论文阅读2022-11-10

多模态预训练论文ViLBERT: Pretraining Task-Agnostic Visiolinguistic Representations for Vision-and-Language Tasksvision-and-language tasks&#xff1a; visual question answering,visual commonsense reasoning, referring expressions, and caption-based image ret…

基于DeepLabV3实践路面、桥梁、基建裂缝裂痕分割

在我前面的文章中有基于改进的模型开发的裂缝裂痕检测模型&#xff0c;感兴趣的话可以看下&#xff1a; 《基于yolov5sbifpn实践隧道裂缝裂痕检测》 今天主要是趁着有时间基于deeplabv3来实践裂缝裂痕分割。首先来看效果图&#xff1a; 为了整体直观&#xff0c;这里专门是开…

腾讯蓝鲸 API 网关如何借助 APISIX 实现产品升级与业务完善

分享嘉宾朱雷&#xff0c;腾讯 IEG 运维 PaaS 平台技术负责人。 蓝鲸&#xff08;全名“蓝鲸智云”&#xff09;是一套孵化于腾讯 IEG&#xff08;互动娱乐事业群&#xff09;内部&#xff0c;服务于多业务与各内部平台的研运一体化 PaaS。 其作用是在 CI、CD 和 CO 三个阶段&a…

Spring 概述

Spring是 Java 应用程序开发框架。 Spring 框架的目标是使 J2EE 开发变得更容易使用&#xff0c;通过启用基于 POJO编程模型来促进良好的编程实践。 Spring Framework Spring 基础框架是 Spring Framework &#xff0c;基本上任何其他 Spring 项目都是以 Spring Framework 为…

如何进入 mysql?

目录 1. win r 2. 输入cmd点确定 3. 输入 mysql -u -t 4. 点回车出现 下面的 就代表已经进入 mysql 退出 mysql的 方法&#xff1a; 1. win r 2. 输入cmd点确定 3. 输入 mysql -u -t -u &#xff1a;代表你的用户名&#xff0c;如果是本地登录 则为 -uroot-p &am…

学习python第7天

Python绘制图形库turtle 1.介绍&#xff1a; turtle库根据一组函数指令的控制&#xff0c;在平面坐标系中移动&#xff0c;从 而它爬行的路径上绘制图形。 2.原理&#xff1a;turtle(海龟&#xff09;由程序控制在画布上游走&#xff0c;走过的轨迹形成绘 制的图形&#xff0c…

子不语IPO下限定价:预计2022年全年净利润下滑,华丙如为实控人

11月10日&#xff0c;子不语集团有限公司&#xff08;HK:02420&#xff0c;下称“子不语”&#xff09;在港交所公布发售结果。公告显示&#xff0c;子不语在香港公开发售及国际配售&#xff08;不含基石部分&#xff09;阶段均获得超额认购&#xff0c;将于2022年11月11日在港…

【前端】Vue+Element UI案例:通用后台管理系统-登陆页面Login

文章目录目标代码0.路由1.结构2.校验规则3.样式总代码Login.vue效果本篇很短&#xff0c;因为只有一个页面。没有功能。 目标 登陆页面&#xff0c;路由为/login有表单验证 代码 0.路由 在router的index.js文件中的routes中添加对象&#xff1a; {path:/login,component:L…

SpringBoot+Mybatis+CRUD项目

一、项目要求 创建一个 SpringBoot 项目&#xff0c;项目名”week11_学号”&#xff1b;使用 Mybatis 框架&#xff0c;也可以时可用 MybatisPlus 框架&#xff1b;访问 myschool 数据库&#xff1b;对 student 表进行操作&#xff0c;向 student 插入自己的一条记录&#xff…