Skip to content

运动规划与轨迹跟踪

本文档介绍机器人运动规划和轨迹跟踪的基础知识,重点关注 LeBot 轮腿混合机器人的应用。运动规划是机器人从起点运动到终点的路径生成过程,轨迹跟踪是让机器人沿着规划的轨迹运动。

1. 运动规划基础

1.1 什么是运动规划

运动规划(Motion Planning)是在给定的环境中,为机器人找到一条从初始配置到目标配置的可行路径,同时满足各种约束条件(如避免碰撞、限制速度等)。

运动规划的层次:

┌─────────────────────────────────────┐
│  高层规划:全局路径规划             │
│  输出:关键路径点序列                │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│  中层规划:轨迹生成                  │
│  输出:平滑连续的轨迹               │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│  低层规划:轨迹跟踪控制             │
│  输出:电机指令                      │
└─────────────────────────────────────┘

1.2 运动规划的分类

按方法分类:

  • 基于采样的方法:RRT、PRM

    • 优点:计算效率高,易于处理高维问题
    • 缺点:可能不是最优解
  • 基于图搜索的方法:A*、Dijkstra

    • 优点:保证最优性(栅格化)
    • 缺点:计算复杂度高
  • 基于势场的方法:势场法

    • 优点:直观简单
    • 缺点:容易陷入局部最小值
  • 基于曲线的方法:Bezier 曲线、B 样条

    • 优点:轨迹平滑
    • 缺点:需要预先确定控制点

1.3 LeBot 的运动规划挑战

对于轮腿混合机器人 LeBot:

  1. 运动学约束:轮子和腿各有不同的运动学特性
  2. 动力学约束:需要考虑加速度、扭矩限制
  3. 地形适应:不规则地面需要腿部调整
  4. 实时性:需要在有限计算资源内完成规划
  5. 鲁棒性:需要对环境变化有容错能力

2. 栅格化路径规划

2.1 环境表示

第一步是将连续的环境离散化为栅格。

python
import numpy as np
from collections import deque
import heapq

