跳转至

运动规划

运动规划是机器人学中的核心问题,涉及如何让机器人从一个位置安全地移动到另一个位置。本章将介绍运动规划的核心概念和算法。

学习目标

完成本章后,你将能够:

  • 理解运动规划的基本概念
  • 掌握路径规划算法
  • 了解运动规划方法
  • 应用运动规划解决实际问题

1. 运动规划基础

1.1 运动规划问题定义

运动规划问题:
- 配置空间(C-space):机器人所有可能的位置和姿态
- 障碍物空间(C-obstacle):与障碍物碰撞的配置
- 自由空间(C-free):不与障碍物碰撞的配置
- 初始配置(q_init):机器人起始位置
- 目标配置(q_goal):机器人目标位置

目标:找到一条从 q_init 到 q_goal 的路径,完全在 C-free 中

数学公式

场景:在规划之前,我们需要数学上定义机器人所处的空间。

配置空间 \(\mathcal{C}\) 的形式化定义为:

  • 配置空间\(\mathcal{C} = \{ \mathbf{q} \mid \mathbf{q} \in \mathbb{R}^n, \mathbf{q}_{min} \leq \mathbf{q} \leq \mathbf{q}_{max} \}\)
  • 障碍物空间\(\mathcal{C}_{obs} = \{ \mathbf{q} \mid \text{robot}(\mathbf{q}) \cap \text{obstacle} \neq \emptyset \}\)
  • 自由空间\(\mathcal{C}_{free} = \mathcal{C} \setminus \mathcal{C}_{obs}\)
  • 距离度量\(d(\mathbf{q}_1, \mathbf{q}_2) = \|\mathbf{q}_1 - \mathbf{q}_2\|_2 = \sqrt{\sum_{i=1}^{n}(q_{1,i} - q_{2,i})^2}\)

代码实现

import numpy as np

class ConfigurationSpace:
    """配置空间"""

    def __init__(self, dimension, bounds):
        """
        初始化配置空间

        参数:
        dimension: 维度
        bounds: 边界 [(min1, max1), (min2, max2), ...]
        """
        self.dimension = dimension
        self.bounds = bounds
        self.obstacles = []

    def add_obstacle(self, obstacle):
        """添加障碍物"""
        self.obstacles.append(obstacle)

    def is_collision_free(self, config):
        """检查配置是否无碰撞"""
        for obstacle in self.obstacles:
            if obstacle.contains(config):
                return False
        return True

    def sample_random(self):
        """随机采样配置"""
        config = []
        for low, high in self.bounds:
            config.append(np.random.uniform(low, high))
        return tuple(config)

    def distance(self, config1, config2):
        """计算两个配置之间的距离"""
        return np.sqrt(sum((a - b) ** 2 for a, b in zip(config1, config2)))

class Obstacle:
    """障碍物"""

    def __init__(self, center, size):
        """
        初始化障碍物

        参数:
        center: 中心点
        size: 大小
        """
        self.center = center
        self.size = size

    def contains(self, config):
        """检查配置是否在障碍物内"""
        for i, (c, s) in enumerate(zip(self.center, self.size)):
            if abs(config[i] - c) > s / 2:
                return False
        return True

# 示例:2D 配置空间
cspace = ConfigurationSpace(2, [(0, 10), (0, 10)])

# 添加障碍物
cspace.add_obstacle(Obstacle((3, 3), (2, 2)))
cspace.add_obstacle(Obstacle((7, 7), (2, 2)))

# 测试
config1 = (1, 1)
config2 = (3, 3)

print(f"配置 {config1} 无碰撞: {cspace.is_collision_free(config1)}")
print(f"配置 {config2} 无碰撞: {cspace.is_collision_free(config2)}")

1.2 路径表示

数学公式

场景:路径由通过线段连接的航点组成。

路径表示为航点序列:

  • 路径长度\(L = \sum_{i=1}^{n-1} \|\mathbf{w}_{i+1} - \mathbf{w}_i\|_2\)

  • 线性插值\(\mathbf{p}(t) = (1-t)\mathbf{a} + t\mathbf{b}, \quad t \in [0, 1]\)

代码实现

