Автоматизированная парковка

В этом примере показано, как создать автоматическую систему парковочных клапанов. В этом примере вы узнаете об инструментах и методах поддержки планирования пути, генерации траектории и управления автомобилем. Хотя этот пример посвящен ориентированному на MATLAB ® рабочему процессу, эти инструменты также доступны в Simulink ®. Для версии Simulink ® этого примера смотрите Automated Parking Valet in Simulink.

Обзор

Автоматическая парковка автомобиля, который остается перед парковкой, является сложной проблемой. Ожидается, что автоматизированные системы транспортного средства возьмут на себя управление и направят транспортное средство к доступному месту стоянки. Такая функция использует несколько бортовых датчиков. Для примера:

  • Фронтальная и боковая камеры для обнаружения разметки маршрута, дорожных знаков (стоповые знаки, выездная разметка и т.д.), других транспортных средств и пешеходов

  • Лидар и ультразвуковые датчики для обнаружения препятствий и вычисления точных измерений расстояния

  • Ультразвуковые датчики для обнаружения препятствий

  • БИНС и энкодеры колес для мертвых расчетов

Бортовые датчики используются для восприятия окружения вокруг транспортного средства. Воспринимаемое окружение включает понимание дорожной разметки для интерпретации правил дорожного движения и вывода управляемых областей, распознавание препятствий и обнаружение имеющихся мест стоянки.

Когда датчики транспортного средства воспринимают мир, транспортное средство должна планировать путь через окружение к свободному парковочному месту и выполнять последовательность действий управления, необходимых для движения к нему. При этом она должна реагировать на динамические изменения в окружении, такие, как переход пешеходами своего пути, и корректировать свой план.

Этот пример реализует подмножество функций, требуемых для реализации такой системы. Он фокусируется на планировании допустимого пути через окружение и выполнении действий, необходимых для прохождения пути. Создание карты и предотвращение динамических препятствий исключены из этого примера.

Модель окружения

Модель окружения представляет карту окружения. Для системы парковочных камердинеров эта карта включает доступные и занятые парковочные места, дорожную разметку и препятствия, такие как пешеходы или другие транспортные средства. Карты заполнения являются общим представлением для этой формы модели окружения. Такая карта обычно строится с использованием Simultaneous Localization and Mapping (SLAM) путем интегрирования наблюдений с датчиков лидара и камеры. Этот пример сконцентрирован на более простом сценарии, где карта уже предусмотрена, например, системой «машина - инфраструктура» (V2X) или камерой с видом на все парковочное пространство. Он использует статическую карту парковки и принимает, что самостоятельная локализация транспортного средства точна.

Пример парковки, используемый в этом примере, состоит из трех слоев сетки заполнения.

  • Стационарные препятствия: Этот слой содержит стационарные препятствия, такие как стенки, барьеры и границы парковки.

  • Дорожная разметка: Этот слой содержит информацию о заполнении, относящуюся к дорожной разметке, включая дорожную разметку для парковочных пространств

  • Припаркованные автомобили: Этот слой содержит информацию о том, какие парковочные места уже заняты.

Каждый слой карты содержит различные виды препятствий, которые представляют различные уровни опасности для автомобиля, перемещающегося через него. С помощью этой структуры каждый слой может обрабатываться, обновляться и поддерживаться независимо.

Загрузите и отобразите три слоя карты. В каждом слое темные камеры представляют занятые камеры, а светлые камеры свободные камеры.

mapLayers = loadParkingLotMapLayers;
plotMapLayers(mapLayers)

Для простоты объедините три слоя в одну косметическую карту.

costmap = combineMapLayers(mapLayers);

figure
plot(costmap, 'Inflation', 'off')
legend off

Объединенная costmap является vehicleCostmap объект, который представляет окружение транспортного средства как 2-D сетку заполнения. Каждая сетка в камеру имеет значения от 0 до 1, представляющие стоимость навигации по камере. Препятствия имеют более высокую стоимость, в то время как свободное пространство имеет более низкую стоимость. Камера считается препятствием, если ее стоимость выше OccupiedThreshold свойство, и бесплатно, если ее стоимость ниже FreeThreshold свойство.

The costmap покрывает всю область парковки 75m на 50m, разделенную на 0.5m-by-0.5m квадратные камеры.

costmap.MapExtent % [x, width, y, height] in meters

costmap.CellSize  % cell size in meters
ans =

     0    75     0    50


ans =

    0.5000

Создайте vehicleDimensions объект для хранения размерностей транспортного средства, который будет автоматически парковаться. Также определите максимальный угол поворота руля транспортного средства. Это значение определяет пределы радиуса поворота во время планирования движения и управления.