class GridEnvironment:
    """栅格环境表示"""

    def __init__(self, width, height, resolution=0.1):
        """
        初始化栅格环境

        参数:
            width: 环境宽度 (m)
            height: 环境高度 (m)
            resolution: 栅格分辨率 (m)
        """
        self.width = width
        self.height = height
        self.resolution = resolution

        # 栅格数量
        self.grid_width = int(width / resolution)
        self.grid_height = int(height / resolution)

        # 栅格地图 (0: 可通行, 1: 障碍)
        self.grid = np.zeros((self.grid_height, self.grid_width), dtype=np.uint8)

    def add_obstacle(self, x, y, size):
        """
        添加矩形障碍

        参数:
            x, y: 障碍中心位置 (m)
            size: 障碍大小 (m)
        """
        # 转换为栅格坐标
        grid_x = int(x / self.resolution)
        grid_y = int(y / self.resolution)
        grid_size = int(size / self.resolution)

        x_min = max(0, grid_x - grid_size // 2)
        x_max = min(self.grid_width, grid_x + grid_size // 2)
        y_min = max(0, grid_y - grid_size // 2)
        y_max = min(self.grid_height, grid_y + grid_size // 2)

        self.grid[y_min:y_max, x_min:x_max] = 1

    def is_free(self, x, y):
        """检查栅格是否可通行"""
        grid_x = int(x / self.resolution)
        grid_y = int(y / self.resolution)

        if 0 <= grid_x < self.grid_width and 0 <= grid_y < self.grid_height:
            return self.grid[grid_y, grid_x] == 0
        return False

    def get_neighbors(self, x, y):
        """获取相邻的可通行栅格"""
        neighbors = []
        # 8 邻接
        for dx in [-1, 0, 1]:
            for dy in [-1, 0, 1]:
                if dx == 0 and dy == 0:
                    continue
                nx, ny = x + dx, y + dy
                if self.is_free(nx, ny):
                    # 计算欧氏距离作为权重
                    distance = np.sqrt(dx**2 + dy**2)
                    neighbors.append(((nx, ny), distance))
        return neighbors

2.2 A* 搜索算法

A* 是最常用的栅格路径规划算法。

python
class AStarPlanner:
    """A* 路径规划器"""

    def __init__(self, environment):
        """
        初始化 A* 规划器

        参数:
            environment: GridEnvironment 实例
        """
        self.env = environment

    def heuristic(self, pos, goal):
        """
        启发函数:欧氏距离

        参数:
            pos: 当前位置 (x, y)
            goal: 目标位置 (x, y)

        返回:
            估计的距离
        """
        dx = pos[0] - goal[0]
        dy = pos[1] - goal[1]
        return np.sqrt(dx**2 + dy**2)

    def plan(self, start, goal):
        """
        规划路径

        参数:
            start: 起点 (x, y)
            goal: 目标点 (x, y)

        返回:
            路径点列表,如果无法到达返回 None
        """
        # 转换为栅格坐标
        start_grid = (int(start[0] / self.env.resolution),
                      int(start[1] / self.env.resolution))
        goal_grid = (int(goal[0] / self.env.resolution),
                     int(goal[1] / self.env.resolution))

        # 检查起点和终点可通行
        if not self.env.is_free(start_grid[0], start_grid[1]):
            print("起点不可通行")
            return None
        if not self.env.is_free(goal_grid[0], goal_grid[1]):
            print("终点不可通行")
            return None

        # 初始化
        open_set = [(0, start_grid)]
        came_from = {}
        g_score = {start_grid: 0}
        f_score = {start_grid: self.heuristic(start_grid, goal_grid)}
        closed_set = set()

        while open_set:
            # 选择 f 值最小的节点
            _, current = heapq.heappop(open_set)

            if current in closed_set:
                continue

            closed_set.add(current)

            # 到达目标
            if current == goal_grid:
                # 回溯路径
                path = []
                while current in came_from:
                    path.append(current)
                    current = came_from[current]
                path.append(start_grid)
                path.reverse()

                # 转换回物理坐标
                physical_path = [
                    (p[0] * self.env.resolution, p[1] * self.env.resolution)
                    for p in path
                ]
                return physical_path

            # 探索邻接节点
            neighbors = self.env.get_neighbors(current[0], current[1])
            for (nx, ny), distance in neighbors:
                if (nx, ny) in closed_set:
                    continue

                tentative_g = g_score[current] + distance

                if (nx, ny) not in g_score or tentative_g < g_score[(nx, ny)]:
                    came_from[(nx, ny)] = current
                    g_score[(nx, ny)] = tentative_g
                    f_score[(nx, ny)] = tentative_g + self.heuristic((nx, ny), goal_grid)
                    heapq.heappush(open_set, (f_score[(nx, ny)], (nx, ny)))

        print("无法找到路径")
        return None

# 使用示例
env = GridEnvironment(10, 10, 0.1)
env.add_obstacle(5, 5, 2)
env.add_obstacle(3, 7, 1.5)

planner = AStarPlanner(env)
path = planner.plan((1, 1), (9, 9))

if path:
    print("找到路径,点数:", len(path))
    for i, p in enumerate(path):
        print(f"  点 {i}: ({p[0]:.2f}, {p[1]:.2f})")

2.3 Dijkstra 算法

Dijkstra 算法保证找到最短路径。

python
class DijkstraPlanner:
    """Dijkstra 路径规划器"""

    def __init__(self, environment):
        self.env = environment

    def plan(self, start, goal):
        """
        使用 Dijkstra 算法规划路径

        参数:
            start: 起点 (x, y)
            goal: 目标点 (x, y)

        返回:
            路径点列表
        """
        start_grid = (int(start[0] / self.env.resolution),
                      int(start[1] / self.env.resolution))
        goal_grid = (int(goal[0] / self.env.resolution),
                     int(goal[1] / self.env.resolution))

        # 初始化距离
        distances = {}
        distances[start_grid] = 0
        previous = {}
        unvisited = set()

        # 添加所有可通行的栅格
        for y in range(self.env.grid_height):
            for x in range(self.env.grid_width):
                if self.env.grid[y, x] == 0:
                    pos = (x, y)
                    if pos not in distances:
                        distances[pos] = float('inf')
                    unvisited.add(pos)

        while unvisited:
            # 选择距离最小的未访问节点
            current = min(unvisited, key=lambda p: distances[p])

            if distances[current] == float('inf'):
                break

            if current == goal_grid:
                # 构建路径
                path = []
                while current in previous:
                    path.append(current)
                    current = previous[current]
                path.append(start_grid)
                path.reverse()

                physical_path = [
                    (p[0] * self.env.resolution, p[1] * self.env.resolution)
                    for p in path
                ]
                return physical_path

            unvisited.remove(current)

            # 更新邻接节点
            neighbors = self.env.get_neighbors(current[0], current[1])
            for (nx, ny), distance in neighbors:
                if (nx, ny) in unvisited:
                    new_distance = distances[current] + distance
                    if new_distance < distances[(nx, ny)]:
                        distances[(nx, ny)] = new_distance
                        previous[(nx, ny)] = current

        return None

3. 采样基础的路径规划

3.1 RRT(快速随机树)

RRT 是一种高效的采样基础算法。

python
import random
import math

class RRTPlanner:
    """RRT 路径规划器"""

    def __init__(self, environment, max_iterations=5000, step_size=0.5):
        """
        初始化 RRT 规划器

        参数:
            environment: GridEnvironment 实例
            max_iterations: 最大迭代次数
            step_size: 单步大小 (m)
        """
        self.env = environment
        self.max_iterations = max_iterations
        self.step_size = step_size
        self.tree = None
        self.parent = None

    def distance(self, p1, p2):
        """计算两点之间的欧氏距离"""
        return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)

    def random_point(self):
        """生成随机点"""
        x = random.uniform(0, self.env.width)
        y = random.uniform(0, self.env.height)
        return (x, y)

    def nearest_node(self, point):
        """找到树中最近的节点"""
        min_dist = float('inf')
        nearest = None

        for node in self.tree:
            dist = self.distance(point, node)
            if dist < min_dist:
                min_dist = dist
                nearest = node

        return nearest, min_dist

    def steer(self, from_point, to_point):
        """
        从 from_point 向 to_point 方向走一步

        参数:
            from_point: 起点
            to_point: 方向点

        返回:
            新点
        """
        dist = self.distance(from_point, to_point)

        if dist < self.step_size:
            return to_point

        # 计算方向
        ratio = self.step_size / dist
        new_x = from_point[0] + (to_point[0] - from_point[0]) * ratio
        new_y = from_point[1] + (to_point[1] - from_point[1]) * ratio

        return (new_x, new_y)

    def is_collision_free(self, p1, p2):
        """
        检查 p1 到 p2 的路径是否无碰撞

        参数:
            p1: 点 1
            p2: 点 2

        返回:
            True 如果无碰撞
        """
        # 分段检查
        steps = int(self.distance(p1, p2) / (self.env.resolution * 0.5)) + 1

        for i in range(steps + 1):
            t = i / (steps + 1) if steps > 0 else 0
            x = p1[0] + (p2[0] - p1[0]) * t
            y = p1[1] + (p2[1] - p1[1]) * t

            if not self.env.is_free(int(x / self.env.resolution),
                                   int(y / self.env.resolution)):
                return False

        return True

    def plan(self, start, goal, goal_threshold=0.5):
        """
        规划路径

        参数:
            start: 起点 (x, y)
            goal: 目标点 (x, y)
            goal_threshold: 到达目标的距离阈值 (m)

        返回:
            路径点列表,如果无法规划返回 None
        """
        # 初始化树
        self.tree = [start]
        self.parent = {start: None}

        for iteration in range(self.max_iterations):
            # 生成随机点(有概率选择目标)
            if random.random() < 0.1:  # 10% 概率选择目标
                rand_point = goal
            else:
                rand_point = self.random_point()

            # 找最近节点
            nearest, _ = self.nearest_node(rand_point)

            # 向随机点方向走一步
            new_point = self.steer(nearest, rand_point)

            # 检查碰撞
            if self.is_collision_free(nearest, new_point):
                self.tree.append(new_point)
                self.parent[new_point] = nearest

                # 检查是否到达目标
                if self.distance(new_point, goal) < goal_threshold:
                    self.parent[goal] = new_point

                    # 构建路径
                    path = []
                    current = goal
                    while current is not None:
                        path.append(current)
                        current = self.parent[current]
                    path.reverse()

                    print(f"RRT 在 {iteration} 次迭代后找到路径")
                    return path

        print("RRT 无法找到路径")
        return None

# 使用示例
env = GridEnvironment(10, 10, 0.1)
env.add_obstacle(4, 5, 3)
env.add_obstacle(7, 3, 2)

planner = RRTPlanner(env, max_iterations=5000, step_size=0.3)
path = planner.plan((1, 1), (9, 9))

if path:
    print(f"找到 RRT 路径,点数: {len(path)}")

4. 轨迹生成

4.1 轨迹平滑

路径规划得到的是离散的路径点,需要平滑处理。

python
import numpy as np
from scipy.interpolate import CubicSpline

class TrajectoryGenerator:
    """轨迹生成器"""

    def __init__(self, path, velocity=1.0):
        """
        初始化轨迹生成器

        参数:
            path: 路径点列表
            velocity: 期望速度 (m/s)
        """
        self.path = path
        self.velocity = velocity
        self.trajectory = None
        self.timestamps = None

    def linear_interpolation(self, num_points):
        """
        线性插值:连接路径点

        参数:
            num_points: 插值点数

        返回:
            插值后的轨迹
        """
        trajectory = []

        for i in range(len(self.path) - 1):
            p1 = np.array(self.path[i])
            p2 = np.array(self.path[i + 1])

            for t in np.linspace(0, 1, num_points):
                point = p1 + t * (p2 - p1)
                trajectory.append(tuple(point))

        self.trajectory = trajectory
        self._generate_timestamps()
        return trajectory

    def spline_interpolation(self, num_points=100):
        """
        样条插值:平滑曲线

        参数:
            num_points: 插值点数

        返回:
            平滑的轨迹
        """
        path_array = np.array(self.path)

        # 计算路径的参数 t
        t = np.linspace(0, len(self.path) - 1, len(self.path))

        # 对 x 和 y 分别进行样条插值
        cs_x = CubicSpline(t, path_array[:, 0])
        cs_y = CubicSpline(t, path_array[:, 1])

        # 生成平滑轨迹
        t_smooth = np.linspace(0, len(self.path) - 1, num_points)
        x_smooth = cs_x(t_smooth)
        y_smooth = cs_y(t_smooth)

        self.trajectory = list(zip(x_smooth, y_smooth))
        self._generate_timestamps()
        return self.trajectory

    def _generate_timestamps(self):
        """生成时间戳"""
        if self.trajectory is None:
            return

        self.timestamps = [0.0]

        for i in range(1, len(self.trajectory)):
            p1 = np.array(self.trajectory[i - 1])
            p2 = np.array(self.trajectory[i])
            distance = np.linalg.norm(p2 - p1)
            time_interval = distance / self.velocity
            self.timestamps.append(self.timestamps[-1] + time_interval)

    def get_point_at_time(self, t):
        """
        获取指定时间的轨迹点

        参数:
            t: 时间 (s)

        返回:
            轨迹点 (x, y)
        """
        if self.timestamps is None:
            return None

        # 找到对应的时间段
        for i in range(len(self.timestamps) - 1):
            if self.timestamps[i] <= t <= self.timestamps[i + 1]:
                # 线性插值
                ratio = (t - self.timestamps[i]) / (self.timestamps[i + 1] - self.timestamps[i])
                p1 = np.array(self.trajectory[i])
                p2 = np.array(self.trajectory[i + 1])
                point = p1 + ratio * (p2 - p1)
                return tuple(point)

        # 超出时间范围,返回最后一点
        if t >= self.timestamps[-1]:
            return self.trajectory[-1]

        return self.trajectory[0]

# 使用示例
path = [(0, 0), (2, 1), (4, 3), (6, 2), (8, 4)]
gen = TrajectoryGenerator(path, velocity=2.0)

# 线性插值
linear_traj = gen.linear_interpolation(10)
print(f"线性插值: {len(linear_traj)} 个点")

# 样条插值(更平滑)
spline_traj = gen.spline_interpolation(100)
print(f"样条插值: {len(spline_traj)} 个点")

# 获取特定时间的点
for t in [0, 0.5, 1.0, 1.5]:
    point = gen.get_point_at_time(t)
    print(f"t={t}s: {point}")

4.2 速度规划

规划沿轨迹的速度分布。

python
class VelocityProfile:
    """速度规划"""

    def __init__(self, trajectory_length, max_velocity, max_acceleration):
        """
        初始化速度规划

        参数:
            trajectory_length: 轨迹总长度 (m)
            max_velocity: 最大速度 (m/s)
            max_acceleration: 最大加速度 (m/s²)
        """
        self.length = trajectory_length
        self.v_max = max_velocity
        self.a_max = max_acceleration

    def trapezoidal_profile(self):
        """
        梯形速度分布

        返回:
            (加速阶段长度, 匀速阶段长度, 减速阶段长度)
        """
        # 加速和减速的距离
        accel_distance = self.v_max**2 / (2 * self.a_max)
        decel_distance = accel_distance  # 对称

        if 2 * accel_distance > self.length:
            # 无法达到最大速度,三角形分布
            accel_distance = self.length / 2
            decel_distance = self.length / 2
            const_distance = 0
        else:
            # 有匀速阶段
            const_distance = self.length - 2 * accel_distance

        return accel_distance, const_distance, decel_distance

    def get_velocity_at_distance(self, distance):
        """
        获取指定距离处的速度

        参数:
            distance: 沿轨迹的距离 (m)

        返回:
            速度 (m/s)
        """
        accel_dist, const_dist, decel_dist = self.trapezoidal_profile()

        if distance < accel_dist:
            # 加速阶段
            v = np.sqrt(2 * self.a_max * distance)
        elif distance < accel_dist + const_dist:
            # 匀速阶段
            v = self.v_max
        else:
            # 减速阶段
            remaining = self.length - distance
            v = np.sqrt(2 * self.a_max * remaining)

        return v

# 使用示例
vp = VelocityProfile(10.0, 2.0, 1.0)

print("速度分布:")
for dist in np.linspace(0, 10, 11):
    v = vp.get_velocity_at_distance(dist)
    print(f"距离 {dist:.1f}m: 速度 {v:.2f} m/s")

5. 轨迹跟踪控制

5.1 Pure Pursuit 控制

Pure Pursuit 是一种常见的轨迹跟踪算法。

python
import math

class PurePursuitController:
    """Pure Pursuit 轨迹跟踪控制器"""

    def __init__(self, lookahead_distance=1.0):
        """
        初始化 Pure Pursuit 控制器

        参数:
            lookahead_distance: 前向距离 (m)
        """
        self.lookahead_distance = lookahead_distance

    def compute_steering_angle(self, robot_position, robot_heading, trajectory):
        """
        计算转向角度

        参数:
            robot_position: 机器人位置 (x, y)
            robot_heading: 机器人朝向角 (rad)
            trajectory: 目标轨迹点列表

        返回:
            转向角度 (rad)
        """
        # 找到前向距离内的目标点
        target_point = None

        for point in trajectory:
            dx = point[0] - robot_position[0]
            dy = point[1] - robot_position[1]
            distance = np.sqrt(dx**2 + dy**2)

            if distance >= self.lookahead_distance:
                target_point = point
                break

        if target_point is None:
            # 如果没找到,使用最后一点
            target_point = trajectory[-1]

        # 计算目标方向
        dx = target_point[0] - robot_position[0]
        dy = target_point[1] - robot_position[1]
        target_heading = math.atan2(dy, dx)

        # 计算航向误差
        heading_error = target_heading - robot_heading

        # 归一化到 [-π, π]
        while heading_error > math.pi:
            heading_error -= 2 * math.pi
        while heading_error < -math.pi:
            heading_error += 2 * math.pi

        # 计算转向角度
        distance_to_target = np.sqrt(dx**2 + dy**2)
        steering_angle = math.atan2(2 * math.sin(heading_error), distance_to_target)

        return steering_angle

    def update(self, robot_position, robot_heading, velocity, trajectory, dt):
        """
        执行一步控制更新

        参数:
            robot_position: 机器人位置
            robot_heading: 机器人朝向
            velocity: 前进速度 (m/s)
            trajectory: 目标轨迹
            dt: 时间步长 (s)

        返回:
            (新位置, 新朝向, 转向角度)
        """
        # 计算转向角度
        steering_angle = self.compute_steering_angle(
            robot_position, robot_heading, trajectory
        )

        # 使用自行车模型更新位置
        L = 0.5  # 轴距 (m),根据实际 LeBot 调整

        new_x = robot_position[0] + velocity * math.cos(robot_heading) * dt
        new_y = robot_position[1] + velocity * math.sin(robot_heading) * dt
        new_heading = robot_heading + (velocity / L) * math.tan(steering_angle) * dt

        return (new_x, new_y), new_heading, steering_angle

# 使用示例
controller = PurePursuitController(lookahead_distance=2.0)

# 模拟轨迹
trajectory = [(i*0.5, math.sin(i*0.3)) for i in range(20)]

robot_pos = (0, 0)
robot_heading = 0
velocity = 1.0
dt = 0.1

print("Pure Pursuit 跟踪:")
for _ in range(50):
    new_pos, new_heading, steering = controller.update(
        robot_pos, robot_heading, velocity, trajectory, dt
    )
    print(f"位置: ({new_pos[0]:.2f}, {new_pos[1]:.2f}), 朝向: {new_heading:.3f}")
    robot_pos = new_pos
    robot_heading = new_heading

5.2 LQR 轨迹跟踪

使用 LQR(线性二次调节器)进行精确的轨迹跟踪。

python
from scipy import linalg

class LQRTrajectoryTracker:
    """LQR 轨迹跟踪控制器"""

    def __init__(self, Q, R, dt=0.01):
        """
        初始化 LQR 跟踪器

        参数:
            Q: 状态权重矩阵
            R: 控制权重矩阵
            dt: 采样时间
        """
        self.Q = Q  # 4x4 矩阵,权重状态误差
        self.R = R  # 2x2 矩阵,权重控制输入
        self.dt = dt

    def compute_optimal_gain(self, A, B):
        """
        计算 LQR 增益

        参数:
            A: 状态转移矩阵 (4x4)
            B: 控制矩阵 (4x2)

        返回:
            增益矩阵 K (2x4)
        """
        # 求解代数Riccati方程
        P = linalg.solve_continuous_are(A, B, self.Q, self.R)

        # 计算增益 K = R^(-1) * B^T * P
        K = np.linalg.inv(self.R) @ B.T @ P

        return K

    def update(self, state_error, A, B):
        """
        计算控制输入

        参数:
            state_error: 状态误差向量 [x_err, y_err, theta_err, v_err]
            A: 状态转移矩阵
            B: 控制矩阵

        返回:
            控制输入 [v, omega]
        """
        # 计算最优增益
        K = self.compute_optimal_gain(A, B)

        # 计算控制输入
        u = -K @ state_error

        return u

# 使用示例
# 定义 LeBot 的状态空间模型
# 状态: [x, y, theta, v]
# 控制: [v, omega]

Q = np.diag([10, 10, 5, 1])  # 位置和方向权重较高
R = np.diag([0.1, 0.1])

# 离散化模型
dt = 0.01
A = np.eye(4)  # 简化的状态转移矩阵
B = np.array([[dt, 0],
              [0, dt],
              [0, dt],
              [dt, 0]])

tracker = LQRTrajectoryTracker(Q, R, dt)

6. LeBot 完整运动规划系统

python
class LeBotMotionPlanner:
    """LeBot 完整运动规划系统"""

    def __init__(self, env_width=20, env_height=20):
        self.env = GridEnvironment(env_width, env_height, 0.2)
        self.astar_planner = AStarPlanner(self.env)
        self.trajectory_gen = None
        self.controller = PurePursuitController(lookahead_distance=1.0)

    def add_obstacle(self, x, y, size):
        """添加障碍"""
        self.env.add_obstacle(x, y, size)

    def plan_mission(self, start, goal, velocity=1.0):
        """
        规划完整的运动任务

        参数:
            start: 起点
            goal: 目标点
            velocity: 期望速度

        返回:
            (路径, 轨迹)
        """
        # 第一步:路径规划
        path = self.astar_planner.plan(start, goal)

        if path is None:
            print("无法规划路径")
            return None, None

        # 第二步:轨迹平滑
        self.trajectory_gen = TrajectoryGenerator(path, velocity)
        trajectory = self.trajectory_gen.spline_interpolation(100)

        return path, trajectory

    def execute_trajectory(self, trajectory, max_steps=1000):
        """
        执行轨迹跟踪

        参数:
            trajectory: 轨迹点列表
            max_steps: 最大步数

        返回:
            执行结果
        """
        robot_pos = trajectory[0]
        robot_heading = 0
        velocity = 1.0

        positions = [robot_pos]

        for step in range(max_steps):
            new_pos, new_heading, steering = self.controller.update(
                robot_pos, robot_heading, velocity, trajectory, 0.01
            )

            positions.append(new_pos)
            robot_pos = new_pos
            robot_heading = new_heading

            # 检查是否到达目标
            dist_to_goal = np.sqrt((trajectory[-1][0] - new_pos[0])**2 +
                                  (trajectory[-1][1] - new_pos[1])**2)
            if dist_to_goal < 0.1:
                print(f"到达目标,用时 {step} 步")
                break

        return positions

# 使用示例
planner = LeBotMotionPlanner()
planner.add_obstacle(5, 5, 2)
planner.add_obstacle(10, 8, 2)

path, trajectory = planner.plan_mission((1, 1), (18, 18), velocity=1.5)

if trajectory:
    print(f"规划完成: 路径点 {len(path)}, 轨迹点 {len(trajectory)}")
    positions = planner.execute_trajectory(trajectory)
    print(f"执行完成: 记录 {len(positions)} 个位置点")

7. 参数调整与优化

7.1 路径规划参数

参数影响推荐值
栅格分辨率分辨率越高越精确,但计算量增加0.1-0.5 m
前向距离(Pure Pursuit)越大响应越平缓,越小响应越灵敏1.0-3.0 m
最大速度限制机器人速度1.0-2.0 m/s
最大加速度限制加速能力0.5-1.0 m/s²

7.2 调参流程

  1. 初始设置:使用推荐值
  2. 测试路径规划:简单环境下测试
  3. 调整轨迹参数:根据执行效果调整
  4. 测试跟踪控制:在实际机器人上测试
  5. 优化参数:根据性能指标持续改进

参考资源

  • Lavalle, S. M. "Planning Algorithms". Cambridge University Press, 2006.
  • Paden, B., et al. "A Survey of Motion Planning and Control Techniques for Self-driving Urban Vehicles". IEEE Transactions on Intelligent Vehicles, 2016.
  • Thrun, S., Burgard, W., & Fox, D. "Probabilistic Robotics". MIT Press, 2005.
  • ROS Navigation Stack: http://wiki.ros.org/navigation/
  • OpenPlanner: https://github.com/autowarefoundation/core_planning/

由 LeBot 开发团队编写