class Path:
    """路径"""

    def __init__(self, waypoints):
        """
        初始化路径

        参数:
        waypoints: 路径点列表
        """
        self.waypoints = waypoints

    @property
    def length(self):
        """路径长度"""
        total_length = 0
        for i in range(len(self.waypoints) - 1):
            total_length += np.sqrt(sum((a - b) ** 2 
                                       for a, b in zip(self.waypoints[i], 
                                                      self.waypoints[i + 1])))
        return total_length

    def interpolate(self, num_points):
        """插值路径"""
        if len(self.waypoints) < 2:
            return self.waypoints

        interpolated = []
        for i in range(len(self.waypoints) - 1):
            start = np.array(self.waypoints[i])
            end = np.array(self.waypoints[i + 1])

            for t in np.linspace(0, 1, num_points // (len(self.waypoints) - 1)):
                point = start + t * (end - start)
                interpolated.append(tuple(point))

        return interpolated

    def is_valid(self, cspace):
        """检查路径是否有效"""
        for waypoint in self.waypoints:
            if not cspace.is_collision_free(waypoint):
                return False
        return True

# 示例
path = Path([(0, 0), (1, 1), (2, 2), (3, 3)])
print(f"路径长度: {path.length:.2f}")
print(f"路径有效: {path.is_valid(cspace)}")

2. 路径规划算法

2.1 基于采样的规划

随机路径图(PRM)

数学公式

场景:如何在连续空间中找路径?采样并连接。

PRM(概率路线图法)构建配置空间的图表示:

  • 采样:从 \(\mathcal{C}_{free}\) 中生成 \(N\) 个随机配置
  • 图构建\(\mathcal{G} = (\mathcal{V}, \mathcal{E})\),其中:
  • \(\mathcal{V}\) = 采样配置
  • \(\mathcal{E} = \{ (v_i, v_j) \mid d(v_i, v_j) < r \text{ 且路径无碰撞} \}\)
  • 查询阶段:将起点/终点连接到最近节点,使用 BFS 或 Dijkstra 找最短路径

代码分解: - build_roadmap():采样配置 + 连接有碰撞自由路径的附近节点 - find_path():将查询点添加到图 + 使用 BFS 搜索

代码实现

class PRM:
    """概率路线图(PRM)"""

    def __init__(self, cspace, num_samples=100, connection_radius=2.0):
        """
        初始化 PRM

        参数:
        cspace: 配置空间
        num_samples: 采样数量
        connection_radius: 连接半径
        """
        self.cspace = cspace
        self.num_samples = num_samples
        self.connection_radius = connection_radius
        self.nodes = []
        self.edges = {}

    def build_roadmap(self):
        """构建路线图"""
        # 采样节点
        for _ in range(self.num_samples):
            config = self.cspace.sample_random()
            if self.cspace.is_collision_free(config):
                self.nodes.append(config)

        # 连接节点
        for i, node1 in enumerate(self.nodes):
            self.edges[i] = []
            for j, node2 in enumerate(self.nodes):
                if i != j:
                    dist = self.cspace.distance(node1, node2)
                    if dist <= self.connection_radius:
                        # 检查路径是否无碰撞
                        if self._is_path_collision_free(node1, node2):
                            self.edges[i].append(j)

    def _is_path_collision_free(self, config1, config2):
        """检查路径是否无碰撞"""
        # 简化:只检查中间点
        num_checks = 10
        for t in np.linspace(0, 1, num_checks):
            config = tuple(
                a + t * (b - a) 
                for a, b in zip(config1, config2)
            )
            if not self.cspace.is_collision_free(config):
                return False
        return True

    def find_path(self, start, goal):
        """查找路径"""
        # 将起点和终点添加到路线图
        start_idx = len(self.nodes)
        self.nodes.append(start)
        self.edges[start_idx] = []

        goal_idx = len(self.nodes)
        self.nodes.append(goal)
        self.edges[goal_idx] = []

        # 连接起点和终点
        for i, node in enumerate(self.nodes):
            if i != start_idx and i != goal_idx:
                if self.cspace.distance(start, node) <= self.connection_radius:
                    if self._is_path_collision_free(start, node):
                        self.edges[start_idx].append(i)
                        self.edges[i].append(start_idx)

                if self.cspace.distance(goal, node) <= self.connection_radius:
                    if self._is_path_collision_free(goal, node):
                        self.edges[goal_idx].append(i)
                        self.edges[i].append(goal_idx)

        # 使用 BFS 查找路径
        return self._bfs(start_idx, goal_idx)

    def _bfs(self, start_idx, goal_idx):
        """广度优先搜索"""
        from collections import deque

        queue = deque([start_idx])
        visited = {start_idx}
        parent = {start_idx: None}

        while queue:
            current = queue.popleft()

            if current == goal_idx:
                # 重建路径
                path = []
                while current is not None:
                    path.append(self.nodes[current])
                    current = parent[current]
                return Path(path[::-1])

            for neighbor in self.edges[current]:
                if neighbor not in visited:
                    visited.add(neighbor)
                    parent[neighbor] = current
                    queue.append(neighbor)

        return None  # 无路径

# 示例
prm = PRM(cspace, num_samples=100, connection_radius=3.0)
prm.build_roadmap()

start = (0, 0)
goal = (9, 9)

path = prm.find_path(start, goal)

if path:
    print(f"找到路径,长度: {path.length:.2f}")
    print(f"路径点: {path.waypoints}")
else:
    print("未找到路径")

快速探索随机树(RRT)

数学公式

场景:不构建完整图,而是逐步向未探索区域生长树。

RRT(快速探索随机树)通过构建树来探索配置空间:

  • 最近邻\(q_{nearest} = \arg\min_{q \in \mathcal{V}} \|q - q_{rand}\|_2\)
  • 导向\(q_{new} = q_{nearest} + \frac{\delta}{\|q_{rand} - q_{nearest}\|}_2 \cdot (q_{rand} - q_{nearest})\)
  • 目标偏置\(P(q_{rand} = q_{goal}) = \varepsilon\)(通常 \(\varepsilon = 0.1\)
  • 扩展条件\(q_{new} \in \mathcal{C}_{free} \land q_{new} \in \text{bounds}\)

代码分解: - _find_nearest():找到离随机样本最近的树节点 - _extend():沿样本方向创建新节点 - _build_path():从目标到起点追踪父指针

代码实现

class RRT:
    """快速探索随机树(RRT)"""

    def __init__(self, cspace, step_size=0.5, max_iterations=1000):
        """
        初始化 RRT

        参数:
        cspace: 配置空间
        step_size: 步长
        max_iterations: 最大迭代次数
        """
        self.cspace = cspace
        self.step_size = step_size
        self.max_iterations = max_iterations
        self.nodes = []
        self.parent = {}

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

        参数:
        start: 起始配置
        goal: 目标配置

        返回:
        path: 路径(如果找到)
        """
        # 初始化
        self.nodes = [start]
        self.parent = {start: None}

        for iteration in range(self.max_iterations):
            # 随机采样
            if np.random.random() < 0.1:  # 10% 概率采样目标
                random_config = goal
            else:
                random_config = self.cspace.sample_random()

            # 找到最近的节点
            nearest_node = self._find_nearest(random_config)

            # 扩展
            new_node = self._extend(nearest_node, random_config)

            if new_node:
                self.nodes.append(new_node)
                self.parent[new_node] = nearest_node

                # 检查是否到达目标
                if self.cspace.distance(new_node, goal) < self.step_size:
                    # 构建路径
                    path = self._build_path(new_node, goal)
                    return path

        return None  # 未找到路径

    def _find_nearest(self, config):
        """找到最近的节点"""
        min_dist = float('inf')
        nearest = None

        for node in self.nodes:
            dist = self.cspace.distance(node, config)
            if dist < min_dist:
                min_dist = dist
                nearest = node

        return nearest

    def _extend(self, from_config, to_config):
        """扩展节点"""
        # 计算方向
        direction = np.array(to_config) - np.array(from_config)
        length = np.linalg.norm(direction)

        if length < 1e-6:
            return None

        # 归一化方向
        direction = direction / length

        # 计算新配置
        new_config = tuple(
            np.array(from_config) + self.step_size * direction
        )

        # 检查边界
        for i, (low, high) in enumerate(self.cspace.bounds):
            if new_config[i] < low or new_config[i] > high:
                return None

        # 检查碰撞
        if not self.cspace.is_collision_free(new_config):
            return None

        return new_config

    def _build_path(self, last_node, goal):
        """构建路径"""
        path = [goal]
        current = last_node

        while current is not None:
            path.append(current)
            current = self.parent[current]

        return Path(path[::-1])

# 示例
rrt = RRT(cspace, step_size=0.5, max_iterations=1000)

start = (0, 0)
goal = (9, 9)

path = rrt.plan(start, goal)

if path:
    print(f"找到路径,长度: {path.length:.2f}")
    print(f"路径点数: {len(path.waypoints)}")
else:
    print("未找到路径")

2.2 基于搜索的规划

A* 搜索

数学公式

场景:如果我们大致知道目标在哪里,利用这个知识引导搜索。

A* 结合实际代价和启发式估计来找到最优路径:

  • 代价函数\(f(n) = g(n) + h(n)\),其中:
  • \(g(n)\) = 从起点到 \(n\) 的实际代价(累积距离)
  • \(h(n)\) = 从 \(n\) 到目标的启发式估计(如欧几里得距离)
  • \(f(n)\) = 通过 \(n\) 的估计总代价
  • 最优性条件:如果 \(h(n) \leq h^*(n)\) 对所有 \(n\) 成立(可采纳),则 A* 保证最优
  • 优先队列:按 \(f\) 值排序节点(最低 \(f\) = 最高优先级)

代码分解: - g_score 更新:跟踪从起点到每个节点的实际代价 - heuristic 计算:估计到目标的剩余代价 - 路径重建:从目标向后追踪父指针

代码实现

def a_star_path_planning(cspace, start, goal, heuristic=None):
    """
    A* 路径规划

    参数:
    cspace: 配置空间
    start: 起始配置
    goal: 目标配置
    heuristic: 启发函数

    返回:
    path: 路径(如果找到)
    """
    import heapq

    # 初始化
    if heuristic is None:
        heuristic = lambda config: cspace.distance(config, goal)

    # 优先队列:(f值, 配置)
    frontier = [(heuristic(start), start)]
    explored = set()
    g_score = {start: 0}
    parent = {start: None}

    while frontier:
        f, current = heapq.heappop(frontier)

        # 检查是否到达目标
        if cspace.distance(current, goal) < 0.5:
            # 构建路径
            path = [goal]
            while current is not None:
                path.append(current)
                current = parent[current]
            return Path(path[::-1])

        if current in explored:
            continue

        explored.add(current)

        # 扩展邻居
        for neighbor in _get_neighbors(cspace, current):
            if neighbor in explored:
                continue

            new_g = g_score[current] + cspace.distance(current, neighbor)

            if neighbor not in g_score or new_g < g_score[neighbor]:
                g_score[neighbor] = new_g
                f = new_g + heuristic(neighbor)
                heapq.heappush(frontier, (f, neighbor))
                parent[neighbor] = current

    return None  # 无路径

def _get_neighbors(cspace, config, step_size=0.5):
    """获取邻居配置"""
    neighbors = []

    # 生成邻居(简化:只考虑基本方向)
    for delta in [(step_size, 0), (-step_size, 0), (0, step_size), (0, -step_size)]:
        neighbor = tuple(c + d for c, d in zip(config, delta))

        # 检查边界
        valid = True
        for i, (low, high) in enumerate(cspace.bounds):
            if neighbor[i] < low or neighbor[i] > high:
                valid = False
                break

        if valid and cspace.is_collision_free(neighbor):
            neighbors.append(neighbor)

    return neighbors

3. 运动规划

3.1 轨迹规划

数学公式

场景:规划路径只是几何形状。现在需要添加时间——以最大速度移动且不超过加速度限制。

梯形速度曲线将运动分为三个阶段:

  • 加速阶段 (\(0 \leq t \leq t_a\)):
  • \(v(t) = a \cdot t\)
  • \(d(t) = \frac{1}{2}a \cdot t^2\)

  • 匀速阶段 (\(t_a \leq t \leq t_a + t_c\)):

  • \(v(t) = V_{max}\)
  • \(d(t) = d_{accel} + V_{max} \cdot (t - t_a)\)

  • 减速阶段 (\(t_a + t_c \leq t \leq T\)):

  • \(v(t) = a \cdot (T - t)\)
  • \(d(t) = D - \frac{1}{2}a \cdot (T - t)^2\)

  • 时间计算

  • 梯形当 \(D \geq 2D_{accel}\)\(t_a = \frac{V_{max}}{a}\), \(t_c = \frac{D - 2D_{accel}}{V_{max}}\)
  • 三角形当 \(D < 2D_{accel}\)\(t_a = t_d = \sqrt{\frac{D}{a}}\)

代码分解: - 加速阶段:\(d = 0.5 \cdot a \cdot t^2\), \(v = a \cdot t\) - 匀速阶段:\(d = d_{accel} + V_{max} \cdot (t - t_a)\), \(v = V_{max}\) - 减速阶段:\(d = D - 0.5 \cdot a \cdot t_{decel}^2\), \(v = a \cdot t_{decel}\)

代码实现

class TrajectoryPlanner:
    """轨迹规划器"""

    def __init__(self, path, max_velocity=1.0, max_acceleration=0.5):
        """
        初始化轨迹规划器

        参数:
        path: 路径
        max_velocity: 最大速度
        max_acceleration: 最大加速度
        """
        self.path = path
        self.max_velocity = max_velocity
        self.max_acceleration = max_acceleration

    def plan_trajectory(self):
        """规划轨迹"""
        # 计算路径长度
        path_length = self.path.length

        # 计算时间
        # 使用梯形速度曲线
        t_accel = self.max_velocity / self.max_acceleration
        d_accel = 0.5 * self.max_acceleration * t_accel ** 2

        if 2 * d_accel > path_length:
            # 三角形速度曲线
            t_accel = np.sqrt(path_length / self.max_acceleration)
            t_total = 2 * t_accel
        else:
            # 梯形速度曲线
            d_constant = path_length - 2 * d_accel
            t_constant = d_constant / self.max_velocity
            t_total = 2 * t_accel + t_constant

        # 生成轨迹点
        trajectory = []
        num_points = 100

        for t in np.linspace(0, t_total, num_points):
            if t < t_accel:
                # 加速阶段
                d = 0.5 * self.max_acceleration * t ** 2
                v = self.max_acceleration * t
            elif t < t_total - t_accel:
                # 匀速阶段
                d = d_accel + self.max_velocity * (t - t_accel)
                v = self.max_velocity
            else:
                # 减速阶段
                t_decel = t_total - t
                d = path_length - 0.5 * self.max_acceleration * t_decel ** 2
                v = self.max_acceleration * t_decel

            # 在路径上插值
            config = self._interpolate_path(d / path_length)

            trajectory.append({
                'time': t,
                'position': config,
                'velocity': v,
                'distance': d
            })

        return trajectory

    def _interpolate_path(self, t):
        """在路径上插值"""
        waypoints = self.path.waypoints

        if t <= 0:
            return waypoints[0]
        if t >= 1:
            return waypoints[-1]

        # 找到对应的路径段
        segment_length = 1.0 / (len(waypoints) - 1)
        segment_index = int(t / segment_length)
        segment_t = (t - segment_index * segment_length) / segment_length

        if segment_index >= len(waypoints) - 1:
            return waypoints[-1]

        start = np.array(waypoints[segment_index])
        end = np.array(waypoints[segment_index + 1])

        return tuple(start + segment_t * (end - start))

# 示例
if path:
    planner = TrajectoryPlanner(path, max_velocity=1.0, max_acceleration=0.5)
    trajectory = planner.plan_trajectory()

    print(f"轨迹点数: {len(trajectory)}")
    print(f"总时间: {trajectory[-1]['time']:.2f}")

3.2 运动学约束

数学公式

场景:机器人路径必须满足物理约束——非完整系统不能侧向移动。

非完整约束:像汽车或差分驱动机器人这样的系统,其自由度受到约束: - 配置:\(\mathbf{q} = [x, y, \theta]^T\) - 约束:\(\dot{x}\sin\theta - \dot{y}\cos\theta = 0\)(禁止侧滑)

路径捷径平滑:通过移除不必要的航点来减少路径长度: - 对于航点 \(\mathbf{w}_i, \mathbf{w}_j\)\(i < j\)),如果直线无碰撞,用直接连接替换 \(\mathbf{w}_{i+1}, ..., \mathbf{w}_{j-1}\) - 有效捷径:\(\forall t \in [0,1]: (1-t)\mathbf{w}_i + t\mathbf{w}_j \in \mathcal{C}_{free}\)

代码分解: - _smooth_path():随机捷径迭代——尝试移除中间航点 - _is_path_valid():通过采样检查捷径路径是否无碰撞

代码实现

class KinematicPlanner:
    """运动学规划器"""

    def __init__(self, robot_model, cspace):
        """
        初始化运动学规划器

        参数:
        robot_model: 机器人模型
        cspace: 配置空间
        """
        self.robot_model = robot_model
        self.cspace = cspace

    def plan(self, start_config, goal_config):
        """规划运动"""
        # 使用 RRT 规划
        rrt = RRT(self.cspace, step_size=0.1, max_iterations=5000)
        path = rrt.plan(start_config, goal_config)

        if path:
            # 应用运动学约束
            constrained_path = self._apply_kinematic_constraints(path)
            return constrained_path

        return None

    def _apply_kinematic_constraints(self, path):
        """应用运动学约束"""
        # 简化:只进行路径平滑
        smoothed_path = self._smooth_path(path)
        return smoothed_path

    def _smooth_path(self, path, iterations=100):
        """平滑路径"""
        waypoints = list(path.waypoints)

        for _ in range(iterations):
            # 随机选择两个点
            i = np.random.randint(1, len(waypoints) - 1)
            j = np.random.randint(1, len(waypoints) - 1)

            if i > j:
                i, j = j, i

            # 检查直接连接是否无碰撞
            if self._is_path_valid(waypoints[i], waypoints[j]):
                # 删除中间点
                waypoints = waypoints[:i + 1] + waypoints[j:]

        return Path(waypoints)

    def _is_path_valid(self, config1, config2, num_checks=10):
        """检查路径是否有效"""
        for t in np.linspace(0, 1, num_checks):
            config = tuple(
                a + t * (b - a) 
                for a, b in zip(config1, config2)
            )
            if not self.cspace.is_collision_free(config):
                return False
        return True

4. 实践练习

练习 1:实现 PRM

def prm_exercise():
    # 创建配置空间
    cspace = ConfigurationSpace(2, [(0, 10), (0, 10)])
    cspace.add_obstacle(Obstacle((3, 3), (2, 2)))
    cspace.add_obstacle(Obstacle((7, 7), (2, 2)))

    # 创建 PRM
    prm = PRM(cspace, num_samples=200, connection_radius=3.0)
    prm.build_roadmap()

    # 查找路径
    start = (0, 0)
    goal = (9, 9)

    path = prm.find_path(start, goal)

    if path:
        print(f"PRM 找到路径,长度: {path.length:.2f}")
    else:
        print("PRM 未找到路径")

练习 2:实现 RRT

def rrt_exercise():
    # 创建配置空间
    cspace = ConfigurationSpace(2, [(0, 10), (0, 10)])
    cspace.add_obstacle(Obstacle((3, 3), (2, 2)))
    cspace.add_obstacle(Obstacle((7, 7), (2, 2)))

    # 创建 RRT
    rrt = RRT(cspace, step_size=0.5, max_iterations=1000)

    # 规划路径
    start = (0, 0)
    goal = (9, 9)

    path = rrt.plan(start, goal)

    if path:
        print(f"RRT 找到路径,长度: {path.length:.2f}")
        print(f"路径点数: {len(path.waypoints)}")
    else:
        print("RRT 未找到路径")

练习 3:实现轨迹规划

def trajectory_exercise():
    # 创建路径
    path = Path([(0, 0), (2, 2), (5, 5), (8, 8), (10, 10)])

    # 创建轨迹规划器
    planner = TrajectoryPlanner(path, max_velocity=1.0, max_acceleration=0.5)

    # 规划轨迹
    trajectory = planner.plan_trajectory()

    print(f"轨迹点数: {len(trajectory)}")
    print(f"总时间: {trajectory[-1]['time']:.2f}")

    # 显示轨迹
    for i in range(0, len(trajectory), 10):
        point = trajectory[i]
        print(f"t={point['time']:.2f}, pos={point['position']}, v={point['velocity']:.2f}")

5. 常见问题

1. 规划失败

问题:找不到路径

解决方案: - 增加采样数量 - 增加连接半径 - 检查起始和目标配置是否有效

2. 路径质量差

问题:路径不平滑或太长

解决方案: - 使用路径平滑 - 增加采样密度 - 使用 RRT* 算法

3. 计算时间长

问题:规划时间太长

解决方案: - 减少采样数量 - 使用启发式搜索 - 使用并行计算

下一步

参考资源


← 返回索引 | 下一页:规划与学习 →