主要内容

Generate ASAM OpenCRG Using Lidar Data

Since R2025b

This example shows how to generate road surface information from lidar point cloud and vehicle trajectory data, export the road surface to an ASAM OpenCRG® file, and use the file to simulate a vehicle on the reconstructed road surface in Simscape™.

Accurate road surface representation is essential for advanced driver assistance system (ADAS), autonomous driving (AD), and vehicle dynamics simulation development. By creating digital reconstructions of road surfaces, which contain information such as roughness, unevenness, and pothole locations, from real world lidar data, you can create more realistic simulations of vehicle dynamics. Incorporating these digital road surfaces in driving simulations enables automotive engineers to enhance ADAS, AD, and vehicle control algorithms, which can improve the comfort of the ride and provide safer navigation in complex driving conditions.

In this example, you:

  • Load, preprocess, and visualize lidar data.

  • Extract the road surface information from the lidar data, and export it to an ASAM OpenCRG file.

  • Simulate a vehicle on the exported ASAM OpenCRG road surface using Simscape.

This example requires the Scenario Builder for Automated Driving Toolbox™ support package. You can install the Scenario Builder for Automated Driving Toolbox support package from the Add-On Explorer. For more information on installing add-ons, see Get and Manage Add-Ons.

Additionally, this example also requires Simulink®, Simscape™, Simscape Driveline™, Simscape Multibody™, and Vehicle Dynamics Blockset™ license.

Load Lidar Data

This example uses data from a dense lidar point cloud, DigitransData.laz, recorded at the Digitrans Test Center for Automated Driving, on the proving ground at the Magna Powertrain Engineering Center Steyr. The data was collected collaboratively by the Magna Powertrain Engineering Center Steyr, Digitrans Test Center for Automated Driving, Digital Twin Lab from Joanneum Research, and Persival. The data features specialized rough terrain, designed to assess a vehicle under controlled, but challenging test conditions. For more information on the data, see the ASAM OpenX Standards in Offroad Applications [1].

Download and unzip DigitransData.zip which contains the dense lidar point cloud data file DigitransData.laz. Then, create a lasFileReader object for reading the LAZ file.

Note: To extract accurate road surface information, you must use dense point cloud data.

dataFolder = tempdir;
lidarDataFileName = "DigitransData.zip"; 
url = "https://ssd.mathworks.com/supportfiles/driving/data/" + lidarDataFileName;
filePath = fullfile(dataFolder,lidarDataFileName); 
if ~isfile(filePath)
    websave(filePath,url)
end
unzip(filePath,dataFolder)

% Specify the path of the downloaded LAZ file.
lazFile = fullfile(dataFolder,"DigitransData.laz");

% Create a lasFileReader object.
lasReader = lasFileReader(lazFile);

The point cloud in the LAZ file is in a projected coordinate system. To ensure accurate spatial analysis and measurements tailored to a specific region of interest, you must convert the point cloud data from the projected coordinate system to a georeferenced Cartesian coordinate system.

Transform the point cloud data to a georeferenced Cartesian coordinate system by using the helperTransformPointCloud helper function, attached to this example as a supporting file. The function takes a lasFileReader (Lidar Toolbox) object as input and returns a transformed point cloud along with its local origin point in the form [latitude longitude altitude]. By default, the function uses the center of the point cloud as the local origin. If the input lasFileReader (Lidar Toolbox) object does not contain coordinate reference system (CRS) information, you can specify an EPSG code, which defines the CRS of the point cloud. The point cloud data used in this example uses a projected CRS with the EPSG code 32644. For more information on valid EPSG codes, see the EPSG home page.

if ~isempty(readCRS(lasReader))
    % Obtain a transformed point cloud along with its local origin.
    [transformedPointCloud,localOrigin] = helperTransformPointCloud(lasReader);
else
    % If the LAZ file does not contain CRS data, specify an EPSG code that defines the CRS of the point cloud.
    code = 32644;
    
    % Obtain a transformed point cloud along with its local origin.
    [transformedPointCloud,localOrigin] = helperTransformPointCloud(lasReader,code);
end

