reeds_sheep运动规划算法Python源码分析


   本文用于记录Python版本zhm-real / PathPlanning运动规划库中reeds_sheep算法的源码分析

   关于reeds sheep算法的原理介绍前文已经介绍过了,链接如下所示:

   《Reeds-Shepp曲线学习笔记及相关思考》

   《Reeds-Shepp曲线基础运动公式推导过程》

在这里插入图片描述


   正文:

   1、导入相关库

import math
import numpy as np
import matplotlib.pyplot as plt
import draw   (非Python官方库)

   2、参数初始化

STEP_SIZE = 0.2         #步长
MAX_LENGTH = 1000.0     #最大长度
PI = math.pi            

   (1)STEP_SIZE = 0.2:步长设定为0.2

   (2)MAX_LENGTH = 1000.0:最大长度限制设定为1000

   (3)PI = math.pi:math.pi是Python中math模块提供的圆周率的近似值,约为3.141592653589793。


   3、定义PATH类

# PATH 类
class PATH:def __init__(self, lengths, ctypes, L, x, y, yaw, directions):self.lengths = lengths  # 包含了路径各部分长度的列表 (+: forward, -: backward) [float]self.ctypes = ctypes  # 包含了路径各部分类型的列表 [string]self.L = L  # 整个路径的总长度 [float]self.x = x  # 表示路径最终位置的x坐标 [m]self.y = y  # 表示路径最终位置的y坐标 [m]self.yaw = yaw  # 表示路径最终位置的偏航角(偏转角) [rad]self.directions = directions  #包含了路径各部分方向的列表。列表中的每个元素都是一个整数,表示路径的每个部分的方向。正数表示正向前进,负数表示反向后退  forward: 1, backward:-1

   这段代码定义了一个名为PATH的类,这个类表示一个路径对象,其中包含了描述路径的各种信息。类的构造函数__init__用于初始化类的实例对象。

   该PATH类有七个属性:

   (1)lengths: 这是一个包含了路径各部分长度的列表。列表中的每个元素都是一个浮点数,表示路径的每个部分的长度。列表中的正数表示路径向前延伸,负数表示路径向后延伸。例如,如果路径由一段长为2米的向前部分和一段长为1.5米的向后部分组成,那么lengths列表可能是[2.0, -1.5]。

   (2)ctypes: 这是一个包含了路径各部分类型的列表。列表中的每个元素都是一个字符串,表示路径的每个部分的类型。路径可以由不同类型的部分组成,比如直线段、曲线段等。例如,如果路径由一段直线和一段圆弧组成,那么ctypes列表可能是[“直线”, “圆弧”]。

   (3)L: 这是一个浮点数,表示整个路径的总长度。

   (4)x: 这是一个浮点数,表示路径最终位置的x坐标。

   (5)y: 这是一个浮点数,表示路径最终位置的y坐标。

   (6)yaw: 这是一个浮点数,表示路径最终位置的偏航角(偏转角)。偏航角通常用弧度来表示,它描述了路径最终位置的朝向角度。

   (7)directions: 这是一个包含了路径各部分方向的列表。列表中的每个元素都是一个整数,表示路径的每个部分的方向。正数表示正向前进,负数表示反向后退。例如,如果路径由一段向前的部分和一段向后的部分组成,那么directions列表可能是[1, -1]。

   通过这个类,可以方便地创建和管理描述路径的对象,存储路径的各个属性,并在需要时进行相关操作和计算。


   4、calc_optimal_path函数

def calc_optimal_path(sx, sy, syaw, gx, gy, gyaw, maxc, step_size=STEP_SIZE):paths = calc_all_paths(sx, sy, syaw, gx, gy, gyaw, maxc, step_size=step_size)minL = paths[0].Lmini = 0for i in range(len(paths)):if paths[i].L <= minL:minL, mini = paths[i].L, ireturn paths[mini]

   calc_optimal_path函数,用于计算最优路径。函数接受一系列参数,包括起始位置、起始偏航角、目标位置、目标偏航角、最大曲率和步长大小。函数的目标是找到一条最优的路径,使得该路径的总长度最小。

   函数首先调用calc_all_paths函数来计算所有可能的路径。calc_all_paths函数返回一个路径对象列表,其中包含了所有可能的路径。

   接下来,函数通过遍历路径对象列表来找到总长度最小的路径。它首先将第一个路径的总长度设为当前的最小长度,并将对应的索引设为0。然后,对于每个路径,它将比较路径的总长度与当前最小长度的大小。如果路径的总长度小于等于当前最小长度,则将路径的总长度更新为当前最小长度,并将对应的索引更新为当前路径的索引。

   最后,函数返回总长度最小的路径对象。

   总的来说,calc_optimal_path函数的作用是根据给定的起始点和目标点的位姿信息、最大曲率信息、步长信息,从reeds_sheep曲线中找到所有可能得路径,并从中选出总长度最小的路径,作为最优路径并返回。


   5、calc_all_paths函数