vehicleDims      = vehicleDimensions;
maxSteeringAngle = 35; % in degrees

Обновление VehicleDimensions свойство проверки столкновения с помощью косметической карты с размерностями транспортного средства. Эта настройка корректирует степень инфляции в карте вокруг препятствий, чтобы соответствовать размеру транспортного средства, гарантируя, что пути без столкновения могут быть найдены через стоянку.

costmap.CollisionChecker.VehicleDimensions = vehicleDims;

Определите стартовое положение транспортного средства. Положение получается посредством локализации, которая для простоты остается вне этого примера. Положение транспортного средства задается как, $[x,y,\theta]$в мировых координатах.$(x,y)$ представляет положение центра задней оси транспортного средства в мировой системе координат.$\theta$ представляет ориентацию транспортного средства относительно мировой оси X. Для получения дополнительной информации смотрите Системы координат в Automated Driving Toolbox.

currentPose = [4 12 0]; % [x, y, theta]

Поведенческий слой

Планирование включает организацию всей соответствующей информации в иерархические слои. Каждый последующий слой отвечает за более мелкозернистую задачу. Поведенческий слой [1] находится в верхней части этого стека. Он отвечает за активацию и управление различными частями миссии путем предоставления последовательности навигационных задач. Поведенческий слой собирает информацию из всех соответствующих частей системы, включая:

  • Локализация: Поведенческий слой проверяет модуль локализации для оценки текущего местоположения транспортного средства.

  • Модель окружения: Системы восприятия и слияния датчиков сообщают карту окружения вокруг транспортного средства.

  • Определение места парковки: Поведенческий слой анализирует карту, чтобы определить ближайшее доступное место парковки.

  • Поиск глобального маршрута: Модуль маршрутизации вычисляет глобальный маршрут через дорожную сеть, полученный либо от отображения услуги, либо от V2X инфраструктуры. Разложение глобального маршрута как ряда дорожных ссылок позволяет планировать траекторию для каждой ссылки по-разному. Например, конечный маневр парковки требует другого профиля скорости, чем подход к парковочному месту. В более общих условиях это становится решающим для навигации по улицам, которые включают различные пределы скорости, количество полос и дорожных знаков.

Вместо того, чтобы полагаться на датчики транспортного средства, чтобы создать карту окружения, этот пример использует карту, которая поступает с интеллектуальной парковки через V2X связь. Для простоты примите, что карта имеет форму сетки заполнения, с дорожными ссылками и местоположениями доступных парковочных мест, предоставляемых V2X.

The HelperBehavioralPlanner класс имитирует интерфейс слоя поведенческого планирования. The HelperBehavioralPlanner создается с использованием карты и плана глобальной трассы. Этот пример использует статический план глобального маршрута, хранящийся в таблице MATLAB, но обычно алгоритм маршрутизации, предоставляемый локальной инфраструктурой парковки или службой отображения, определяет этот план. Глобальный план трассы описывается как последовательность сегментов маршрута для прохождения до места стоянки.

Загрузите MAT-файл, содержащий план маршрута, который хранится в таблице. Таблица имеет три переменные: StartPose, EndPose, и Attributes. StartPose и EndPose задайте начальное и конечное положения сегмента, выраженные как. $[x,y,\theta]$Attributes задает свойства сегмента, такие как предел скорости.

data = load('routePlan.mat');
routePlan = data.routePlan %#ok<NOPTS>
routePlan =

  4×3 table

      StartPose           EndPose          Attributes 
    ______________    ________________    ____________

     4    12     0    56     11      0    [1×1 struct]
    56    11     0    70     19     90    [1×1 struct]
    70    19    90    70     32     90    [1×1 struct]
    70    32    90    53     39    180    [1×1 struct]

Постройте график транспортного средства в текущем положении и вдоль каждой цели в плане маршрута.

% Plot vehicle at current pose
hold on
helperPlotVehicle(currentPose, vehicleDims, 'DisplayName', 'Current Pose')
legend

for n = 1 : height(routePlan)
    % Extract the goal waypoint
    vehiclePose = routePlan{n, 'EndPose'};

    % Plot the pose
    legendEntry = sprintf('Goal %i', n);
    helperPlotVehicle(vehiclePose, vehicleDims, 'DisplayName', legendEntry);
end
hold off

Создайте объект помощника планировщика поведения. The requestManeuver метод запрашивает поток задач навигации от планировщика поведения до достижения адресата.

behavioralPlanner = HelperBehavioralPlanner(routePlan, maxSteeringAngle);

