Main Content

Simulate an Automotive 4D Imaging MIMO Radar

This example shows how to simulate a 4D imaging MIMO radar for automotive applications using radarTransceiver. You will learn how to generate I/Q data with the radar in a simulated environment. The I/Q data will be processed to generate a point cloud of radar detections.

Model a MIMO Radar Transceiver

Use radarTransceiver to create a signal-level radar transceiver model for an FMCW automotive MIMO radar. You will use this model to simulate baseband I/Q signals at the output of the radar receiver.

rng default; % Set random seed for reproducible results
xcvrMIMO = radarTransceiver();

% Create FMCW waveform
rgRes = 0.5; % meters
rgMax = 150; % meters
sweepBW = rangeres2bw(rgRes); % Hz
Fs = sweepBW;

% Set the sweep time to at least twice the max range time to avoid power
% loss at the max range extent. Longer sweep times give higher
% signal-to-noise ratios (SNRs) at greater ranges, but at the cost of
% maximum ambiguous velocity
sweepTime = 4*range2time(rgMax); % s
Ncp = 512; % Number of chirps

wfm = phased.FMCWWaveform( ...
    'SweepBandwidth',sweepBW, ...
    'SweepTime',sweepTime, ...
    'SampleRate',Fs);
xcvrMIMO.Waveform = wfm;
xcvrMIMO.Receiver.SampleRate = Fs;
xcvrMIMO.NumRepetitions = Ncp;

% Set receiver gain and noise level
xcvrMIMO.Receiver.NoiseFigure = 12;

disp(xcvrMIMO);
  radarTransceiver with properties:

                Waveform: [1×1 phased.FMCWWaveform]
             Transmitter: [1×1 phased.Transmitter]
         TransmitAntenna: [1×1 phased.Radiator]
          ReceiveAntenna: [1×1 phased.Collector]
                Receiver: [1×1 phased.ReceiverPreamp]
      MechanicalScanMode: 'None'
      ElectronicScanMode: 'None'
        MountingLocation: [0 0 0]
          MountingAngles: [0 0 0]
    NumRepetitionsSource: 'Property'
          NumRepetitions: 512
       RangeLimitsSource: 'Property'
             RangeLimits: [0 Inf]
         RangeOutputPort: false
          TimeOutputPort: false

Define MIMO Transmit and Receive Arrays

MIMO radar is widely used for automotive radar applications due to its ability to realize small angular resolutions in azimuth and elevation within a small package. For high-resolution 4D radar, beamwidths in azimuth and elevation less than 2 degrees are desired. Compute the number of array elements that would be needed to realize a 2 degree beamwidth for a linear array.

fc = 77e9; % Hz
lambda = freq2wavelen(fc); % m

% Compute array aperture
d = beamwidth2ap(2,lambda,0.8859);

% Compute number of array elements required for half-wavelength spacing
disp(2*d/lambda)
   50.7583

If conventional arrays with elements at half-wavelength spacing were used, an array with 50 elements would be required to realize a resolution of 2 degree in azimuth, and another 50 elements would be needed if a similar resolution was desired in elevation. If a uniform rectangular array (URA) was to be used, a total of 50 x 50 = 2500 elements would be required.

Instead, let's define a virtual array which is composed of separate transmit and receive antenna arrays. Create a uniform linear array (ULA) for the transmit antenna with half-wavelength spacing and a rectangular receive array which does not maintain a half-wavelength spacing in the azimuth dimension.

% Define transmit ULA element positions
Ntx = 10;
txPos = [ ...
    0:Ntx-1; ...        % y positions
    zeros(1,Ntx)];      % z positions