def calc_all_paths(sx, sy, syaw, gx, gy, gyaw, maxc, step_size=STEP_SIZE):q0 = [sx, sy, syaw]q1 = [gx, gy, gyaw]paths = generate_path(q0, q1, maxc)for path in paths:x, y, yaw, directions = \generate_local_course(path.L, path.lengths,path.ctypes, maxc, step_size * maxc)# convert global coordinatepath.x = [math.cos(-q0[2]) * ix + math.sin(-q0[2]) * iy + q0[0] for (ix, iy) in zip(x, y)]path.y = [-math.sin(-q0[2]) * ix + math.cos(-q0[2]) * iy + q0[1] for (ix, iy) in zip(x, y)]path.yaw = [pi_2_pi(iyaw + q0[2]) for iyaw in yaw]path.directions = directionspath.lengths = [l / maxc for l in path.lengths]path.L = path.L / maxcreturn paths

   calc_all_paths函数,用于计算所有可能的路径。该函数的输入参数包括起始位置、起始偏航角、目标位置、目标偏航角、最大曲率和步长大小(默认为STEP_SIZE)。

   在该函数中,首先将起始位置和目标位置以及其对应的偏航角保存在变量q0和q1中。然后,函数调用generate_path函数生成路径。generate_path函数接受起始位置、目标位置和最大曲率作为参数,并返回一个路径对象列表,其中包含了所有可能的路径。

   接下来,函数遍历路径对象列表,对于每个路径,它调用generate_local_course函数生成局部路径。generate_local_course函数接受路径的总长度、每个部分路径的长度、每个部分路径的类型、最大曲率和步长作为参数,并返回局部路径的x坐标、y坐标、偏航角和方向信息。

   然后,函数将局部路径转换为全局坐标系。它使用起始位置的偏航角将局部路径的x和y坐标进行旋转和平移,从而得到全局路径的x和y坐标。它还使用起始位置的偏航角将局部路径的偏航角进行旋转,得到全局路径的偏航角。

   最后,函数将路径对象的属性进行更新,包括x坐标、y坐标、偏航角、方向信息、部分路径长度和总路径长度。其中,每段路径的长度和总长度被归一化为最大曲率,以便进行比较和分析。

   最终,函数返回包含所有可能路径的列表。

   总的来说,calc_all_paths函数的作用是生成起点到终点之间的所有可能路径,并将这些路径转换为全局坐标系下的路径。


   6、set_path函数

def set_path(paths, lengths, ctypes):path = PATH([], [], 0.0, [], [], [], [])path.ctypes = ctypespath.lengths = lengths# check same path existfor path_e in paths:if path_e.ctypes == path.ctypes:if sum([x - y for x, y in zip(path_e.lengths, path.lengths)]) <= 0.01:return paths  # not insert pathpath.L = sum([abs(i) for i in lengths])if path.L >= MAX_LENGTH:return pathsassert path.L >= 0.01paths.append(path)return paths

   set_path函数用于将一个路径添加到路径列表中。该函数接受三个参数:paths表示路径列表,lengths表示路径的长度列表,ctypes表示路径的类型列表。

   首先,函数创建一个空的PATH对象,并将路径的类型和长度分配给该对象的ctypes和lengths属性。

   然后,函数遍历已有的路径列表paths,检查是否存在相同类型的路径。如果找到一个类型相同且长度相差不大的路径,即两者长度之差小于等于0.01,则直接返回原始路径列表,不插入新路径。

   接下来,函数计算路径的总长度,并将其赋值给路径对象的L属性。如果路径的总长度超过了最大长度(MAX_LENGTH),则直接返回原始路径列表,不插入新路径。

   最后,函数对路径的总长度进行断言,确保其大于等于0.01。然后,将路径对象添加到路径列表中,并返回更新后的路径列表。

   总的来说,set_path函数的作用是将一个新的路径添加到路径列表中,并进行一些简单的验证和检查。


   7、48种reeds_sheep曲线计算函数

在这里插入图片描述