Перемещение по каждому сегменту контура транспортного средства осуществляется с помощью следующих шагов:

  1. Планирование движения: Планируйте допустимый путь через карту окружения с помощью оптимального быстро исследующего алгоритма случайного дерева (RRT *) (pathPlannerRRT).

  2. Сглаживание пути: сглаживайте путь ссылки, подгоняя сплайны к нему с помощью smoothPathSpline.

  3. Генерация траектории: Преобразуйте сглаженный путь в траекторию, генерируя профиль скорости с помощью helperGenerateVelocityProfile.

  4. Управление автомобилем: Учитывая сглаженный эталонный путь, HelperPathAnalyzer вычисляет положение и скорость ссылки на основе текущего положения и скорости транспортного средства. Обеспечивается ссылочными значениями, lateralControllerStanley вычисляет угол поворота руля для управления курсом транспортного средства. HelperLongitudinalController вычисляет команды ускорения и замедления, чтобы сохранить желаемую скорость транспортного средства.

  5. Проверка цели: Проверьте, достиг ли транспортное средство конечного положения сегмента с помощью helperGoalChecker.

Остальная часть этого примера описывает эти шаги подробно, прежде чем собирать их в полное решение.

Планирование движения

Учитывая глобальный маршрут, планирование движения может использоваться, чтобы планировать путь через окружение, чтобы достичь каждой промежуточной точки пути, пока транспортное средство не достигнет конечного пункта назначения. Запланированный путь для каждой ссылки должен быть допустимым и свободным от столкновения. Допустимый путь является таким, который может быть реализован транспортным средством, учитывая наложенные на него движение и динамические ограничения. Система парковочного клапана включает низкие скорости и малые ускорения. Это позволяет нам безопасно игнорировать динамические ограничения, возникающие из-за инерционных эффектов.

Создайте pathPlannerRRT объект для конфигурирования планировщика пути с помощью оптимального быстро исследуемого метода случайного дерева (RRT *). Семейство алгоритмов планирования RRT находит путь путем построения дерева связанных, свободных от столкновения положений транспортного средства. Положения соединяются с помощью рулевого управления Dubins или Reeds-Shepp, гарантируя, что сгенерированный путь кинематически допустим.

motionPlanner = pathPlannerRRT(costmap, 'MinIterations', 1000, ...
    'ConnectionDistance', 10, 'MinTurningRadius', 20);

Планируйте путь от текущего положения до первой цели с помощью plan функция. Возвращенный driving.Path объект, refPath, является допустимым и свободным от столкновения опорным путем.

goalPose = routePlan{1, 'EndPose'};
refPath = plan(motionPlanner, currentPose, goalPose);

Ссылочный путь состоит из последовательности сегментов контура. Каждый сегмент контура описывает набор маневров Дубинса или Ридса-Шеппа, используемых для соединения с следующим сегментом. Осмотрите сегменты контура.

refPath.PathSegments
ans = 

  1×6 DubinsPathSegment array with properties:

    StartPose
    GoalPose
    MinTurningRadius
    MotionLengths
    MotionTypes
    Length

Путь ссылки содержит переходные положения вдоль пути, представляющие точки вдоль пути, соответствующие переходу от одного маневра к следующему. Они могут также представлять изменения в направлении, например, от прямого до обратного движения вдоль пути Ридса-Шеппа.

Извлеките положения и направления перехода из запланированного пути.

[transitionPoses, directions] = interpolate(refPath);

% Visualize the planned path
plot(motionPlanner)

В дополнение к запланированному опорному пути заметьте красные области на графике. Эти области представляют области косметики, где источник транспортного средства (центр задней оси) не должны пересекаться в порядок, чтобы избежать столкновения с какими-либо препятствиями. pathPlannerRRT находит пути, которые избегают препятствий путем проверки, чтобы убедиться, что сгенерированные положения транспортного средства не лежат на этих областях.

Сглаживание пути и генерация траектории

Ссылка пути, сгенерированная планировщиком пути, состоит из сегментов Dubins или Reeds-Shepp. Кривизна в соединениях двух таких сегментов не непрерывна и может привести к резким изменениям угла поворота руля. Чтобы избежать такого неестественного движения и обеспечить комфорт пассажиров, путь должен быть непрерывно дифференцируемым и, следовательно, плавным [2]. Один из подходов к сглаживанию пути включает в себя подбор кривой параметрического кубического сплайна. Сплайн подбора кривой позволяет вам сгенерировать плавный путь, который может выполнить контроллер.

Использование smoothPathSpline для подгонки параметрического кубического сплайна, который проходит через все точки перехода в ссылку пути. Сплайн приблизительно совпадает с начальным и конечным направлениями с начальным и конечным углом рыскания транспортного средства.

