单车模型下Stanley循迹

1 Stanley方法

  Stanley与pure pursuit方法都是基于几何的路径跟踪方法,pure pursuit的思想是要让车辆的后轴中心经过目标点,从而计算车辆的前轮转角。Stanley则除了利用横向跟踪误差外,还利用车辆的航向角偏差来计算车辆的前轮转角。因此相对于pure pursuit方法,stanley的计算起来要更复杂一些,它还需要轨迹线的角度作为输入,同时在弯路上的跟踪效果精度要优于pure pursuit
在这里插入图片描述

  Stanley基于航向角误差 θ e \theta_e θe与横向误差 e e e,如上图。其中航向角误差是当前车辆航向角与距离前轮中心最近的轨迹点所在的切线方向的夹角。在忽略航向角误差的情况下,横向误差 e e e越大,前轮转角应该越大(很符合直觉,车要想尽力开到轨迹线上就会努力往那个方向打轮)。 d ( t ) d(t) d(t)是最近轨迹点所在的切线方向与前轮预期的行进方向的交点产生,则有:
δ e ( t ) = a r c t a n e ( t ) d ( t ) \delta_e(t) = arctan\cfrac{e(t)}{d(t)} δe(t)=arctand(t)e(t)
由于 d ( t ) d(t) d(t)与车速相关,可以写成 v ( t ) v(t) v(t)的形式

δ e ( t ) = a r c t a n k e ( t ) v ( t ) \delta_e(t) = arctan\cfrac{ke(t)}{v(t)} δe(t)=arctanv(t)ke(t)
最后:
δ ( t ) = θ e ( t ) + a r c t a n k e ( t ) v ( t ) \delta(t) = \theta_e(t) + arctan\cfrac{ke(t)}{v(t)} δ(t)=θe(t)+arctanv(t)ke(t)

2 实现

  同样利用manim动画系统演示一下

class FollowTraj(Scene):

    def construct(self) -> None:
		# 动画相关
        self.frame.set_width(80)
        axes = Axes(x_range=[-500, 500], y_range=[-20, 20])
        self.play(ShowCreation(axes))
		
		# 轨迹线,sin函数
        trajectory = np.zeros((1000, 2))
        trajectory[:, 0] = np.linspace(0, 1000, 1000)
        trajectory[:, 1] = 5 * np.sin(trajectory[:, 0] / 20)
        # 轨迹点的切线方向,sin的导数
        trajectory_head = 0.25 * np.cos(trajectory[:, 0] / 20)

        refer_tree = KDTree(trajectory)

        # 动画相关
        trajectory_dot = VGroup()
        for i in range(len(trajectory)):
            dot = Dot().move_to(axes.c2p(trajectory[i, 0], trajectory[i, 1]))
            dot.set_color(BLUE)
            trajectory_dot.add(dot)
        self.play(ShowCreation(trajectory_dot))

		# 轴距
        L = 2.8
        v = 5.0 # 初始速度

        # 初始化车辆,仿真模拟步长0.1s,单车模型
        car = KinematicModelBackCenter(0, 2.0, 0, v, L, 0.1)

		# 动画相关
        car_center = Dot(color=RED).move_to(axes.c2p(car.x, car.y))
        car_polygon = get_polygon(car.x, car.y, car.psi, 3.0, 1.0, 2.1).set_color(RED)

        idx = 0
        pidx = 0
        k = 0.5
        for i in range(1000):
            self.frame.move_to(axes.c2p(car.x, car.y))
            car_pos = np.array([car.x, car.y])
            car_fwheel = np.array([car.x + car.L * np.cos(car.psi),
                                   car.y + car.L * np.sin(car.psi)])

            _, idx = refer_tree.query(car_fwheel)
            if idx < pidx:
                idx = pidx
            else:
                pidx = idx

            # 前轮中心到当前最近的轨迹线的距离
            dist = np.linalg.norm(car_fwheel - trajectory[idx])
            # 前轮中心到最近的轨迹点的向量,以及该向量的角度
            dx, dy = trajectory[idx] - car_fwheel
            alpha = math.atan2(dy, dx)
            # 这个是航向角误差
            dtheta = normalize_angle(trajectory_head[idx] - car.psi)
    		# 横向误差
            e = np.sign(np.sin(alpha - car.psi)) * dist
            theta_d = np.arctan2(k*e, car.v)
            # 前轮转角
            delta = dtheta + theta_d
            #  限制车轮转角 [-30, 30]
		    if delta > np.pi / 6.0:
		        delta = np.pi / 6.0
		    elif delta < - np.pi / 6.0:
		        delta = - np.pi / 6.0
            # 更新车辆状态
            car.update_state(0, delta)

            # 动画相关
            cur_car_center = Dot(color=RED).move_to(axes.c2p(car.x, car.y))
            cur_car_polygon = get_polygon(car.x, car.y, car.psi, 3.0, 1.0, 2.1).set_color(RED)

            # 动画相关
            self.play(Transform(car_center, cur_car_center),
                      Transform(car_polygon, cur_car_polygon), run_time=0.01)

运行效果如下
请添加图片描述

3 参考资料

  • https://blog.csdn.net/renyushuai900/article/details/98460758
  • https://windses.blog.csdn.net/article/details/103518011
  • manim动画系统
  • 30
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Stanley算法也被称为Stanley-Stembridge算法,主要用于计算可逆写作有限生成排列的生长函数。以下是一个基本的Stanley算法的MATLAB程序示例: ```matlab function sf = stanley(n) % 输入一个正整数n,计算Stanley算法的生长函数 sf = sym(zeros(1,n)); % 创建一个符号变量数组来存储生长函数的值 % 初始化第一个排列(1) p = 1; % 对于每个排列长度m从1到n-1 for m = 1:n-1 % 计算长度为m的排列的生成标志函数 gf = genFlag(p, m); % 计算长度为m的排列的生成多项式 gm = sum(gf); % 更新生长函数数组 sf(m+1) = gm; % 生成下一个长度为m+1的排列 p = nextPerm(p, m); end % 打印生长函数数组 disp(sf); end function gf = genFlag(p, m) % 输入一个排列p和排列的长度m,计算排列的生成标志函数 n = length(p); gf = sym(ones(1,m)); for i = 1:m for j = 1:m if p(i) < p(j) gf(i) = gf(i) * (n - j + 1); end end end end function np = nextPerm(p, m) % 输入一个排列p和排列的长度m,生成下一个长度为m+1的排列 n = length(p); np = [p, m+1]; indexes = 1:n; for i = 1:m indexes(p(i)) = []; end np = [np, indexes]; end ``` 在此程序中,我们首先定义了一个函数`stanley`,其中处理Stanley算法的主要步骤。我们创建一个符号变量数组`sf`来存储生长函数的值,并初始化第一个排列[1]。然后,我们使用一个循环来计算每个长度为m的排列的生成多项式,并将其存储在生长函数数组中。最后,我们输出生长函数数组。 我们还定义了两个辅助函数`genFlag`和`nextPerm`。`genFlag`函数用于计算排列的生成标志函数,`nextPerm`函数用于生成下一个长度为m+1的排列。 请注意,这只是一个基本的Stanley算法程序示例,可能需要根据具体应用进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值