def LSL(x, y, phi):u, t = R(x - math.sin(phi), y - 1.0 + math.cos(phi))if t >= 0.0:v = M(phi - t)if v >= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def LSR(x, y, phi):u1, t1 = R(x + math.sin(phi), y - 1.0 - math.cos(phi))u1 = u1 ** 2if u1 >= 4.0:u = math.sqrt(u1 - 4.0)theta = math.atan2(2.0, u)t = M(t1 + theta)v = M(t - phi)if t >= 0.0 and v >= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def LRL(x, y, phi):u1, t1 = R(x - math.sin(phi), y - 1.0 + math.cos(phi))if u1 <= 4.0:u = -2.0 * math.asin(0.25 * u1)t = M(t1 + 0.5 * u + PI)v = M(phi - t + u)if t >= 0.0 and u <= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def SCS(x, y, phi, paths):flag, t, u, v = SLS(x, y, phi)if flag:paths = set_path(paths, [t, u, v], ["S", "L", "S"])flag, t, u, v = SLS(x, -y, -phi)if flag:paths = set_path(paths, [t, u, v], ["S", "R", "S"])return pathsdef SLS(x, y, phi):phi = M(phi)if y > 0.0 and 0.0 < phi < PI * 0.99:xd = -y / math.tan(phi) + xt = xd - math.tan(phi / 2.0)u = phiv = math.sqrt((x - xd) ** 2 + y ** 2) - math.tan(phi / 2.0)return True, t, u, velif y < 0.0 and 0.0 < phi < PI * 0.99:xd = -y / math.tan(phi) + xt = xd - math.tan(phi / 2.0)u = phiv = -math.sqrt((x - xd) ** 2 + y ** 2) - math.tan(phi / 2.0)return True, t, u, vreturn False, 0.0, 0.0, 0.0def CSC(x, y, phi, paths):flag, t, u, v = LSL(x, y, phi)if flag:paths = set_path(paths, [t, u, v], ["L", "S", "L"])flag, t, u, v = LSL(-x, y, -phi)if flag:paths = set_path(paths, [-t, -u, -v], ["L", "S", "L"])flag, t, u, v = LSL(x, -y, -phi)if flag:paths = set_path(paths, [t, u, v], ["R", "S", "R"])flag, t, u, v = LSL(-x, -y, phi)if flag:paths = set_path(paths, [-t, -u, -v], ["R", "S", "R"])flag, t, u, v = LSR(x, y, phi)if flag:paths = set_path(paths, [t, u, v], ["L", "S", "R"])flag, t, u, v = LSR(-x, y, -phi)if flag:paths = set_path(paths, [-t, -u, -v], ["L", "S", "R"])flag, t, u, v = LSR(x, -y, -phi)if flag:paths = set_path(paths, [t, u, v], ["R", "S", "L"])flag, t, u, v = LSR(-x, -y, phi)if flag:paths = set_path(paths, [-t, -u, -v], ["R", "S", "L"])return pathsdef CCC(x, y, phi, paths):flag, t, u, v = LRL(x, y, phi)if flag:paths = set_path(paths, [t, u, v], ["L", "R", "L"])flag, t, u, v = LRL(-x, y, -phi)if flag:paths = set_path(paths, [-t, -u, -v], ["L", "R", "L"])flag, t, u, v = LRL(x, -y, -phi)if flag:paths = set_path(paths, [t, u, v], ["R", "L", "R"])flag, t, u, v = LRL(-x, -y, phi)if flag:paths = set_path(paths, [-t, -u, -v], ["R", "L", "R"])# backwardsxb = x * math.cos(phi) + y * math.sin(phi)yb = x * math.sin(phi) - y * math.cos(phi)flag, t, u, v = LRL(xb, yb, phi)if flag:paths = set_path(paths, [v, u, t], ["L", "R", "L"])flag, t, u, v = LRL(-xb, yb, -phi)if flag:paths = set_path(paths, [-v, -u, -t], ["L", "R", "L"])flag, t, u, v = LRL(xb, -yb, -phi)if flag:paths = set_path(paths, [v, u, t], ["R", "L", "R"])flag, t, u, v = LRL(-xb, -yb, phi)if flag:paths = set_path(paths, [-v, -u, -t], ["R", "L", "R"])return pathsdef calc_tauOmega(u, v, xi, eta, phi):delta = M(u - v)A = math.sin(u) - math.sin(delta)B = math.cos(u) - math.cos(delta) - 1.0t1 = math.atan2(eta * A - xi * B, xi * A + eta * B)t2 = 2.0 * (math.cos(delta) - math.cos(v) - math.cos(u)) + 3.0if t2 < 0:tau = M(t1 + PI)else:tau = M(t1)omega = M(tau - u + v - phi)return tau, omegadef LRLRn(x, y, phi):xi = x + math.sin(phi)eta = y - 1.0 - math.cos(phi)rho = 0.25 * (2.0 + math.sqrt(xi * xi + eta * eta))if rho <= 1.0:u = math.acos(rho)t, v = calc_tauOmega(u, -u, xi, eta, phi)if t >= 0.0 and v <= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def LRLRp(x, y, phi):xi = x + math.sin(phi)eta = y - 1.0 - math.cos(phi)rho = (20.0 - xi * xi - eta * eta) / 16.0if 0.0 <= rho <= 1.0:u = -math.acos(rho)if u >= -0.5 * PI:t, v = calc_tauOmega(u, u, xi, eta, phi)if t >= 0.0 and v >= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def CCCC(x, y, phi, paths):flag, t, u, v = LRLRn(x, y, phi)if flag:paths = set_path(paths, [t, u, -u, v], ["L", "R", "L", "R"])flag, t, u, v = LRLRn(-x, y, -phi)if flag:paths = set_path(paths, [-t, -u, u, -v], ["L", "R", "L", "R"])flag, t, u, v = LRLRn(x, -y, -phi)if flag:paths = set_path(paths, [t, u, -u, v], ["R", "L", "R", "L"])flag, t, u, v = LRLRn(-x, -y, phi)if flag:paths = set_path(paths, [-t, -u, u, -v], ["R", "L", "R", "L"])flag, t, u, v = LRLRp(x, y, phi)if flag:paths = set_path(paths, [t, u, u, v], ["L", "R", "L", "R"])flag, t, u, v = LRLRp(-x, y, -phi)if flag:paths = set_path(paths, [-t, -u, -u, -v], ["L", "R", "L", "R"])flag, t, u, v = LRLRp(x, -y, -phi)if flag:paths = set_path(paths, [t, u, u, v], ["R", "L", "R", "L"])flag, t, u, v = LRLRp(-x, -y, phi)if flag:paths = set_path(paths, [-t, -u, -u, -v], ["R", "L", "R", "L"])return pathsdef LRSR(x, y, phi):xi = x + math.sin(phi)eta = y - 1.0 - math.cos(phi)rho, theta = R(-eta, xi)if rho >= 2.0:t = thetau = 2.0 - rhov = M(t + 0.5 * PI - phi)if t >= 0.0 and u <= 0.0 and v <= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def LRSL(x, y, phi):xi = x - math.sin(phi)eta = y - 1.0 + math.cos(phi)rho, theta = R(xi, eta)if rho >= 2.0:r = math.sqrt(rho * rho - 4.0)u = 2.0 - rt = M(theta + math.atan2(r, -2.0))v = M(phi - 0.5 * PI - t)if t >= 0.0 and u <= 0.0 and v <= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def CCSC(x, y, phi, paths):flag, t, u, v = LRSL(x, y, phi)if flag:paths = set_path(paths, [t, -0.5 * PI, u, v], ["L", "R", "S", "L"])flag, t, u, v = LRSL(-x, y, -phi)if flag:paths = set_path(paths, [-t, 0.5 * PI, -u, -v], ["L", "R", "S", "L"])flag, t, u, v = LRSL(x, -y, -phi)if flag:paths = set_path(paths, [t, -0.5 * PI, u, v], ["R", "L", "S", "R"])flag, t, u, v = LRSL(-x, -y, phi)if flag:paths = set_path(paths, [-t, 0.5 * PI, -u, -v], ["R", "L", "S", "R"])flag, t, u, v = LRSR(x, y, phi)if flag:paths = set_path(paths, [t, -0.5 * PI, u, v], ["L", "R", "S", "R"])flag, t, u, v = LRSR(-x, y, -phi)if flag:paths = set_path(paths, [-t, 0.5 * PI, -u, -v], ["L", "R", "S", "R"])flag, t, u, v = LRSR(x, -y, -phi)if flag:paths = set_path(paths, [t, -0.5 * PI, u, v], ["R", "L", "S", "L"])flag, t, u, v = LRSR(-x, -y, phi)if flag:paths = set_path(paths, [-t, 0.5 * PI, -u, -v], ["R", "L", "S", "L"])# backwardsxb = x * math.cos(phi) + y * math.sin(phi)yb = x * math.sin(phi) - y * math.cos(phi)flag, t, u, v = LRSL(xb, yb, phi)if flag:paths = set_path(paths, [v, u, -0.5 * PI, t], ["L", "S", "R", "L"])flag, t, u, v = LRSL(-xb, yb, -phi)if flag:paths = set_path(paths, [-v, -u, 0.5 * PI, -t], ["L", "S", "R", "L"])flag, t, u, v = LRSL(xb, -yb, -phi)if flag:paths = set_path(paths, [v, u, -0.5 * PI, t], ["R", "S", "L", "R"])flag, t, u, v = LRSL(-xb, -yb, phi)if flag:paths = set_path(paths, [-v, -u, 0.5 * PI, -t], ["R", "S", "L", "R"])flag, t, u, v = LRSR(xb, yb, phi)if flag:paths = set_path(paths, [v, u, -0.5 * PI, t], ["R", "S", "R", "L"])flag, t, u, v = LRSR(-xb, yb, -phi)if flag:paths = set_path(paths, [-v, -u, 0.5 * PI, -t], ["R", "S", "R", "L"])flag, t, u, v = LRSR(xb, -yb, -phi)if flag:paths = set_path(paths, [v, u, -0.5 * PI, t], ["L", "S", "L", "R"])flag, t, u, v = LRSR(-xb, -yb, phi)if flag:paths = set_path(paths, [-v, -u, 0.5 * PI, -t], ["L", "S", "L", "R"])return pathsdef LRSLR(x, y, phi):# formula 8.11 *** TYPO IN PAPER ***xi = x + math.sin(phi)eta = y - 1.0 - math.cos(phi)rho, theta = R(xi, eta)if rho >= 2.0:u = 4.0 - math.sqrt(rho * rho - 4.0)if u <= 0.0:t = M(math.atan2((4.0 - u) * xi - 2.0 * eta, -2.0 * xi + (u - 4.0) * eta))v = M(t - phi)if t >= 0.0 and v >= 0.0:return True, t, u, vreturn False, 0.0, 0.0, 0.0def CCSCC(x, y, phi, paths):flag, t, u, v = LRSLR(x, y, phi)if flag:paths = set_path(paths, [t, -0.5 * PI, u, -0.5 * PI, v], ["L", "R", "S", "L", "R"])flag, t, u, v = LRSLR(-x, y, -phi)if flag:paths = set_path(paths, [-t, 0.5 * PI, -u, 0.5 * PI, -v], ["L", "R", "S", "L", "R"])flag, t, u, v = LRSLR(x, -y, -phi)if flag:paths = set_path(paths, [t, -0.5 * PI, u, -0.5 * PI, v], ["R", "L", "S", "R", "L"])flag, t, u, v = LRSLR(-x, -y, phi)if flag:paths = set_path(paths, [-t, 0.5 * PI, -u, 0.5 * PI, -v], ["R", "L", "S", "R", "L"])return paths

   8、generate_local_cours函数