% Specify number of poses to return using a separation of approximately 0.1 m
approxSeparation = 0.1; % meters
numSmoothPoses   = round(refPath.Length / approxSeparation);

% Return discretized poses along the smooth path
[refPoses, directions, cumLengths, curvatures] = smoothPathSpline(transitionPoses, directions, numSmoothPoses);

% Plot the smoothed path
hold on
hSmoothPath = plot(refPoses(:, 1), refPoses(:, 2), 'r', 'LineWidth', 2, ...
    'DisplayName', 'Smoothed Path');
hold off

Затем преобразуйте сгенерированный сглаженный путь в траекторию, которая может быть выполнена с помощью профиля скорости. Вычислите профиль скорости для каждого пути как последовательность из трех фаз: ускорение до установленной максимальной скорости, поддержание максимальной скорости и замедление до терминальной скорости. The helperGenerateVelocityProfile функция генерирует такой профиль скорости.

Задайте начальную, максимальную и конечную скорости так, чтобы транспортное средство стартовало стационарной, разгонялась до скорости 5 метров/секунду и пришла к остановке.

maxSpeed   = 5; % in meters/second
startSpeed = 0; % in meters/second
endSpeed   = 0; % in meters/second

Сгенерируйте профиль скорости

refVelocities = helperGenerateVelocityProfile(directions, cumLengths, curvatures, startSpeed, endSpeed, maxSpeed);

refVelocities содержит опорные скорости для каждой точки вдоль сглаженного пути. Постройте график сгенерированного профиля скорости.

plotVelocityProfile(cumLengths, refVelocities, maxSpeed)

Управление и симуляция транспортного средства

Задающие скорости вместе с сглаженным путем содержат допустимую траекторию, которой может следовать транспортное средство. Контроллер обратной связи используется, чтобы следовать этой траектории. Контроллер исправляет ошибки в отслеживании траектории, которые возникают из-за проскальзывания шины и других источников шума, таких как неточности в локализации. В частности, контроллер состоит из двух компонентов:

  • Боковое управление: Отрегулируйте угол рулевого управления так, чтобы транспортное средство следовало по ссылке пути.

  • Продольное управление: Следуя по ссылке пути, поддерживайте желаемую скорость, управляя дросселем и тормозом.

Поскольку этот сценарий включает медленные скорости, можно упростить контроллер, чтобы учесть только кинематическую модель. В этом примере боковое управление реализуется с помощью lateralControllerStanley функция. Продольное управление реализуется вспомогательной системой object™ HelperLongitudinalController, что вычисляет команды ускорения и замедления на основе Пропорционально-Интегрального закона.

Контроллер обратной связи требует симулятор, который может выполнять желаемые команды контроллера, используя подходящую модель транспортного средства. The HelperVehicleSimulator класс моделирует такое транспортное средство с помощью следующей кинематической модели велосипеда:

$$\dot x_r = v_r*\cos(\theta) $$

$$\dot y_r = v_r*\sin(\theta) $$

$$\dot \theta = \frac{v_r}{l}*\tan(\delta) $$

$$\dot v_r = a_r $$

В приведенных выше уравнениях$(x_r,y_r,\theta)$ представляет положение транспортного средства в мировых координатах.,, $v_r$$a_r$$l$и $\delta$представляет скорость заднего колеса, ускорение заднего колеса, колесную базу и угол поворота, соответственно. Положение и скорость переднего колеса могут быть получены:

$$ x_f = x_r + l cos(\theta)$$

$$ y_f = y_r + l sin(\theta)$$

$$ v_f = \frac{v_r} {cos(\delta)}$$

% Close all the figures
closeFigures;

% Create the vehicle simulator
vehicleSim = HelperVehicleSimulator(costmap, vehicleDims);

% Set the vehicle pose and velocity
vehicleSim.setVehiclePose(currentPose);
currentVel = 0;
vehicleSim.setVehicleVelocity(currentVel);

% Configure the simulator to show the trajectory
vehicleSim.showTrajectory(true);

% Hide vehicle simulation figure
hideFigure(vehicleSim);

Создайте HelperPathAnalyzer объект для вычисления исходного положения, ссылки скорости и направления движения для контроллера.

pathAnalyzer = HelperPathAnalyzer(refPoses, refVelocities, directions, ...
    'Wheelbase', vehicleDims.Wheelbase);

Создайте HelperLongitudinalController объект для управления скоростью транспортного средства и определения шага расчета.

sampleTime = 0.05;
lonController = HelperLongitudinalController('SampleTime', sampleTime);

Используйте HelperFixedRate объект обеспечивает выполнение контроллера обратной связи с фиксированной скоростью. Используйте скорость управления, чтобы соответствовать продольному контроллеру.