If your point cloud is in a projected coordinate system and it does not contain CRS information, or if you are not aware of the projected CRS code, you can use the helperTransformPCToLocal helper function, attached to this example as a supporting file, to transform your point cloud data to local coordinates. This function takes a lasFileReader (Lidar Toolbox) object as input and transforms all the points in the point cloud with respect to the center of the point cloud.

Visualize the transformed point cloud.

ax = pcshow(transformedPointCloud);
colormap(ax,"hsv")
zoom(ax,3)

To extract a road surface from the input point cloud, you must specify a reference line along the direction of the road. The road reference line defines the geometric shape of the road surface, and is represented by a set of 3D or 2D points. If you have the GPS or trajectory information, you can represent the road reference line using a Trajectory object. You can also label the road reference line on the point cloud using the Lidar Labeler (Lidar Toolbox) app. In this example, you use the trajectory information as the road reference line.

Load the vehicle trajectory.

sensorData = load(fullfile(dataFolder,"sensorData.mat"));
traj = sensorData.traj;

Overlay the road reference line on the transformed point cloud, and visualize it.

ax = pcshow(transformedPointCloud,ViewPlane="XY");
hold(ax,"on")
plot3(ax,traj.Position(:,1),traj.Position(:,2),traj.Position(:,3),LineWidth=5)
xlabel(ax,"X-Axis")
ylabel(ax,"Y-Axis")
hold(ax,"off")

Extract Road Surface and Export to ASAM OpenCRG File

Extract the road surface information from the point cloud along the road reference line by using a roadSurface object.

Note: The time required to create the object depends on the density of the input point cloud and the specified grid resolution.

% Specify the width of the road from the reference line.
roadWidthFromRefLine = 3.5;

% Specify a resolution of 6 cm in the longitudinal and 4.5 cm in the lateral direction.
gridResolution = [0.06 0.045];

% Smooth the road reference line to remove any jitters.
traj = smooth(traj,Method="sgolay",Degree=3,SmoothingFactor=0.2);

% Generate the road surface.
rsObj = roadSurface(transformedPointCloud,traj,roadWidthFromRefLine,gridResolution,NormalizeElevation=false,LocalOrigin=localOrigin,ShowProgress=false)
rsObj = 
  roadSurface with properties:

         RoadReferenceLine: [12×3 double]
         LateralResolution: 0.0450
    LongitudinalResolution: 0.0600
      RoadWidthFromRefLine: [3.5000 3.5000]
       InterpolationMethod: "nearest"
       ExtrapolationMethod: "nearest"
               LocalOrigin: [21.1503 79.0774 0]
    HasNormalizedElevation: 0

Visualize the road surface.

show(rsObj)

Export the road surface to an ASAM OpenCRG file by using the exportToASAMOpenCRG function.

% Specify the name of the OpenCRG file.
filename = "digitransRoad.crg";

% Export the road surface to an ASAM OpenCRG file.
exportToASAMOpenCRG(rsObj,filename)

Simulate Vehicle on Exported ASAM OpenCRG Road Surface

You can test your vehicle designs on realistic road profiles that contain 3D road surface information by using the Simscape Vehicle Templates. Use the exported OpenCRG file to represent the road and simulate a vehicle model traveling along the road reference line of the generated road surface.

To simulate vehicle on the exported ASAM OpenCRG road surface, you must first download the Simscape Vehicle Templates from the Add-On Explorer. For more information about installing add-ons, see Get and Manage Add-Ons. When installing the add-on, select Download Only to avoid automatically adding third-party libraries to the MATLAB® path. Alternatively, you can download the Simscape Vehicle Templates from the GitHub® repository.

Specify the path of the downloaded Simscape Vehicle Templates file.

pathToSimscapeVehicleTemplatesFile = fullfile("/media/user/micronExternal/SimscapeVehicleTemplates/mathworks-Simscape-Vehicle-Templates-24.2.4.4.zip");

Unzip and open the Simscape Vehicle Templates project by using the helperSetupSimscapeVehicleTemplates helper function, attached to this example as a supporting file. This helper function also copies the generated OpenCRG file to the Simscape Vehicle Templates library.

Note: The first time you open the project, MATLAB must unpack the associated library files, requiring additional computation time.

% Specify the full path to the CRG File.
crgFile = fullfile(pwd,filename);