def generate_local_course(L, lengths, mode, maxc, step_size):point_num = int(L / step_size) + len(lengths) + 3px = [0.0 for _ in range(point_num)]py = [0.0 for _ in range(point_num)]pyaw = [0.0 for _ in range(point_num)]directions = [0 for _ in range(point_num)]ind = 1if lengths[0] > 0.0:directions[0] = 1else:directions[0] = -1if lengths[0] > 0.0:d = step_sizeelse:d = -step_sizepd = dll = 0.0for m, l, i in zip(mode, lengths, range(len(mode))):if l > 0.0:d = step_sizeelse:d = -step_sizeox, oy, oyaw = px[ind], py[ind], pyaw[ind]ind -= 1if i >= 1 and (lengths[i - 1] * lengths[i]) > 0:pd = -d - llelse:pd = d - llwhile abs(pd) <= abs(l):ind += 1px, py, pyaw, directions = \interpolate(ind, pd, m, maxc, ox, oy, oyaw, px, py, pyaw, directions)pd += dll = l - pd - d  # calc remain lengthind += 1px, py, pyaw, directions = \interpolate(ind, l, m, maxc, ox, oy, oyaw, px, py, pyaw, directions)# remove unused datawhile px[-1] == 0.0:px.pop()py.pop()pyaw.pop()directions.pop()return px, py, pyaw, directions

   generate_local_cours函数是用于生成局部路径的。输入参数包括::总路径的长度L、包含每个局部路径的长度的列表lengths、包含每个局部路径模式的列表mode、 控制曲线的最大曲率maxc、生成路径时进行插值时的步长大小step_size。

   该函数的主要步骤如下:

   (1)根据总路径长度和步长大小计算局部路径的点数(point_num)。

   (2)创建空列表 px、py、pyaw 和 directions,用于存储路径点的 x 坐标、y 坐标、偏航角和方向信息。

   (3)初始化变量 ind 为 1,directions[0] 为起始路径的方向(正向或反向)。

   (4)根据起始路径的长度,确定路径方向(正向为正步长,反向为负步长)。

   (5)进入循环,依次处理每个局部路径:

     (5.1)计算局部路径的步长方向 d。

     (5.2)获取前一个路径点的坐标和偏航角作为起点。

     (5.3)根据前一个路径点的索引,以及前一个路径长度和当前路径长度的符号关系,计算当前路径段的步长方向 pd。

     (5.4)在当前路径段的长度范围内,以步长 d 进

     (5.5)行插值,生成路径点,并更新路径信息。

     (5.6)计算剩余长度 ll。

     (5.7)在当前路径段的末尾,以当前路径长度进行插值,生成路径点,并更新路径信息。

   (6)移除未使用的数据,即末尾多余的零值点。

   (7)返回生成的路径点列表 px、py、pyaw 和 directions。

   总的来说,generate_local_cours函数的作用是根据给定的局部路径长度和模式,生成一系列插值点,用于描述机器人在整个路径上的运动轨迹。路径点包括 x 和 y 坐标、偏航角和方向信息,可以在后续的路径规划和控制中使用。


   9、interpolate函数