controlRate = HelperFixedRate(1/sampleTime); % in Hertz

Пока цель не достигнута, сделайте следующее:

  • Вычислите команды управления и ускорения/замедления, необходимые для отслеживания запланированной траектории.

  • Подайте команды управления на симулятор.

  • Запись положения и скорости возвращённого транспортного средства для подачи в контроллер в следующей итерации.

reachGoal = false;

while ~reachGoal
    % Find the reference pose on the path and the corresponding velocity
    [refPose, refVel, direction] = pathAnalyzer(currentPose, currentVel);

    % Update driving direction for the simulator
    updateDrivingDirection(vehicleSim, direction);

    % Compute steering command
    steeringAngle = lateralControllerStanley(refPose, currentPose, currentVel, ...
        'Direction', direction, 'Wheelbase', vehicleDims.Wheelbase);

    % Compute acceleration and deceleration commands
    lonController.Direction = direction;
    [accelCmd, decelCmd] = lonController(refVel, currentVel);

    % Simulate the vehicle using the controller outputs
    drive(vehicleSim, accelCmd, decelCmd, steeringAngle);

    % Check if the vehicle reaches the goal
    reachGoal = helperGoalChecker(goalPose, currentPose, currentVel, endSpeed, direction);

    % Wait for fixed-rate execution
    waitfor(controlRate);

    % Get current pose and velocity of the vehicle
    currentPose  = getVehiclePose(vehicleSim);
    currentVel   = getVehicleVelocity(vehicleSim);
end

% Show vehicle simulation figure
showFigure(vehicleSim);

Это завершает первый этап плана трассы и демонстрирует каждый шаг процесса. На следующих участках запускается симулятор для всего маршрута, который отводит транспортное средство близко к месту парковки, и, наконец, выполняет маневр парковки, чтобы поместить транспортное средство в место парковки.

Выполнение полного плана

Теперь объедините все предыдущие шаги в процессе планирования и запустите симуляцию для полного плана трассы. Этот процесс включает включение поведенческого планировщика.

% Set the vehicle pose back to the initial starting point
currentPose = [4 12 0]; % [x, y, theta]
vehicleSim.setVehiclePose(currentPose);

% Reset velocity
currentVel  = 0; % meters/second
vehicleSim.setVehicleVelocity(currentVel);

while ~reachedDestination(behavioralPlanner)

    % Request next maneuver from behavioral layer
    [nextGoal, plannerConfig, speedConfig] = requestManeuver(behavioralPlanner, ...
        currentPose, currentVel);

    % Configure the motion planner
    configurePlanner(motionPlanner, plannerConfig);

    % Plan a reference path using RRT* planner to the next goal pose
    refPath = plan(motionPlanner, currentPose, nextGoal);

    % Check if the path is valid. If the planner fails to compute a path,
    % or the path is not collision-free because of updates to the map, the
    % system needs to re-plan. This scenario uses a static map, so the path
    % will always be collision-free.
    isReplanNeeded = ~checkPathValidity(refPath, costmap);
    if isReplanNeeded
        warning('Unable to find a valid path. Attempting to re-plan.')

        % Request behavioral planner to re-plan
        replanNeeded(behavioralPlanner);
        continue;
    end

    % Retrieve transition poses and directions from the planned path
    [transitionPoses, directions] = interpolate(refPath);

    % Smooth the path
    numSmoothPoses   = round(refPath.Length / approxSeparation);
    [refPoses, directions, cumLengths, curvatures] = smoothPathSpline(transitionPoses, directions, numSmoothPoses);

    % Generate a velocity profile
    refVelocities = helperGenerateVelocityProfile(directions, cumLengths, curvatures, startSpeed, endSpeed, maxSpeed);

    % Configure path analyzer
    pathAnalyzer.RefPoses     = refPoses;
    pathAnalyzer.Directions   = directions;
    pathAnalyzer.VelocityProfile = refVelocities;

    % Reset longitudinal controller
    reset(lonController);

    reachGoal = false;

    % Execute control loop
    while ~reachGoal
        % Find the reference pose on the path and the corresponding velocity
        [refPose, refVel, direction] = pathAnalyzer(currentPose, currentVel);

        % Update driving direction for the simulator
        updateDrivingDirection(vehicleSim, direction);

        % Compute steering command
        steeringAngle = lateralControllerStanley(refPose, currentPose, currentVel, ...
            'Direction', direction, 'Wheelbase', vehicleDims.Wheelbase);

        % Compute acceleration and deceleration commands
        lonController.Direction = direction;
        [accelCmd, decelCmd] = lonController(refVel, currentVel);

        % Simulate the vehicle using the controller outputs
        drive(vehicleSim, accelCmd, decelCmd, steeringAngle);

        % Check if the vehicle reaches the goal
        reachGoal = helperGoalChecker(nextGoal, currentPose, currentVel, speedConfig.EndSpeed, direction);

        % Wait for fixed-rate execution
        waitfor(controlRate);

        % Get current pose and velocity of the vehicle
        currentPose  = getVehiclePose(vehicleSim);
        currentVel   = getVehicleVelocity(vehicleSim);
    end
