Perceive EM is a highly performant, physical optics (PO)-based shooting and bouncing rays (SBR) technology deployed through a lightweight API that seamlessly integrates into any digital twin platform.
Note
Requires Perceive EM 2025 R1 or later.
The PerceiveEM class is the main interface to the Ansys Perceive EM API, designed to provide simulation control,
scene management, and material definition for radar sensor simulation scenarios. It acts as a Python wrapper
within PyAEDT to seamlessly interact with the underlying native API.
.. currentmodule:: ansys.aedt.core.perceive_em.core.api_interface
.. autosummary:: :toctree: _autosummary :nosignatures: PerceiveEM
The constructor allows specifying a Perceive EM version. If not provided, it attempts to auto-detect the latest supported version installed on the system.
from ansys.aedt.core.perceive_em.core.api_interface import PerceiveEM
perceive_em = PerceiveEM()Perceive EM has the simulation workflow described in the following picture:
The Perceive EM API includes classes for the different simulation steps:
.. grid:: 2
.. grid-item-card:: Scene
:link: perceive_em/scene
:link-type: doc
:margin: 2 2 0 0
Manage actors and antenna platforms
.. grid-item-card:: Material
:link: perceive_em/material
:link-type: doc
:margin: 2 2 0 0
Manage materials
.. grid-item-card:: Simulation
:link: perceive_em/simulation
:link-type: doc
:margin: 2 2 0 0
Manage simulation settings
This example builds a complete scene and launch the simulation.
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
from ansys.aedt.core.generic.settings import settings
from ansys.aedt.core.perceive_em.core.api_interface import PerceiveEM
from ansys.aedt.core.perceive_em.scene.antenna_platform import AntennaPlatform
from ansys.aedt.core.perceive_em.modules.antenna_device import AntennaDevice
from ansys.aedt.core.perceive_em.modules.antenna import Antenna
from ansys.aedt.core.perceive_em.modules.material import MaterialManager
from ansys.aedt.core.perceive_em import MISC_PATH
from ansys.aedt.core.perceive_em.visualization.scene_visualization import SceneVisualization
from ansys.aedt.core.perceive_em.modules.waveform import RangeDopplerWaveform
from ansys.aedt.core.perceive_em.modules.antenna import Transceiver
"""User inputs"""
"""Waveform"""
center_freq = 77e9
num_freqs = 512
bandwidth = 300e6
cpi_duration = 10e-3
num_pulse_CPI = 101
waveform = RangeDopplerWaveform()
waveform.center_frequency = center_freq
waveform.bandwidth = bandwidth
waveform.pulse_cpi = num_pulse_CPI
waveform.frequency_samples = num_freqs
waveform.cpi_duration = cpi_duration
"""Scene definition"""
frames_per_second = 30
dt = 1 / frames_per_second
T = 10
frame_number = int(T / dt)
update_camera_view = False
actor_attachment = None
# side, top, front , scene_top, first_person, third_person
camera_orientation = "scene_top"
distance_factor = 3.0
height_factor = 2.2
"""Transceivers"""
tx = Transceiver()
tx.name = 'antenna_tx'
tx.antenna_type = "farfield"
tx.input_data = MISC_PATH / "antenna_device_library" / "dipole.ffd"
tx.operation_mode = "Tx"
rx = Transceiver()
rx.name = 'antenna_rx'
rx.antenna_type = "farfield"
rx.input_data = MISC_PATH / "antenna_device_library" / "dipole.ffd"
rx.operation_mode = "Rx"
"""Simulation options"""
# Set to -1 if no GO blockage, set to 0 or higher for GO blockage
go_blockage = 1
max_num_refl = 5
max_num_trans = 1
ray_spacing = 0.55
# Bird trajectory
circle_radius = 3
orbit_center = np.array([5, 0, 1])
########################################################################################################################
"""PyAEDT Perceive EM API"""
########################################################################################################################
"""Initialize scene"""
perceive_em = PerceiveEM()
"""Add bird to scene"""
actor_bird = perceive_em.scene.add_bird(name="bird",
input_file=MISC_PATH / "actor_library" / "bird" / "bird.json")
"""Add city to scene"""
material_manager = MaterialManager(perceive_em)
available_materials = material_manager.available_materials
# If material is not available you can add a new one using add_material method
actor_city = perceive_em.scene.add_actor(name="city")
actor_city.add_part(input_file=MISC_PATH / "actor_library" / "city.stl",
name=None,
material="asphalt",
color="white",
transparency=0.8)
"""Add antenna single Tx and Rx"""
antenna_platform = perceive_em.scene.add_single_tx_rx(tx=tx, rx=rx, waveform=waveform)
"""Bird scene"""
interp_func_pos_bird, interp_func_rot_bird = actor_bird.circle_trajectory(
duration=T,
n_frames=frame_number,
circle_radius=circle_radius,
orbit_center=orbit_center,
rotation_start_deg=90,
rotation_end_deg=450)
# Visualization object
# Scale farfield to improve visibility
antenna_devices = antenna_platform.antenna_devices[antenna_platform.antenna_device_names[0]]
modes = antenna_devices.modes[antenna_devices.mode_names[0]]
antenna_rx = modes.antennas_rx["antenna_rx"].scale_mesh = [0.1, 0.1, 0.1]
antenna_tx = modes.antennas_tx["antenna_tx"].scale_mesh = [0.1, 0.1, 0.1]
actors = perceive_em.scene.actors
actors.update(perceive_em.scene.antenna_platforms)
modeler = SceneVisualization(actors, size=(1920, 1088))
modeler.camera_orientation = camera_orientation
modeler.camera_attachment = actor_attachment
modeler.height_factor = height_factor
modeler.distance_factor = distance_factor
# Simulation setup
perceive_em.simulation.ray_spacing = ray_spacing
perceive_em.simulation.max_reflections = max_num_refl
perceive_em.simulation.max_transmissions = max_num_trans
perceive_em.simulation.go_blockage = go_blockage
perceive_em.simulation.field_of_view = 180
perceive_em.simulation.auto_configure_simulation()
perceive_em.simulation.response_type = perceive_em.simulation.response_types["range_doppler"]
perceive_em.simulation.mode = antenna_platform.antenna_devices[antenna_platform.antenna_device_names[0]].active_mode
perceive_em.simulation.validate()
plt.ion()
fig, ax = plt.subplots()
self = type('', (), {})()
self.ax = ax
self.fig = fig
imData = np.random.rand(100, 100)
self.mpl_ax_handle = ax.imshow(imData, cmap='viridis')
plot_limits = None
update_time = 0
for frame in range(frame_number):
time = frame * dt
# Update actor position
for actor_name, actor in perceive_em.scene.actors.items():
if time > 10:
update_time = np.mod(update_time, 10)
else:
update_time = time
if hasattr(actor, "actor_type") and actor.actor_type == "bird":
actor.coordinate_system.auto_update = False
actor.coordinate_system.position = interp_func_pos_bird(update_time)
actor.coordinate_system.rotation = interp_func_rot_bird(update_time)
actor.coordinate_system.auto_update = True
actor.update(time=update_time)
for antenna_platform_name, antenna_platform in perceive_em.scene.antenna_platforms.items():
if time > 10:
update_time = np.mod(update_time, 10)
else:
update_time = time
antenna_platform.update(time=update_time)
modeler.update_frame(update_camera_view=update_camera_view)
perceive_em.simulation.analyze()
response = perceive_em.simulation.get_solution_data()
imData = np.rot90(20 * np.log10(np.fmax(np.abs(response[0][0]), 1.e-30)))
if isinstance(imData, list):
imData = np.array(imData)
if imData.ndim == 1:
if not hasattr(self, 'mpl_ax_handle') or not isinstance(self.mpl_ax_handle, plt.Line2D):
ax.clear()
self.mpl_ax_handle, = ax.plot(imData)
else:
self.mpl_ax_handle.set_ydata(imData)
self.ax.set_ylim(np.min(imData), np.max(imData))
else:
self.mpl_ax_handle.set_data(imData)
if plot_limits is not None:
self.mpl_ax_handle.set_clim(vmin=plot_limits[0], vmax=plot_limits[1])
else:
self.mpl_ax_handle.set_clim(vmin=np.min(imData), vmax=np.max(imData))
self.fig.canvas.draw()
self.fig.canvas.flush_events()
plt.pause(0.001)
modeler.close().. toctree:: :hidden: :maxdepth: 2 perceive_em/scene perceive_em/material perceive_em/simulation