def interpolate(ind, l, m, maxc, ox, oy, oyaw, px, py, pyaw, directions):if m == "S":px[ind] = ox + l / maxc * math.cos(oyaw)py[ind] = oy + l / maxc * math.sin(oyaw)pyaw[ind] = oyawelse:ldx = math.sin(l) / maxcif m == "L":ldy = (1.0 - math.cos(l)) / maxcelif m == "R":ldy = (1.0 - math.cos(l)) / (-maxc)gdx = math.cos(-oyaw) * ldx + math.sin(-oyaw) * ldygdy = -math.sin(-oyaw) * ldx + math.cos(-oyaw) * ldypx[ind] = ox + gdxpy[ind] = oy + gdyif m == "L":pyaw[ind] = oyaw + lelif m == "R":pyaw[ind] = oyaw - lif l > 0.0:directions[ind] = 1else:directions[ind] = -1return px, py, pyaw, directions

   这个程序定义了一个插值函数 interpolate,它根据给定的参数生成路径点。函数接受以下参数::要生成的路径点的索引ind、路径段的长度l、路径段的模式m(可以是 “S”、“L” 或 “R”)控制曲线的最大曲率maxc、前一个路径点的坐标和偏航角ox, oy, oyaw:、存储路径点的列表px, py, pyaw、存储路径方向的列表directions。

   函数的主要逻辑如下:

   首先,根据路径段的模式,分为直线段(“S”)和曲线段(“L”、“R”)两种情况进行处理。如果路径段是直线段,则计算直线段上每个点的 x 和 y 坐标,基于前一个路径点的坐标、偏航角和路径长度。更新路径点列表 px、py 和 pyaw。

   如果路径段是曲线段,则根据曲线段的模式和路径长度,计算曲线段在 x 和 y 方向上的增量,将增量根据前一个路径点的偏航角进行旋转和平移,计算得到当前路径点的坐标。更新路径点列表 px 和 py。

   然后,根据路径段的模式,计算当前路径点的偏航角,并更新路径点列表 pyaw。之后,根据路径段的长度,确定路径方向(正向或反向),并更新路径方向列表 directions。

   最后,返回更新后的路径点列表 px、py、pyaw 和 directions。

   总的来说,interpolate函数的作用是根据给定的路径段信息,在前一个路径点的基础上计算出当前路径点的坐标、偏航角和方向,并更新路径信息。这些路径点可以用于描述机器人在路径上的运动轨迹。


   10、generate_path函数

def generate_path(q0, q1, maxc):dx = q1[0] - q0[0]dy = q1[1] - q0[1]dth = q1[2] - q0[2]c = math.cos(q0[2])s = math.sin(q0[2])x = (c * dx + s * dy) * maxcy = (-s * dx + c * dy) * maxcpaths = []paths = SCS(x, y, dth, paths)paths = CSC(x, y, dth, paths)paths = CCC(x, y, dth, paths)paths = CCCC(x, y, dth, paths)paths = CCSC(x, y, dth, paths)paths = CCSCC(x, y, dth, paths)return paths

   这个程序定义了一个函数 generate_path,用于生成从起始状态到目标状态的路径。函数接受以下参数:

   q0: 起始状态,包含 x、y 和偏航角(以弧度表示)的列表。

   q1: 目标状态,与 q0 结构相同。

   maxc: 控制曲线的最大曲率。

   函数的主要逻辑如下:

   首先,计算 x、y 和偏航角的差值 dx、dy 和 dth。根据起始状态的偏航角,计算出一个旋转矩阵的元素 c 和 s。根据旋转矩阵将差值 dx 和 dy 进行旋转和缩放,得到 x 和 y。

   然后,创建一个空的路径列表 paths。将 x、y 和 dth 分别传递给一系列路径生成函数(SCS、CSC、CCC、CCCC、CCSC 和 CCSCC),并在每次调用后更新 paths。

   最后,返回生成的路径列表 paths。

   总的来说,generate_path函数的作用是根据起始状态和目标状态之间的差异,利用不同的路径生成函数生成一系列可能的路径。这些路径描述了从起始状态到目标状态的运动轨迹,可以用于路径规划和机器人运动控制。


   11、pi_2_pi函数

def pi_2_pi(theta):while theta > PI:theta -= 2.0 * PIwhile theta < -PI:theta += 2.0 * PIreturn theta

   这个程序定义了一个函数 pi_2_pi,用于将给定的角度值 theta 转换到区间 [-π, π] 内。函数的实现逻辑如下:

   进入一个循环,只要 theta 大于 π,就执行下面的操作。循环的目的是将角度值转换为 [-π, π] 范围内的值。在每次迭代中,从 theta 中减去 2π,直到 theta 小于或等于 π。

   当 theta 小于 -π 时,执行另一个循环。循环的目的是将角度值转换为 [-π, π] 范围内的值。在每次迭代中,将 theta 加上 2π,直到 theta 大于或等于 -π。

   最后,返回转换后的角度值 theta。

   总的来说,pi_2_pi函数的作用是将任意角度值映射到一个等价的角度值,使其处于 [-π, π] 范围内。这在许多应用中很有用,例如在机器人控制中,确保角度的连续性和一致性。


   12、R函数

def R(x, y):"""Return the polar coordinates (r, theta) of the point (s, y)"""r = math.hypot(x, y)theta = math.atan2(y, x)return r, theta

   这个程序定义了一个函数 R,用于计算点 (x, y) 的极坐标 (r, theta)。函数的实现逻辑如下:

   首先,使用 math.hypot(x, y) 函数计算点 (x, y) 到原点的距离 r。math.hypot 函数返回两个参数的欧几里得范数,即 (x, y) 的平方和的平方根。然后,使用 math.atan2(y, x) 函数计算点 (x, y) 的极角 theta。math.atan2 函数返回 (y, x) 的反正切值,以弧度表示。最后,返回计算得到的极坐标 (r, theta)。

   总的来说,这个R函数可以用于将直角坐标系下的点 (x, y) 转换为极坐标系下的 (r, theta) 表示。极坐标可以更方便地描述点的位置和方向,特别适用于需要考虑距离和角度的问题,如机器人路径规划、图像处理等领域。


   13、M函数