end

% Show vehicle simulation figure
showFigure(vehicleSim);

Маневр парковки

Теперь, когда транспортное средство находится рядом с местом стоянки, для парковки транспортного средства в месте конечной стоянки используется специализированный парковочный маневр. Этот маневр требует прохождения через узкий коридор, фланкированный ребрами парковочного места с обоих концов. Такой маневр обычно сопровождается ультразвуковыми датчиками или лазерными сканерами, постоянно проверяющими на наличие препятствий.

% Hide vehicle simulation figure
hideFigure(vehicleSim);

The vehicleCostmap использует инфляционную проверку столкновения. Во-первых, визуально осмотрите ток используемого устройства проверки столкновения.

ccConfig = costmap.CollisionChecker;

figure
plot(ccConfig)
title('Current Collision Checker')

Проверка столкновения выполняется путем надувания препятствий в косметике по радиусу инфляции и проверки, лежит ли центр окружности, показанной выше, на надутой камере сетки. Итоговый маневр парковки требует более точного, менее консервативного механизма проверки столкновения. Обычно это решается путем представления формы транспортного средства с использованием нескольких (3-5) перекрывающихся кругов вместо одной окружности.

Используйте большее количество кругов в средстве проверки столкновения и визуально осмотрите средство проверки столкновения. Это позволяет планировать через узкие проходы.

ccConfig.NumCircles = 4;

figure
plot(ccConfig)
title('New Collision Checker')

Обновите косметику, чтобы использовать эту проверку на столкновение.

costmap.CollisionChecker = ccConfig;

Заметьте, что радиус инфляции уменьшился, что позволяет планировщику найти беспрепятственный путь к парковочному месту.

figure
plot(costmap)
title('Costmap with updated collision checker')

% Set up the pathPlannerRRT to use the updated costmap
parkMotionPlanner = pathPlannerRRT(costmap, 'MinIterations', 1000);

% Define desired pose for the parking spot, returned by the V2X system
parkPose = [36 44 90];
preParkPose = currentPose;

% Compute the required parking maneuver
refPath = plan(parkMotionPlanner, preParkPose, parkPose);

% Plot the resulting parking maneuver
figure
plotParkingManeuver(costmap, refPath, preParkPose, parkPose)

После того, как маневр будет найден, повторите предыдущий процесс, чтобы определить полный план: сглаживайте путь, генерируйте профиль скорости и следуйте траектории с помощью контроллера обратной связи.

% Retrieve transition poses and directions from the planned path
[transitionPoses, directions] = interpolate(refPath);

% Smooth the path
numSmoothPoses   = round(refPath.Length / approxSeparation);
[refPoses, directions, cumLengths, curvatures] = smoothPathSpline(transitionPoses, directions, numSmoothPoses);

% Set up the velocity profile generator to stop at the end of the trajectory,
% with a speed limit of 5 mph
refVelocities = helperGenerateVelocityProfile(directions, cumLengths, curvatures, currentVel, 0, 2.2352);

pathAnalyzer.RefPoses     = refPoses;
pathAnalyzer.Directions   = directions;
pathAnalyzer.VelocityProfile = refVelocities;

% Reset longitudinal controller
reset(lonController);

reachGoal = false;

while ~reachGoal
    % Find the reference pose on the path and the corresponding velocity
    [refPose, refVel, direction] = pathAnalyzer(currentPose, currentVel);

    % Update driving direction for the simulator
    updateDrivingDirection(vehicleSim, direction);

    % Compute steering command
    steeringAngle = lateralControllerStanley(refPose, currentPose, currentVel, ...
        'Direction', direction, 'Wheelbase', vehicleDims.Wheelbase);

    % Compute acceleration and deceleration commands
    lonController.Direction = direction;
    [accelCmd, decelCmd] = lonController(refVel, currentVel);

    % Simulate the vehicle using the controller outputs
    drive(vehicleSim, accelCmd, decelCmd, steeringAngle);

    % Check if the vehicle reaches the goal
    reachGoal = helperGoalChecker(parkPose, currentPose, currentVel, 0, direction);

    % Wait for fixed-rate execution
    waitfor(controlRate);

    % Get current pose and velocity of the vehicle
    currentPose  = getVehiclePose(vehicleSim);
    currentVel   = getVehicleVelocity(vehicleSim);