helperSetupSimscapeVehicleTemplates(pathToSimscapeVehicleTemplatesFile,crgFile)

To simulate a vehicle on the road surface, you must configure and specify a maneuver for the vehicle along the vehicle trajectory. Configure and create a maneuver by using the helperConfigureSimulation helper function.

Note: The computation time of the helper function depends on the size of the generated ASAM OpenCRG file.

[Maneuver,Init] = helperConfigureSimulation(traj,Init,MDatabase);

Create a simulation object, and set the simulation parameters. Specify the simulation stop time as 30 seconds, and initialize the simulation.

sm = simulation("sm_car");
sm = setModelParameter(sm,StopTime="30");
initialize(sm)

Run the simulation.

start(sm)

Simscape Vehicle Templates simulation of ASAM OpenCRG

Summary and Further Exploration

In this example, you learned how to generate a detailed road surface from a dense point cloud. You also exported the generated road surface to an ASAM OpenCRG file and simulated the vehicle on it by using the Simscape Vehicle Templates.

You can review the simulation results using the Simulation Data Inspector in Simulink®. The Simulation Data Inspector enables you to analyze the chassis, suspension, damper, and body parameters. You can also modify the vehicle models and their parameters, and compare the simulation results.

References

[1] Association for Standardization of Automation and Measuring Systems (ASAM). "ASAM OpenX Standards in Offroad Applications." Accessed September 30, 2025. https://www.asam.net/standards/asam-openx-in-offroad-applications/.

Helper Functions

function [maneuver,init] = helperConfigureSimulation(sbTraj,init,MDatabase,NameValueArgs)
        %helperConfigureSimulation Configures and creates a maneuver from a
        % trajectory for CRG simulation.

    arguments
        sbTraj (1,1) {mustBeA(sbTraj,'scenariobuilder.Trajectory')}
        init
        MDatabase
        NameValueArgs.TrajHeightOffset = 0.3
    end

    % Configure and define a vehicle maneuver.

    % Open the Simulink model.
    open_system("sm_car")

    % Configure the model for simulation
    sm_car_config_maneuver('sm_car','GS Uneven Road');

    % Create a maneuver using the ego trajectory.
    ts = sbTraj.Timestamps;
    wayPts = sbTraj.Position;

    % Waypoints must be unique along the x-dimension.
    [~,idxUnique,~] = unique(wayPts(:,1),"stable");
    
    ts = ts(idxUnique,:);
    wayPts = wayPts(idxUnique,:);

    % Add a small height along the z-dimension in the trajectory so that the
    % vehicle does not fall below the road if the trajectory is 
    % labeled using labeler.
    wayPts(:,3) = wayPts(:,3) + NameValueArgs.TrajHeightOffset;

    % Create an updated trajectory.
    traj = recordedSensorData("trajectory",ts,wayPts);

    % Calculate the velocity and orientation values.
    % vx = vecnorm(traj.Velocity,2,2)';
    dx = diff(traj.Position(:,1)');
    dy = diff(traj.Position(:,2)');
    dz = diff(traj.Position(:,3)');
    s  = [0 cumsum(sqrt(dx.^2 + dy.^2 + dz.^2))];
    dt = diff(traj.Timestamps)';
    vx = [0 diff(s)./dt];
    
    yaw = sbTraj.Orientation(:,1);

    % Create a maneuver from the trajectory.
    maneuver = MDatabase.GS_Uneven_Road.Sedan_Hamba;
    maneuver.Trajectory.x.Value = traj.Position(:,1);
    maneuver.Trajectory.y.Value = traj.Position(:,2);
    maneuver.Trajectory.z.Value = traj.Position(:,3);
    maneuver.Trajectory.vx.Value = vx;
    maneuver.Trajectory.xTrajectory.Value = s;
    maneuver.Trajectory.aYaw.Value = yaw;

    % Configure the initial values of the simulation.
    init.Chassis.sChassis.Value = [maneuver.Trajectory.x.Value(3) maneuver.Trajectory.y.Value(3) maneuver.Trajectory.z.Value(3)];
    init.Chassis.aChassis.Value(3) = maneuver.Trajectory.aYaw.Value(1);
end

See Also

Objects

Functions