def M(theta):"""Regulate theta to -pi <= theta < pi"""phi = theta % (2.0 * PI)if phi < -PI:phi += 2.0 * PIif phi > PI:phi -= 2.0 * PIreturn phi

   这个程序定义了一个函数 M,用于将给定角度 theta 调整到 -pi <= theta < pi 的范围内。函数的实现逻辑如下:

   首先,使用取模运算 % 将输入角度 theta 转换为在 0 到 2*pi 之间的角度 phi。取模运算是将一个数除以另一个数得到的余数。

   然后进行判断,如果 phi 小于 -pi,则将其调整增加 2pi,使其落在 [-pi, pi) 的范围内。如果 phi 大于等于 pi,则将其调整减去 2pi,使其落在 [-pi, pi) 的范围内。

   最后,返回调整后的角度 phi。

   总的来说,这个M函数的作用是规范化角度,确保其在 -pi 到 pi 之间。这在很多计算和编程场景中非常有用,因为在这个范围内,角度的表示是唯一的,且方便进行数值计算和比较。


   14、get_label函数

def get_label(path):label = ""for m, l in zip(path.ctypes, path.lengths):label = label + mif l > 0.0:label = label + "+"else:label = label + "-"return label

   这个程序定义了一个函数 get_label,用于根据给定路径 path 生成一个标签。路径是由一系列运动模式(m)和对应的长度(l)组成的。函数的实现逻辑如下:

   首先,定义一个空字符串 label,用于存储生成的标签。然后,使用 zip 函数将运动模式和长度两个列表进行逐个配对,依次遍历路径中的每个运动模式和长度。对于每个配对的运动模式和长度,将运动模式添加到 label 字符串中。

   然后进行判断,如果长度 l 大于 0.0,则在运动模式后面添加一个正号 +,否则添加一个负号 -。最后,遍历完所有配对后,返回生成的标签 label。

   总的来说,get_label函数的作用是根据路径中的运动模式和长度生成一个可读性较高的标签,用于描述路径的运动方式和方向。


   15、calc_curvature函数

def calc_curvature(x, y, yaw, directions):c, ds = [], []for i in range(1, len(x) - 1):dxn = x[i] - x[i - 1]dxp = x[i + 1] - x[i]dyn = y[i] - y[i - 1]dyp = y[i + 1] - y[i]dn = math.hypot(dxn, dyn)dp = math.hypot(dxp, dyp)dx = 1.0 / (dn + dp) * (dp / dn * dxn + dn / dp * dxp)ddx = 2.0 / (dn + dp) * (dxp / dp - dxn / dn)dy = 1.0 / (dn + dp) * (dp / dn * dyn + dn / dp * dyp)ddy = 2.0 / (dn + dp) * (dyp / dp - dyn / dn)curvature = (ddy * dx - ddx * dy) / (dx ** 2 + dy ** 2)d = (dn + dp) / 2.0if np.isnan(curvature):curvature = 0.0if directions[i] <= 0.0:curvature = -curvatureif len(c) == 0:ds.append(d)c.append(curvature)ds.append(d)c.append(curvature)ds.append(ds[-1])c.append(c[-1])return c, ds

   这个程序定义了一个函数 calc_curvature,用于计算路径的曲率。函数接受四个参数:x、y、yaw和directions,分别表示路径的横坐标、纵坐标、偏航角和方向。

   函数的实现逻辑如下:

   首先,定义两个空列表 c 和 ds,用于存储计算得到的曲率和路径段长度。然后,使用 range 函数从索引1开始,遍历路径中的每个点,到倒数第2个点为止。

   对于每个点,计算该点与前后相邻点的坐标差值,并计算出前后两个向量的模长。根据差值和模长计算出局部斜率和曲率,并考虑曲率的正负。如果曲率为非数值(NaN),将其设置为0.0。

   如果路径段的方向 directions[i] 小于等于0.0,则将曲率取反。如果 c 列表为空(即第一个点),则将当前路径段长度 d 添加到 ds 列表中,并将当前曲率 curvature 添加到 c 列表中。

   对于其他点,将路径段长度 d 添加到 ds 列表中,并将曲率 curvature 添加到 c 列表中。将最后一个路径段的长度和曲率添加到 ds 和 c 列表的末尾,使其长度与输入的 x 和 y 列表相同。

   最后,返回计算得到的曲率列表 c 和路径段长度列表 ds。

   总的来说,calc_curvature函数的目的是根据路径的坐标和方向信息计算路径上各点的曲率,并返回曲率和路径段长度的列表。曲率描述了路径弯曲的程度,可以用于路径规划和控制。


   16、check_path函数