txPos = sortrows(txPos',[2 1])';

% Define receive URA element positions
Naz = 5; % Azimuth array is number of transmit elements times half-wavelength spacing
Nel = 50; % Elevation array at half-wavelength spacing

[yPos,zPos] = meshgrid((0:Naz-1)*Ntx,0:Nel-1);
rxPos = [yPos(:) zPos(:)]';

% Sort the rows to allow the virtual array elements to be assembled later
rxPos = sortrows(rxPos',[2 1])';

Use helperVirtualArray to generate the transmit and receive arrays and the corresponding virtual array element positions.

[txArray,rxArray,vxPos] = helperVirtualArray(txPos,rxPos,lambda);
helperViewArrays(txPos,rxPos,vxPos);

Figure contains 2 axes objects. Axes object 1 with title 10 element transmit array 250 element receive array, xlabel Y position (\lambda/2), ylabel Z position (\lambda/2) contains 2 objects of type scatter, line. One or more of the lines displays its values using only markers These objects represent Rx element, Tx element. Axes object 2 with title 2500 element virtual array, xlabel Y position (\lambda/2), ylabel Z position (\lambda/2) contains an object of type scatter. This object represents Virtual element.

A virtual array with 2500 elements is created by using a 10 element transmit array and a 5 x 50 = 250 element receive array. This results in a significant reduction in the number of required elements and hardware complexity. For more information on how a virtual array is defined, see the Increasing Angular Resolution with Virtual Arrays example.

Use helperPlotDirectivities to plot the directivities for the transmit, receive, and virtual arrays in azimuth and elevation.

bwAz = helperPlotDirectivities(txArray,rxArray,fc);

Figure Azimuth Directivity contains an axes object. The axes object with title Azimuth Beam Directivity (dBi), xlabel Azimuth (deg), ylabel Directivity (dBi) contains 4 objects of type line. These objects represent Receive array, Transmit array, Virtual array, Beamwidth: 2.0 deg.

bwEl = helperPlotDirectivities(txArray,rxArray,fc,'Elevation');

Figure Elevation Directivity contains an axes object. The axes object with title Elevation Beam Directivity (dBi), xlabel Elevation (deg), ylabel Directivity (dBi) contains 4 objects of type line. These objects represent Receive array, Transmit array, Virtual array, Beamwidth: 2.0 deg.

% Compute the total number of elements used
disp(getNumElements(txArray) + getNumElements(rxArray))
   260

We realized a 2 degree beamwidth in azimuth and elevation by using 260 elements instead of the 2500 elements that would have been needed for a full rectangular array.

Attach the transmit and receive arrays used to define the virtual array to the MIMO radar model.

% Attach transmit and receive arrays
xcvrMIMO.TransmitAntenna.Sensor = txArray;
xcvrMIMO.TransmitAntenna.OperatingFrequency = fc;
xcvrMIMO.ReceiveAntenna.Sensor = rxArray;
xcvrMIMO.ReceiveAntenna.OperatingFrequency = fc;

% Configure transmitter for MIMO mode
xcvrMIMO.ElectronicScanMode = 'Custom';
xcvrMIMO.TransmitAntenna.WeightsInputPort = true;

% Set transmitter gain and power level
xcvrMIMO.Transmitter.PeakPower = db2pow(10);

Define MIMO DDMA Waveform

An essential characteristic of any MIMO radar implementation is a waveform that maintains orthogonality along all the transmit antennas. This orthogonality is often realized by encoding the transmitted waveforms in time, frequency, or code domains. Maintaining this orthogonality across the transmit antennas is essential in order to assemble the virtual array used by the MIMO radar as will be demonstrated later in the Signal Processing section of this example. Time Division Multiple Access (TDMA) is a common technique used to maintain orthogonality in the time domain. TDMA only transmits from one antenna element at any given time. Only using one transmit element at a time maintains the needed orthogonality but at the cost of reducing the total transmit power and decreasing the maximum unambiguous velocity of the radar. For more information on TDMA MIMO radar, see the Increasing Angular Resolution with Virtual Arrays example.

Instead of maintaining orthogonality in the time domain, new techniques using the frequency and code domains have been proposed. One such technique, Doppler Division Multiple Access (DDMA) MIMO maintains orthogonality in the frequency domain by shifting the waveforms transmitted by each antenna in Doppler. The motivation of this conventional DDMA scheme is to separate transmit signals from the Ntx transmit antennas into Ntx Doppler frequency subbands at the receiver. This is realized by DDMA MIMO weights which modulate each transmit antenna's signal into its own Doppler frequency subband. A drawback of this technique is that the maximum ambiguous velocity is still reduced by the number of transmit antennas due to the Ntx peaks from the transmitted signals spread across the Doppler space. An adaptation of the conventional DDMA MIMO which introduces an unused region of Doppler without regularly spaced peaks by adding virtual transmit elements to the array is used here. This enables the maximum ambiguous velocity of the radar's waveform to be maintained. [1], [2]

Create a DDMA waveform for the MIMO radar that assigns a unique Doppler offset to each of the transmit elements so that each element will transmit the same FMCW waveform, but with a unique offset in Doppler. Generate the Doppler offsets uniformly across the Doppler space for the 10 transmit antennas and 2 virtual antennas. This will assign two Doppler offsets to unused virtual transmit elements. These unused Doppler offsets will enable Doppler ambiguities to be unwrapped, preventing a reduction in the maximum ambiguous velocity of the MIMO radar.

% Define an (Ntx + Nvtx)-by-Ncp DFT matrix where Nvtx > 0 so that Nvtx
% Doppler bins are unused
Nvtx = 2;

[dopOffsets,Nv] = DDMAOffsets(Ntx,Nvtx);
% Recompute Ncp so that it is a multiple of Nv. This is important for
% recovering transmitted signal.
Ncp = ceil(Ncp/Nv)*Nv;
xcvrMIMO.NumRepetitions = Ncp;

% Select Ntx consecutive Doppler offsets for the DDMA weights
wDDMA = dopMatrix(dopOffsets(1:Ntx),Ncp);
% Show the DDMA Doppler offsets
plotDDMAOffsets(dopOffsets,Ntx);

Figure DDMA Tx Doppler offsets contains an axes object with type polaraxes. The polaraxes object contains 12 objects of type line. These objects represent Tx_{1}, Tx_{2}, Tx_{3}, Tx_{4}, Tx_{5}, Tx_{6}, Tx_{7}, Tx_{8}, Tx_{9}, Tx_{10}, Tx_{virtual}.

The FMCW waveform transmitted on each of the 10 elements will have a Doppler offset corresponding to each of the vectors in the previous figure. The DDMA weights have been chosen so that they are centered on zero Doppler. In the preceding figure, Doppler is represented as degrees on the unit circle by normalizing the Doppler shift frequencies from [-PRF/2,PRF/2] by PRF/2π. The Doppler shift applied to transmit element is -135 degrees.

Simulate Radar Data Cube

Use helperCreateOverpassScenario to create a drivingScenario that includes a highway overpass along with a vehicle merging into the lane in front of the ego vehicle. Mount the MIMO radar to the front of the ego vehicle as a forward-looking radar.

% Create drivingScenario
[scenario, egoVehicle] = helperCreateOverpassScenario();

% Mount MIMO radar as forward-looking radar on the ego vehicle
xcvrMIMO.MountingLocation(1) = egoVehicle.Length-egoVehicle.RearOverhang;
xcvrMIMO.MountingLocation(3) = 0.5;
xcvrMIMO.MountingAngles(2) = -5; % Pitch radar upward, away from the road's surface

% Define a coverage configuration structure for use with helperSetupDisplay
fov = [85 25]; % [azimuth elevation] degrees
% rgRes = bw2rangeres(wfm.SweepBandwidth); % meters
covCfg = struct( ...
    'SensorLocation',xcvrMIMO.MountingLocation(1:2), ...
    'MaxRange',rgMax, ...
    'Yaw',xcvrMIMO.MountingAngles(1), ...
    'FieldOfView',fov, ...
    'AzimuthResolution',bwAz, ...
    'ElevationResolution',bwEl, ...
    'RangeResolution',rgRes);

% Plot scenario
bep = helperSetupDisplay(egoVehicle,covCfg);
delete(bep.Plotters(end));
axis(bep.Parent,[-2 110 70*[-1 1]]);

Figure BEP contains an axes object. The axes object with xlabel X (m), ylabel Y (m) contains 4 objects of type patch, line. One or more of the lines displays its values using only markers These objects represent Road, Truth origin, Radar FoV.

Use helperPosesToPaths to generate propagation path structs from the scenario.

% Convert target poses to path structs
tposes = targetPoses(egoVehicle);
paths = helperPosesToPaths(tposes,xcvrMIMO,scenario,covCfg);

Use the MIMO radar model to generate a Radar Data Cube from the propagation path structs. The radar data cube contains the I/Q signals corresponding to the complex-valued baseband samples collected at the output of the radar's receiver.

time = scenario.SimulationTime;
datacube = xcvrMIMO(paths,time,conj(wDDMA));

Process MIMO Radar Data Cube

Range, Doppler, beamforming, and detection processing need to be applied to the data cube to extract the detection point cloud. For DDMA MIMO, the range and Doppler processing and CFAR detection are applied first so that the virtual array elements can be assembled prior to beamforming.

Range and Doppler Processing

Use phased.RangeDopplerResponse to take the 2D FFT along the first and third dimensions of the data cube to transform the fast- and slow-time dimensions of the data cube into range and Doppler bins respectively. Use Hann windows along the fast- and slow-time dimensions to suppress sidelobes in range and Doppler.

wfm = xcvrMIMO.Waveform;
rgdpProc = phased.RangeDopplerResponse( ...
    'RangeMethod','FFT', ...
    'SampleRate',wfm.SampleRate, ...
    'SweepSlope',wfm.SweepBandwidth/wfm.SweepTime, ...
    'DechirpInput',true, ...
    'RangeWindow','Hann', ...
    'DopplerWindow','Hann', ...
    'DopplerOutput','Frequency');
refSig = wfm();
[rgdpCube,rgBins,dpBins] = rgdpProc(datacube,refSig);

% Limit data to ranges of interest
inRg = rgBins>=0 & rgBins<=covCfg.MaxRange;
rgBins = rgBins(inRg);
rgdpCube = rgdpCube(inRg,:,:);
dpBins = -dpBins;

CFAR Detection

Use a constant false-alarm rate (CFAR) detector to identify the locations of the target returns in the range and Doppler processed data cube.

% Compute the power of each cell in the data cube
pwrCube = abs(rgdpCube).^2;

Configure the CFAR detector to exclude the multiple peaks generated in Doppler for each target due to the Doppler offsets applied at each transmit element by the DDMA MIMO waveform.

% CFAR configuration example
%   [++++---o---++++]
%   o: cell under test
%   -: 6 guard cells
%   +: 8 training cells
fpks = Ncp/Nv; % Distance between DDMA peaks
cfar = phased.CFARDetector( ...
    'NumGuardCells',even(ceil(fpks*0.5)), ... % Use 1/2 of space between DDMA Doppler peaks as guard cells
    'ProbabilityFalseAlarm',1e-6, ...
    'NoisePowerOutputPort',true);
cfar.NumTrainingCells = 2*(fpks-cfar.NumGuardCells); % Use remaining space for training

Compute the locations of the target returns in the range and Doppler processed data cube for each receive element.

% Reshape data cube to perform CFAR averaging along the Doppler dimension.
[~,Nrx,Ndp] = size(rgdpCube);
data = reshape(pwrCube,[],Ndp);
[detMtx,nPow] = cfar(data,1:size(data,1));

% Reshape the detection matrix to match the dimensions of the original
% range and Doppler processed data cube.
detMtx = reshape(detMtx,size(pwrCube));

% Compute the signal-to-noise ratio (SNR) for the range-Doppler map.
nPow = reshape(nPow,size(pwrCube));
snrCube = pwrCube./nPow;

% Identify the range and Doppler bins where the same target appears across
% the receive elements in the data cube.
detMtx = squeeze(sum(detMtx,2)); % Count the number of common target returns.

% Plot the range and Doppler map.
prf = 1/xcvrMIMO.Waveform.SweepTime;
helperPlotRangeDopplerDDMA(snrCube,rgBins,dpBins,prf,Ntx,Nv);

MATLAB figure

A range and Doppler map for one of the receive elements is shown in the previous figure. The returns from the target vehicle merging into the lane in front of the ego vehicle appear at a range of 40 meters from the radar and returns corresponding to the highway overpass are located at approximately 80 meters. There are ten returns modulated in Doppler at the same range for each target corresponding to the ten Doppler offsets applied across each of the ten transmit elements. A gap in Doppler between the first and last transmit element's target returns exists because of the virtual transmit antennas used to compute the DDMA Doppler offsets. If no virtual transmit elements were used, it would be impossible to identify the first and last elements of the transmit array due to the cyclic nature of the Doppler domain.

Remove Doppler Ambiguities

Create a matched filter to identify the location of the first transmit element for each of the target returns locations identified by the CFAR detector.

% Generate Doppler transmit test sequence
dPks = Ncp/Nv; % Number of Doppler bins between modulated DDMA target peaks
mfDDMA = zeros(dPks,Ntx);
mfDDMA(1,:) = 1;
mfDDMA = mfDDMA(:)';

fig = findfig('DDMA Matched Filter'); ax = axes(fig);
stem(ax,mfDDMA);
xlabel(ax,'Doppler bin (k)'); ylabel(ax,'h_k');
title(ax,fig.Name);
grid(ax,'on'); grid(ax,'minor');

Figure DDMA Matched Filter contains an axes object. The axes object with title DDMA Matched Filter, xlabel Doppler bin (k), ylabel h indexOf k baseline h_k contains an object of type stem.

Compute the cyclical cross-correlation between the DDMA matched filter and the detection matrix returned by the CFAR detector to find the Doppler bins corresponding to the target returns generated by the waveform from the first transmit element.

% Compute cyclical cross-correlation.
[Nrg,Ndp] = size(detMtx);

% Require the same target to appear across the receive channels.
corrMtx = detMtx>=Nrx*0.8;
corrMtx = ifft( fft(corrMtx,Ndp,2) .* fft(mfDDMA,Ndp,2), Ndp,2);
rdDetMtx = corrMtx>=Ntx*0.95;

% Multiple detection crossings will occur due to the waveform's ambiguity
% function in range and Doppler. Isolate the local maxima for each group of
% detections in the range-Doppler image.
snrDetMtx = squeeze(sum(snrCube,2));
snrDetMtx(~rdDetMtx) = NaN;
rdDetMtx = islocalmax(snrDetMtx,1);

% Identify the range and Doppler bins associated with localized detection
% maxima.
iRD = find(rdDetMtx(:));
[iRgDet,iDpDet] = ind2sub(size(rdDetMtx),iRD);
rgDet = rgBins(iRgDet);

% Show the locations of the unambiguous detections corresponding to the
% signal returns from the first transmit element.
ax = findaxes('Range-Doppler Map'); hold(ax,'on');
p = plot(ax,dpBins(iDpDet)/prf,rgDet,'rd','DisplayName','Unambiguous detections');
legend(p,'Unambiguous detections','Location','southwest');

MATLAB figure

The previous figure shows the locations of the target returns generated by the first transmit element. The merging target vehicle, located at approximately 40 meters in front of the radar is moving at approximately the same velocity as the ego vehicle, so it should have a Doppler offset of zero degrees. However, because a DDMA Doppler offset is applied to the FMCW waveform transmitted from each transmit element, we should expect this DDMA offset in addition to the Doppler offset due to the velocity difference between the ego vehicle and the target vehicle. The returns from the first transmit element should then include the -135 degrees Doppler offset due to the DDMA modulation applied to that element as well.

Remove DDMA Doppler offset applied to the first transmit element to recover the Doppler offset attributed to the velocity difference between the ego and target vehicles. Compute the range-rate of the target returns from the corrected Doppler offsets.

% Apply Doppler correction for DDMA Doppler shift applied on the first
% transmit element.
fd = (0:Ncp-1)/Ncp-0.5;
f1 = dopOffsets(1);
dpDet = mod(fd(iDpDet)+f1+0.5,1)-0.5;

% Compute unambiguous range-rate of detected Doppler bin
prf = 1/xcvrMIMO.Waveform.SweepTime;
dpDet = dpDet*prf;
rrDet = dop2speed(dpDet,lambda)/2;

% Show the range and range-rate extracted from the detected target returns.
fig = findfig('Range-Doppler Detections'); ax = axes(fig);
plot(ax,rrDet,rgDet,'s');
xlabel(ax,'Range-rate (m/s)'); ylabel('Range (m)');
title(ax,'Range-Doppler Detections');
ax.YDir = 'normal';
grid(ax,'on'); grid(ax,'minor'); ax.Layer = 'top';
axis(ax,[dop2speed(prf/2,lambda)/2*[-1 1] 0 covCfg.MaxRange]);

Figure Range-Doppler Detections contains an axes object. The axes object with title Range-Doppler Detections, xlabel Range-rate (m/s), ylabel Range (m) contains a line object which displays its values using only markers.

Virtual MIMO Array Beamspace Processing

Use the location of the target returns detected for the first transmit elements to identify the locations of the target returns from the other nine transmit elements to assemble the virtual elements for the virtual MIMO array for each target detection. The virtual elements correspond to every possible pair of transmit and receive array elements.

% Assemble virtual array elements from range-Doppler detections.
dPks = Ncp/Nv; % Number of Doppler bins between modulated DDMA target peaks
iDpTx = mod(iDpDet(:)-(0:Ntx-1)*dPks-1,Ncp)'+1; % Transmit elements are isolated in Doppler.
iRgTx = repmat(iRgDet,1,Ntx)';

% Plot the locations of the target returns to be extracted for the ten
% transmit elements in the range and Doppler map.
plotTxArrayReturns(rgBins,dpBins,iRgTx,iDpTx,Ntx,prf);

MATLAB figure

The range and Doppler bins corresponding to the target returns from the ten transmit elements have been recovered for each receive element.

Use the range and Doppler bin locations for each of the transmit elements to assemble the elements of the virtual array for each target detection.

% Extract virtual elements from range-Doppler data cube for each detection.
iTx = sub2ind([size(rgdpCube,3) size(rgdpCube,1)],iDpTx(:),iRgTx(:));
vxTgts = permute(rgdpCube,[2 3 1]); % Nrx x Ntx x Nrg
vxTgts = vxTgts(:,iTx); % Nrx x (Ntx x Ndet)
vxTgts = reshape(vxTgts,Nrx,Ntx,[]); % Nrx x Ntx x Ndet

Perform beamforming by computing a 2D FFT along the elements in the virtual array corresponding to the azimuth and elevation dimensions of the virtual array. Use Hanning windows along the azimuth and elevation dimensions to suppress sidelobes in those directions.

% Apply DDMA Doppler offset correction to the virtual array's targets.
wgts = dopMatrix(dopOffsets(1:Ntx));
vxTgts = vxTgts.*wgts';

% Reshape the virtual array target returns so that the elements
% corresponding the azimuth and elevation dimensions lie along the second
% and third dimensions of the data cube.
vxTgts = permute(vxTgts,[3 2 1]); % Ndet x Ntx x Nrx
vxTgts = reshape(vxTgts,[],Naz*Ntx,Nel); % Ndet x Naz x Nel
% Apply azimuth taper along the azimuth dimension of the data cube.
Nvxaz = size(vxTgts,2);
winAz = hanning(Nvxaz);
vxTgts = reshape(winAz,1,Nvxaz,1).*vxTgts;

% Apply elevation taper along the elevation dimension of the data cube.
Nvxel = size(vxTgts,3);
winEl = hanning(Nvxel);
vxTgts = reshape(winEl,1,1,Nvxel).*vxTgts;

% Apply 2D FFT along azimuth and elevation dimensions of data cube.
azelTgts = fft(fft(conj(vxTgts),Nvxaz,2),Nvxel,3);
azelTgts = fftshift(fftshift(azelTgts,2),3);

% Compute azimuth and elevation bins of beamformed data cube.
azBins = asind(2*((0:size(azelTgts,2)-1)/size(azelTgts,2))-1);
elBins = asind(2*((0:size(azelTgts,3)-1)/size(azelTgts,3))-1);

Use a CFAR detector to estimate the signal-to-noise ratio (SNR) of the detections in the azimuth and elevation beams.

% Estimate signal-to-noise ratio from detection beams
pwrCube = abs(azelTgts).^2;

cfar = phased.CFARDetector( ...
    'NumGuardCells',4, ...
    'NumTrainingCells',16, ...
    'NoisePowerOutputPort',true);

% Reshape data to perform CFAR averaging for each detection across azimuth
% and elevation bins.
pwrCube = permute(pwrCube,[2 1 3]); % Naz x Ndet x Nel
[~,nPow] = cfar(pwrCube(:,:),1:size(pwrCube,1));
nPow = reshape(nPow,size(pwrCube));
pwrCube = permute(pwrCube,[2 1 3]); % Ndet x Naz x Nel
nPow = permute(nPow,[2 1 3]); % Ndet x Naz x Nel
snrCube = pwrCube./nPow;
snrdB = pow2db(snrCube);

Use the SNR to compute the weighted average for the detections to estimate the azimuth and elevation of the target returns.

% Find the azimuth, elevation, and range bins associated with each target
% detection.
[zBins,yBins] = meshgrid(elBins,azBins);
xGrid = repmat(rgDet(:),[1 size(yBins)]);
yBins = reshape(yBins,[1 size(yBins)]);
zBins = reshape(zBins,[1 size(zBins)]);
yBins = repmat(yBins,numel(rgDet),1,1);
zBins = repmat(zBins,numel(rgDet),1,1);

% Exclude detections outside of the field of view or with a low SNR.
isGd = snrdB>10 & abs(yBins)<covCfg.FieldOfView(1)/2 & abs(zBins)<covCfg.FieldOfView(2)/2;

% Use a weighted average to estimate the azimuth and elevation of the
% target detections.
wgts = snrCube.*isGd;
wgts = wgts./sum(wgts,3);
azDet = sum(wgts.*yBins,3);
elDet = sum(wgts.*zBins,3);

% Find the max SNR for each target detection.
snrdBDet = max(snrdB,[],3);

Use the azimuth and elevation estimates to compute 3D Cartesian point-cloud locations for the target detections.

% Compute point-cloud locations from azimuth, elevation, and range bins.
rgs = repmat(rgDet(:),1,size(azDet,2));
[x,y,z] = sph2cart(deg2rad(azDet(:)),deg2rad(elDet(:)),rgs(:));
locDet = [x(:) y(:) z(:)];

% Remove excluded detection points.
isGd = ~isnan(x(:));
locDet = locDet(isGd,:);
snrdBDet = snrdBDet(isGd);

% Plot the point cloud detections in the simulated scenario.
ax = helperPlotPointCloudDetectionsDDMA(locDet,snrdBDet,egoVehicle,xcvrMIMO,covCfg);
campos(ax,[-10 0 5]);
camtarget(ax,[60 0 -10]);
camup(ax,[0.18 0 0.98]);

The preceding figure shows the point cloud detections overlayed upon the 3D scenario truth. This figure demonstrates the ability of the 4D MIMO radar to discriminate the difference in elevation between the detections of the merging target vehicle on the road in front of the radar and the overpass crossing over the road.

Reposition the figure's camera better illustrate the locations of the overpass detections.

campos(ax,[145 60 30]);
camtarget(ax,[60 0 0]);
camup(ax,[-0.35 -0.25 0.90]);

The preceding figure shows the same scenario and point cloud detections viewed from above the overpass and looking back towards the ego vehicle and radar. The overpass detections are correctly located along the elevated road's surface of the overpass. The target vehicle's detections are also observed to lie on the surface of the road passing beneath the overpass.

Summary

In this example, you learned how to model a high resolution 4D radar using DDMA MIMO techniques. By implementing a DDMA MIMO radar, you were able to simultaneously transmit across all the transmit array elements while maintaining the maximum ambiguous velocity associated with the FMCW waveform transmitted by the radar. By using a virtual array, you were also able to form beams in azimuth and elevation with a beamwidth of 2 degrees with a total of only 260 combined transmit and receive elements, demonstrating a significant reduction in array size over what would have been required if a full array had been used. Lastly, you used a highway overpass scenario to demonstrate the 4D radar's ability to resolve the difference in target height between targets on the same road as the ego vehicle and detections from the overpass crossing above the road being traveled.

References

[1] F. Jansen, “Automotive radar Doppler division MIMO with velocity ambiguity resolving capabilities,” in 2019 16th European Radar Conference (EuRAD), Paris, France, Oct. 2019, pp. 245–248.

[2] F. Xu, S. A. Vorobyov, and F. Yang, “Transmit beamspace DDMA based automotive MIMO radar,” IEEE Trans. Veh. Technol., vol. 71, no. 2, pp. 1669–1684, Feb. 2022.

Supporting Functions

function [dopOff,Mv] = DDMAOffsets(Ntx,Nvtx)

% Ensure even number of bins to avoid a Doppler bin at fd = 0 or fd = 1
Mv = Ntx+Nvtx;
Mv = Mv+mod(Mv,2);
Nvtx = Mv-Ntx;

mdx = (1:Mv).';
dopOff = (mdx-0.5)/Mv;

% Shift modulated Doppler frequencies from -PRF/2 to PRF/2 and prevent
% separation of signals at ±PRF/2 into two parts.
dopOff = dopOff-1/2+Nvtx/Mv/2;
end

function dft = dopMatrix(fd,N)
if nargin<2
    n = 1;
else
    n = 1:N;
end
dft = exp(-1i*2*pi*fd(:).*n);
end

function y = even(x)
y = round(x/2)*2;
end

function plotDDMAOffsets(dopOffsets,Ntx)
fig = findfig('DDMA Tx Doppler offsets'); ax = polaraxes(fig);
h = helperPolarQuiver(ax,dopOffsets(1:Ntx)*2*pi,1);
hold(ax,'on');
hvx = helperPolarQuiver(ax,dopOffsets(Ntx+1:end)*2*pi,1,'k','Color',0.7*[1 1 1]);
title(ax,fig.Name);
str = strsplit(strtrim(sprintf('Tx_{%i} ',1:Ntx)));
legend([h;hvx(1)],str{:},'Tx_{virtual}');
ax.ThetaLim = [-180 180];
end

function plotTxArrayReturns(rgBins,dpBins,iRgTx,iDpTx,Ntx,prf)
ax = findaxes('Range-Doppler Map'); hold(ax,'on');
p = findobj(ax,'Type','line');
p = [p;plot(ax,dpBins(iDpTx(:))/prf,rgBins(iRgTx(:)),'k.','DisplayName','Tx elements')];
legend(p(1:2),'Unambiguous detections','Tx elements','Location','southwest','AutoUpdate','off');

for m = 1:Ntx
    text(ax,dpBins(iDpTx(m))/prf,rgBins(iRgTx(m)),sprintf('%i',m), ...
        'Color','w','FontWeight','bold','FontSize',10, ...
        'HorizontalAlignment','center','VerticalAlignment','bottom');
end
end

function fig = findfig(name)
fig = findobj(groot,'Type','figure','Name',name);
if isempty(fig) || ~any(ishghandle(fig))
    fig = figure('Name',name);
    fig.Visible = 'on';
    fig.MenuBar = 'figure';
elseif any(ishghandle(fig))
    fig = fig(ishghandle(fig));
end
fig = fig(1); clf(fig); figure(fig);
end

function ax = findaxes(name)
fig = findobj(groot,'Type','figure','Name',name);
if isempty(fig) || ~any(ishghandle(fig))
    fig = figure('Name',name);
    fig.Visible = 'on';
elseif any(ishghandle(fig))
    fig = fig(ishghandle(fig));
end
fig = fig(1);
ax = fig.CurrentAxes;
if isempty(ax)
    ax = axes(fig);
end
end