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

news2025/1/16 16:17:41

文章目录

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

参考资料

  • 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. c++实现

由于在自动驾驶中算法实现一般使用C++,所以我也使用C++实现了相关功能,代码结构与python代码实现类似,这边就不再做相关代码解释了。完整代码详见另一个github仓库。

4. 总结

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

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

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

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

相关文章

如何在Windows 10中启用和使用上帝模式,这里有详细步骤

序言 上帝模式&#xff08;God Mode&#xff09;是一个特殊的文件夹&#xff0c;只在一个窗口中显示所有可用的操作设置。它可以节省搜索命令的时间&#xff0c;而无需知道通过“开始”菜单或“控制面板”查找命令的步骤。上帝模式默认情况下是隐藏的&#xff0c;所以我们需要…

ROS 2边学边练(29)-- 使用替换机制

前言 启动文件用于启动节点、服务和执行流程。这组操作可能有影响其行为的参数。替换机制可以在参数中使用&#xff0c;以便在描述可重复使用的启动文件时提供更大的灵活性。替换是仅在执行启动描述期间评估的变量&#xff0c;可用于获取特定信息&#xff0c;如启动配置、环境变…

链表带环问题——leetcode环形链表1 2

证明链表带环 链表的带环问题指的是本该指向NULL的最后一个节点指向了之前的节点&#xff0c;导致链表成环&#xff0c;找不到尾结点的情况&#xff0c;那么我们该如何证明链表带环呢&#xff1f; 我们可以类比物理中的追及问题&#xff0c;让快慢指针同时走&#xff0c;两者相…

在wsl下安装QT

文章目录 一、前言二、安装QT1、安装依赖 2、安装qt1、先下载到window中&#xff0c;复制到wsl上2、执行命令 三、命令行打开QT1、打开~/.bashrc,在里面添加命令2、测试 四、mysql驱动 一、前言 本方案可以在wsl下正常安装QT&#xff0c;但是QT菜单栏的字体大小调整不了&#…

【开源】使用Python+Flask+Mysql快速开发一个用户增删改查系统

项目演示 项目本身很简单&#xff0c;增删改查是几乎所有系统的骨架。正所谓万丈高楼平地起&#xff0c;学会了增删改查&#xff0c;航母就指日可待了&#xff1a;&#xff09;&#xff0c;光速入门&#xff0c;直接看演示图&#xff1a; 项目地址 https://github.com/mudf…

[C++][算法基础]欧拉函数(常规求质数)

给定 n 个正整数 &#xff0c;请你求出每个数的欧拉函数。 欧拉函数的定义 1∼N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为 ϕ(N)。 若在算数基本定理中&#xff0c;N…&#xff0c;则&#xff1a; ϕ(N) N… 输入格式 第一行包含整数 n。 接下来 n 行&#xf…

雨云:让你的服务器体验不再“阴霾”

引言 在当今数字化的时代&#xff0c;服务器已经成为了我们生活中不可或缺的一部分。无论是个人网站、企业应用还是游戏服务器&#xff0c;都需要一个稳定可靠的平台来运行。然而&#xff0c;在选择服务器提供商时&#xff0c;很多人常常陷入选择困难&#xff0c;不知道哪家更适…

IO进程(线程Thread)

线程Thread 1.什么是线程 1.1 概念 线程是一个轻量级的进程&#xff0c;为了提高系统的性能引入线程。 线程和进程都参与统一的调度。 在同一个进程中可以创建的多个线程, 共享进程资源。 &#xff08;Linux里同样用task_struct来描述一个线程&#xff09; 1.2 进程和线程的区别…

精益思维驱动人工智能革新:理论到实践的跃迁之旅

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已成为引领未来的关键力量。在这个变革的时代&#xff0c;如何将精益思维与人工智能相结合&#xff0c;推动AI从理论走向实践&#xff0c;成为行业内外关注的焦点。本文&#xff0c;天行健精益生产顾问将分享…

拷贝构造函数与运算符重载