def check_path(sx, sy, syaw, gx, gy, gyaw, maxc):paths = calc_all_paths(sx, sy, syaw, gx, gy, gyaw, maxc)assert len(paths) >= 1for path in paths:assert abs(path.x[0] - sx) <= 0.01assert abs(path.y[0] - sy) <= 0.01assert abs(path.yaw[0] - syaw) <= 0.01assert abs(path.x[-1] - gx) <= 0.01assert abs(path.y[-1] - gy) <= 0.01assert abs(path.yaw[-1] - gyaw) <= 0.01# course distance checkd = [math.hypot(dx, dy)for dx, dy in zip(np.diff(path.x[0:len(path.x) - 1]),np.diff(path.y[0:len(path.y) - 1]))]for i in range(len(d)):assert abs(d[i] - STEP_SIZE) <= 0.001

   这个程序定义了一个函数 check_path,用于检查计算得到的路径是否满足一些约束条件。

   函数接受七个参数:起点坐标 sx 和 sy、起点偏航角 syaw、终点坐标 gx 和 gy、终点偏航角 gyaw,以及最大曲率 maxc。

   函数的实现逻辑如下:

   首先,通过调用 calc_all_paths 函数计算从起点到终点的所有可能路径,并将这些路径存储在列表 paths 中。并使用 assert 语句确保计算得到的路径列表 paths 不为空(长度至少为1),如果为空,则会触发异常。

   然后,对于每个路径 path,使用 assert 语句确保起点和终点的坐标、偏航角与输入的起点 sx、sy、syaw 以及终点 gx、gy、gyaw 的坐标、偏航角相差不超过0.01。如果超过了这个阈值,也会触发异常。

   接下来,检查路径上相邻两点之间的距离是否接近预设的步长 STEP_SIZE(这个步长的值未在提供的代码片段中给出,可能在其他地方定义)。对于每个路径,计算路径上相邻两点的距离 d 并与 STEP_SIZE 比较,如果距离与 STEP_SIZE 的差值超过0.001,则会触发异常。

   总的来说,check_path函数的作用是在路径规划过程中验证计算得到的路径是否满足一些基本的约束条件,包括起点、终点和路径长度的检查。如果有任何条件不满足,该函数将会触发异常,提示路径计算存在问题。


   17、主函数

def main():states = [(-3, 3, 120), (10, -7, 30), (10, 13, 30), (20, 5, -25),(35, 10, 180), (32, -10, 180), (5, -12, 90)]max_c = 0.1  # max curvaturepath_x, path_y, yaw = [], [], []for i in range(len(states) - 1):s_x = states[i][0]s_y = states[i][1]s_yaw = np.deg2rad(states[i][2])g_x = states[i + 1][0]g_y = states[i + 1][1]g_yaw = np.deg2rad(states[i + 1][2])path_i = calc_optimal_path(s_x, s_y, s_yaw,g_x, g_y, g_yaw, max_c)path_x += path_i.xpath_y += path_i.yyaw += path_i.yaw# animationplt.ion()plt.figure(1)for i in range(len(path_x)):plt.clf()plt.plot(path_x, path_y, linewidth=1, color='gray')for x, y, theta in states:draw.Arrow(x, y, np.deg2rad(theta), 2, 'blueviolet')draw.Car(path_x[i], path_y[i], yaw[i], 1.5, 3)plt.axis("equal")plt.title("Simulation of Reeds-Shepp Curves")plt.axis([-5, 15,-25, -10])plt.draw()plt.pause(0.001)plt.pause(1)if __name__ == '__main__':main()

   终于到主函数了,主函数中执行路径规划的示例并进行动画展示。

   首先。定义了一个状态列表 states,其中包含了一系列起点和终点的坐标以及偏航角。每个状态由三个元素组成,分别表示 x 坐标、y 坐标和偏航角(以度为单位)。

   接下来定义了最大曲率 max_c,用于路径规划过程中限制曲率的最大值。

   然后,通过循环遍历状态列表,对相邻的两个状态进行路径规划。对于每对相邻状态,提取起点的 x、y 坐标和偏航角,以及终点的 x、y 坐标和偏航角,并调用 calc_optimal_path 函数计算最优路径。并将每个计算得到的路径的 x、y 坐标和偏航角分别添加到 path_x、path_y 和 yaw 列表中。

   接下来,使用 plt.ion() 开启交互模式,并创建一个图形窗口。通过循环遍历 path_x 列表中的每个元素,在每个时间步中,清除图形窗口的内容,绘制路径、起点和终点的箭头以及当前时间步的车辆位置。然后设置图形窗口的坐标轴范围、标题等,并调用 plt.draw() 和 plt.pause() 函数进行动画展示。

   最后,通过调用 plt.pause(1) 在动画结束后暂停一秒钟。

   总的来说,主函数中主要执行路径规划的示例,并通过动画展示路径规划的结果。


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

防丢器Airtag国产版

Airtag是什么&#xff1f; AirTag是苹果公司设计的一款定位神奇&#xff0c;它通过一款纽扣电池进行供电&#xff0c;即可实现长达1-2年的关键物品的定位、查找的功能。 按照苹果公司自己的话说—— 您“丢三落四这门绝技&#xff0c;要‍失‍传‍了”。 AirTag 可帮你轻松追…

阿里云与中国中医科学院合作,推动中医药行业数字化和智能化发展

据相关媒体消息&#xff0c;阿里云与中国中医科学院的合作旨在推动中医药行业的数字化和智能化发展。随着互联网的进步和相关政策的支持&#xff0c;中医药产业受到了国家的高度关注。这次合作将以“互联网 中医药”为载体&#xff0c;致力于推进中医药文化的传承和创新发展。…

2023最新红盟云卡个人自动发卡系统源码 全开源

​ 简介&#xff1a; 2023最新红盟云卡个人自动发卡系统源码 全开源 该系统完全开源且无任何加密&#xff0c;可商业使用&#xff0c;并支持个人免签多个接口。 ​ 图片&#xff1a;

总结,由于顺丰的问题,产生了电脑近期一个月死机问题集锦

由于我搬家&#xff0c;我妈搞顺丰发回家&#xff0c;但是没有检查有没有坏&#xff0c;并且我自己由于不可抗力因素&#xff0c;超过了索赔时间&#xff0c;反馈给顺丰客服&#xff0c;说超过了造成了无法索赔的情况&#xff0c;现在总结发生了损坏配件有几件&#xff0c;显卡…

成集云 | 鼎捷ERP采购单同步钉钉 | 解决方案

源系统成集云目标系统 方案介绍 鼎捷ERP&#xff08;Enterprise Resource Planning&#xff09;是一款综合性的企业管理软件&#xff0c;它包括了多个模块来管理企业的各个方面&#xff0c;其中之一就是采购订单模块。鼎捷ERP的采购订单模块可以帮助企业有效管理和控制采购过程…