end

% Show vehicle simulation figure
closeFigures;
showFigure(vehicleSim);

Альтернативный способ припарковать транспортное средство - вернуться на парковочное место. Когда транспортному средству необходимо выполнить резервное копирование в точку, планировщику движения необходимо использовать метод соединения Ридса-Шеппа для поиска допустимого пути. Соединение Ридс-Шепп позволяет осуществлять противоположные движения во время планирования.

% Specify a parking pose corresponding to a back-in parking maneuver
parkPose = [49 47.2 -90];

% Change the connection method to allow for reverse motions
parkMotionPlanner.ConnectionMethod = 'Reeds-Shepp';

Чтобы найти допустимый путь, планировщик движения должен быть скорректирован. Используйте больший радиус поворота и расстояние соединения, чтобы обеспечить плавность спинки.

parkMotionPlanner.MinTurningRadius   = 10; % meters
parkMotionPlanner.ConnectionDistance = 15;

% Reset vehicle pose and velocity
currentVel = 0;
vehicleSim.setVehiclePose(preParkPose);
vehicleSim.setVehicleVelocity(currentVel);

% Compute the parking maneuver
replan = true;
while replan
    refPath = plan(parkMotionPlanner, preParkPose, parkPose);

    % The path corresponding to the parking maneuver is small and requires
    % precise maneuvering. Instead of interpolating only at transition poses,
    % interpolate more finely along the length of the path.

    numSamples = 10;
    stepSize   = refPath.Length / numSamples;
    lengths    = 0 : stepSize : refPath.Length;

    [transitionPoses, directions] = interpolate(refPath, lengths);

    % Replan if the path contains more than one direction switching poses
    % or if the path is too long
    replan = sum(abs(diff(directions)))~=2 || refPath.Length > 20;
end

% Visualize the parking maneuver
figure
plotParkingManeuver(costmap, refPath, preParkPose, parkPose)

% Smooth the path
numSmoothPoses   = round(refPath.Length / approxSeparation);
[refPoses, directions, cumLengths, curvatures] = smoothPathSpline(transitionPoses, directions, numSmoothPoses, 0.5);

% Generate velocity profile
refVelocities = helperGenerateVelocityProfile(directions, cumLengths, curvatures, currentVel, 0, 1);

pathAnalyzer.RefPoses     = refPoses;
pathAnalyzer.Directions   = directions;
pathAnalyzer.VelocityProfile = refVelocities;

% Reset longitudinal controller
reset(lonController);

reachGoal = false;

while ~reachGoal
    % Get current driving direction
    currentDir = getDrivingDirection(vehicleSim);

    % Find the reference pose on the path and the corresponding velocity.
    [refPose, refVel, direction] = pathAnalyzer(currentPose, currentVel);

    % If the vehicle changes driving direction, reset vehicle velocity in
    % the simulator and reset longitudinal controller
    if currentDir ~= direction
        currentVel = 0;
        setVehicleVelocity(vehicleSim, currentVel);
        reset(lonController);
    end

    % Update driving direction for the simulator. If the vehicle changes
    % driving direction, reset and return the current vehicle velocity as zero.
    currentVel = updateDrivingDirection(vehicleSim, direction, currentDir);

    % Compute steering command
    steeringAngle = lateralControllerStanley(refPose, currentPose, currentVel, ...
        'Direction', direction, 'Wheelbase', vehicleDims.Wheelbase);

    % Compute acceleration and deceleration commands
    lonController.Direction = direction;
    [accelCmd, decelCmd] = lonController(refVel, currentVel);

    % Simulate the vehicle using the controller outputs
    drive(vehicleSim, accelCmd, decelCmd, steeringAngle);

    % Check if the vehicle reaches the goal
    reachGoal = helperGoalChecker(parkPose, currentPose, currentVel, 0, direction);

    % Wait for fixed-rate execution
    waitfor(controlRate);

    % Get current pose and velocity of the vehicle
    currentPose  = getVehiclePose(vehicleSim);
    currentVel   = getVehicleVelocity(vehicleSim);
end

% Take a snapshot for the example
closeFigures;
snapnow;

% Delete the simulator
delete(vehicleSim);

Заключение

Этот пример показал, как:

  1. Планируйте допустимый путь в полуструктурированном окружении, такой как парковка, с помощью алгоритма планирования пути RRT *.

  2. Сглаживайте путь с помощью сплайнов и генерируйте профиль скорости вдоль сглаженного пути.

  3. Управляйте транспортным средством, чтобы следовать по контрольному пути с желаемой скоростью.

  4. Реализуйте различное поведение при парковке с помощью различных настроек планировщика движения.