目录 一、拷贝构造函数 1.概念 2.特性 二、运算符重载 1.运算符重载 2.运算符重载实现的形式 3.赋值运算符重载 一、拷贝构造函数 1.概念 拷贝构造函数是一种特殊的构造函数&#xff0c;它在创建对象时&#xff0c;使用同一类中之前创建的对象来初始化新创建的对象…

Vitis HLS 学习笔记--scal 函数-探究

目录 1. Vitis HLS重器-Vitis_Libraries 2. 初识scal() 3. 函数具体实现 3.1 变量命名规则 3.2 t_ParEntries解释 3.3 流类型详解 3.4 双重循环 4. 总结 1. Vitis HLS重器-Vitis_Libraries 在深入探索Vitis HLS&#xff08;High-Level Synthesis&#xff09;的旅程中&…

【单调栈】力扣85.最大矩形

好久没更新了 ~ 我又回来啦&#xff01; 两个好消息&#xff1a; 我考上研了&#xff0c;收到拟录取通知啦&#xff01;开放 留言功能 了&#xff0c;小伙伴对于内容有什么疑问可以在文章底部评论&#xff0c;看到之后会及时回复大家的&#xff01; 前面更新过的算法&#x…

kafka的概念以及Zookeeper集群 + Kafka集群 +elfk集群

目录 zookeeper同步过程 分布式通知和协调 zookeeper同步过程 分布式通知和协调 准备 3 台服务器做 Zookeeper 集群 192.168.68.5 192.168.68.6 192.168.68.7 安装前准备 //关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 node1服务器&a…

了解8大Python小陷阱,深入理解Python

学习了解python常见的使用陷阱&#xff0c;避免二次踩坑 Python是最流行的且适合初学者学习的语言之一。它的语法非常优雅简洁。只要知道python基础知识&#xff0c;阅读代码几无障碍。 然而&#xff0c;就像其他语言一样&#xff0c;Python确实有一些古怪特殊的地方。本文将介…

ssm062会员管理系统+jsp

会员管理系统 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于会员管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了会员管理系统&#xff0c;它彻底改…

关于二级指针void**的一点问题与思考

前言 这两天写一个高并发内存池的项目时&#xff0c;遇到了一个关于二级指针的问题&#xff0c;剖析清楚后发觉有必要记录一下&#xff0c;这让我加深了对于C/C中指针的理解&#xff08;果然学到老活到老&#xff09;。 问题的分析 在我的内存池项目中&#xff0c;有一个需求…

2024华中杯C题光纤传感器平面曲线重建原创论文分享

大家好&#xff0c;从昨天肝到现在&#xff0c;终于完成了2024华中杯数学建模C题的完整论文啦。 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 10 一、问题重述 12 二&#xff0e;问题分析 13 2.1问题一 13 2.2问题二 14 2.3问题三 14 三、模型假设 15 四、…

Spring Task 定时任务调度

一、概念 Spring Task 是 Spring 框架的一个组件&#xff0c;它为任务调度提供了支持&#xff0c;使得开发者能够创建后台任务或定期执行的任务。通过 Spring Task&#xff0c;您可以方便地在 Java 应用程序中实现定时任务&#xff0c;比如每天凌晨进行数据同步、每小时执行一…

day02-新增员工

day01 新增员工业务逻辑整理 EmployeeController.java PostMappingApiOperation("新增员工")public Result save(RequestBody EmployeeDTO employeeDTO){System.out.println("当前线程的ID:" Thread.currentThread().getId());log.info("新增员工&a…

2024年华中杯数模竞赛A题完整解析(附代码)

2024年华中杯数模竞赛A题 基于动态优化的太阳能路灯光伏板朝向以最大化能量收集研究摘要问题重述问题分析模型假设符号说明 代码问题一 完整资料获取 基于动态优化的太阳能路灯光伏板朝向以最大化能量收集研究 摘要 随着可再生能源技术的发展&#xff0c;太阳能作为一种清洁的…