Java 项目日志实例基础:Log4j

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 介绍几个日志使用方面的基础知识。 1 Log4j 1、Log4j 介绍 Log4j&#xff08;log for java&#xff09;是 Apache 的一个开源项目&#xff0c;通过使用 Log4j&#xff0c;我…

从零开始学习 Java:简单易懂的入门指南之Objects、BigInteger、BigDecimal(十四)

常用API&#xff0c;Objects、BigInteger、BigDecimal 5 Objects类5.1 概述5.2 常见方法 6 BigInteger类6.1 引入6.2 概述6.3 常见方法6.4 底层存储方式&#xff1a; 7 BigDecimal类7.1 引入7.2 概述7.3 常见方法7.4 底层存储方式&#xff1a; 5 Objects类 5.1 概述 tips&…

Tesla Model S 3对比分析拆解图

文章来源&#xff1a;网络 需要特斯拉电驱样件的请&#xff1a;shbinzer &#xff08;拆车邦&#xff09; 5 款电机&#xff0c;其中扁线永磁同步电机最大功率从 202kW 提升至 220kW&#xff0c;最大扭矩从 404Nm提升至 440Nm。 Model S/X→Model 3/Y&#xff1a;双电机版本…

使用SpringBoot + Thymeleaf 完成简单的用户登录

&#x1f600;前言 本篇博文是关于Thymeleaf 的综合案例&#xff0c; 使用SpringBoot Thymeleaf 完成简单的用户登录-列表功能&#xff0c;希望你能够喜欢&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨…

PyTorch训练深度卷积生成对抗网络DCGAN

文章目录 DCGAN介绍代码结果参考 DCGAN介绍 将CNN和GAN结合起来&#xff0c;把监督学习和无监督学习结合起来。具体解释可以参见 深度卷积对抗生成网络(DCGAN) DCGAN的生成器结构&#xff1a; 图片来源&#xff1a;https://arxiv.org/abs/1511.06434 代码 model.py impor…

win系统部署Apollo-quick-start-2.1.0

win系统部署Apollo-quick-start-2.1.0 携程Apollo配置中心&#xff0c;官方部署包里提供了2个sql文件&#xff0c;需要刷入数据库。之后修改demo.sh里的数据库配置,最后使用git bash启动demo.sh刷sql脚本 官方部署包里提供了2个sql文件 修改demo.sh文件 使用git bash启动demo…

WinCC V7.5 中的C脚本对话框不可见,将编辑窗口移动到可见区域的具体方法

WinCC V7.5 中的C脚本对话框不可见&#xff0c;将编辑窗口移动到可见区域的具体方法 由于 Windows 系统更新或使用不同的显示器&#xff0c;在配置C动作时&#xff0c;有可能会出现C脚本编辑窗口被移动到不可见区域的现象。 由于该窗口无法被关闭&#xff0c;故无法进行进一步…

安防监控视频云存储EasyCVR平台H.265转码功能更新:新增分辨率配置

安防视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求&#xff0c;让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上&#xff0c;视频云存储平台EasyCVR可实现视频实时直播、云端录像、视频云存储、视频存储…

微服务中间件-分布式缓存Redis

分布式缓存 a.Redis持久化1) RDB持久化1.a) RDB持久化-原理 2) AOF持久化3) 两者对比 b.Redis主从1) 搭建主从架构2) 数据同步原理&#xff08;全量同步&#xff09;3) 数据同步原理&#xff08;增量同步&#xff09; c.Redis哨兵1) 哨兵的作用2) 搭建Redis哨兵集群3) RedisTem…

小程序的数据绑定和事件绑定

小程序的数据绑定 1.需要渲染的数据放在index.js中的data里 Page({data: {info:HELLO WORLD,imgSrc:/images/1.jpg,randomNum:Math.random()*10,randomNum1:Math.random().toFixed(2)}, }) 2.在WXML中通过{{}}获取数据 <view>{{info}}</view><image src"{{…

设计模式之责任链模式

简介 责任链模式是一种行为设计模式&#xff0c; 允许你将请求沿着处理者链&#xff08;单向链表&#xff09;进行发送。 收到请求后&#xff0c; 每个处理者根据自身条件对请求进行处理&#xff0c; &#xff0c;如果处理不了则将其传递给链上的下个处理者&#xff0c;以此类…

【LVS】3、LVS+Keepalived群集

为什么用它&#xff0c;为了做高可用 服务功能 1.故障自动切换 2.健康检查 3.节点服务器高可用-HA Keepalived的三个模块&#xff1a; core&#xff1a;Keepalived的核心&#xff0c;负责主进程的启动、维护&#xff1b;调用全局配置文件进行加载和解析 vrrp&#xff1a;实…

5个高清视频素材网站

推荐5个高清视频素材网站&#xff0c;免费、付费、商用的都有&#xff0c;可根据自己需求去选择&#xff0c;赶紧收藏吧&#xff01; 菜鸟图库 https://www.sucai999.com/video.html?vNTYxMjky ​ 菜鸟图库网素材非常丰富&#xff0c;网站主要还是以设计类素材为主&#xff…

Spring-Bean的生命周期

目录 生命周期汇总 细分生命周期 1.实例化 2.属性赋值&#xff08;依赖注入&#xff09; 3.Aware接口 4.BeanPostProcessor接口 5.初始化 6.销毁 测试验证 类结构 业务类 测试类 生命周期汇总 Spring Bean 的生命周期见下图 &#xff08;一定记忆好下图&#x…

二进制数的左移和右移位运算numpy.left_shift()numpy.right_shift()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 二进制数的左移和右移位运算 numpy.left_shift() numpy.right_shift() [太阳]选择题 下列代码最后一次输出的结果是&#xff1f; import numpy as np a 8 print("【显示】a ", a)…