Ссылки

[1] Бюлер, Мартин, Карл Иагнемма и Санджив Сингх. The DARPA Urban Challenge: Автономные транспортные средства в городском движении (1-е изд.). Издательская компания Springer, Incorporated, 2009.

[2] Lepetic, Marko, Gregor Klancar, Igor Skrjanc, Drago Matko, Bostjan Potocnik, «Time Optimal Path Planning Учитывая Пределы ускорения». Робототехника и автономные системы. Том 45, Выпуски 3-4, 2003, стр. 199-210.

Вспомогательные функции

loadParkingLotMapLayers Загрузка слоев карты окружения для парковки

function mapLayers = loadParkingLotMapLayers()
%loadParkingLotMapLayers
%   Load occupancy maps corresponding to 3 layers - obstacles, road
%   markings, and used spots.

mapLayers.StationaryObstacles = imread('stationary.bmp');
mapLayers.RoadMarkings        = imread('road_markings.bmp');
mapLayers.ParkedCars          = imread('parked_cars.bmp');
end

plotMapLayers struct график, содержащая слои карты

function plotMapLayers(mapLayers)
%plotMapLayers
%   Plot the multiple map layers on a figure window.

figure
cellOfMaps = cellfun(@imcomplement, struct2cell(mapLayers), 'UniformOutput', false);
montage( cellOfMaps, 'Size', [1 numel(cellOfMaps)], 'Border', [5 5], 'ThumbnailSize', [300 NaN] )
title('Map Layers - Stationary Obstacles, Road markings, and Parked Cars')
end

combineMapLayers Объедините слои карты в одну карту костюмов

function costmap = combineMapLayers(mapLayers)
%combineMapLayers
%   Combine map layers struct into a single vehicleCostmap.

combinedMap = mapLayers.StationaryObstacles + mapLayers.RoadMarkings + ...
    mapLayers.ParkedCars;
combinedMap = im2single(combinedMap);

res = 0.5; % meters
costmap = vehicleCostmap(combinedMap, 'CellSize', res);
end

configurePlanner Сконфигурируйте планировщик пути с заданными параметрами

function configurePlanner(pathPlanner, config)
%configurePlanner
% Configure the path planner object, pathPlanner, with settings specified
% in struct config.

fieldNames = fields(config);
for n = 1 : numel(fieldNames)
    if ~strcmpi(fieldNames{n}, 'IsParkManeuver')
        pathPlanner.(fieldNames{n}) = config.(fieldNames{n});
    end
end
end

plotVelocityProfile График профиля скорости

function plotVelocityProfile(cumPathLength, refVelocities, maxSpeed)
%plotVelocityProfile
% Plot the generated velocity profile

% Plot reference velocity along length of the path
plot(cumPathLength, refVelocities, 'LineWidth', 2);

% Plot a line to display maximum speed
hold on
line([0;cumPathLength(end)], [maxSpeed;maxSpeed], 'Color', 'r')
hold off

% Set axes limits
buffer = 2;
xlim([0 cumPathLength(end)]);
ylim([0 maxSpeed + buffer])

% Add labels
xlabel('Cumulative Path Length (m)');
ylabel('Velocity (m/s)');

% Add legend and title
legend('Velocity Profile', 'Max Speed')
title('Generated velocity profile')
end

closeFigures

function closeFigures()
% Close all the figures except the simulator visualization

% Find all the figure objects
figHandles = findobj('Type', 'figure');
for i = 1: length(figHandles)
    if ~strcmp(figHandles(i).Name, 'Automated Valet Parking')
        close(figHandles(i));
    end
end
end

plotParkingManeuver Отобразите сгенерированный парковочный маневр на косметике

function plotParkingManeuver(costmap, refPath, currentPose, parkPose)
%plotParkingManeuver
% Plot the generated parking maneuver on a costmap.

% Plot the costmap, without inflated areas
plot(costmap, 'Inflation', 'off')

% Plot reference parking maneuver on the costmap
hold on
plot(refPath, 'DisplayName', 'Parking Maneuver')

title('Parking Maneuver')

% Zoom into parking maneuver by setting axes limits
lo = min([currentPose(1:2); parkPose(1:2)]);
hi = max([currentPose(1:2); parkPose(1:2)]);

buffer = 6; % meters

xlim([lo(1)-buffer hi(1)+buffer])
ylim([lo(2)-buffer hi(2)+buffer])
end

См. также

Функции

Объекты

Похожие темы