%% Create Egocentric Occupancy Map from Driving Scenario Designer
% This example shows how to create an egocentric occupancy map from the Driving Scenario Designer app. This example uses obstacle information from the vision detection generator to update the egocentric occupancy map.
% This example:
% Gets obstacle information and road geometry using vision detection generator.
% Creates an ego centric occupancy map using binary occupancy map.
% Updates the ego centric occupancy map using lane boundaries and obstacle information.
%
%% Introduction
% Automated driving systems use multiple on-board sensors on the ego vehicle like radar, cameras, and lidar. These sensors are used to perceive information from the surroundings and environment. It is important to collate information from these heterogeneous sensors into a common temporal frame of reference. This is usually done using an egocentric occupancy map. This map contains information about the surrounding environment like road geometry, free space, and obstacles. This egocentric occupancy map is used by planning algorithms for navigation. The ego vehicle can respond to dynamic changes in the environment by periodically updating the information in this ego centric occupancy map.
% This example shows how to use lane and obstacle information obtained from a scenario to create and update an ego centric occupancy map.
% This example also uses a straight road scenario designed using Driving Scenario Designer (DSD). For more details see Driving Scenario Designer. User can also create a Driving Scenario programmatically. For more details please refer to Driving Scenario Tutorial.
%% Get Lane Boundaries and Obstacle Information from Driving Scenario
% The scenario used in this example is a straight road with four lanes. This scenario has one ego vehicle and six target vehicles which follow their respective predefined paths. Create a scenario using the helper function, exampleHelperCreateStraightRoadScenario.
% The scenario used in this example is shown in the following figure.

[scenario, egoVehicle] = exampleHelperCreateStraightRoadScenario;                           

%
% This example uses a vision detection generator which synthesizes camera sensor mounted at the front of the ego vehicle in the driving scenario. This generator is configured to detect lane boundaries and obstacles.
% For more details, see Vision Detection Generator.
% The update interval of the vision detection generator is configured to generate detections at 0.1 second intervals, which is consistent with the update rate of typical automotive vision sensors.
% Create the actorProfiles for the scenario, which contain physical and radar profiles of all driving scenario actors including the ego vehicle. Specify those actors and other configuration parameters for the vision detection generator.

profiles = actorProfiles(scenario);

% Configure vision detection generator to detect lanes and obstacles
sensor = visionDetectionGenerator('SensorIndex', 1, ...
    'SensorLocation', [1.9 0], ...
    'DetectionProbability', 1, ...
    'MinObjectImageSize', [5 5], ...
    'FalsePositivesPerImage', 0, ...
    'DetectorOutput', 'Lanes and objects', ...
    'Intrinsics', cameraIntrinsics([700 1814],[320 240],[480 640]), ...
    'ActorProfiles', profiles,...
    'UpdateInterval', 0.1);

% To get detections, the sensor object is called for each simulation step. This sensor object call occurs in the helper function exampleHelperGetObstacleDataFromSensors.
%% Create an Egocentric Occupancy Map
% This example uses a binaryOccupancyMap object to create an egocentric occupancy map.
% Create a square occupancy map with 100 meters per side and a resolution of 2 cells per meter. Set all the cells to occupied state by default.

egoMap = binaryOccupancyMap(100, 100, 2);
setOccupancy(egoMap,ones(200, 200));

% By default, the map origin is at bottom-left. Move the egoMap origin to the center of the occupancy map. This converts the occupancy map to an egocentric occupancy map.

egoMap.GridOriginInLocal = [-egoMap.XLocalLimits(2)/2, ...
                            -egoMap.YLocalLimits(2)/2];

%% Update the Egocentric Occupancy Map with Obstacle Information
% Before updating the egoMap, initialize the visualization window. Obstacles in the egoMap visualization are represented as black (occupied) and free space as white (unoccupied).

hAxes = exampleHelperSetupVisualization(scenario);

% Setup a loop for executing the scenario using advance(scenario). This loop should move the egoVehicle along the road, updating the pose as it moves.
% Call move on the egoMap using the updated pose to get updated detections. Clear the map of all obstacles for each update.

% Advancing the scenario
while advance(scenario)
    % Ego vehicle position
    egoPose = egoVehicle.Position;
    egoYaw = deg2rad(egoVehicle.Yaw);
    
    % Move the origin of grid to the face of the ego vehicle.
    move(egoMap, [egoPose(1), egoPose(2)]);
    
    %Reset the egoMap before updating with obstacle information
    setOccupancy(egoMap, ones(egoMap.GridSize));

% Lane boundaries and obstacles information generated from a vision detection generator are used to find occupied space that needs to be updated in the egoMap.
% This example uses road boundaries information extracted from the lane boundaries. The region outside road boundaries is also considered occupied.
% The exampleHelperGetObstacleDataFromSensor helper function extracts road boundaries and obstacle information generated from the vision detection generator.

    [obstacleInfo, roadBorders, isValidLaneTime] = ...
      exampleHelperGetObstacleDataFromSensor(scenario, egoMap, ...
                                egoVehicle, sensor);
                            

% Depending upon the range of the vision detection generator, the detections may fall outside the egoMap bounds. Using the egoMap bounds, the exampleHelperFitlerObstacles function extracts the obstacles and free space that are within the egocentric map.

    [obstaclePoints, unoccupiedSpace] = exampleHelperFilterObstacles(...
                                         egoMap, obstacleInfo, ...
                                         roadBorders, ...
                                         isValidLaneTime, egoVehicle);

% Use the filtered obstacle and free space locations to update the egocentric map. Update the visualization.

        % Set the occupancy of free space to 0
        if ~isempty(unoccupiedSpace)
            setOccupancy(egoMap, unoccupiedSpace, 0);
        end
        
        % Set the occupancy of occupied space to 1
        if ~isempty(obstaclePoints)
            setOccupancy(egoMap, obstaclePoints, 1);
        end
        
        % Updating the visualization
        exampleHelperUpdateVisualization(hAxes, egoVehicle, egoPose, egoYaw, egoMap);
end

% Copyright 2019 The MathWorks, Inc.