Browse Source
feat: Integrate C-SHENRON physics-based radar simulation pipeline
feat: Integrate C-SHENRON physics-based radar simulation pipeline
Comprehensive integration of the Shenron radar model, enabling high-fidelity physics-driven radar synthesis from semantic LiDAR data. Core Changes: - Sensor Migration: Updated EGO vehicle to use 'sensor.lidar.ray_cast_semantic'. - Recorder Enhancement: Modified src/recorder.py to dynamically capture 6/7nd-column semantic LiDAR buffers [x, y, z, cos, obj, tag]. - Orchestration: Created scripts/ISOLATE/model_wrapper.py with the ShenronRadarModel class for a unified physics-to-detection API. - Processor Logic: Fixed broadcasting bugs and hardcoded antenna counts in RadarProcessor to support dynamic radar models (radarbook/ti_cascade). - Rich Data Output: Updated extraction logic to include [velocity, magnitude] parameters for ADAS performance analysis. - Batch Pipeline: Added scripts/generate_shenron.py to synthesize radar data as a post-processing step, maintaining simulation performance. - Visualization: Updated scripts/data_to_mcap.py to support the new /radar/shenron topic with a 5-field rich PointCloud schema. Stability & Fixes: - Implemented CUDA-enabled PyTorch checks with a robust CPU fallback. - Corrected CARLA 0.9.16 semantic column mapping in ISOLATE/lidar.py. - Handled indexing and range-slicing errors in the CFAR detection routine.1843_integration
30 changed files with 2986 additions and 3 deletions
-
2scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/.gitignore
-
105scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/ConfigureRadar.py
-
16scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/README.md
-
0scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/__init__.py
-
2scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/carla_shenron_config.yaml
-
229scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/lidar.py
-
216scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/lidar_utils.py
-
63scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/main.py
-
63scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/map_carla_semantic_lidar_shenron.py
-
BINscripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/noise_data/noise.mat
-
BINscripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/noise_data/noise_adc.mat
-
BINscripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/noise_data/noise_with_ground.mat
-
43scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/path_config.py
-
546scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/shenron/Sceneset.py
-
177scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/shenron/heatmap_gen.py
-
280scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/shenron/heatmap_gen_fast.py
-
46scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/simulator_configs.yaml
-
264scripts/ISOLATE/e2e_agent_sem_lidar2shenron_package/transform_dsp_utils.py
-
118scripts/ISOLATE/model_wrapper.py
-
70scripts/ISOLATE/sim_radar_utils/cfar_detector.py
-
101scripts/ISOLATE/sim_radar_utils/config.yaml
-
116scripts/ISOLATE/sim_radar_utils/convert2D_img.py
-
96scripts/ISOLATE/sim_radar_utils/radar_processor.py
-
91scripts/ISOLATE/sim_radar_utils/transform_utils.py
-
112scripts/ISOLATE/sim_radar_utils/utils_radar.py
-
29scripts/data_to_mcap.py
-
95scripts/generate_shenron.py
-
98scripts/test_shenron_model.py
-
9src/recorder.py
-
2src/sensors.py
@ -0,0 +1,2 @@ |
|||||
|
__pycache__ |
||||
|
.idea/ |
||||
@ -0,0 +1,105 @@ |
|||||
|
import numpy as np |
||||
|
# from numba.experimental import jitclass |
||||
|
from mat4py import loadmat |
||||
|
import pdb |
||||
|
|
||||
|
class radar(): |
||||
|
|
||||
|
""" |
||||
|
class to define the radar object with it's settings and to extract time intervals for updating the scene. |
||||
|
Also contains voxel filter specs |
||||
|
""" |
||||
|
def __init__(self, radartype): |
||||
|
self.radartype = radartype |
||||
|
if radartype == "ti_cascade": |
||||
|
self.center = np.array([0.0, 4.0]) # center of radar |
||||
|
self.elv = np.array([0.5]) # self position in z-axis |
||||
|
self.orientation = 90 # orientation of radar |
||||
|
|
||||
|
self.f = 77e9 |
||||
|
self.B = 0.256e9 # Bandwidth |
||||
|
self.c = 3e8 |
||||
|
self.N_sample = 256 |
||||
|
self.samp_rate = 15e6 |
||||
|
self.doppler_mode = 1 |
||||
|
self.chirps = 3 # 128 |
||||
|
self.nRx = 86 #16 # number of antennas(virtual antennas included, AOA dim) |
||||
|
self.noise_amp = 0.005 #0.0001(concrete+metal) # 0.00001(metal) #0.005(after skyward data) |
||||
|
self.gain = 10 ** (105 / 10) #190(concrete+metal) # 210(metal) |
||||
|
self.angle_fft_size = 256 |
||||
|
|
||||
|
self.range_res = self.c / (2 * self.B) # range resolution |
||||
|
self.max_range = self.range_res * self.N_sample |
||||
|
|
||||
|
self.idle = 0 ## Idle time |
||||
|
self.chirpT = self.N_sample / self.samp_rate ## Time of chirp |
||||
|
self.chirp_rep = 12*27e-6 |
||||
|
|
||||
|
Ts = 1 / self.samp_rate |
||||
|
self.t = np.arange(0, self.chirpT, Ts) |
||||
|
self.tau_resolution = 1 / self.B |
||||
|
self.k = self.B / self.chirpT |
||||
|
|
||||
|
self.voxel_theta = 2.0 # 0.5 # 0.1 |
||||
|
self.voxel_phi = 2.0 # 0.5 # 0.1 |
||||
|
self.voxel_rho = 0.05 # 0.1 # 0.05 |
||||
|
|
||||
|
elif radartype == "radarbook": |
||||
|
self.center = np.array([0.0, 4.0]) # center of radar |
||||
|
self.elv = np.array([0.5]) # self position in z-axis |
||||
|
self.orientation = 90 # orientation of radar |
||||
|
|
||||
|
self.f = 24e9 |
||||
|
self.B = 0.250e9 # Bandwidth |
||||
|
self.c = 3e8 |
||||
|
self.N_sample = 256 |
||||
|
self.samp_rate = 1e6 |
||||
|
self.doppler_mode = 1 |
||||
|
self.chirps = 128 |
||||
|
self.nRx = 8 # number of antennas(virtual antennas included, AOA dim) |
||||
|
self.noise_amp = 0.005 #0.0001(concrete+metal) # 0.00001(metal) #0.005(after skyward data) |
||||
|
self.gain = 10 ** (105 / 10) #190(concrete+metal) # 210(metal) |
||||
|
self.angle_fft_size = 256 |
||||
|
|
||||
|
self.range_res = self.c / (2 * self.B) # range resolution |
||||
|
self.max_range = self.range_res * self.N_sample |
||||
|
|
||||
|
|
||||
|
self.chirpT = self.N_sample / self.samp_rate ## Time of chirp |
||||
|
self.chirp_rep = 0.75e-3 |
||||
|
self.idle = self.chirp_rep - self.chirpT## Idle time |
||||
|
|
||||
|
Ts = 1 / self.samp_rate |
||||
|
self.t = np.arange(0, self.chirpT, Ts) |
||||
|
self.tau_resolution = 1 / self.B |
||||
|
self.k = self.B / self.chirpT |
||||
|
|
||||
|
self.voxel_theta = 2 # 0.5 # 0.1 |
||||
|
self.voxel_phi = 2 # 0.5 # 0.1 |
||||
|
self.voxel_rho = 0.05 # 0.1 # 0.05 |
||||
|
|
||||
|
else: |
||||
|
raise Exception("Incorrect radartype selected") |
||||
|
|
||||
|
def get_noise(self): |
||||
|
if self.radartype == "ti_cascade": |
||||
|
# noise_prop = loadmat('/radar-imaging-dataset/mmfn_project/mmfn_scripts/team_code/e2e_agent_sem_lidar2shenron_package/noise_data/noise_adc.mat') |
||||
|
# real_fft_ns = np.random.normal(noise_prop['noise_mean_real'], noise_prop['noise_std_real']).T |
||||
|
# complex_fft_ns = np.random.normal(noise_prop['noise_mean_real'], noise_prop['noise_std_real']).T |
||||
|
# final_noise = real_fft_ns + 1j * complex_fft_ns |
||||
|
# # signal_Noisy = np.fft.ifft(final_noise, radar.N_sample, 1) #* 10**4.5 |
||||
|
# # signal_Noisy = final_noise |
||||
|
# signal_Noisy = 0*final_noise |
||||
|
|
||||
|
# for low resolution 16 channels |
||||
|
signal_Noisy = np.random.normal(0,1,size=(self.nRx,self.N_sample)) |
||||
|
signal_Noisy = 0*(signal_Noisy + 1j*signal_Noisy) |
||||
|
|
||||
|
elif self.radartype == "radarbook": |
||||
|
signal_Noisy = np.random.normal(0,1,size=(self.nRx,self.N_sample)) |
||||
|
signal_Noisy = 0*(signal_Noisy + 1j*signal_Noisy) |
||||
|
|
||||
|
else: |
||||
|
raise Exception("Incorrect radartype selected") |
||||
|
|
||||
|
return signal_Noisy |
||||
@ -0,0 +1,16 @@ |
|||||
|
# SHENRON: Radar Simulation |
||||
|
Packaging shenron into minimal working code |
||||
|
|
||||
|
To run the simulation, follow these steps: |
||||
|
|
||||
|
1. Open the `simulator_config.yaml` file. |
||||
|
2. Add the file paths for the input and output files in the appropriate fields. For example: |
||||
|
|
||||
|
```yaml |
||||
|
PATHS : |
||||
|
LIDAR_PATH : "/home/Kshitiz/" |
||||
|
LIDAR_FOLDERS : ["semantic_lidar"] |
||||
|
OUT_PATH : "/home/Kshitiz/semantic_lidar/" |
||||
|
3. Run the simulation using main file |
||||
|
```python |
||||
|
python main.py |
||||
@ -0,0 +1,2 @@ |
|||||
|
INPUT_PATH : "/radar-imaging-dataset/Jason/mmfn/data1/nss_0702/Town04_short/" |
||||
|
OUTPUT_PATH : "/radar-imaging-dataset/Jason/mmfn/data1/nss_0702/Town04_short/" |
||||
@ -0,0 +1,229 @@ |
|||||
|
import sys |
||||
|
import os |
||||
|
# sys.path.append("../") |
||||
|
sys.path.append('radar-imaging-dataset/carla_garage_radar/team_code/e2e_agent_sem_lidar2shenron_package/') |
||||
|
|
||||
|
import numpy as np |
||||
|
from e2e_agent_sem_lidar2shenron_package.path_config import * |
||||
|
from e2e_agent_sem_lidar2shenron_package.ConfigureRadar import radar |
||||
|
from e2e_agent_sem_lidar2shenron_package.shenron.Sceneset import * |
||||
|
from e2e_agent_sem_lidar2shenron_package.shenron.heatmap_gen_fast import * |
||||
|
# from pointcloud_raytracing.pointraytrace import ray_trace |
||||
|
import scipy.io as sio |
||||
|
from e2e_agent_sem_lidar2shenron_package.lidar_utils import * |
||||
|
import time |
||||
|
import shutil |
||||
|
import pdb |
||||
|
|
||||
|
def map_carla_semantic_lidar_latest(carla_sem_lidar_data): |
||||
|
''' |
||||
|
Function to map material column in the collected carla ray_cast_shenron to shenron input |
||||
|
''' |
||||
|
# CARLA 0.9.16 [x, y, z, intensity, cos, obj, tag] |
||||
|
# We want [x, y, z, tag] |
||||
|
carla_sem_lidar_data_crop = carla_sem_lidar_data[:, (0, 1, 2, 6)] |
||||
|
temp_list = np.array([0, 4, 2, 0, 11, 5, 0, 0, 1, 8, 12, 3, 7, 10, 0, 1, 0, 12, 6, 0, 0, 0, 0]) |
||||
|
|
||||
|
col = temp_list[(carla_sem_lidar_data_crop[:, 3].astype(int))] |
||||
|
carla_sem_lidar_data_crop[:, 3] = col |
||||
|
|
||||
|
return carla_sem_lidar_data_crop |
||||
|
|
||||
|
# def map_carla_semantic_lidar(carla_sem_lidar_data): |
||||
|
# ''' |
||||
|
# Function to map material column in the collected carla ray_cast_shenron to shenron input |
||||
|
# ''' |
||||
|
# # print(carla_sem_lidar_data.shape()) |
||||
|
# carla_sem_lidar_data_crop = carla_sem_lidar_data[:, (0, 1, 2, 5)] |
||||
|
# carla_sem_lidar_data_crop[:, 3] = carla_sem_lidar_data_crop[:, 3]-1 |
||||
|
# carla_sem_lidar_data_crop[carla_sem_lidar_data_crop[:, 3]>18, 3] = 255. |
||||
|
# carla_sem_lidar_data_crop[carla_sem_lidar_data_crop[:, 3]<0, 3] = 255. |
||||
|
# carla_sem_lidar_data_crop[:, (0, 1, 2)] = carla_sem_lidar_data_crop[:, (0, 2, 1)] |
||||
|
# return carla_sem_lidar_data_crop |
||||
|
|
||||
|
def check_save_path(save_path): |
||||
|
if not os.path.exists(save_path): |
||||
|
os.makedirs(save_path) |
||||
|
return |
||||
|
|
||||
|
def rotate_points(points, angle): |
||||
|
rotMatrix = np.array([[np.cos(np.deg2rad(angle)), np.sin(np.deg2rad(angle)), 0] |
||||
|
, [- np.sin(np.deg2rad(angle)), np.cos(np.deg2rad(angle)), 0] |
||||
|
, [0, 0, 1]]) |
||||
|
return np.matmul(points, rotMatrix) |
||||
|
|
||||
|
def Cropped_forRadar(pc, veh_coord, veh_angle, radarobj): |
||||
|
""" |
||||
|
Removes Occlusions and calculates loss for each point |
||||
|
""" |
||||
|
|
||||
|
skew_pc = rotate_points(pc[:, 0:3] , veh_angle ) |
||||
|
# skew_pc = np.vstack(((skew_pc ).T, pc[:, 3], pc[:, 5])).T #x,y,z,speed,material |
||||
|
skew_pc = np.vstack(((skew_pc ).T, pc[:, 3], pc[:, 5],pc[:,6])).T #x,y,z,speed,material, cosines |
||||
|
|
||||
|
rowy = np.where((skew_pc[:, 1] > 0.8)) |
||||
|
new_pc = skew_pc[rowy, :].squeeze(0) |
||||
|
|
||||
|
new_pc = new_pc[new_pc[:,4]!=0] |
||||
|
|
||||
|
new_pc = new_pc[(new_pc[:,0]<50)*(new_pc[:,0]>-50)] |
||||
|
new_pc = new_pc[(new_pc[:,1]<100)] |
||||
|
new_pc = new_pc[(new_pc[:,2]<2)] |
||||
|
|
||||
|
simobj = Sceneset(new_pc) |
||||
|
|
||||
|
[rho, theta, loss, speed, angles] = simobj.specularpoints(radarobj) |
||||
|
print(f"Number of points = {rho.shape[0]}") |
||||
|
return rho, theta, loss, speed, angles |
||||
|
|
||||
|
def run_lidar(sim_config, sem_lidar_frame): |
||||
|
|
||||
|
#restructed lidar.py code |
||||
|
|
||||
|
# lidar_path = f'{base_folder}/{sim_config["CARLA_SHENRON_SEM_LIDAR"]}' |
||||
|
# # lidar_velocity_path = f'{base_folder}/{sim_config["LIDAR_PATH_POINT_VELOCITY"]}/' |
||||
|
# out_path = f'{base_folder}/{sim_config["RADAR_PATH_SIMULATED"]}' |
||||
|
|
||||
|
# if not os.path.exists(out_path): |
||||
|
# os.makedirs(out_path) |
||||
|
|
||||
|
# shutil.copyfile('ConfigureRadar.py',f'{base_folder}/radar_params.py') |
||||
|
|
||||
|
# lidar_files = os.listdir(lidar_path) |
||||
|
# lidar_velocity_files = os.listdir(lidar_velocity_path) |
||||
|
# lidar_files.sort() |
||||
|
# lidar_velocity_files.sort() |
||||
|
|
||||
|
# print(lidar_files) |
||||
|
|
||||
|
#Lidar specific settings |
||||
|
radarobj = radar(sim_config["RADAR_TYPE"]) |
||||
|
# radarobj.chirps = 128 |
||||
|
radarobj.center = np.array([0.0, 0.0]) # center of radar |
||||
|
radarobj.elv = np.array([0.0]) |
||||
|
|
||||
|
# setting the sem lidar inversion angle here |
||||
|
veh_angle = sim_config['INVERT_ANGLE'] |
||||
|
|
||||
|
# all_speeds = [] |
||||
|
|
||||
|
# temp_angles = [] |
||||
|
# temp_rho = [] |
||||
|
# for file_no, file in enumerate(lidar_files): |
||||
|
|
||||
|
# start = time.time() |
||||
|
# if file.endswith('.npy'): # .pcd |
||||
|
# print(file) |
||||
|
|
||||
|
# lidar_file_path = os.path.join(f"{lidar_path}/", file) |
||||
|
# load_pc = np.load(lidar_file_path) |
||||
|
|
||||
|
# load_velocity = np.load(f'{lidar_velocity_path}/{file}') |
||||
|
|
||||
|
# test = map_material(test) |
||||
|
# cos_inc_angle is at index 4 in [x, y, z, intensity, cos, obj, tag] |
||||
|
cosines = sem_lidar_frame[:, 4] |
||||
|
load_pc = sem_lidar_frame |
||||
|
load_pc = map_carla_semantic_lidar_latest(load_pc) |
||||
|
test = new_map_material(load_pc) |
||||
|
|
||||
|
points = np.zeros((np.shape(test)[0], 7)) |
||||
|
# points[:, [0, 1, 2]] = test[:, [0, 2, 1]] |
||||
|
points[:, [0, 1, 2]] = test[:, [1, 0, 2]] |
||||
|
|
||||
|
""" |
||||
|
points mapping |
||||
|
+ve ind 0 == right |
||||
|
+ve ind 1 == +ve depth |
||||
|
+ve ind 2 == +ve height |
||||
|
""" |
||||
|
# add the velocity channel here to the lidar points on the channel number 3 most probably |
||||
|
# points[:, 3] = load_velocity |
||||
|
|
||||
|
points[:, 5] = test[:, 3] |
||||
|
points[:, 6] = cosines |
||||
|
### if jason carla lidar |
||||
|
# points = np.zeros((np.shape(test)[0], 6)) |
||||
|
# points[:, [0, 1, 2]] = load_pc[:, [0, 1, 2]] |
||||
|
# points[:, 5] = 4 |
||||
|
########## |
||||
|
|
||||
|
# if USE_DIGITAL_TWIN: |
||||
|
# gt_label = gt[file_no,:] |
||||
|
# points, veh_speeds = create_digital_twin(points, gt_label) ## This also claculates and outputs speed |
||||
|
|
||||
|
# all_speeds.append(veh_speeds) |
||||
|
|
||||
|
# if sim_config["RADAR_MOVING"]: |
||||
|
# # when the radar is moving, we add a negative doppler from all the points |
||||
|
# if INDOOR: |
||||
|
# curr_radar_speed = radar_speeds[file_no,:] |
||||
|
|
||||
|
# cos_theta = (points[:,1]/np.linalg.norm(points[:,:2],axis=1)) |
||||
|
# radial_speed = -np.linalg.norm(curr_radar_speed)*cos_theta |
||||
|
|
||||
|
# points[:,3] += radial_speed |
||||
|
# points[:,5] = 4 ## harcoded |
||||
|
|
||||
|
|
||||
|
Crop_rho, Crop_theta, Crop_loss, Crop_speed, Crop_angles = Cropped_forRadar(points, np.array([0, 0, 0]), veh_angle, radarobj) |
||||
|
|
||||
|
""" DEBUG CODE |
||||
|
spec_angle_thresh = 2*np.pi/180#*(1/rho) |
||||
|
|
||||
|
print(f"Number of points < 2deg = {np.sum(abs(Crop_angles)<spec_angle_thresh)}") |
||||
|
temp_angles.append(np.sum(abs(Crop_angles)<spec_angle_thresh)) |
||||
|
temp_rho.append(Crop_rho.shape[0]) |
||||
|
continue |
||||
|
""" |
||||
|
|
||||
|
|
||||
|
if sim_config["RAY_TRACING"]: |
||||
|
rt_rho, rt_theta = ray_trace(points) |
||||
|
|
||||
|
rt_loss = np.mean(Crop_loss)*np.ones_like(rt_rho) |
||||
|
rt_speed = np.zeros_like(rt_rho) |
||||
|
Crop_rho = np.append(Crop_rho, rt_rho) |
||||
|
Crop_theta = np.append(Crop_theta, rt_theta) |
||||
|
Crop_loss = np.append(Crop_loss, rt_loss) |
||||
|
Crop_speed = np.append(Crop_speed, rt_speed) |
||||
|
|
||||
|
adc_data = heatmap_gen(Crop_rho, Crop_theta, Crop_loss, Crop_speed, radarobj, 1, 0) |
||||
|
return adc_data |
||||
|
# check_save_path(out_path) |
||||
|
# np.save(f'{out_path}/{file[:-4]}', adc_data) |
||||
|
# diction = {"adc_data": adc_data} |
||||
|
# sio.savemat(f"{out_path}/{file[:-4]}.mat", diction) |
||||
|
# sio.savemat(f"test_pc.mat", diction) |
||||
|
# print(f'Time: {time.time()-start}') |
||||
|
# np.save("all_speeds_no_micro.npy",np.array(all_speeds)) |
||||
|
""" DEBUG CODE |
||||
|
fig, ax = plt.subplots(1,2) |
||||
|
ax[0].plot(temp_angles) |
||||
|
ax[1].plot(temp_rho) |
||||
|
|
||||
|
plt.plot(temp_rho) |
||||
|
plt.show() |
||||
|
pdb.set_trace() |
||||
|
""" |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
|
||||
|
points = np.zeros((100,6)) |
||||
|
|
||||
|
points[:,5] = 4 |
||||
|
|
||||
|
points[:,0] = 1 |
||||
|
points[:,1] = np.linspace(0,15,100) |
||||
|
|
||||
|
points[:,3] = -0.5*np.cos(np.arctan2(points[:,0],points[:,1])) |
||||
|
radarobj = radar('radarbook') |
||||
|
# radarobj.chirps = 128 |
||||
|
radarobj.center = np.array([0.0, 0.0]) # center of radar |
||||
|
radarobj.elv = np.array([0.0]) |
||||
|
|
||||
|
Crop_rho, Crop_theta, Crop_loss, Crop_speed = Cropped_forRadar(points, np.array([0, 0, 0]), 0, radarobj) |
||||
|
Crop_loss = np.ones_like(Crop_loss) |
||||
|
adc_data = heatmap_gen(Crop_rho, Crop_theta, Crop_loss, Crop_speed, radarobj, 1, 0) |
||||
|
diction = {"adc_data": adc_data} |
||||
|
sio.savemat(f"test_pc.mat", diction) |
||||
@ -0,0 +1,216 @@ |
|||||
|
import numpy as np |
||||
|
import open3d as o3d |
||||
|
import os |
||||
|
import scipy.io as sio |
||||
|
import sys |
||||
|
sys.path.append("../") |
||||
|
# from path_config import * |
||||
|
import pdb |
||||
|
|
||||
|
def create_digital_twin(lidar_points, gt_labels): |
||||
|
|
||||
|
""" |
||||
|
Augments the input point cloud using the ground truth labels. |
||||
|
Could densify or replace dynamic objects with CAD |
||||
|
Input: |
||||
|
lidar_points: Nx6 |
||||
|
gt_labels: GT boxes for the lidar frame |
||||
|
|
||||
|
Output: |
||||
|
digital_twin: N'x6 Augmented point cloud |
||||
|
""" |
||||
|
cad_id = 1 |
||||
|
# pdb.set_trace() |
||||
|
# car_data = sio.loadmat(path_config["path_cad"] + f"car_cart{cad_id}.mat") |
||||
|
# car_cart = np.array(car_data['car_cart']) |
||||
|
|
||||
|
car_data = sio.loadmat(path_config["path_cad"] + f"chassis_camry.mat") |
||||
|
car_cart = np.array(car_data['chassis_loc']) |
||||
|
|
||||
|
wheel_data = sio.loadmat(path_config["path_cad"] + f"wheel_camry.mat") |
||||
|
wheel_cart = np.array(wheel_data['wheel_loc']) |
||||
|
|
||||
|
speed_multiplier = sio.loadmat(path_config["path_cad"] + f"speed_multiplier.mat") |
||||
|
speed_multiplier = np.array(speed_multiplier['mult_all']) |
||||
|
|
||||
|
speed_multiplier = np.concatenate((np.ones(car_cart.shape[0],), np.squeeze(speed_multiplier)), axis=0) |
||||
|
car_cart = np.concatenate((car_cart, wheel_cart), axis=0) |
||||
|
|
||||
|
|
||||
|
veh_angle = gt_labels[-1] |
||||
|
rotMatrix = np.array([[np.cos(np.deg2rad(veh_angle)), |
||||
|
np.sin(np.deg2rad(veh_angle))] |
||||
|
, [- np.sin(np.deg2rad(veh_angle)), |
||||
|
np.cos(np.deg2rad(veh_angle))]]) |
||||
|
|
||||
|
|
||||
|
car_cart[:, 0:2] = np.matmul(car_cart[:, 0:2], np.array([[0, -1], [1, 0]])) |
||||
|
gt_dims = gt_labels[3:6] |
||||
|
# dims_mat = sio.loadmat(path_config["path_cad"] + "dimensions.mat") |
||||
|
# dims_mat = dims_mat['dims'] |
||||
|
# cad_dims = np.squeeze(dims_mat[dims_mat[:,3]==cad_id,:3]).tolist() |
||||
|
|
||||
|
cad_dims = np.array([4.805,1.795,1.495]) |
||||
|
|
||||
|
|
||||
|
scale = gt_dims/cad_dims |
||||
|
|
||||
|
new_car_cart = np.matmul(car_cart[:, 0:2], rotMatrix) |
||||
|
|
||||
|
veh_x_new = gt_labels[1] + new_car_cart[:, 0]*scale[0] |
||||
|
veh_y_new = -gt_labels[0] + new_car_cart[:, 1]*scale[1] |
||||
|
veh_z_new = -1 + car_cart[:, 2]*scale[2] # get the ground height |
||||
|
|
||||
|
###calculate radial_speed |
||||
|
|
||||
|
speed = np.ones((veh_x_new.shape[0],2))*gt_labels[9:11] |
||||
|
# speed[points[:,5]==0.1,:] = gt_labels[9:11] |
||||
|
# x = -points[:,1] |
||||
|
# y = points[:,0] |
||||
|
x = -veh_y_new |
||||
|
y = veh_x_new |
||||
|
|
||||
|
xy = np.hstack((x[:,None],y[:,None])) |
||||
|
rad_speed = np.sum((speed*xy)/np.linalg.norm(xy,axis=1)[:,None],1) |
||||
|
|
||||
|
veh_speed = rad_speed#*speed_multiplier |
||||
|
|
||||
|
# veh_speed = np.zeros(np.size(new_car_cart[:, 0])) |
||||
|
veh_angle = np.zeros(np.size(new_car_cart[:, 0])) |
||||
|
veh_material = 0.1 * np.ones(np.size(new_car_cart[:, 0])) ## Car material |
||||
|
|
||||
|
|
||||
|
|
||||
|
car_pc = np.vstack((veh_x_new, veh_y_new, veh_z_new, veh_speed, veh_angle, veh_material)).T |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
digital_twin = np.vstack((lidar_points,car_pc)) |
||||
|
|
||||
|
return digital_twin, veh_speed |
||||
|
|
||||
|
def map_material(test): |
||||
|
# test = test[test[:, 1] > 0, :] # filter to obtain calibration points |
||||
|
# test = test[test[:, 1] < 10, :] |
||||
|
# test[test[:, 3] != 255, 3] = 5 |
||||
|
|
||||
|
|
||||
|
# amplify = 1 |
||||
|
# after metal tuning - 0.01,0.1,0.001 |
||||
|
conc = 0.03 # .04 # 0.3 # 2 # best # calculated # lucky |
||||
|
steel = 0.1 # 0.99 # 200 |
||||
|
tree = 0.001 # 0.0001 # 0.01 # 0.02 |
||||
|
human = 0.1 |
||||
|
test[test[:, 3] == 255, 3] = 0 # for unlabeled assign 0 |
||||
|
test[test[:, 3] == 0, 3] = 0 # for road->concrete, 0 to suppress the ground rings that show up as objects |
||||
|
test[test[:, 3] == 1, 3] = conc # for sidewalk->concrete |
||||
|
test[test[:, 3] == 2, 3] = conc # for building->concrete |
||||
|
test[test[:, 3] == 3, 3] = conc # for wall->concrete |
||||
|
test[test[:, 3] == 4, 3] = steel # for fence->metal |
||||
|
test[test[:, 3] == 5, 3] = steel # for pole->metal |
||||
|
test[test[:, 3] == 6, 3] = steel # for traffic light->metal |
||||
|
test[test[:, 3] == 7, 3] = steel # for traffic sign->metal |
||||
|
test[test[:, 3] == 8, 3] = tree # for vegetation->trees # just for wall data |
||||
|
test[test[:, 3] == 9, 3] = tree # for terrain->trees # just for wall data |
||||
|
test[test[:, 3] == 10, 3] = 0 # for sky->0 |
||||
|
test[test[:, 3] == 11, 3] = human # for person->metal (reflects significant energy, approx) |
||||
|
test[test[:, 3] == 12, 3] = steel # for rider->metal |
||||
|
test[test[:, 3] == 13, 3] = steel # for car->metal |
||||
|
test[test[:, 3] == 14, 3] = steel # for truck->metal |
||||
|
test[test[:, 3] == 15, 3] = steel # for bus->metal |
||||
|
test[test[:, 3] == 16, 3] = steel # for train->metal |
||||
|
test[test[:, 3] == 17, 3] = steel # for motorbike->metal |
||||
|
test[test[:, 3] == 18, 3] = steel # for bicycle->metal |
||||
|
|
||||
|
return test |
||||
|
|
||||
|
def new_map_material(test): |
||||
|
# map material to index |
||||
|
#0 -> unlabeled |
||||
|
#1 -> wood |
||||
|
#2 -> concrete |
||||
|
#3 -> human |
||||
|
#4 -> metal |
||||
|
|
||||
|
unlabel = 0 |
||||
|
wood = 1 |
||||
|
conc = 2 |
||||
|
human = 3 |
||||
|
metal = 4 |
||||
|
|
||||
|
unlabel_roughness = 0 |
||||
|
wood_roughness = 0.0017 #1.7mm |
||||
|
conc_roughness = 0.0017 #1.7mm |
||||
|
human_roughness = 0.0001 # 100um |
||||
|
metal_roughness = 0.0001 # 100um |
||||
|
|
||||
|
roughness = np.array([unlabel_roughness,wood_roughness,conc_roughness,human_roughness,metal_roughness]) |
||||
|
|
||||
|
unlabel_perm = 1 |
||||
|
wood_perm = 2 |
||||
|
conc_perm = 5.24 |
||||
|
human_perm = 15 |
||||
|
metal_perm = 100000 |
||||
|
|
||||
|
permittivity = np.array([unlabel_perm,wood_perm,conc_perm,human_perm,metal_perm]) |
||||
|
|
||||
|
|
||||
|
test[test[:, 3] == 255, 3] = unlabel # for unlabeled assign 0 |
||||
|
test[test[:, 3] == 0, 3] = unlabel # for road->unlabeled, to suppress the ground rings that show up as objects |
||||
|
test[test[:, 3] == 1, 3] = conc # for sidewalk->concrete |
||||
|
test[test[:, 3] == 2, 3] = conc # for building->concrete |
||||
|
test[test[:, 3] == 3, 3] = conc # for wall->concrete |
||||
|
test[test[:, 3] == 4, 3] = metal # for fence->metal |
||||
|
test[test[:, 3] == 5, 3] = metal # for pole->metal |
||||
|
test[test[:, 3] == 6, 3] = metal # for traffic light->metal |
||||
|
test[test[:, 3] == 7, 3] = metal # for traffic sign->metal |
||||
|
test[test[:, 3] == 8, 3] = wood # for vegetation->trees # just for wall data |
||||
|
test[test[:, 3] == 9, 3] = wood # for terrain->trees # just for wall data |
||||
|
test[test[:, 3] == 10, 3] = unlabel # for sky->0 |
||||
|
test[test[:, 3] == 11, 3] = human # for person->metal (reflects significant energy, approx) |
||||
|
test[test[:, 3] == 12, 3] = metal # for rider->metal |
||||
|
test[test[:, 3] == 13, 3] = metal # for car->metal |
||||
|
test[test[:, 3] == 14, 3] = metal # for truck->metal |
||||
|
test[test[:, 3] == 15, 3] = metal # for bus->metal |
||||
|
test[test[:, 3] == 16, 3] = metal # for train->metal |
||||
|
test[test[:, 3] == 17, 3] = metal # for motorbike->metal |
||||
|
test[test[:, 3] == 18, 3] = metal # for bicycle->metal |
||||
|
|
||||
|
return test |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
|
||||
|
gt = sio.loadmat("../gt_11_08_car_round2.mat") |
||||
|
gt = gt["a"] |
||||
|
|
||||
|
|
||||
|
# print(gt_label) |
||||
|
exp_date = "11_08" |
||||
|
lidar_path = f"Y:/media/Kshitiz/RADARIMAGING/radar_simulator_data/{exp_date}_data/{exp_date}_lidar_appended" |
||||
|
lidar_folder = f"test_11_08_car_round2" |
||||
|
# file = "0001.npy" |
||||
|
files = np.array(os.listdir(f"{lidar_path}/{lidar_folder}/")) |
||||
|
|
||||
|
select_array = np.arange(50,51) |
||||
|
for file_idx, file in enumerate(files[select_array]): |
||||
|
lidar_file_path = os.path.join(f"{lidar_path}/{lidar_folder}/", file) |
||||
|
|
||||
|
test = np.load(lidar_file_path) |
||||
|
test = map_material(test) |
||||
|
|
||||
|
points = np.zeros((np.shape(test)[0], 6)) |
||||
|
|
||||
|
points[:, [0, 1, 2]] = test[:, [0, 2, 1]] |
||||
|
|
||||
|
points[:, 5] = test[:, 3] |
||||
|
|
||||
|
gt_label = gt[select_array[file_idx],:] |
||||
|
points = create_digital_twin(points, gt_label) |
||||
|
|
||||
|
pcd = o3d.geometry.PointCloud() |
||||
|
pcd.points = o3d.utility.Vector3dVector(points[:, 0:3]) |
||||
|
o3d.visualization.draw_geometries([pcd]) |
||||
|
|
||||
|
|
||||
@ -0,0 +1,63 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
""" |
||||
|
Created on Wed Apr 7 16:00:26 2021 |
||||
|
|
||||
|
@author: ksban |
||||
|
""" |
||||
|
|
||||
|
from lidar import run_lidar |
||||
|
import os |
||||
|
import yaml |
||||
|
|
||||
|
def run_shenron(sim_config, base_folder): |
||||
|
print("in run shenron") |
||||
|
# with open('simulator_configs.yaml', 'r') as f: |
||||
|
# sim_config = yaml.safe_load(f) |
||||
|
|
||||
|
# printing the base path here for the check |
||||
|
# print(f"the base folder currently is base/") |
||||
|
|
||||
|
folders = os.listdir(f"{sim_config['BASE_PATH']}/{base_folder}") |
||||
|
folders.sort() |
||||
|
# print(folders) |
||||
|
# run_folders = ["set4_vertical_crossing_2023-10-08-13-07-48", "set3_split_vertical_crossing_2023-10-08-12-56-58", "set2_vertical_crossing_2023-10-08-12-24-52", "set1_vertical_crossing_2023-10-08-12-02-41"] |
||||
|
# return |
||||
|
# run_folders= ["Town01_20_08_10_09_47_16"] |
||||
|
# run_folders= ["Town01_10_08_10_06_43_59"] #with 3 chirps only |
||||
|
for folder in folders: |
||||
|
# if folder in run_folders: |
||||
|
if os.path.isdir(f'{sim_config["BASE_PATH"]}/{base_folder}/{folder}/'): |
||||
|
exec_path = f'{sim_config["BASE_PATH"]}/{base_folder}/{folder}/' |
||||
|
print(f"currently running the folder {exec_path.split('/')[-2]}") |
||||
|
if sim_config["INPUT"] == "lidar": |
||||
|
run_lidar(sim_config, exec_path) |
||||
|
else: |
||||
|
print("Incorrect input in config") |
||||
|
|
||||
|
print("this folder done") |
||||
|
|
||||
|
else: |
||||
|
print("skipping this, not a folder") |
||||
|
continue |
||||
|
|
||||
|
# else: |
||||
|
# continue |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
|
||||
|
with open('simulator_configs.yaml', 'r') as f: |
||||
|
sim_config = yaml.safe_load(f) |
||||
|
|
||||
|
# if sim_config["INPUT"] == "lidar": |
||||
|
# run_lidar(sim_config) |
||||
|
# else: |
||||
|
# print("Incorrect input in config") |
||||
|
print("in main") |
||||
|
|
||||
|
base_base_folders = os.listdir(sim_config["BASE_PATH"]) |
||||
|
base_base_folders.sort() |
||||
|
run_folders = ["Town05_tiny", "Town06_short", "Town06_tiny", "Town07_short", "Town07_tiny", "Town10_short", "Town10_tiny",] |
||||
|
for base_folder in base_base_folders: |
||||
|
if base_folder in run_folders: |
||||
|
print(f"currently running the {base_folder}") |
||||
|
run_shenron(sim_config, base_folder) |
||||
@ -0,0 +1,63 @@ |
|||||
|
import numpy as np |
||||
|
import os |
||||
|
import yaml |
||||
|
# import run_shenron |
||||
|
# from main import run_shenron |
||||
|
|
||||
|
def check_save_path(save_path): |
||||
|
if not os.path.exists(save_path): |
||||
|
os.makedirs(save_path) |
||||
|
return |
||||
|
|
||||
|
def main(): |
||||
|
|
||||
|
with open('carla_shenron_config.yaml', 'r') as f: |
||||
|
config = yaml.safe_load(f) |
||||
|
|
||||
|
in_path = config["INPUT_PATH"] |
||||
|
out_path = config["OUTPUT_PATH"] |
||||
|
|
||||
|
if not os.path.exists(out_path): |
||||
|
os.makedirs(out_path, exist_ok=True) |
||||
|
|
||||
|
folders = os.listdir(in_path) |
||||
|
folders.sort() |
||||
|
print(folders) |
||||
|
print("folders loaded, mapping now") |
||||
|
|
||||
|
for folder in folders: |
||||
|
if os.path.isdir(f'{in_path}/{folder}'): |
||||
|
print(f"starting {folder}") |
||||
|
out_path_temp = f'{out_path}/{folder}/carla_shenron_sem_lidar' |
||||
|
check_save_path(out_path_temp) |
||||
|
|
||||
|
files = os.listdir(f'{in_path}/{folder}/semantic_lidar/') |
||||
|
files.sort() |
||||
|
|
||||
|
for file in files: |
||||
|
carla_sem_lidar_data = np.load(f'{in_path}/{folder}/semantic_lidar/{file}') |
||||
|
carla_sem_lidar_data = carla_sem_lidar_data[:, (0, 1, 2, 5)] |
||||
|
carla_sem_lidar_data[:, 3] = carla_sem_lidar_data[:, 3]-1 |
||||
|
|
||||
|
carla_sem_lidar_data[carla_sem_lidar_data[:, 3]>18, 3] = 255. |
||||
|
carla_sem_lidar_data[carla_sem_lidar_data[:, 3]<0, 3] = 255. |
||||
|
# print(carla_sem_lidar_data) |
||||
|
carla_sem_lidar_data[:, (0, 1, 2)] = carla_sem_lidar_data[:, (0, 2, 1)] |
||||
|
# print(carla_sem_lidar_data) |
||||
|
# break |
||||
|
# print(np.unique(carla_sem_lidar_data[:, 3])) |
||||
|
# break |
||||
|
np.save(f'{out_path_temp}/{file[:-4]}.npy', carla_sem_lidar_data) |
||||
|
print(f"{folder} done") |
||||
|
else: |
||||
|
print("skipping this, not a directory") |
||||
|
continue |
||||
|
|
||||
|
#call shenron here after saving carla_shenron_semantic_lidar for all the folders |
||||
|
# print("runnning shenron now") |
||||
|
# run_shenron() |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
print("starting map_carla_semantic_lidar_shenron") |
||||
|
main() |
||||
@ -0,0 +1,43 @@ |
|||||
|
# class path_config: |
||||
|
# path_csv = "/media/ehdd_8t1/RadarImaging/collab_radar_data/simulator_inputs/sumo_traffic_data/" |
||||
|
# path_ply = "/media/ehdd_8t1/RadarImaging/collab_radar_data/simulator_inputs/point_cloud_maps/" |
||||
|
# path_cad = "/media/ehdd_8t1/RadarImaging/collab_radar_data/simulator_inputs/car_cad_models/" |
||||
|
|
||||
|
path_config = { |
||||
|
"path_csv": "/media/ehdd_8t1/RadarImaging/collab_radar_data/simulator_inputs/sumo_traffic_data/", |
||||
|
"path_ply": "/media/ehdd_8t1/RadarImaging/collab_radar_data/simulator_inputs/point_cloud_maps/", |
||||
|
"path_cad": "/media/ehdd_8t1/RadarImaging/collab_radar_data/simulator_inputs/car_cad_models/", |
||||
|
"path_ped": "/media/ehdd_8t1/RadarImaging/collab_radar_data/simulator_inputs/human_cad_models/" |
||||
|
} |
||||
|
|
||||
|
out_paths = { |
||||
|
"path_tensors": "/media/ehdd_8t1/RadarImaging/collab_radar_data/radar_tensors/", |
||||
|
"path_gt": "/media/ehdd_8t1/RadarImaging/collab_radar_data/ground_truth/", |
||||
|
"path_images": "/media/ehdd_8t1/RadarImaging/collab_radar_data/radar_bev_images/" |
||||
|
} |
||||
|
|
||||
|
dataset_config = { |
||||
|
"dataset_name": "downtown_SD_10thru_50count", #"ucsd_atkinson_1" |
||||
|
"ply_name": "downtown_SD_10_7.ply", #"ucsd_atkinson_10_7.ply" |
||||
|
"csv_name": "downtown_SD_10thru_50count_10Hz_with_cad_id" |
||||
|
} |
||||
|
|
||||
|
ped_dataset_config = { |
||||
|
"dataset_name": "test_11_08_person3", |
||||
|
"npy_name": "test_11_08_person3_segmented", |
||||
|
} |
||||
|
|
||||
|
out_config = { |
||||
|
"save_dataset_name" : "downtown_SD_10thru_50count_1deg_10Hz" |
||||
|
} |
||||
|
|
||||
|
debug_config = { |
||||
|
"save_cropped_path" : "/media/ehdd_8t1/RadarImaging/collab_radar_data/debug/" |
||||
|
} |
||||
|
|
||||
|
# class data_paths: |
||||
|
# path_tensors = "/media/ehdd_8t1/RadarImaging/collab_radar_data/radar_tensors/" |
||||
|
# path_gt = "/media/ehdd_8t1/RadarImaging/collab_radar_data/ground_truth/" |
||||
|
# path_images = "/media/ehdd_8t1/RadarImaging/collab_radar_data/images_no_white/" |
||||
|
|
||||
|
|
||||
@ -0,0 +1,546 @@ |
|||||
|
from re import A |
||||
|
import open3d as o3d |
||||
|
import os |
||||
|
import numpy as np |
||||
|
import math |
||||
|
import matplotlib.pyplot as plt |
||||
|
import pdb |
||||
|
from e2e_agent_sem_lidar2shenron_package.ConfigureRadar import radar |
||||
|
import sys |
||||
|
|
||||
|
def cart2sph(x, y, z): |
||||
|
hxy = np.hypot(x, y) |
||||
|
r = np.hypot(hxy, z) |
||||
|
el = np.arctan2(z, hxy) |
||||
|
az = np.arctan2(y, x) |
||||
|
return az, el, r |
||||
|
|
||||
|
def sph2cart( az, el, r): |
||||
|
rcos_theta = r * np.cos(el) |
||||
|
x = rcos_theta * np.cos(az) |
||||
|
y = rcos_theta * np.sin(az) |
||||
|
z = r * np.sin(el) |
||||
|
return x, y, z |
||||
|
|
||||
|
class Sceneset(): |
||||
|
|
||||
|
""" |
||||
|
Class with all the functions to modify OSM scenario and thus generate the points for radar heatmap |
||||
|
""" |
||||
|
def __init__(self, pc): |
||||
|
self.points_3D = pc |
||||
|
self.rad_scene = self.points_3D |
||||
|
pass |
||||
|
|
||||
|
def removeocclusion(self, radar): |
||||
|
|
||||
|
row = np.where(self.points_3D.any(axis=1))[0] |
||||
|
self.points_3D = self.points_3D[row, :] |
||||
|
|
||||
|
# to reduce volume to 100x150x25, else voxel filter too big |
||||
|
rowx = np.where((self.points_3D[:, 0] < 50) & (self.points_3D[:, 0] > -50)) # x limits |
||||
|
self.points_3D = self.points_3D[rowx, :].squeeze(0) |
||||
|
rowy = np.where((self.points_3D[:, 1] < 150) & (self.points_3D[:, 1] > 0)) # y limits |
||||
|
self.points_3D = self.points_3D[rowy,:].squeeze(0) |
||||
|
rowz = np.where((self.points_3D[:, 2] < 5) & (self.points_3D[:, 2] > -2)) #z limits |
||||
|
self.points_3D = self.points_3D[rowz,:].squeeze(0) |
||||
|
rowx = [] |
||||
|
rowy = [] |
||||
|
rowz = [] |
||||
|
|
||||
|
self.rad_scene = self.points_3D |
||||
|
sph_v = np.zeros((self.points_3D.shape[0], 3)) |
||||
|
sph_v[:, 0], sph_v[:, 1], sph_v[:, 2] = cart2sph(self.points_3D[:, 0]-radar.center[0], self.points_3D[:, 1]-radar.center[1], self.points_3D[:, 2]-radar.elv) |
||||
|
sphlim = np.array([np.min(sph_v, axis=0), np.max(sph_v, axis=0)]) # limits in all three dimensions in the spherical coordinates |
||||
|
|
||||
|
|
||||
|
pcd2 = o3d.geometry.PointCloud() |
||||
|
pcd2.points = o3d.utility.Vector3dVector(self.rad_scene[:, 0:3]) |
||||
|
# o3d.visualization.draw_geometries([pcd2]) |
||||
|
|
||||
|
|
||||
|
# convert the point cloud into a grid of 0s and 1s |
||||
|
# define voxel size of the grid |
||||
|
phi_res = radar.voxel_phi / 180 * math.pi |
||||
|
theta_res = radar.voxel_theta / 180 * math.pi |
||||
|
rho_res = radar.voxel_rho |
||||
|
|
||||
|
sph_m_phi = np.arange(sphlim[0, 0], sphlim[1, 0] + phi_res,phi_res) + phi_res |
||||
|
sph_m_theta = np.arange( sphlim[0, 1], sphlim[1, 1] + theta_res, theta_res) + theta_res |
||||
|
sph_m_rho = np.arange(sphlim[0, 2], sphlim[1, 2] + rho_res, rho_res) + rho_res |
||||
|
sph_m_size = [len(sph_m_phi), len(sph_m_theta), len(sph_m_rho)] |
||||
|
sph_m = np.zeros(sph_m_size) |
||||
|
vel_m = np.zeros(sph_m_size) |
||||
|
mtl_m = np.zeros(sph_m_size) |
||||
|
|
||||
|
phi_m_idx = np.round_((sph_v[:, 0] - sphlim[0, 0]) / phi_res) # +1 |
||||
|
theta_m_idx = np.round_((sph_v[:, 1] - sphlim[0, 1]) / theta_res) # +1 |
||||
|
rho_m_idx = np.round_((sph_v[:, 2] - sphlim[0, 2]) / rho_res) # +1 |
||||
|
for k_pt in range(0, len(self.points_3D)): |
||||
|
sph_m[int(phi_m_idx[k_pt]), int(theta_m_idx[k_pt]), int(rho_m_idx[k_pt])] = 1 |
||||
|
vel_m[int(phi_m_idx[k_pt]), int(theta_m_idx[k_pt]), int(rho_m_idx[k_pt])] = self.points_3D[k_pt, 3] |
||||
|
mtl_m[int(phi_m_idx[k_pt]), int(theta_m_idx[k_pt]), int(rho_m_idx[k_pt])] = self.points_3D[k_pt, 4] |
||||
|
|
||||
|
visible_sph_m = np.zeros(sph_m.shape) |
||||
|
visible_vel_m = np.zeros(vel_m.shape) |
||||
|
visible_mtl_m = np.zeros(mtl_m.shape) |
||||
|
for kphi in range(0, sph_m_size[0]): |
||||
|
for ktheta in range(0, sph_m_size[1]): |
||||
|
if sph_m[kphi, ktheta, :].any() > 0: |
||||
|
krho = np.where(sph_m[kphi, ktheta, :] > 0)[0][0] |
||||
|
visible_sph_m[kphi, ktheta, krho] = sph_m[kphi, ktheta, krho] |
||||
|
visible_vel_m[kphi, ktheta, krho] = vel_m[kphi, ktheta, krho] |
||||
|
visible_mtl_m[kphi, ktheta, krho] = mtl_m[kphi, ktheta, krho] |
||||
|
|
||||
|
visible_sph_m_idx = np.where(visible_sph_m) |
||||
|
# sph_v_idx = [] |
||||
|
sph_v_idx = np.array([visible_sph_m_idx[0], visible_sph_m_idx[1], visible_sph_m_idx[2]]).T # np.unravel_index( visible_sph_m_idx, sph_m_size) |
||||
|
visible_vel_v = visible_vel_m[visible_sph_m_idx] |
||||
|
visible_mtl_v = visible_mtl_m[visible_sph_m_idx] |
||||
|
visible_sph_v = np.array([sph_m_phi[sph_v_idx[:, 0]], sph_m_theta[sph_v_idx[:, 1]], sph_m_rho[sph_v_idx[:, 2]]]).T |
||||
|
|
||||
|
visible_cart_v = np.zeros(visible_sph_v.shape) |
||||
|
[visible_cart_v[:, 0], visible_cart_v[:, 1], visible_cart_v[:, 2]] = sph2cart(visible_sph_v[:, 0], visible_sph_v[:, 1], visible_sph_v[:, 2]) |
||||
|
self.rad_scene = np.vstack((visible_cart_v[:, :].T, visible_vel_v, visible_mtl_v)).T+np.hstack((radar.center, radar.elv, np.array([0, 0]))) |
||||
|
|
||||
|
|
||||
|
# pcd2 = o3d.geometry.PointCloud() |
||||
|
# pcd2.points = o3d.utility.Vector3dVector(self.rad_scene[:, 0:3]) |
||||
|
|
||||
|
# o3d.visualization.draw_geometries([pcd2]) |
||||
|
|
||||
|
|
||||
|
# ground_refl = np.vstack((visible_cart_v[:, :].T, visible_vel_v, visible_mtl_v)).T+np.array(radar.center + [1] + [0, 0]) |
||||
|
# self.rad_scene = np.append(self.rad_scene, ground_refl*np.array([1, 1, -1, 1, 1]), axis=0) |
||||
|
|
||||
|
def removeocclusion_hiddenpoint(self, radar): |
||||
|
row = np.where(self.points_3D.any(axis=1))[0] |
||||
|
self.points_3D = self.points_3D[row, :] |
||||
|
|
||||
|
# to reduce volume to 100x150x25, else voxel filter too big |
||||
|
rowx = np.where((self.points_3D[:, 0] < 50) & (self.points_3D[:, 0] > -50)) # x limits |
||||
|
self.points_3D = self.points_3D[rowx, :].squeeze(0) |
||||
|
rowy = np.where((self.points_3D[:, 1] < 150) & (self.points_3D[:, 1] > 0)) # y limits |
||||
|
self.points_3D = self.points_3D[rowy,:].squeeze(0) |
||||
|
rowz = np.where((self.points_3D[:, 2] < 5) & (self.points_3D[:, 2] > -2)) #z limits |
||||
|
self.points_3D = self.points_3D[rowz,:].squeeze(0) |
||||
|
rowx = [] |
||||
|
rowy = [] |
||||
|
rowz = [] |
||||
|
|
||||
|
self.rad_scene = self.points_3D |
||||
|
vel = self.points_3D[:,3] |
||||
|
mtl = self.points_3D[:,4] |
||||
|
|
||||
|
pcd = o3d.geometry.PointCloud() |
||||
|
pcd.points = o3d.utility.Vector3dVector(self.rad_scene[:, 0:3]) |
||||
|
# o3d.visualization.draw_geometries([pcd]) |
||||
|
|
||||
|
|
||||
|
diameter = np.linalg.norm( |
||||
|
np.asarray(pcd.get_max_bound()) - np.asarray(pcd.get_min_bound())) |
||||
|
|
||||
|
print("Define parameters used for hidden_point_removal",diameter) |
||||
|
camera = [radar.center[0], radar.center[1], radar.elv] |
||||
|
radius = 10000 |
||||
|
# pdb.set_trace() |
||||
|
print("Get all points that are visible from given view point") |
||||
|
try: |
||||
|
_, pt_map = pcd.hidden_point_removal(camera, radius) |
||||
|
except Exception as e: |
||||
|
pt_map = np.arange(self.points_3D.shape[0]) |
||||
|
print(e) |
||||
|
|
||||
|
|
||||
|
self.rad_scene = np.hstack((np.array(pcd.points)[pt_map],vel[pt_map,None],mtl[pt_map,None])) |
||||
|
|
||||
|
print(f"Number of points in point cloud = {self.rad_scene.shape[0]}") |
||||
|
vel = self.rad_scene[:,3] |
||||
|
mtl = self.rad_scene[:,4] |
||||
|
pcd = o3d.geometry.PointCloud() |
||||
|
pcd.points = o3d.utility.Vector3dVector(self.rad_scene[:, 0:3]) |
||||
|
pcd_tree = o3d.geometry.KDTreeFlann(pcd) |
||||
|
downpcd = pcd.voxel_down_sample(voxel_size=0.05) |
||||
|
valid_points= np.zeros((1,5)) |
||||
|
for point in range(0,len(np.asarray(downpcd.points))): |
||||
|
[_, idx,_] = pcd_tree.search_knn_vector_3d(downpcd.points[point], 1) |
||||
|
valid_point = np.concatenate((np.array(pcd.points)[idx].T,vel[idx,None],mtl[idx,None]),axis=0).flatten() |
||||
|
valid_points = np.concatenate((valid_points,[valid_point]),axis=0) |
||||
|
valid_points=np.delete(valid_points,0,axis=0) |
||||
|
self.rad_scene = valid_points |
||||
|
print(f"Number of points after downsampling = {self.rad_scene.shape[0]}") |
||||
|
|
||||
|
# print(f"Number of points in point cloud = {self.rad_scene.shape[0]}") |
||||
|
# # downsample_ind = np.random.choice(self.rad_scene.shape[0], round(0.25*self.rad_scene.shape[0])) |
||||
|
# try: |
||||
|
# downsample_ind = np.random.choice(self.rad_scene.shape[0], 4000) ## need to fix this by breaking the number of points in heatmapgen |
||||
|
# self.rad_scene = self.rad_scene[downsample_ind,:] |
||||
|
# except Exception as e: |
||||
|
# pass |
||||
|
|
||||
|
|
||||
|
|
||||
|
# print(f"Number of points after downsampling = {self.rad_scene.shape[0]}") |
||||
|
|
||||
|
# pcd2 = o3d.geometry.PointCloud() |
||||
|
# pcd2.points = o3d.utility.Vector3dVector(self.rad_scene[:, 0:3]) |
||||
|
# o3d.visualization.draw_geometries([pcd2]) |
||||
|
# sys.exit() |
||||
|
|
||||
|
def specularpoints(self, radar): |
||||
|
|
||||
|
pcd = o3d.geometry.PointCloud() |
||||
|
pcd.points = o3d.utility.Vector3dVector(self.rad_scene[:, 0:3]) |
||||
|
|
||||
|
pcd.estimate_normals( search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=10, max_nn=10)) |
||||
|
# o3d.visualization.draw_geometries([pcd]) |
||||
|
|
||||
|
# plt.figure() |
||||
|
# pcd = o3d.geometry.PointCloud() |
||||
|
# pcd.points = o3d.utility.Vector3dVector(self.rad_scene) |
||||
|
# o3d.visualization.draw_geometries([pcd]) |
||||
|
# plt.title('Estimated Normals of Point Cloud') |
||||
|
|
||||
|
x = np.array(pcd.points)[:, 0] |
||||
|
y = np.array(pcd.points)[:, 1] |
||||
|
z = np.array(pcd.points)[:, 2] |
||||
|
u = np.array(pcd.normals)[:, 0] |
||||
|
v = np.array(pcd.normals)[:, 1] |
||||
|
w = np.array(pcd.normals)[:, 2] |
||||
|
|
||||
|
# fig = plt.figure() |
||||
|
# ax = fig.gca(projection='3d') |
||||
|
# ax.quiver(x, y, z, u, v, w) |
||||
|
# plt.show() |
||||
|
|
||||
|
angles = np.arccos(np.sum(np.array(pcd.normals)[:, 0: 3]*((np.array([x, y, z])-np.hstack((radar.center, radar.elv)).reshape((3, 1))).T /np.linalg.norm(np.array([x, y, z])-np.hstack((radar.center, radar.elv)).reshape((3, 1)), axis=0)[:, np.newaxis]), axis=1)) |
||||
|
angles_carla = self.rad_scene[:, 5] |
||||
|
spec_mask = self.rad_scene |
||||
|
|
||||
|
rho = np.linalg.norm(spec_mask[:, 0:3] - np.hstack((radar.center, radar.elv)), axis=1) |
||||
|
theta = math.pi / 2 - np.arctan(((spec_mask[:, 0] - radar.center[0]) / (spec_mask[:, 1] - radar.center[1]))) |
||||
|
elev_angle = np.arccos((spec_mask[:, 2] - radar.elv)/rho) |
||||
|
# loss = np.zeros((len(theta),), dtype=float) |
||||
|
# for i in range(len(theta)): |
||||
|
# if self.rad_scene[i, 4] == 0.1: |
||||
|
# loss[i] = (0.02 * (((1 + np.abs(np.cos(angles[i]))) / 2) ** 0.01) + 0.15*np.abs(np.cos(angles[i]))**300) * self.rad_scene[i, 4] * rho[i] * rho[i] * 3282 * math.radians(radar.voxel_phi) * math.radians(radar.voxel_theta) # + 0.1*np.abs(np.cos(angles))**300 |
||||
|
# else: # after metal without concrete 0.15,300 # after concrete 0.6,600 (just for the else loss) |
||||
|
# loss[i] = (0.02 * (((1 + np.abs(np.cos(angles[i]))) / 2) ** 0.01) + 0.6*np.abs(np.cos(angles[i]))**600) * self.rad_scene[i, 4] * rho[i] * rho[i] * 3282 * math.radians(radar.voxel_phi) * math.radians(radar.voxel_theta) # radar.voxel_phi * radar.voxel_theta |
||||
|
speed = self.rad_scene[:, 3] # 0.05&5 , 0.1&300 |
||||
|
|
||||
|
|
||||
|
|
||||
|
# file_path = os.path.join('F:/28_07_data/28_07_lidar_data/test_28_7_run11/mtlb-pcdFromBag', '0001.pcd') |
||||
|
# pc = o3d.io.read_point_cloud(file_path) |
||||
|
# pc.points = o3d.utility.Vector3dVector(self.rad_scene[:, 0:3]) |
||||
|
# o3d.visualization.draw_geometries([pc]) |
||||
|
loss_att,_,_ = get_loss_3(self.rad_scene, rho, elev_angle , angles_carla, radar, use_spec = False, use_diffused = True, no_material=False) |
||||
|
|
||||
|
return rho, theta, loss_att, speed, angles |
||||
|
|
||||
|
|
||||
|
def get_loss(points, rho, angles, radar, use_spec = True, use_diffused = True, no_material = False): |
||||
|
|
||||
|
# loss = np.zeros((len(theta),), dtype=float) |
||||
|
|
||||
|
if no_material: |
||||
|
points[:,4] = 1 |
||||
|
|
||||
|
constant = points[:, 4] * rho * rho * 3282 * math.radians(radar.voxel_phi) * math.radians(radar.voxel_theta) # N-points length |
||||
|
|
||||
|
diff_scatter = 0.02 * (((1 + np.abs(np.cos(angles))) / 2) ** 0.01) # N-points length |
||||
|
|
||||
|
spec_gain = 0.6 * np.ones(len(angles),) |
||||
|
spec_exp = 600 * np.ones(len(angles),) |
||||
|
|
||||
|
#for metal |
||||
|
spec_gain[points[:, 4] == 0.1] = 0.15 |
||||
|
spec_exp[points[:, 4] == 0.1] = 300 |
||||
|
|
||||
|
specular = spec_gain*np.abs(np.cos(angles))**spec_exp |
||||
|
|
||||
|
loss = (use_diffused * diff_scatter + use_spec * specular) * constant |
||||
|
|
||||
|
return loss, diff_scatter*constant, specular*constant |
||||
|
|
||||
|
|
||||
|
def get_loss_2(points, rho, angles, radar, use_spec = True, use_diffused = True, no_material = False): |
||||
|
''' |
||||
|
Note: angles should be used for power calculation as it is in reflecting surface coordinate system while theta is in radar coordinate system |
||||
|
|
||||
|
Implemement power conservation here: |
||||
|
P_inc = P_return + P_trans |
||||
|
P_inc = P_inc * T^2 + P_inc * (1 - T^2) |
||||
|
P_inc = P_inc * T^2 (R^2 + S^2) + P_trans |
||||
|
|
||||
|
Assume P_trans=0 or T=1. This can be more accurately calculated from fresnel equations |
||||
|
|
||||
|
P_inc = (P_inc * R^2) + (P_inc * S^2) |
||||
|
P_inc = P_spec + P_scat |
||||
|
''' |
||||
|
|
||||
|
if no_material: |
||||
|
points[:,4] = 1 |
||||
|
|
||||
|
|
||||
|
##Assuming rho and theta are calculated correctly |
||||
|
P_spec_inc = np.zeros((len(angles),), dtype=float) |
||||
|
P_scat_inc = np.zeros((len(angles),), dtype=float) |
||||
|
|
||||
|
K_sq = 3282 # Gt Pt |
||||
|
voxel_theta = np.deg2rad(radar.voxel_theta) |
||||
|
voxel_phi = np.deg2rad(radar.voxel_phi) |
||||
|
|
||||
|
tx_dist_loss_exponent = 0 |
||||
|
rx_dist_loss_exponent = 0 |
||||
|
|
||||
|
P_incident = np.power(rho,2) * np.sin(voxel_theta) * voxel_phi * voxel_theta * (1/np.power(rho,tx_dist_loss_exponent)) * K_sq # rho.^1.4 |
||||
|
|
||||
|
|
||||
|
|
||||
|
#Material dependent qualtities |
||||
|
if use_spec and use_diffused: |
||||
|
R_sq = np.ones((len(angles),))*0.8 # metal 0.3 |
||||
|
R_sq[points[:,4]==0.03] = 0.2 # Concrete |
||||
|
R_sq[points[:,4]==0.001] = 0.2 # Vegetation |
||||
|
R_sq[points[:,4]==0] = 0.1 # Road |
||||
|
S_sq = 1 - R_sq # Scattering coefficient |
||||
|
|
||||
|
elif not use_spec: |
||||
|
R_sq = np.zeros((len(angles),)) |
||||
|
S_sq = 1 - R_sq # Scattering coefficient |
||||
|
|
||||
|
elif not use_diffused: |
||||
|
R_sq = np.ones((len(angles),)) |
||||
|
S_sq = 1 - R_sq # Scattering coefficient |
||||
|
|
||||
|
else: |
||||
|
print("Both Scatter and specular can't be false") |
||||
|
AssertionError |
||||
|
|
||||
|
P_absorbed_fac = np.zeros((len(angles),)) # metal |
||||
|
P_absorbed_fac[points[:,4]==0.03] = 0.8 # concrete |
||||
|
P_absorbed_fac[points[:,4]==0.001] = 0.8 # vegetation |
||||
|
P_absorbed_fac[points[:,4]==0] = 1 # road |
||||
|
|
||||
|
|
||||
|
P_reflected = P_incident*(1 - P_absorbed_fac) |
||||
|
|
||||
|
spec_exp = np.ones((len(angles),))*600 |
||||
|
spec_exp[points[:,4]==0.1] = 200 #300 # metal |
||||
|
|
||||
|
normalization = np.ones((len(angles),))*(1/0.10229) |
||||
|
normalization[points[:,4]==0.1] = (1/0.17702) # metal |
||||
|
|
||||
|
|
||||
|
|
||||
|
P_spec = P_reflected * R_sq |
||||
|
P_scat = P_reflected * S_sq |
||||
|
|
||||
|
##find power in the incident direction now |
||||
|
# pdb.set_trace() |
||||
|
# P_spec_inc[points[:,4]==0.1] = points[points[:,4]==0.1,4]*0.15*np.abs(np.cos(angles[points[:,4]==0.1]))**300 |
||||
|
# P_spec_inc[points[:,4]!=0.1] = points[points[:,4]!=0.1,4]*0.6*np.abs(np.cos(angles[points[:,4]!=0.1]))**600 |
||||
|
|
||||
|
P_spec_inc = normalization*(np.abs(np.cos(angles))**spec_exp)*(1/np.power(rho,rx_dist_loss_exponent)) |
||||
|
# P_spec_inc[points[:,4]!=0.1] = (1/0.10229)*(np.abs(np.cos(angles[points[:,4]==0.1]))**600)*(1/np.power(rho,2)) |
||||
|
|
||||
|
P_spec_inc = P_spec_inc * P_spec |
||||
|
|
||||
|
#Lambertian |
||||
|
P_scat_inc = P_scat* ((0.5*(1+abs(np.cos(angles))))**0.1)*(1/3.07385) * (1/np.power(rho,rx_dist_loss_exponent)) # lambda sqaure aperture taken care in heatmap |
||||
|
|
||||
|
P_spec_inc = P_spec_inc * 10e2 #30e2 #0.8e2 |
||||
|
P_scat_inc = P_scat_inc * 10e2 #30e2 |
||||
|
|
||||
|
loss = P_spec_inc + P_scat_inc |
||||
|
|
||||
|
return loss, P_scat_inc, P_spec_inc |
||||
|
|
||||
|
def get_loss_3(points, rho, elev_angle, angles, radar, use_spec = True, use_diffused = True, no_material = False): |
||||
|
''' |
||||
|
Note: angles should be used for power calculation as it is in reflecting surface coordinate system while theta is in radar coordinate system |
||||
|
|
||||
|
Implemement power conservation here: |
||||
|
P_inc = P_return(rough_loss^2 + S^2) + P_trans |
||||
|
P_scatter = P_return*S^2 |
||||
|
P_return = P_inc * R^2 (R -> reflection co-eff from fresnel equations) |
||||
|
|
||||
|
S = sqrt(1 - rough_loss^2) |
||||
|
|
||||
|
|
||||
|
R_TE = cos(incd_angle) - sqrt(perm - sin(incd_angle)^2) / cos(incd_angle) + sqrt(perm - sin(incd_angle)^2) |
||||
|
R_TM = perm*cos(incd_angle) - sqrt(perm - sin(incd_angle)^2) / perm*cos(incd_angle) + sqrt(perm - sin(incd_angle)^2) |
||||
|
R^2 = (R_TE^2 + R_TM^2)/2 |
||||
|
|
||||
|
Scatter_loss = R^2 * S^2 = R^2 * (1- rough_loss^2) |
||||
|
rough_loss = exp(-0.5((4*pi*std_rough*cos(inc_angle)/lambda)^2)) |
||||
|
|
||||
|
backscatter_lobe_loss = E_so^2 * (lobe_frac*cos(incd_angle)^2*alpha_R + (1-lobe_frac)) |
||||
|
''' |
||||
|
|
||||
|
# standard deviation of surface roughness |
||||
|
unlabel_roughness = 0 |
||||
|
wood_roughness = 0.0017 #1.7mm |
||||
|
conc_roughness = 0.0017 #1.7mm |
||||
|
human_roughness = 0.01 # 100um |
||||
|
metal_roughness = 0.00005 # 100um |
||||
|
|
||||
|
roughness = np.array([unlabel_roughness,wood_roughness,conc_roughness,human_roughness,metal_roughness]) |
||||
|
|
||||
|
unlabel_perm = 1 |
||||
|
wood_perm = 2 |
||||
|
conc_perm = 5.24 |
||||
|
human_perm = 1#15 |
||||
|
metal_perm = 100000 |
||||
|
|
||||
|
permittivity = np.array([unlabel_perm,wood_perm,conc_perm,human_perm,metal_perm]) |
||||
|
|
||||
|
scat_normalization = 1/1.09*np.ones((len(angles),)) #*(1/0.10229) #####?????????#### this should bring the Eso^2 #####?????????#### |
||||
|
lobe_frac = 0.9 # ratio of energy in main_lobe/back_scatter_lobe |
||||
|
spec_lobe_exp = 4.0 #lobe exponential along specular direction |
||||
|
|
||||
|
K_sq = 3282 # Gt Pt #####?????????#### is this from the peak amplitude calculation #####?????????#### |
||||
|
voxel_theta = np.deg2rad(radar.voxel_theta) |
||||
|
voxel_phi = np.deg2rad(radar.voxel_phi) |
||||
|
|
||||
|
tx_dist_loss_exponent = 2 |
||||
|
rx_dist_loss_exponent = 0 |
||||
|
|
||||
|
spec_angle_thresh = 2*np.pi/180#*(1/rho) |
||||
|
|
||||
|
P_incident = np.power(rho,2) * np.sin(elev_angle) * voxel_phi * voxel_theta * (1/np.power(rho,tx_dist_loss_exponent)) * K_sq |
||||
|
|
||||
|
material = np.array(points[:,4]) |
||||
|
material = np.asarray(material, dtype = 'int') |
||||
|
# material[:] = 3 |
||||
|
|
||||
|
#Material dependent qualtities |
||||
|
# if use_spec and use_diffused: |
||||
|
R_TE_sq = np.power(((abs(np.cos(angles)) - np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))/(abs(np.cos(angles)) + np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))),2) |
||||
|
R_TM_sq = np.power(((permittivity[material]*abs(np.cos(angles)) - np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))/(permittivity[material]*abs(np.cos(angles)) + np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))),2) |
||||
|
R_sq = R_TE_sq #0.5 * (R_TE_sq + R_TM_sq) |
||||
|
rough_sq = np.exp(-0.5*np.power(4*np.pi*roughness[material]*abs(np.cos(angles))*radar.f/radar.c,2)) |
||||
|
# if len(rough_sq[abs(angles) < spec_angle_thresh])!=0: |
||||
|
# print(f"Materials = {np.unique(material)}") |
||||
|
# print(f"R value max= {np.max(rough_sq[abs(angles) < spec_angle_thresh])}; average = {np.mean(rough_sq[abs(angles) < spec_angle_thresh])}") |
||||
|
|
||||
|
S_sq = 1 - rough_sq # Scattering coefficient = P_scat/P_reflec |
||||
|
P_reflected = P_incident * R_sq |
||||
|
P_scat = P_reflected * S_sq |
||||
|
P_scat_lobe = scat_normalization*(lobe_frac * np.power(abs(np.cos(angles)),2*spec_lobe_exp) + (1-lobe_frac)) |
||||
|
P_scat = P_scat * np.power(P_scat_lobe,2) #lobe shape is for amplitude, need to square for power |
||||
|
|
||||
|
P_spec = P_reflected * rough_sq |
||||
|
P_spec = P_spec * np.ones((len(angles),)) * (abs(angles) < spec_angle_thresh) # specular reflection only if angle < 2 degrees |
||||
|
|
||||
|
if not use_spec: |
||||
|
# R_TE_sq = np.power(((abs(np.cos(angles)) - np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))/(abs(np.cos(angles)) + np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))),2) |
||||
|
# R_TM_sq = np.power(((permittivity[material]*abs(np.cos(angles)) - np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))/(permittivity[material]*abs(np.cos(angles)) + np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))),2) |
||||
|
# R_sq = R_TE_sq #0.5 * (R_TE_sq + R_TM_sq) |
||||
|
# rough_sq = np.exp(-0.5*np.power(4*np.pi*roughness[material]*abs(np.cos(angles))*radar.f/radar.c,2)) |
||||
|
|
||||
|
# S_sq = 1 - rough_sq # Scattering coefficient = P_scat/P_reflec |
||||
|
# P_reflected = P_incident * R_sq |
||||
|
# P_scat = P_reflected * S_sq |
||||
|
# P_scat_lobe = scat_normalization*(lobe_frac * np.power(abs(np.cos(angles)),2*spec_lobe_exp) + (1-lobe_frac)) |
||||
|
# P_scat = P_scat * np.power(P_scat_lobe,2) #lobe shape is for amplitude, need to square for power |
||||
|
|
||||
|
P_spec = np.zeros((len(angles),)) |
||||
|
|
||||
|
elif not use_diffused: |
||||
|
# R_TE_sq = np.power(((abs(np.cos(angles)) - np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))/(abs(np.cos(angles)) + np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))),2) |
||||
|
# R_TM_sq = np.power(((permittivity[material]*abs(np.cos(angles)) - np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))/(permittivity[material]*abs(np.cos(angles)) + np.sqrt(permittivity[material] - np.power(np.sin(angles),2)))),2) |
||||
|
# R_sq = R_TE_sq #0.5 * (R_TE_sq + R_TM_sq) |
||||
|
# rough_sq = np.exp(-0.5*np.power(4*np.pi*roughness[material]*abs(np.cos(angles))*radar.f/radar.c,2)) |
||||
|
|
||||
|
# S_sq = 1 - rough_sq # Scattering coefficient = P_scat/P_reflec |
||||
|
# P_reflected = P_incident * R_sq |
||||
|
# P_scat = P_reflected * S_sq |
||||
|
# P_scat_lobe = scat_normalization*(lobe_frac * np.power(abs(np.cos(angles)),2*spec_lobe_exp) + (1-lobe_frac)) |
||||
|
# P_scat = P_scat * np.power(P_scat_lobe,2) #lobe shape is for amplitude, need to square for power |
||||
|
|
||||
|
P_scat = np.zeros((len(angles),)) |
||||
|
|
||||
|
# P_spec = P_reflected * rough_sq |
||||
|
# P_spec = P_spec * np.ones((len(angles),)) * (abs(angles) < 2*np.pi/180) #####?????????#### specular reflection only if angle < 2 degrees #####?????????#### |
||||
|
|
||||
|
elif not use_spec and not use_diffused: |
||||
|
print("Both Scatter and specular can't be false") |
||||
|
AssertionError |
||||
|
|
||||
|
P_spec = P_spec *4*100*25/9 |
||||
|
P_scat = P_scat *100*25/9 |
||||
|
|
||||
|
loss = P_spec + P_scat |
||||
|
# loss = loss * 2**7.2 |
||||
|
|
||||
|
# fig, ax = plt.subplots(1,7, figsize=(10,5)) |
||||
|
# ax[0].plot(loss) |
||||
|
# ax[0].set_title("total loss",fontsize=10) |
||||
|
# ax[1].plot(P_spec) |
||||
|
# ax[1].set_title("specular loss",fontsize=10) |
||||
|
# ax[2].plot(P_scat) |
||||
|
# ax[2].set_title("scatter loss",fontsize=10) |
||||
|
# ax[3].plot(material) |
||||
|
# ax[3].set_title("material",fontsize=10) |
||||
|
# ax[4].plot(angles) |
||||
|
# ax[4].set_title("angles",fontsize=10) |
||||
|
# ax[5].plot(R_TE_sq) |
||||
|
# ax[5].set_title("R TE",fontsize=10) |
||||
|
# ax[6].plot(R_TM_sq) #abs(numerator)/abs(denominator)) |
||||
|
# ax[6].set_title("R TM",fontsize=10) |
||||
|
|
||||
|
# plt.show() |
||||
|
# plt.suptitle('loss profile',fontsize=15, y=1) |
||||
|
# plt.savefig('loss_comp_sim.jpg') |
||||
|
|
||||
|
return loss, P_scat, P_spec |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
|
||||
|
# dummy_points = np.array([[0,0,0,0,0.1], # |
||||
|
# [0,0,0,0,0.03], |
||||
|
# [0,0,0,0,0.006]]) |
||||
|
|
||||
|
|
||||
|
dummy_points = np.vstack((np.tile([0,0,0,0,0.1],(180,1)),np.tile([0,0,0,0,0.03],(180,1)),np.tile([0,0,0,0,0.001],(180,1)))) |
||||
|
latest_dummy_points = np.vstack((np.tile([0,0,0,0,4],(180,1)),np.tile([0,0,0,0,2],(180,1)),np.tile([0,0,0,0,1],(180,1)))) |
||||
|
|
||||
|
dummy_rho = np.array([20]*540) |
||||
|
|
||||
|
dummy_angles = np.hstack((np.linspace(-np.pi/2,np.pi/2,180),np.linspace(-np.pi/2,np.pi/2,180),np.linspace(-np.pi/2,np.pi/2,180))) |
||||
|
|
||||
|
radar = radar() |
||||
|
|
||||
|
#loss_old, scat_old, spec_old = get_loss(dummy_points,dummy_rho,dummy_angles,radar) |
||||
|
loss_new, scat_new, spec_new = get_loss_2(dummy_points,dummy_rho,dummy_angles,radar) |
||||
|
loss_latest, scat_latest, spec_latest = get_loss_3(latest_dummy_points,dummy_rho,dummy_angles,radar) |
||||
|
|
||||
|
|
||||
|
# print(f"Old Loss: {loss_old}") |
||||
|
# print(f"New Loss: {loss_new}") |
||||
|
|
||||
|
fig, ax = plt.subplots(2,3, figsize=(10,5)) |
||||
|
|
||||
|
#ax[0,0].plot(loss_old) |
||||
|
#ax[0,1].plot(spec_old) |
||||
|
#ax[0,2].plot(scat_old) |
||||
|
ax[0,0].plot(loss_latest) |
||||
|
ax[0,0].set_title("New total loss",fontsize=10) |
||||
|
ax[0,1].plot(spec_latest) |
||||
|
ax[0,1].set_title("New specular loss",fontsize=10) |
||||
|
ax[0,2].plot(scat_latest) |
||||
|
ax[0,2].set_title("New scatter loss",fontsize=10) |
||||
|
ax[1,0].plot(loss_new) |
||||
|
ax[1,0].set_title("Prev total loss",fontsize=10) |
||||
|
ax[1,1].plot(spec_new) |
||||
|
ax[1,1].set_title("Prev specular loss",fontsize=10) |
||||
|
ax[1,2].plot(scat_new) |
||||
|
ax[1,2].set_title("Prev scatter loss",fontsize=10) |
||||
|
|
||||
|
plt.show() |
||||
|
plt.suptitle('0:179 => Metal 180:359 =>Concrete 360:539=>Wood',fontsize=15, y=1) |
||||
|
plt.savefig('loss_comparison.jpg') |
||||
|
|
||||
@ -0,0 +1,177 @@ |
|||||
|
import numpy as np |
||||
|
from e2e_agent_sem_lidar2shenron_package.ConfigureRadar import radar |
||||
|
import matplotlib.pyplot as plt |
||||
|
from matplotlib import cm |
||||
|
from mat4py import loadmat |
||||
|
import math |
||||
|
from mpl_toolkits.mplot3d import axes3d, Axes3D |
||||
|
import time |
||||
|
import scipy.io as sio |
||||
|
from numba import jit |
||||
|
from numba import int32, float64, complex128 |
||||
|
from noise_utils import get_noise |
||||
|
import pdb |
||||
|
|
||||
|
# @jit(nopython=True) |
||||
|
def heatmap_gen(rho, theta, loss, speed, radar, plot_fig, return_power): |
||||
|
|
||||
|
range_res = radar.c / (2 * radar.B) |
||||
|
max_range = range_res * radar.N_sample |
||||
|
|
||||
|
Ts = 1 / radar.samp_rate |
||||
|
t = np.arange(0, radar.chirpT, Ts) |
||||
|
tau_resolution = 1 / radar.B |
||||
|
k = radar.B / radar.chirpT |
||||
|
x = np.exp(1j * 2 * np.pi * (radar.f + 0.5 * k * t) * t) |
||||
|
|
||||
|
# noise_prop = loadmat('noise.mat') |
||||
|
# real_fft_ns = np.random.normal(noise_prop['noise_mean_real'], noise_prop['noise_std_real']).T |
||||
|
# complex_fft_ns = np.random.normal(noise_prop['noise_mean_real'], noise_prop['noise_std_real']).T |
||||
|
# final_noise = real_fft_ns + 1j * complex_fft_ns |
||||
|
# signal_Noisy = np.fft.ifft(final_noise, radar.N_sample, 1) * 10 |
||||
|
signal_Noisy = get_noise(radar) |
||||
|
|
||||
|
if radar.doppler_mode: |
||||
|
measurement = np.zeros((radar.chirps, radar.nRx, radar.N_sample), dtype="complex128") # doppler,AoA,range |
||||
|
# start = time.time() |
||||
|
for chirp in range(radar.chirps): |
||||
|
tau = 2 * rho / radar.c + chirp * radar.chirpT * speed / radar.c |
||||
|
_lambda = radar.c / radar.f |
||||
|
sRx = _lambda / 2 |
||||
|
_lambda = radar.c / radar.f |
||||
|
tau_vec = np.zeros((radar.nRx, len(rho))) |
||||
|
for i in range(radar.nRx): |
||||
|
tau_vec[i, :] = tau + i * sRx * np.sin(np.pi / 2 - theta) / radar.c |
||||
|
|
||||
|
sum_samp = np.zeros((radar.nRx, radar.N_sample), dtype="complex128") |
||||
|
for j in range(rho.shape[0]): |
||||
|
if return_power: |
||||
|
if rho[j] != 0: |
||||
|
signal = np.sqrt(loss[j]) * np.exp( |
||||
|
1j * 2 * np.pi * (radar.f + 0.5 * k * (np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) * ( |
||||
|
np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) |
||||
|
else: |
||||
|
if rho[j] != 0: |
||||
|
signal = loss[j] * (1 / rho[j] ** 2) * np.exp( |
||||
|
1j * 2 * np.pi * (radar.f + 0.5 * k * (np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) * ( |
||||
|
np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) |
||||
|
|
||||
|
# noise_real = (1j*1j*-1) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
# noise_complex = (1j) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
# noise = (noise_real+noise_complex) * radar.noise_amp |
||||
|
# signal_Noisy = signal + noise |
||||
|
|
||||
|
sum_samp = sum_samp + signal |
||||
|
adc_sampled = np.sqrt(radar.gain * _lambda ** 2 / (4 * np.pi) ** 3) * np.conj(sum_samp) * (x) |
||||
|
adc_sampled = adc_sampled + signal_Noisy |
||||
|
measurement[chirp, :, :] = adc_sampled |
||||
|
|
||||
|
return measurement |
||||
|
# end = time.time() |
||||
|
# diction = {"doppler_cube": measurement} |
||||
|
# sio.savemat("E:/Radar_sim/simlator/git repo/Heatmap-sim/doppler_cube.mat", diction) |
||||
|
# RangeFFT = np.fft.fft(measurement, radar.N_sample, 2) |
||||
|
# AngleFFT = np.fft.fftshift(np.fft.fft(RangeFFT[0, :, :], radar.angle_fft_size, 0), 0) |
||||
|
# Doppler_data = np.fft.fftshift(np.fft.fft(np.fft.fftshift(np.fft.fft(RangeFFT, radar.angle_fft_size, 1), 1), |
||||
|
# radar.chirps, 0), 0) |
||||
|
# Doppler_heatmap = np.sum(np.arange(radar.chirps)[:, None, None] * np.abs(Doppler_data), axis=0) / np.sum( |
||||
|
# np.abs(Doppler_data), axis=0) - radar.chirps / 2 |
||||
|
|
||||
|
else: |
||||
|
tau = 2 * rho / radar.c |
||||
|
_lambda = radar.c / radar.f |
||||
|
sRx = _lambda / 2 # separation |
||||
|
_lambda = radar.c / radar.f |
||||
|
|
||||
|
tau_vec = np.zeros((radar.nRx, rho.shape[0])) |
||||
|
for i in range(radar.nRx): |
||||
|
tau_vec[i, :] = tau + (i) * sRx * np.sin(np.pi / 2 - theta) / radar.c |
||||
|
|
||||
|
sum_samp = np.zeros((radar.nRx, radar.N_sample), dtype="complex128") |
||||
|
for j in range(rho.shape[0]): |
||||
|
if (rho[j] != 0): |
||||
|
if return_power: |
||||
|
if rho[j] != 0: |
||||
|
signal = 10**10 * np.sqrt(loss[j]) * np.exp( |
||||
|
1j * 2 * np.pi * (radar.f + 0.5 * k * (np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) * ( |
||||
|
np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) |
||||
|
else: |
||||
|
if rho[j] != 0: |
||||
|
signal = loss[j] * (1 / rho[j] ** 2) * np.exp( |
||||
|
1j * 2 * np.pi * (radar.f + 0.5 * k * (np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) * ( |
||||
|
np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) |
||||
|
|
||||
|
# noise_real = (1j*1j*-1) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
# noise_complex = (1j) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
# noise = (noise_real+noise_complex) * radar.noise_amp |
||||
|
# signal_Noisy = signal + noise |
||||
|
|
||||
|
sum_samp = sum_samp + signal |
||||
|
|
||||
|
adc_sampled = np.sqrt(radar.gain * _lambda ** 2 / (4 * np.pi) ** 3) * np.conj(sum_samp) * (x) |
||||
|
adc_sampled = adc_sampled + signal_Noisy |
||||
|
# RangeFFT = np.fft.fft(adc_sampled, radar.N_sample, 1) |
||||
|
|
||||
|
# pwr_prof = 10*np.log10(np.sum(abs(RangeFFT)**2, 0)+1) |
||||
|
# plt.plot(radar.range_res*np.arange(radar.N_sample), pwr_prof) |
||||
|
# plt.axis([0, 256*radar.range_res, 70, 180]) |
||||
|
# plt.show() |
||||
|
|
||||
|
# AngleFFT = np.fft.fftshift(np.fft.fft(RangeFFT, radar.angle_fft_size, 0), 0) |
||||
|
|
||||
|
# Doppler_heatmap = np.zeros(np.shape(AngleFFT)) |
||||
|
|
||||
|
return measurement |
||||
|
|
||||
|
|
||||
|
# print('runtime is ', end - start) |
||||
|
|
||||
|
# d = 1 |
||||
|
# sine_theta = -2 * np.arange(-radar.angle_fft_size / 2, (radar.angle_fft_size / 2) + 1) / radar.angle_fft_size / d |
||||
|
# # sine_theta = -2*np.arange(-radar.angle_fft_size/2,(radar.angle_fft_size/2)+1)/radar.angle_fft_size/d |
||||
|
# cos_theta = np.sqrt(1 - sine_theta ** 2) |
||||
|
# indices_1D = np.arange(0, radar.N_sample) |
||||
|
# [R_mat, sine_theta_mat] = np.meshgrid(indices_1D * range_res, sine_theta) |
||||
|
# [_, cos_theta_mat] = np.meshgrid(indices_1D, cos_theta) |
||||
|
|
||||
|
# x_axis = R_mat * cos_theta_mat |
||||
|
# y_axis = R_mat * sine_theta_mat |
||||
|
# mag_data_static = abs(np.vstack( |
||||
|
# (AngleFFT, AngleFFT[255, :]))) # np.column_stack((abs(AngleFFT[indices_1D,:]),abs(AngleFFT[indices_1D,0]))) |
||||
|
# mag_data_doppler = abs(np.vstack((Doppler_heatmap, Doppler_heatmap[255, :]))) |
||||
|
# # doppler_cube = np.concatenate((Doppler_data, Doppler_data[:, 255, :][:, np.newaxis, :]), 1) |
||||
|
|
||||
|
# mag_data_static = np.flipud(mag_data_static) |
||||
|
# mag_data_doppler = np.flipud(mag_data_doppler) |
||||
|
# # doppler_cube = np.flipud(doppler_cube) |
||||
|
|
||||
|
# return x_axis, y_axis, mag_data_static, mag_data_doppler |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
radar = radar() |
||||
|
|
||||
|
test_WI=1 |
||||
|
if (test_WI): |
||||
|
points = np.load("E:/Radar_sim/simlator/git repo/Heatmap-sim/simulation5.npy") |
||||
|
rho = np.linalg.norm(points[:, 0:3] - np.array(radar.center+radar.elv), axis=1) |
||||
|
theta = math.pi / 2 - np.arctan(((points[:, 0] - radar.center[0]) / (points[:, 1] - radar.center[1]))) |
||||
|
loss = points[:, 3] |
||||
|
speed = np.zeros_like(rho) |
||||
|
else: |
||||
|
rho = np.array([5, 5, 10]) # np.linspace(7.9749, 9.2793, 97) |
||||
|
theta = np.array([math.pi / 3, math.pi * 2 / 3, math.pi / 2]) # np.linspace(1.6856, 1.521, 97) |
||||
|
loss = np.ones_like(rho) |
||||
|
speed = np.zeros_like(rho) |
||||
|
|
||||
|
x_axis, y_axis, plot_data, doppler_data = heatmap_gen(rho, theta, loss, speed, radar, 1) |
||||
|
|
||||
|
diction = {"x_axis": x_axis, "y_axis": y_axis, "plot_data": plot_data} |
||||
|
sio.savemat('test_heatmap.mat', diction) |
||||
|
|
||||
|
# fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) |
||||
|
# ax.view_init(elev=90, azim=180) |
||||
|
# surf = ax.plot_surface(x_axis, y_axis, plot_data ** 0.7, cmap=cm.coolwarm, |
||||
|
# linewidth=0, antialiased=False) |
||||
|
# plt.show() |
||||
|
|
||||
@ -0,0 +1,280 @@ |
|||||
|
import numpy as np |
||||
|
from e2e_agent_sem_lidar2shenron_package.ConfigureRadar import radar |
||||
|
import matplotlib.pyplot as plt |
||||
|
from matplotlib import cm |
||||
|
from mat4py import loadmat |
||||
|
import math |
||||
|
from mpl_toolkits.mplot3d import axes3d, Axes3D |
||||
|
import time |
||||
|
import scipy.io as sio |
||||
|
# from numba import jit |
||||
|
# from numba import int32, float64, complex128 |
||||
|
import pdb |
||||
|
import torch |
||||
|
from pynvml import * |
||||
|
|
||||
|
def get_gpu_id_most_avlbl_mem(): |
||||
|
try: |
||||
|
nvmlInit() |
||||
|
deviceCount = nvmlDeviceGetCount() |
||||
|
free = [] |
||||
|
for i in range(deviceCount): |
||||
|
h = nvmlDeviceGetHandleByIndex(i) |
||||
|
info = nvmlDeviceGetMemoryInfo(h) |
||||
|
free.append(info.free) |
||||
|
free = np.array(free) |
||||
|
# h0 = nvmlDeviceGetHandleByIndex(0) |
||||
|
# h1 = nvmlDeviceGetHandleByIndex(1) |
||||
|
# h2 = nvmlDeviceGetHandleByIndex(2) |
||||
|
# h3 = nvmlDeviceGetHandleByIndex(3) |
||||
|
# info0 = nvmlDeviceGetMemoryInfo(h0) |
||||
|
# info1 = nvmlDeviceGetMemoryInfo(h1) |
||||
|
# info2 = nvmlDeviceGetMemoryInfo(h2) |
||||
|
# info3 = nvmlDeviceGetMemoryInfo(h3) |
||||
|
|
||||
|
# free = np.array([info0.free,info1.free,info2.free,info3.free]) |
||||
|
|
||||
|
return np.argmax(free), np.max(free) |
||||
|
except Exception: |
||||
|
return 0, 0 |
||||
|
|
||||
|
# @jit(nopython=True) |
||||
|
def heatmap_gen(rho, theta, loss, speed, radar, plot_fig, return_power): |
||||
|
start = time.time() |
||||
|
range_res = radar.c / (2 * radar.B) |
||||
|
max_range = range_res * radar.N_sample |
||||
|
|
||||
|
Ts = 1 / radar.samp_rate |
||||
|
t = np.arange(0, radar.chirpT, Ts) |
||||
|
tau_resolution = 1 / radar.B |
||||
|
k = radar.B / radar.chirpT |
||||
|
x = np.exp(1j * 2 * np.pi * (radar.f + 0.5 * k * t) * t) |
||||
|
|
||||
|
|
||||
|
_lambda = radar.c / radar.f |
||||
|
sRx = _lambda / 2 |
||||
|
_lambda = radar.c / radar.f |
||||
|
|
||||
|
# Declare if we should use cpu or cuda |
||||
|
try: |
||||
|
if torch.cuda.is_available(): |
||||
|
gpu_id, _ = get_gpu_id_most_avlbl_mem() |
||||
|
device = torch.device(f'cuda:{gpu_id}') |
||||
|
else: |
||||
|
gpu_id = 0 |
||||
|
device = torch.device('cpu') |
||||
|
except Exception: |
||||
|
gpu_id = 0 |
||||
|
device = torch.device('cpu') |
||||
|
|
||||
|
print(f"------- Using {device}:{gpu_id} -------") |
||||
|
|
||||
|
# beamforming_vector_constant = np.zeros((radar.nRx,rho.shape[0])) |
||||
|
delta = torch.zeros((radar.nRx,rho.shape[0]),device= device) |
||||
|
|
||||
|
|
||||
|
##Noise |
||||
|
# noise_prop = loadmat('noise.mat') |
||||
|
# real_fft_ns = np.random.normal(noise_prop['noise_mean_real'], noise_prop['noise_std_real']).T |
||||
|
# complex_fft_ns = np.random.normal(noise_prop['noise_mean_real'], noise_prop['noise_std_real']).T |
||||
|
# final_noise = real_fft_ns + 1j * complex_fft_ns |
||||
|
# signal_Noisy = np.fft.ifft(final_noise, radar.N_sample, 1) * 10 |
||||
|
signal_Noisy = radar.get_noise() |
||||
|
|
||||
|
##initialising on torch |
||||
|
loss = torch.tensor(loss*(1 / rho ** 2), device=device).float() |
||||
|
rho = torch.tensor(rho, device=device).float() |
||||
|
signal_Noisy = torch.tensor(signal_Noisy, device=device) |
||||
|
theta = torch.tensor(theta, device=device).float() |
||||
|
speed = torch.tensor(speed, device=device).float() |
||||
|
t = torch.tensor(t, device=device).float() |
||||
|
|
||||
|
torch.set_printoptions(precision=10) |
||||
|
if radar.doppler_mode: |
||||
|
measurement = np.zeros((radar.chirps, radar.nRx, radar.N_sample), dtype="complex128") # doppler,AoA,range |
||||
|
# start = time.time() |
||||
|
for chirp in range(radar.chirps): |
||||
|
tau = 2 * (rho / radar.c + chirp * (radar.chirp_rep) * speed / radar.c) |
||||
|
# tau = 2 * rho / radar.c + chirp * (12*27e-6) * speed / radar.c |
||||
|
# print(tau[torch.argmax(abs(speed))]) |
||||
|
for i in range(radar.nRx): |
||||
|
delta[i,:] = i * sRx * torch.sin(np.pi / 2 - theta) / radar.c |
||||
|
|
||||
|
# delta = torch.tensor(delta,device='cuda') |
||||
|
# tau = torch.tensor(tau,device='cuda') |
||||
|
# t = torch.tensor(t,device='cuda') |
||||
|
|
||||
|
# beamforming_vector = np.exp(1j*2*np.pi*(radar.f*delta[:,:,None] - 0.5*k*delta[:,:,None]**2 - k*tau[None,:,None]*delta[:,:,None] + k*delta[:,:,None]@t[None,None,:])) #(80xN) |
||||
|
beamforming_vector = torch.exp(1j*2*np.pi*(radar.f*delta[:,:,None] - 0.5*k*delta[:,:,None]**2 - k*tau[None,:,None]*delta[:,:,None] + k*delta[:,:,None]@t[None,None,:])) #(80xN) |
||||
|
|
||||
|
|
||||
|
# tau = tau.cpu().numpy() |
||||
|
# t = t.cpu().numpy() |
||||
|
# beamforming_vector = beamforming_vector.cpu().numpy() |
||||
|
|
||||
|
|
||||
|
dechirped = torch.exp((1j * 2 * np.pi) *(radar.f * tau[:,None] + k * tau[:,None]@t[None,:] - 0.5 * k * tau[:,None]**2)) |
||||
|
# loss_factor = (loss * (1 / rho ** 2))[:,None] ## from network |
||||
|
loss_factor = (torch.sqrt(loss)[:,None]) ## from network |
||||
|
|
||||
|
signal_single_antenna = loss_factor*dechirped # (Nx256) |
||||
|
|
||||
|
signal = beamforming_vector*signal_single_antenna[None,:,:] # (80x256) |
||||
|
|
||||
|
signal = torch.squeeze(torch.sum(signal,1)) |
||||
|
|
||||
|
# pdb.set_trace() |
||||
|
|
||||
|
# (np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) * ( |
||||
|
# np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) |
||||
|
|
||||
|
# signal = np.exp(1j*2*np.pi * ()) |
||||
|
|
||||
|
# noise_real = (1j*1j*-1) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
# noise_complex = (1j) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
# noise = (noise_real+noise_complex) * radar.noise_amp |
||||
|
# signal_Noisy = signal |
||||
|
|
||||
|
# sum_samp = sum_samp + signal_Noisy ## Here I am not adding individual noise for each point |
||||
|
|
||||
|
|
||||
|
|
||||
|
# adc_sampled = np.sqrt(radar.gain * _lambda ** 2 / (4 * np.pi) ** 3) * np.conj(signal_Noisy) * (x) |
||||
|
adc_sampled = torch.sqrt(torch.tensor(radar.gain * _lambda ** 2 / (4 * np.pi) ** 3)) * signal |
||||
|
adc_sampled = adc_sampled + signal_Noisy |
||||
|
|
||||
|
adc_sampled = adc_sampled.cpu().numpy() |
||||
|
|
||||
|
|
||||
|
|
||||
|
measurement[chirp, :, :] = adc_sampled |
||||
|
# pdb.set_trace() |
||||
|
del rho |
||||
|
del loss |
||||
|
# del signal_noisy |
||||
|
del speed |
||||
|
del theta |
||||
|
del t |
||||
|
del dechirped |
||||
|
del beamforming_vector |
||||
|
del signal |
||||
|
del signal_single_antenna |
||||
|
if device.type == 'cuda': |
||||
|
with torch.cuda.device(f'cuda:{gpu_id}'): |
||||
|
torch.cuda.empty_cache() |
||||
|
# pdb.set_trace() |
||||
|
return measurement |
||||
|
# end = time.time() |
||||
|
# diction = {"doppler_cube": measurement} |
||||
|
# sio.savemat("E:/Radar_sim/simlator/git repo/Heatmap-sim/doppler_cube.mat", diction) |
||||
|
# RangeFFT = np.fft.fft(measurement, radar.N_sample, 2) |
||||
|
# AngleFFT = np.fft.fftshift(np.fft.fft(RangeFFT[0, :, :], radar.angle_fft_size, 0), 0) |
||||
|
# Doppler_data = np.fft.fftshift(np.fft.fft(np.fft.fftshift(np.fft.fft(RangeFFT, radar.angle_fft_size, 1), 1), |
||||
|
# radar.chirps, 0), 0) |
||||
|
# Doppler_heatmap = np.sum(np.arange(radar.chirps)[:, None, None] * np.abs(Doppler_data), axis=0) / np.sum( |
||||
|
# np.abs(Doppler_data), axis=0) - radar.chirps / 2 |
||||
|
|
||||
|
else: |
||||
|
tau = 2 * rho / radar.c |
||||
|
_lambda = radar.c / radar.f |
||||
|
sRx = _lambda / 2 # separation |
||||
|
_lambda = radar.c / radar.f |
||||
|
|
||||
|
tau_vec = np.zeros((radar.nRx, rho.shape[0])) |
||||
|
for i in range(radar.nRx): |
||||
|
tau_vec[i, :] = tau + (i) * sRx * np.sin(np.pi / 2 - theta) / radar.c |
||||
|
|
||||
|
sum_samp = np.zeros((radar.nRx, radar.N_sample), dtype="complex128") |
||||
|
for j in range(rho.shape[0]): |
||||
|
if (rho[j] != 0): |
||||
|
if return_power: |
||||
|
if rho[j] != 0: |
||||
|
signal = 10**10 * np.sqrt(loss[j]) * np.exp( |
||||
|
1j * 2 * np.pi * (radar.f + 0.5 * k * (np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) * ( |
||||
|
np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) |
||||
|
else: |
||||
|
if rho[j] != 0: |
||||
|
signal = loss[j] * (1 / rho[j] ** 2) * np.exp( |
||||
|
1j * 2 * np.pi * (radar.f + 0.5 * k * (np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) * ( |
||||
|
np.expand_dims(t,0) - np.expand_dims(tau_vec[:,j],1))) |
||||
|
|
||||
|
noise_real = (1j*1j*-1) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
noise_complex = (1j) * (2 * np.random.rand(radar.nRx, radar.N_sample) - 1) |
||||
|
noise = (noise_real+noise_complex) * radar.noise_amp |
||||
|
signal_Noisy = signal + noise |
||||
|
|
||||
|
sum_samp = sum_samp + signal_Noisy*10**5 |
||||
|
|
||||
|
adc_sampled = np.sqrt(radar.gain * _lambda ** 2 / (4 * np.pi) ** 3) * np.conj(sum_samp) * (x) |
||||
|
# RangeFFT = np.fft.fft(adc_sampled, radar.N_sample, 1) |
||||
|
|
||||
|
# pwr_prof = 10*np.log10(np.sum(abs(RangeFFT)**2, 0)+1) |
||||
|
# plt.plot(radar.range_res*np.arange(radar.N_sample), pwr_prof) |
||||
|
# plt.axis([0, 256*radar.range_res, 70, 180]) |
||||
|
# plt.show() |
||||
|
|
||||
|
# AngleFFT = np.fft.fftshift(np.fft.fft(RangeFFT, radar.angle_fft_size, 0), 0) |
||||
|
|
||||
|
# Doppler_heatmap = np.zeros(np.shape(AngleFFT)) |
||||
|
|
||||
|
return measurement |
||||
|
|
||||
|
|
||||
|
# print('runtime is ', end - start) |
||||
|
|
||||
|
# d = 1 |
||||
|
# sine_theta = -2 * np.arange(-radar.angle_fft_size / 2, (radar.angle_fft_size / 2) + 1) / radar.angle_fft_size / d |
||||
|
# # sine_theta = -2*np.arange(-radar.angle_fft_size/2,(radar.angle_fft_size/2)+1)/radar.angle_fft_size/d |
||||
|
# cos_theta = np.sqrt(1 - sine_theta ** 2) |
||||
|
# indices_1D = np.arange(0, radar.N_sample) |
||||
|
# [R_mat, sine_theta_mat] = np.meshgrid(indices_1D * range_res, sine_theta) |
||||
|
# [_, cos_theta_mat] = np.meshgrid(indices_1D, cos_theta) |
||||
|
|
||||
|
# x_axis = R_mat * cos_theta_mat |
||||
|
# y_axis = R_mat * sine_theta_mat |
||||
|
# mag_data_static = abs(np.vstack( |
||||
|
# (AngleFFT, AngleFFT[255, :]))) # np.column_stack((abs(AngleFFT[indices_1D,:]),abs(AngleFFT[indices_1D,0]))) |
||||
|
# mag_data_doppler = abs(np.vstack((Doppler_heatmap, Doppler_heatmap[255, :]))) |
||||
|
# # doppler_cube = np.concatenate((Doppler_data, Doppler_data[:, 255, :][:, np.newaxis, :]), 1) |
||||
|
|
||||
|
# mag_data_static = np.flipud(mag_data_static) |
||||
|
# mag_data_doppler = np.flipud(mag_data_doppler) |
||||
|
# # doppler_cube = np.flipud(doppler_cube) |
||||
|
|
||||
|
# return x_axis, y_axis, mag_data_static, mag_data_doppler |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
radar = radar() |
||||
|
radar.chirps = 1 |
||||
|
radar.center = np.array([0.0, 0.0]) # center of radar |
||||
|
radar.elv = np.array([0.0]) |
||||
|
|
||||
|
test_WI=1 |
||||
|
if (test_WI): |
||||
|
# points = np.load("E:/Radar_sim/simlator/git repo/Heatmap-sim/simulation5.npy") |
||||
|
cell_array = sio.loadmat("wi_data/single_radar_wi_10m_allangles.mat") |
||||
|
|
||||
|
for i in range(36): |
||||
|
print(f"Simulation: {i}") |
||||
|
points = cell_array['cell_array'][i][0] |
||||
|
if points.shape[0]==0: |
||||
|
print("Skipped") |
||||
|
continue |
||||
|
rho = np.linalg.norm(points[:, 0:3], axis=1) |
||||
|
theta = math.pi / 2 - np.arctan(((points[:, 0] - radar.center[0]) / (points[:, 1] - radar.center[1]))) |
||||
|
loss = 10**(points[:, 3]/20) |
||||
|
speed = np.zeros_like(rho) |
||||
|
|
||||
|
|
||||
|
adc_data = heatmap_gen(rho, theta, loss, speed, radar, 1, 0) |
||||
|
diction = {"adc_data": adc_data} |
||||
|
sio.savemat(f"wi_data/simulation_{i}.mat", diction) |
||||
|
|
||||
|
# fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) |
||||
|
# ax.view_init(elev=90, azim=180) |
||||
|
# surf = ax.plot_surface(x_axis, y_axis, plot_data ** 0.7, cmap=cm.coolwarm, |
||||
|
# linewidth=0, antialiased=False) |
||||
|
# plt.show() |
||||
|
|
||||
|
|
||||
@ -0,0 +1,46 @@ |
|||||
|
INPUT : "lidar" |
||||
|
|
||||
|
NOISE : True |
||||
|
|
||||
|
BUILDINGS : True |
||||
|
|
||||
|
# RADAR_TYPE : ti_cascade # 1. radarbook | 2. ti_cascade |
||||
|
RADAR_TYPE : ti_cascade |
||||
|
|
||||
|
INDOOR : False |
||||
|
|
||||
|
RADAR_MOVING : False |
||||
|
|
||||
|
RAY_TRACING : False |
||||
|
|
||||
|
INVERT_ANGLE : 0 |
||||
|
|
||||
|
BASE_PATH : "/radar-imaging-dataset/carla_garage_data/ll_dataset_2023_05_10/Routes_Town04_ll_Repetition0/Town04_ll_route0_11_17_05_03_55/" |
||||
|
|
||||
|
CAMERA_PATH : "rgb_image/" #path for original camera images |
||||
|
|
||||
|
LIDAR_PATH : "lidar_pcd/" #path for unprocessed lidar point clouds |
||||
|
|
||||
|
LIDAR_PATH_SEGMENTED : "lidar_segmented/" #path for segmented lidar point clouds |
||||
|
|
||||
|
LIDAR_PATH_CLUSTERS : "lidar_clusters_centroid_with_labels" #path for saving lidar pcd with cluster labels with a separate channel |
||||
|
|
||||
|
LIDAR_PATH_LABELS : "lidar_with_labels/" #path for saving lidar pcd with cluster labels with a separate channel |
||||
|
|
||||
|
LIDAR_PATH_POINT_VELOCITY : "lidar_velocity/" #path for saving the velocity of all the points and all the frames |
||||
|
|
||||
|
LIDAR_PATH_CLUSTER_VELOCITY : "lidar_clusters_centroid_with_label_velocity/" #path for saving the velocity of all the cluster points and all the frames |
||||
|
|
||||
|
RADAR_PATH_SIMULATED : "radar_data_simulated/" |
||||
|
|
||||
|
# CARLA_SHENRON_SEM_LIDAR : carla_shenron_sem_lidar/ |
||||
|
CARLA_SHENRON_SEM_LIDAR : semantic_lidar/ |
||||
|
|
||||
|
|
||||
|
# PATHS : |
||||
|
|
||||
|
# LIDAR_PATH : "C:\\Users\\Strai\\Downloads\\semantic_lidar" |
||||
|
|
||||
|
# LIDAR_FOLDERS : ["semantic_lidar"] |
||||
|
|
||||
|
# OUT_PATH : "C:\\Users\\Strai\\Downloads\\semantic_lidar\\semantic_lidar" |
||||
@ -0,0 +1,264 @@ |
|||||
|
import numpy as np |
||||
|
import matplotlib.pyplot as plt |
||||
|
import torch |
||||
|
import pdb |
||||
|
from scipy import signal |
||||
|
from scipy import io as sio |
||||
|
import matplotlib.pyplot as plt |
||||
|
|
||||
|
def separate_real_imag(input): |
||||
|
|
||||
|
real = input.real |
||||
|
imag = input.imag |
||||
|
|
||||
|
out_tensor = np.stack((real,imag), axis=0) |
||||
|
|
||||
|
return out_tensor |
||||
|
|
||||
|
def separate_mag_phase(input): |
||||
|
|
||||
|
mag = np.abs(input) |
||||
|
phase = np.angle(input) |
||||
|
|
||||
|
out_tensor = np.stack((mag,phase), axis=0) |
||||
|
|
||||
|
return out_tensor |
||||
|
|
||||
|
def convert_adc_to_3d_fft(adcData, rangefft_size = 256, dopplerfft_size=128, anglefft_size = 256, window=False, distance_null = False): |
||||
|
""" |
||||
|
Function to convert adc data to RA tensor |
||||
|
Input: |
||||
|
adcData: |
||||
|
NxDxM -- where N is the range dimension and M is the angle dimension and D is doppler. |
||||
|
|
||||
|
Output: |
||||
|
RA tensor: R-size x A-size |
||||
|
""" |
||||
|
if window: |
||||
|
range_hanning = np.hanning(rangefft_size)[:,None,None] |
||||
|
range_hanning = np.tile(range_hanning, (1,adcData.shape[1],adcData.shape[2])) |
||||
|
|
||||
|
adcData = adcData*range_hanning |
||||
|
|
||||
|
if distance_null: |
||||
|
radar_range_res = 3e8/(2*2.56e8) |
||||
|
range_vector = np.arange(rangefft_size)*radar_range_res |
||||
|
range_vector_loss = range_vector**4 |
||||
|
range_vector_tiled = np.tile(range_vector_loss[:,None, None], (1,adcData.shape[1],adcData.shape[2])) |
||||
|
|
||||
|
adcData = adcData*range_vector_tiled |
||||
|
rangefft = np.fft.fft(adcData, rangefft_size, 0) |
||||
|
dopplerfft = np.fft.fft(rangefft, dopplerfft_size, 1) |
||||
|
anglefft = np.fft.fftshift(np.fft.fft(dopplerfft, anglefft_size, 2),2) |
||||
|
|
||||
|
# self.anglefft = anglefft |
||||
|
return anglefft |
||||
|
|
||||
|
def cart2polar(x, y, in_pixels, limit, range_res): |
||||
|
|
||||
|
#Assuming uniform theta resolution |
||||
|
r = np.sqrt((limit-x)**2 + (y)**2)*(1/range_res) |
||||
|
|
||||
|
theta = np.arctan2((y),(limit-x)) |
||||
|
theta_px = np.sin(theta)*(in_pixels/2) + in_pixels/2 |
||||
|
return r, theta_px |
||||
|
|
||||
|
def polar_to_cart(RATensor, range_res = 150/256, in_pixels = 256, limit = 150, out_pixels = 256): |
||||
|
|
||||
|
""" |
||||
|
convert a polar range angle tensor to cartesian array |
||||
|
Input: |
||||
|
RATensor: NxM |
||||
|
Output: |
||||
|
cart_RATensor: NxM |
||||
|
""" |
||||
|
|
||||
|
X, Y = np.meshgrid(np.linspace(0, limit, out_pixels), |
||||
|
np.linspace(-limit/2, limit/2, out_pixels)) |
||||
|
|
||||
|
R_samp, Theta_samp = cart2polar(X, Y, in_pixels, limit, range_res) |
||||
|
|
||||
|
R_samp = R_samp.astype(int) |
||||
|
Theta_samp = Theta_samp.astype(int) |
||||
|
|
||||
|
R_samp[(R_samp>(in_pixels-1))] = 0 |
||||
|
R_samp[(R_samp<0)] = 0 |
||||
|
Theta_samp[(Theta_samp>(in_pixels-1))] = 0 |
||||
|
Theta_samp[(Theta_samp<0)] = 0 |
||||
|
|
||||
|
|
||||
|
if RATensor.ndim >2: |
||||
|
polar_img = RATensor[...,R_samp,Theta_samp] |
||||
|
# polar_img = torch.reshape(polar_img,(RATensor.shape[0],RATensor.shape[1],out_pixels,out_pixels)) |
||||
|
if torch.is_tensor(RATensor): |
||||
|
polar_img = torch.transpose(polar_img,-1,-2) |
||||
|
else: |
||||
|
polar_img = np.swapaxes(polar_img,-2,-1) |
||||
|
else: |
||||
|
polar_img = RATensor[R_samp,Theta_samp] |
||||
|
polar_img = torch.reshape(polar_img,(out_pixels,out_pixels)) |
||||
|
polar_img = polar_img.T |
||||
|
|
||||
|
return polar_img |
||||
|
|
||||
|
|
||||
|
|
||||
|
def plot_data(data, ax = None): |
||||
|
|
||||
|
# if not ax: |
||||
|
# fig, ax = plt.subplots(1, len(data)) |
||||
|
|
||||
|
for num, im in enumerate(data): |
||||
|
ax[num].imshow(im) |
||||
|
|
||||
|
# plt.show() |
||||
|
|
||||
|
return ax |
||||
|
# plt.imshow(polar_image[0,0]) |
||||
|
# plt.figure() |
||||
|
# plt.imshow(cart_image[0,0]) |
||||
|
|
||||
|
def CFAR_filtered_output(): |
||||
|
##Cfar layers |
||||
|
guard_cell = 5 |
||||
|
CFAR_cell = 11 |
||||
|
guard_avg = nn.AvgPool2d(guard_cell, stride = 1, padding = 2, divisor_override = 1, count_include_pad=False) |
||||
|
CFAR_avg = nn.AvgPool2d(CFAR_cell, stride = 1, padding = 5, divisor_override=1, count_include_pad=False) |
||||
|
|
||||
|
##Input |
||||
|
data = sio.loadmat("lid2rad10.mat") |
||||
|
a = data["plot_data"] |
||||
|
x_axis = data["x_axis"] |
||||
|
y_axis = data["y_axis"] |
||||
|
|
||||
|
(size1,size2) = x_axis.shape |
||||
|
|
||||
|
##CFAR processing |
||||
|
a = torch.tensor(a,dtype=torch.float) |
||||
|
|
||||
|
b = guard_avg(a[None,:,:]) |
||||
|
c = CFAR_avg(a[None,:,:]) |
||||
|
|
||||
|
CA_region = (c-b)/(CFAR_cell**2-guard_cell**2) |
||||
|
thrshold = 10 |
||||
|
|
||||
|
cfar_indices = np.squeeze(a)>np.squeeze(CA_region)*thrshold |
||||
|
cfar_indices[[0,1,2,size2-3,size2-2,size2-1],:] = 0 |
||||
|
cfar_indices[:,[0,1,2,size2-3,size2-2,size2-1]] = 0 |
||||
|
|
||||
|
imag = np.zeros((size1,size2)) |
||||
|
imag[cfar_indices] = 1 |
||||
|
|
||||
|
x_points = x_axis[cfar_indices] |
||||
|
y_points = y_axis[cfar_indices] |
||||
|
z_points = np.zeros_like(y_points) |
||||
|
|
||||
|
pc = np.vstack((x_points,y_points,z_points)) |
||||
|
pc = pc.T |
||||
|
|
||||
|
##Plotting |
||||
|
pcd=open3d.open3d.geometry.PointCloud() |
||||
|
pcd.points= open3d.open3d.utility.Vector3dVector(pc) |
||||
|
open3d.open3d.visualization.draw_geometries([pcd]) |
||||
|
|
||||
|
fig, ax = plt.subplots(1,5) |
||||
|
ax[0].imshow(a.numpy()) |
||||
|
ax[1].imshow(np.squeeze(b).numpy()) |
||||
|
ax[2].imshow(np.squeeze(c).numpy()) |
||||
|
ax[3].imshow(np.squeeze(CA_region).numpy()) |
||||
|
ax[4].imshow(imag) |
||||
|
plt.show() |
||||
|
|
||||
|
class CA_CFAR(): |
||||
|
""" |
||||
|
Description: |
||||
|
------------ |
||||
|
Cell Averaging - Constant False Alarm Rate algorithm |
||||
|
Performs an automatic detection on the input range-Doppler matrix with an adaptive thresholding. |
||||
|
The threshold level is determined for each cell in the range-Doppler map with the estimation |
||||
|
of the power level of its surrounding noise. The average power of the noise is estimated on a |
||||
|
rectangular window, that is defined around the CUT (Cell Under Test). In order the mitigate the effect |
||||
|
of the target reflection energy spreading some cells are left out from the calculation in the immediate |
||||
|
vicinity of the CUT. These cells are the guard cells. |
||||
|
The size of the estimation window and guard window can be set with the win_param parameter. |
||||
|
Implementation notes: |
||||
|
--------------------- |
||||
|
Implementation based on https://github.com/petotamas/APRiL |
||||
|
Parameters: |
||||
|
----------- |
||||
|
:param win_param: Parameters of the noise power estimation window |
||||
|
[Est. window length, Est. window width, Guard window length, Guard window width] |
||||
|
:param threshold: Threshold level above the estimated average noise power |
||||
|
:type win_param: python list with 4 elements |
||||
|
:type threshold: float |
||||
|
Return values: |
||||
|
-------------- |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, win_param, threshold, rd_size): |
||||
|
win_width = win_param[0] |
||||
|
win_height = win_param[1] |
||||
|
guard_width = win_param[2] |
||||
|
guard_height = win_param[3] |
||||
|
|
||||
|
# Create window mask with guard cells |
||||
|
self.mask = np.ones((2 * win_height + 1, 2 * win_width + 1), dtype=bool) |
||||
|
self.mask[win_height - guard_height:win_height + 1 + guard_height, win_width - guard_width:win_width + 1 + guard_width] = 0 |
||||
|
|
||||
|
# Convert threshold value |
||||
|
self.threshold = 10 ** (threshold / 10) |
||||
|
|
||||
|
# Number cells within window around CUT; used for averaging operation. |
||||
|
self.num_valid_cells_in_window = signal.convolve2d(np.ones(rd_size, dtype=float), self.mask, mode='same') |
||||
|
|
||||
|
def __call__(self, rd_matrix): |
||||
|
""" |
||||
|
Description: |
||||
|
------------ |
||||
|
Performs the automatic detection on the input range-Doppler matrix. |
||||
|
Implementation notes: |
||||
|
--------------------- |
||||
|
Parameters: |
||||
|
----------- |
||||
|
:param rd_matrix: Range-Doppler map on which the automatic detection should be performed |
||||
|
:type rd_matrix: R x D complex numpy array |
||||
|
Return values: |
||||
|
-------------- |
||||
|
:return hit_matrix: Calculated hit matrix |
||||
|
""" |
||||
|
# Convert range-Doppler map values to power |
||||
|
rd_matrix = np.abs(rd_matrix) ** 2 |
||||
|
|
||||
|
# Perform detection |
||||
|
rd_windowed_sum = signal.convolve2d(rd_matrix, self.mask, mode='same') |
||||
|
rd_avg_noise_power = rd_windowed_sum / self.num_valid_cells_in_window |
||||
|
rd_snr = rd_matrix / rd_avg_noise_power |
||||
|
hit_matrix = rd_snr > self.threshold |
||||
|
|
||||
|
return hit_matrix |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
|
||||
|
sim_adc = sio.loadmat("/home/Kshitiz/semantic_lidar/radar/0099.mat") |
||||
|
sim_adc_data = sim_adc['adc_data'] |
||||
|
sim_adc_data = np.transpose(sim_adc_data, (2,0,1)) |
||||
|
|
||||
|
rangefft_size = 256 |
||||
|
anglefft_size = 256 |
||||
|
dopplerfft_size = 128 |
||||
|
|
||||
|
sim_RDA = convert_adc_to_3d_fft(sim_adc_data ,rangefft_size,dopplerfft_size, anglefft_size, distance_null=False) |
||||
|
|
||||
|
sim_dra = np.transpose(sim_RDA,(1,0,2)) |
||||
|
|
||||
|
limit = 50 |
||||
|
|
||||
|
sim_dra_cart = abs(polar_to_cart(sim_dra,limit=limit,out_pixels=256)) |
||||
|
|
||||
|
fig ,ax = plt.subplots(1,2) |
||||
|
|
||||
|
ax[0].imshow(abs(sim_dra[0,:,:])) |
||||
|
ax[1].imshow(abs(sim_dra_cart[0,:,:])) |
||||
|
|
||||
|
plt.show() |
||||
@ -0,0 +1,118 @@ |
|||||
|
import sys |
||||
|
import os |
||||
|
import numpy as np |
||||
|
|
||||
|
# Add the necessary directories to sys.path to ensure internal imports work |
||||
|
current_dir = os.path.dirname(os.path.abspath(__file__)) |
||||
|
package_root = os.path.join(current_dir, 'e2e_agent_sem_lidar2shenron_package') |
||||
|
utils_root = os.path.join(current_dir, 'sim_radar_utils') |
||||
|
|
||||
|
if current_dir not in sys.path: |
||||
|
sys.path.append(current_dir) |
||||
|
if package_root not in sys.path: |
||||
|
sys.path.append(package_root) |
||||
|
if utils_root not in sys.path: |
||||
|
sys.path.append(utils_root) |
||||
|
|
||||
|
# Now import the modules |
||||
|
from e2e_agent_sem_lidar2shenron_package.lidar import run_lidar |
||||
|
from e2e_agent_sem_lidar2shenron_package.ConfigureRadar import radar |
||||
|
from sim_radar_utils.radar_processor import RadarProcessor |
||||
|
from sim_radar_utils.utils_radar import reformat_adc_shenron |
||||
|
|
||||
|
class ShenronRadarModel: |
||||
|
def __init__(self, radar_type='radarbook'): |
||||
|
""" |
||||
|
Initialize the Shenron Radar Model. |
||||
|
|
||||
|
Args: |
||||
|
radar_type (str): Type of radar to simulate (default: 'radarbook'). |
||||
|
""" |
||||
|
print(f"Initializing ShenronRadarModel with type: {radar_type}") |
||||
|
self.radar_type = radar_type |
||||
|
|
||||
|
# Initialize the hardware radar object |
||||
|
self.radar_obj = radar(radar_type) |
||||
|
self.radar_obj.center = np.array([0.0, 0.0]) # center of radar |
||||
|
self.radar_obj.elv = np.array([0.0]) |
||||
|
|
||||
|
# Synchronize global config used by Signal Processor with the Simulated Hardware |
||||
|
self._sync_configs() |
||||
|
|
||||
|
# Initialize the signal processor (FFT, CFAR, etc.) |
||||
|
self.processor = RadarProcessor() |
||||
|
|
||||
|
# Standard simulation config used by the internal physics engine |
||||
|
self.sim_config = { |
||||
|
'RADAR_TYPE': radar_type, |
||||
|
'INVERT_ANGLE': 0, |
||||
|
'RAY_TRACING': False, |
||||
|
'RADAR_MOVING': False |
||||
|
} |
||||
|
|
||||
|
def _sync_configs(self): |
||||
|
"""Important: Sync global variables in sim_radar_utils to match current radar.obj""" |
||||
|
import sim_radar_utils.utils_radar as ur |
||||
|
|
||||
|
# Update Radar Cfg |
||||
|
ur.radarCfg['N'] = self.radar_obj.N_sample |
||||
|
ur.radarCfg['Np'] = self.radar_obj.chirps |
||||
|
ur.radarCfg['NrChn'] = self.radar_obj.nRx |
||||
|
ur.radarCfg['fStrt'] = self.radar_obj.f |
||||
|
ur.radarCfg['fStop'] = self.radar_obj.f + self.radar_obj.B |
||||
|
ur.radarCfg['Tp'] = self.radar_obj.chirp_rep |
||||
|
|
||||
|
# Update FFT Cfg |
||||
|
ur.fftCfg['NFFT'] = self.radar_obj.N_sample |
||||
|
ur.fftCfg['NFFTVel'] = self.radar_obj.chirps |
||||
|
|
||||
|
print(f"Synced global config: N={ur.radarCfg['N']}, Np={ur.radarCfg['Np']}, Ant={ur.radarCfg['NrChn']}") |
||||
|
|
||||
|
def process(self, semantic_lidar_data): |
||||
|
""" |
||||
|
Process semantic LiDAR data to generate a rich radar point cloud. |
||||
|
|
||||
|
Args: |
||||
|
semantic_lidar_data (np.ndarray): Array of shape [N, 7] |
||||
|
format: [x, y, z, intensity, cos_inc_angle, object_idx, semantic_tag] |
||||
|
|
||||
|
Returns: |
||||
|
np.ndarray: Rich radar point cloud [M, 5] |
||||
|
format: [x, y, z, velocity, magnitude] |
||||
|
""" |
||||
|
if semantic_lidar_data is None or len(semantic_lidar_data) == 0: |
||||
|
return np.empty((0, 5)) |
||||
|
|
||||
|
try: |
||||
|
# 1. Physics-based Signal Generation (FMCW Chirps) |
||||
|
# This generates the raw ADC samples [Np, N, Ant] |
||||
|
adc_data = run_lidar(self.sim_config, semantic_lidar_data) |
||||
|
|
||||
|
# 2. Reformat to match Signal Processor expectations |
||||
|
# Internal logic often needs specific axis ordering |
||||
|
adc_data = reformat_adc_shenron(adc_data) |
||||
|
|
||||
|
# 3. Fast Fourier Transform (FFT) Pipeline |
||||
|
# Range FFT converts time data to range profiles |
||||
|
range_profile = self.processor.cal_range_fft(adc_data) |
||||
|
|
||||
|
# Doppler FFT converts range profiles over time to velocity info |
||||
|
doppler_profile = self.processor.cal_doppler_fft(range_profile) |
||||
|
|
||||
|
# 4. Target Detection and Rich Parameter Extraction |
||||
|
# CFAR detection + Angle of Arrival (AoA) estimation |
||||
|
# returns: rangeAoA, pointcloud ([x, y, z, vel, mag]) |
||||
|
_, rich_pcd = self.processor.convert_to_pcd(doppler_profile) |
||||
|
|
||||
|
return rich_pcd |
||||
|
|
||||
|
except Exception as e: |
||||
|
print(f"Error during Shenron processing: {e}") |
||||
|
import traceback |
||||
|
traceback.print_exc() |
||||
|
return np.empty((0, 5)) |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
# Internal test/demo |
||||
|
model = ShenronRadarModel() |
||||
|
print("Model initialized successfully.") |
||||
@ -0,0 +1,70 @@ |
|||||
|
import numpy as np |
||||
|
from scipy import signal |
||||
|
|
||||
|
class CA_CFAR(): |
||||
|
""" |
||||
|
Description: |
||||
|
------------ |
||||
|
Cell Averaging - Constant False Alarm Rate algorithm |
||||
|
Performs an automatic detection on the input range-Doppler matrix with an adaptive thresholding. |
||||
|
The threshold level is determined for each cell in the range-Doppler map with the estimation |
||||
|
of the power level of its surrounding noise. The average power of the noise is estimated on a |
||||
|
rectangular window, that is defined around the CUT (Cell Under Test). In order the mitigate the effect |
||||
|
of the target reflection energy spreading some cells are left out from the calculation in the immediate |
||||
|
vicinity of the CUT. These cells are the guard cells. |
||||
|
The size of the estimation window and guard window can be set with the win_param parameter. |
||||
|
Implementation notes: |
||||
|
--------------------- |
||||
|
Implementation based on https://github.com/petotamas/APRiL |
||||
|
Parameters: |
||||
|
----------- |
||||
|
:param win_param: Parameters of the noise power estimation window |
||||
|
[Est. window length, Est. window width, Guard window length, Guard window width] |
||||
|
:param threshold: Threshold level above the estimated average noise power |
||||
|
:type win_param: python list with 4 elements |
||||
|
:type threshold: float |
||||
|
Return values: |
||||
|
-------------- |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, win_param, threshold, rd_size): |
||||
|
win_width = win_param[0] |
||||
|
win_height = win_param[1] |
||||
|
guard_width = win_param[2] |
||||
|
guard_height = win_param[3] |
||||
|
|
||||
|
# Create window mask with guard cells |
||||
|
self.mask = np.ones((2 * win_height + 1, 2 * win_width + 1), dtype=bool) |
||||
|
self.mask[win_height - guard_height:win_height + 1 + guard_height, win_width - guard_width:win_width + 1 + guard_width] = 0 |
||||
|
|
||||
|
# Convert threshold value |
||||
|
self.threshold = 10 ** (threshold / 10) |
||||
|
|
||||
|
# Number cells within window around CUT; used for averaging operation. |
||||
|
self.num_valid_cells_in_window = signal.convolve2d(np.ones(rd_size, dtype=float), self.mask, mode='same') |
||||
|
|
||||
|
def __call__(self, rd_matrix): |
||||
|
""" |
||||
|
Description: |
||||
|
------------ |
||||
|
Performs the automatic detection on the input range-Doppler matrix. |
||||
|
Implementation notes: |
||||
|
--------------------- |
||||
|
Parameters: |
||||
|
----------- |
||||
|
:param rd_matrix: Range-Doppler map on which the automatic detection should be performed |
||||
|
:type rd_matrix: R x D complex numpy array |
||||
|
Return values: |
||||
|
-------------- |
||||
|
:return hit_matrix: Calculated hit matrix |
||||
|
""" |
||||
|
# Convert range-Doppler map values to power |
||||
|
rd_matrix = np.abs(rd_matrix) ** 2 |
||||
|
|
||||
|
# Perform detection |
||||
|
rd_windowed_sum = signal.convolve2d(rd_matrix, self.mask, mode='same') |
||||
|
rd_avg_noise_power = rd_windowed_sum / self.num_valid_cells_in_window |
||||
|
rd_snr = rd_matrix / rd_avg_noise_power |
||||
|
hit_matrix = rd_snr > self.threshold |
||||
|
|
||||
|
return hit_matrix |
||||
@ -0,0 +1,101 @@ |
|||||
|
radar: |
||||
|
fStrt: 24.0e9 # start frequency |
||||
|
fStop: 24.25e9 # stop frequency |
||||
|
TRampUp: 100e-6 # ramp-up time of chirp |
||||
|
TInt: 100e-3 # inter-frame interval |
||||
|
Tp: 1000e-6 # inter-chirp time |
||||
|
N: 256 # samples per chirp |
||||
|
IniTim: 100e-3 # initial delay before collection of samples |
||||
|
IniEve: 0 # start automatically after IniTim |
||||
|
# TxSeq: [1, 2] |
||||
|
TxSeq: [1] # antenna activation sequence |
||||
|
Np: 3 #128 # number of chirps in one frame |
||||
|
AntIdx: [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15] |
||||
|
# AntIdx: [0, 1, 2, 3, 4, 5, 6, 7] |
||||
|
NrChn: 86 #16 |
||||
|
|
||||
|
|
||||
|
FFT: |
||||
|
NFFT: 256 #512 |
||||
|
NFFTVel: 128 |
||||
|
NFFTAnt: 256 #128 |
||||
|
RMin: 0 |
||||
|
RMax: 1000 |
||||
|
c0: 299792458 |
||||
|
|
||||
|
|
||||
|
Cluster: |
||||
|
xlim: [-20, 20] |
||||
|
ylim: [0, 40] |
||||
|
zlim: [0, 2] |
||||
|
|
||||
|
|
||||
|
ROS: |
||||
|
Topic: |
||||
|
radarCfg: '/radarbook/config' |
||||
|
radarRaw: '/radarbook/data' |
||||
|
radarPCD: '/radarbook/pcd' |
||||
|
cameraRGB: '/camera/color/image_raw' |
||||
|
cameraDepth: '/camera/depth/image_rect_raw' |
||||
|
lidarPCD: '/os1_cloud_node/points' |
||||
|
SavePath: |
||||
|
radarCfg: '/radar_cfg.json' |
||||
|
radarRaw: '/radar_data/' |
||||
|
radarPCD: '/radar_pcd/' |
||||
|
cameraRGB: '/rgb_image/' |
||||
|
# cameraRGB: '/camera_original/' |
||||
|
cameraDepth: '/depth_image/' |
||||
|
# lidarPCD: '/lidar_pcd/' |
||||
|
lidarPCD: '/raw_lidar/' |
||||
|
timeStamp: '/time_stamp.npy' |
||||
|
|
||||
|
CFAR: |
||||
|
win_param: [9, 9, 3, 3] |
||||
|
threshold: 20 |
||||
|
|
||||
|
|
||||
|
Visualize: |
||||
|
rangeAoA: |
||||
|
title: "Range-Angle Plot" |
||||
|
xLabel: "Range" |
||||
|
xUnit: "m" |
||||
|
yLabel: "Angle" |
||||
|
yUnit: "Degree" |
||||
|
winSize: [500, 400] |
||||
|
pos: [50, 50] |
||||
|
rangeDoppler: |
||||
|
title: "Range-Velocity Plot" |
||||
|
xLabel: "Range" |
||||
|
xUnit: "m" |
||||
|
yLabel: "Velocity" |
||||
|
yUnit: "m/s" |
||||
|
winSize: [500, 400] |
||||
|
pos: [600, 50] |
||||
|
radarPCD: |
||||
|
title: "Radar Point Cloud" |
||||
|
xLabel: "Azimuth Position" |
||||
|
xUnit: "m" |
||||
|
yLabel: "Depth Position" |
||||
|
yUnit: "m" |
||||
|
winSize: [500, 400] |
||||
|
pos: [1200, 50] |
||||
|
penWidth: 5 |
||||
|
penColor: 'r' |
||||
|
symbol: 'o' |
||||
|
dotSize: 1 |
||||
|
cameraRGB: |
||||
|
title: "RGB Image" |
||||
|
winSize: [640, 360] |
||||
|
pos: [50, 500] |
||||
|
cameraDepth: |
||||
|
title: "Depth Image" |
||||
|
winSize: [640, 360] |
||||
|
pos: [600, 500] |
||||
|
lidarPCD: |
||||
|
title: "Lidar Point Cloud" |
||||
|
xRange: [-30, 30] |
||||
|
yRange: [-30, 30] |
||||
|
winSize: [500, 400] |
||||
|
pos: [1200, 500] |
||||
|
dotSize: 1 |
||||
|
|
||||
@ -0,0 +1,116 @@ |
|||||
|
import numpy as np |
||||
|
import os |
||||
|
import pickle as pkl |
||||
|
from PIL import Image |
||||
|
import json |
||||
|
import yaml |
||||
|
# import ffmpeg |
||||
|
import matplotlib.pyplot as plt |
||||
|
# import cv2 |
||||
|
import sys |
||||
|
import glob |
||||
|
|
||||
|
# sys.path.append('/radar-imaging-dataset/mmfn_project/mmfn_scripts/team_code/mmfn_utils/sim_radar_utils/') |
||||
|
# sys.path.append('/radar-imaging-dataset/mmfn_project/mmfn_scripts/team_code/e2e_agent_sem_lidar2shenron_package/') |
||||
|
|
||||
|
from e2e_agent_sem_lidar2shenron_package.lidar import run_lidar |
||||
|
|
||||
|
from sim_radar_utils.radar_processor import RadarProcessor |
||||
|
from sim_radar_utils.utils_radar import * |
||||
|
from sim_radar_utils.transform_utils import * |
||||
|
|
||||
|
def convert2D_img_func(sim_radar, limit = 75): |
||||
|
''' |
||||
|
converts the 3D radar raw data to 2d range-angle image of dimension 256X256 |
||||
|
''' |
||||
|
radarProcessor = RadarProcessor() |
||||
|
# radar = sim_radar[0] |
||||
|
radar = sim_radar |
||||
|
|
||||
|
chirpLevelData = reformat_adc_shenron(radar) |
||||
|
rangeProfile = radarProcessor.cal_range_fft(chirpLevelData) |
||||
|
# dopplerProfile = radarProcessor.cal_doppler_fft(rangeProfile) |
||||
|
aoaProfile = radarProcessor.cal_angle_fft(rangeProfile) |
||||
|
range_angle = get_range_angle(aoaProfile) |
||||
|
cart_cord = polar_to_cart(range_angle, limit = limit) |
||||
|
# print(cart_cord_man.shape) |
||||
|
# return [cart_cord] |
||||
|
return cart_cord |
||||
|
|
||||
|
def convert_sem_lidar_2D_img_func(sim_radar, invert_angle, limit = 75): |
||||
|
''' |
||||
|
converts the semantic_lidar data to the range angle 2D image of dimension 256X256 |
||||
|
''' |
||||
|
|
||||
|
# convert semantic lidar to raw 3d radar data |
||||
|
|
||||
|
# with open('/radar-imaging-dataset/mmfn_project/mmfn_scripts/team_code/e2e_agent_sem_lidar2shenron_package/simulator_configs.yaml', 'r') as f: |
||||
|
# sim_config = yaml.safe_load(f) |
||||
|
|
||||
|
# with open(glob.glob('../carla_garage_radar/team_code/e2e_agent_sem_lidar2shenron_package/simulator_configs.yaml'), 'r') as f: |
||||
|
# sim_config = yaml.safe_load(f) |
||||
|
|
||||
|
with open('/radar-imaging-dataset/carla_garage_radar/team_code/e2e_agent_sem_lidar2shenron_package/simulator_configs.yaml', 'r') as f: |
||||
|
sim_config = yaml.safe_load(f) |
||||
|
|
||||
|
sim_config['INVERT_ANGLE'] = invert_angle |
||||
|
|
||||
|
radar = run_lidar(sim_config, sim_radar) |
||||
|
|
||||
|
radarProcessor = RadarProcessor() |
||||
|
# radar = sim_radar[0] |
||||
|
# radar = sim_radar |
||||
|
|
||||
|
chirpLevelData = reformat_adc_shenron(radar) |
||||
|
rangeProfile = radarProcessor.cal_range_fft(chirpLevelData) |
||||
|
# dopplerProfile = radarProcessor.cal_doppler_fft(rangeProfile) |
||||
|
aoaProfile = radarProcessor.cal_angle_fft(rangeProfile) |
||||
|
range_angle = get_range_angle(aoaProfile) |
||||
|
cart_cord = polar_to_cart(range_angle, limit = limit) |
||||
|
# print(cart_cord_man.shape) |
||||
|
# return [cart_cord] |
||||
|
return cart_cord |
||||
|
|
||||
|
def main(): |
||||
|
radarProcessor = RadarProcessor() |
||||
|
|
||||
|
# with open('/mnt/intA-ssdr1-4tb/satyam53/mmfn/data1/pro_train_fnss_0702/0.pkl', 'rb') as f: |
||||
|
# pkl_data = pkl.load(f) |
||||
|
|
||||
|
# print(type(pkl_data)) |
||||
|
# print(pkl_data.keys()) |
||||
|
|
||||
|
# sim_radar = pkl_data['sim_radar'] |
||||
|
# # sim_radar = pkl_data['vectormaps'] |
||||
|
|
||||
|
# print(type(sim_radar[0])) |
||||
|
# print(len(sim_radar)) |
||||
|
# print(sim_radar[0]) |
||||
|
# radar_man = sim_radar[0] |
||||
|
|
||||
|
|
||||
|
# # radar_man = np.load(f'{path_sim_man}/{files_sim_manual[i]}') |
||||
|
# chirpLevelData = reformat_adc_shenron(radar_man) |
||||
|
# rangeProfile = radarProcessor.cal_range_fft(chirpLevelData) |
||||
|
# # dopplerProfile = radarProcessor.cal_doppler_fft(rangeProfile) |
||||
|
# aoaProfile = radarProcessor.cal_angle_fft(rangeProfile) |
||||
|
# range_angle = get_range_angle(aoaProfile) |
||||
|
# cart_cord_man = polar_to_cart(range_angle, limit = 50) |
||||
|
# print(cart_cord_man.shape) |
||||
|
|
||||
|
# fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15,5)) |
||||
|
# # ax1.imshow(cart_cord, aspect='auto') |
||||
|
# # ax1.set_title('radar data simulated using shenron') |
||||
|
|
||||
|
# ax2.imshow(cart_cord_man, aspect='auto') |
||||
|
# ax2.set_title('radar data simulated manually') |
||||
|
# # plt.show() |
||||
|
# # ax3.scatter(lidar[:, 0], lidar[:, 1], s = 0.1, c = lidar[:, 5]) |
||||
|
# # ax3.set_title('lidar data') |
||||
|
# plt.tight_layout() |
||||
|
# plt.show() |
||||
|
# # plt.savefig(f'{save_path}/{i}.png') |
||||
|
# plt.close() |
||||
|
|
||||
|
if __name__=="__main__": |
||||
|
main() |
||||
@ -0,0 +1,96 @@ |
|||||
|
import numpy as np |
||||
|
import yaml |
||||
|
import sys |
||||
|
import glob |
||||
|
|
||||
|
# sys.path.append(glob.glob('../carla_garage_radar/team_code/sim_radar_utils/')) |
||||
|
sys.path.append('radar-imaging-dataset/carla_garage_radar/team_code/sim_radar_utils/') |
||||
|
# sys.path.append('radar-imaging-dataset/carla_garage_radar/team_code/') |
||||
|
|
||||
|
|
||||
|
# sys.path.append("/radar-imaging-dataset/mmfn_project/mmfn_scripts/team_code/sim_radar_utils/") |
||||
|
|
||||
|
from scipy import signal |
||||
|
from scipy.fft import fft, fftshift |
||||
|
from sim_radar_utils.cfar_detector import CA_CFAR |
||||
|
from sim_radar_utils.utils_radar import * |
||||
|
|
||||
|
|
||||
|
cfarCfg = config['CFAR'] |
||||
|
|
||||
|
|
||||
|
class RadarProcessor: |
||||
|
def __init__(self): |
||||
|
# radar data will be shaped as (# of chirp, # of sample, # of antenna) |
||||
|
# self.rangeWin = np.tile(signal.windows.hann(radarCfg['N']), (radarCfg['Np'], len(radarCfg['AntIdx']), 1)) |
||||
|
self.rangeWin = np.tile(signal.windows.hann(radarCfg['N']), (radarCfg['Np'], radarCfg['NrChn'], 1)) |
||||
|
self.rangeWin = np.transpose(self.rangeWin, (2, 0, 1)) |
||||
|
|
||||
|
# self.velWin = np.tile(signal.windows.hann(radarCfg['Np']), (radarCfg['N'], len(radarCfg['AntIdx']), 1)) |
||||
|
self.velWin = np.tile(signal.windows.hann(radarCfg['Np']), (radarCfg['N'], radarCfg['NrChn'], 1)) |
||||
|
self.velWin = np.transpose(self.velWin, (0, 2, 1)) |
||||
|
|
||||
|
rangeRes = fftCfg['c0'] / (2*(radarCfg['fStop'] - radarCfg['fStrt'])) |
||||
|
self.rangeAxis = np.arange(0, fftCfg['NFFT'])*radarCfg['N']/fftCfg['NFFT']*rangeRes |
||||
|
self.RMinIdx = np.argmin(np.abs(self.rangeAxis - fftCfg['RMin'])) |
||||
|
self.RMaxIdx = np.argmin(np.abs(self.rangeAxis - fftCfg['RMax'])) |
||||
|
self.rangeAxis = self.rangeAxis[self.RMinIdx:self.RMaxIdx+1] |
||||
|
|
||||
|
self.angleAxis = np.arcsin(2 * np.arange(-fftCfg['NFFTAnt']/2, fftCfg['NFFTAnt']/2) / fftCfg['NFFTAnt']) |
||||
|
|
||||
|
fc = (radarCfg['fStop'] + radarCfg['fStrt'])/2 |
||||
|
self.velAxis = np.arange(-fftCfg['NFFTVel']//2, fftCfg['NFFTVel']//2)/fftCfg['NFFTVel']*(1/radarCfg['Tp'])*fftCfg['c0']/(2*fc) |
||||
|
|
||||
|
self.cfar = CA_CFAR(win_param=cfarCfg['win_param'], |
||||
|
threshold=cfarCfg['threshold'], |
||||
|
rd_size=(self.RMaxIdx - self.RMinIdx + 1, fftCfg['NFFTVel'])) |
||||
|
|
||||
|
def cal_range_fft(self, data): |
||||
|
'''apply range window and doppler window and apply fft on each sample to get range profile''' |
||||
|
return fft(data * self.rangeWin * self.velWin, fftCfg['NFFT'], 0) |
||||
|
|
||||
|
def cal_doppler_fft(self, rangeProfile): |
||||
|
'''apply fft on each chirp to get doppler profile''' |
||||
|
return fftshift(fft(rangeProfile[self.RMinIdx:self.RMaxIdx+1, :], fftCfg['NFFTVel'], 1), 1) |
||||
|
|
||||
|
def cal_angle_fft(self, rangeProfile): |
||||
|
'''# apply fft on each antenna to get angle profile''' |
||||
|
return fftshift(fft(rangeProfile[self.RMinIdx:self.RMaxIdx+1, :], fftCfg['NFFTAnt'], 2), 2) |
||||
|
|
||||
|
def convert_to_pcd(self, dopplerProfile): |
||||
|
avgDopplerProfile = np.squeeze(np.mean(dopplerProfile, 2)) |
||||
|
|
||||
|
# detect useful peaks using CFAR |
||||
|
detections = self.cfar(np.square(np.abs(avgDopplerProfile))) |
||||
|
|
||||
|
# identify range bin and velocity bin for each detected point |
||||
|
rowSel, colSel = np.nonzero(detections) |
||||
|
|
||||
|
# pointSel = np.zeros(shape=(len(rowSel), len(radarCfg['AntIdx'])), dtype=complex) |
||||
|
pointSel = np.zeros(shape=(len(rowSel), radarCfg['NrChn']), dtype=complex) |
||||
|
for i, (row, col) in enumerate(zip(rowSel, colSel)): |
||||
|
pointSel[i] = dopplerProfile[row, col, :] |
||||
|
|
||||
|
# calculate range and anlge value |
||||
|
rangeVals = self.rangeAxis[rowSel] |
||||
|
aoaProfile = fftshift(fft(pointSel, fftCfg['NFFTAnt'], 1), 1) |
||||
|
angleIdx = np.argmax(np.abs(aoaProfile), axis=1) |
||||
|
angleVals = self.angleAxis[angleIdx] |
||||
|
|
||||
|
# Extract velocity from velocity axis |
||||
|
velVals = self.velAxis[colSel] |
||||
|
|
||||
|
# Extract peak magnitude |
||||
|
magVals = np.abs(avgDopplerProfile[rowSel, colSel]) |
||||
|
|
||||
|
rangeAoA = np.transpose(np.stack([rangeVals, angleVals])) |
||||
|
|
||||
|
# convert Range-AoA to pointcloud |
||||
|
# Format: [x, y, z, velocity, magnitude] |
||||
|
x = rangeVals * np.cos(angleVals) |
||||
|
y = rangeVals * np.sin(angleVals) |
||||
|
z = np.zeros_like(x) # 2D radar assumption, z=0 |
||||
|
|
||||
|
pointcloud = np.stack([x, y, z, velVals, magVals], axis=1) |
||||
|
|
||||
|
return rangeAoA, pointcloud |
||||
@ -0,0 +1,91 @@ |
|||||
|
import numpy as np |
||||
|
import torch |
||||
|
|
||||
|
def get_range_angle(raw_radar_data): |
||||
|
range_angle = np.abs(raw_radar_data).sum(axis=1) |
||||
|
return range_angle |
||||
|
|
||||
|
def cart2polar(x, y, in_pixels, range_res): |
||||
|
# Dividing by range_res to get the range in pixels |
||||
|
r = np.sqrt(x ** 2 + y ** 2) * (1 / range_res) |
||||
|
|
||||
|
#Assuming uniform theta resolution |
||||
|
theta = np.arctan2(y, x) |
||||
|
theta_px = np.sin(theta) * (in_pixels / 2) + in_pixels / 2 |
||||
|
return r, theta_px |
||||
|
|
||||
|
def polar_to_cart(RATensor, range_res = 150 / 256, in_pixels = 256, limit = 150, out_pixels = 256): |
||||
|
|
||||
|
""" |
||||
|
convert a polar range angle tensor to cartesian array |
||||
|
Input: |
||||
|
RATensor: NxM |
||||
|
Output: |
||||
|
cart_RATensor: NxM |
||||
|
""" |
||||
|
|
||||
|
X, Y = np.meshgrid(np.linspace(-limit/2, limit/2, out_pixels), np.linspace(-limit/2, limit/2, out_pixels)) |
||||
|
# Rotating the axes to make the plane point upwards |
||||
|
X = np.rot90(X) |
||||
|
Y = np.rot90(Y) |
||||
|
|
||||
|
R_samp, Theta_samp = cart2polar(X, Y, in_pixels, range_res) |
||||
|
|
||||
|
R_samp = R_samp.astype(int) |
||||
|
Theta_samp = Theta_samp.astype(int) |
||||
|
|
||||
|
R_samp[(R_samp > (in_pixels - 1))] = in_pixels - 1 |
||||
|
R_samp[(R_samp < 0)] = 0 |
||||
|
Theta_samp[(Theta_samp > (in_pixels - 1))] = in_pixels - 1 |
||||
|
Theta_samp[(Theta_samp < 0)] = 0 |
||||
|
|
||||
|
if RATensor.ndim >2: |
||||
|
polar_img = RATensor[...,R_samp,Theta_samp] |
||||
|
else: |
||||
|
polar_img = RATensor[R_samp, Theta_samp] |
||||
|
|
||||
|
# Applying mask to extract the front-side view only |
||||
|
if out_pixels % 2 == 0: |
||||
|
slice_idx = int(out_pixels / 2) |
||||
|
else: |
||||
|
slice_idx = int(out_pixels / 2) + 1 |
||||
|
|
||||
|
polar_img[slice_idx:, :] = 0 |
||||
|
|
||||
|
return polar_img |
||||
|
|
||||
|
# def polar_to_cart(RATensor, range_res = 150/256, in_pixels = 256, limit = 150, out_pixels = 256): |
||||
|
|
||||
|
# """ |
||||
|
# convert a polar range angle tensor to cartesian array |
||||
|
# Input: |
||||
|
# RATensor: NxM |
||||
|
# Output: |
||||
|
# cart_RATensor: NxM |
||||
|
# """ |
||||
|
|
||||
|
# X, Y = np.meshgrid(np.linspace(0, limit, out_pixels), np.linspace(-limit/2, limit/2, out_pixels)) |
||||
|
|
||||
|
# R_samp, Theta_samp = cart2polar(X, Y, in_pixels, range_res) |
||||
|
|
||||
|
# R_samp = R_samp.astype(int) |
||||
|
# Theta_samp = Theta_samp.astype(int) |
||||
|
|
||||
|
# R_samp[(R_samp>(in_pixels-1))] = 0 |
||||
|
# R_samp[(R_samp<0)] = 0 |
||||
|
# Theta_samp[(Theta_samp>(in_pixels-1))] = 0 |
||||
|
# Theta_samp[(Theta_samp<0)] = 0 |
||||
|
|
||||
|
# if RATensor.ndim >2: |
||||
|
# polar_img = RATensor[...,R_samp,Theta_samp] |
||||
|
# # polar_img = torch.reshape(polar_img,(RATensor.shape[0],RATensor.shape[1],out_pixels,out_pixels)) |
||||
|
# if torch.is_tensor(RATensor): |
||||
|
# polar_img = torch.transpose(polar_img,-1,-2) |
||||
|
# else: |
||||
|
# polar_img = np.swapaxes(polar_img,-2,-1) |
||||
|
# else: |
||||
|
# polar_img = RATensor[R_samp,Theta_samp] |
||||
|
# # polar_img = torch.reshape(polar_img,(out_pixels,out_pixels)) |
||||
|
# polar_img = polar_img.T |
||||
|
|
||||
|
# return polar_img |
||||
@ -0,0 +1,112 @@ |
|||||
|
import sys, os |
||||
|
import yaml |
||||
|
import numpy as np |
||||
|
# import open3d |
||||
|
from pyntcloud import PyntCloud |
||||
|
from PIL import Image |
||||
|
import matplotlib.pyplot as plt |
||||
|
import json |
||||
|
|
||||
|
|
||||
|
with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config.yaml'), 'r') as f: |
||||
|
config = yaml.safe_load(f) |
||||
|
radarCfg = config['radar'] |
||||
|
savePath = config['ROS']['SavePath'] |
||||
|
fftCfg = config['FFT'] |
||||
|
clstCfg = config['Cluster'] |
||||
|
radarCfg['fStrt'] = float(radarCfg['fStrt']) |
||||
|
radarCfg['fStop'] = float(radarCfg['fStop']) |
||||
|
radarCfg['TRampUp'] = float(radarCfg['TRampUp']) |
||||
|
radarCfg['TInt'] = float(radarCfg['TInt']) |
||||
|
radarCfg['Tp'] = float(radarCfg['Tp']) |
||||
|
radarCfg['IniTim'] = float(radarCfg['IniTim']) |
||||
|
|
||||
|
|
||||
|
def config_save_path(dirc): |
||||
|
for node in savePath.keys(): |
||||
|
savePath[node] = dirc + savePath[node] |
||||
|
|
||||
|
|
||||
|
def create_folder(activeNode): |
||||
|
for node in activeNode: |
||||
|
if node != 'timeStamp' and node != 'radarCfg': |
||||
|
os.makedirs(savePath[node], exist_ok=True) |
||||
|
|
||||
|
|
||||
|
def read_time_stamp(): |
||||
|
return np.load(savePath['timeStamp']) |
||||
|
|
||||
|
|
||||
|
def load_radar_cfg(): |
||||
|
with open(savePath['radarCfg'], 'r') as f: |
||||
|
newCfg = json.load(f) |
||||
|
for key in radarCfg.keys(): |
||||
|
radarCfg[key] = newCfg[key] |
||||
|
|
||||
|
#reformat adc shenron |
||||
|
def reformat_adc_shenron(data): |
||||
|
data = np.swapaxes(data, 1, 2) |
||||
|
# print(data.shape) |
||||
|
chirpLevelData = data |
||||
|
# return np.transpose(chirpLevelData[:, :, radarCfg['AntIdx']], (1, 0, 2)) |
||||
|
return np.transpose(chirpLevelData[:, :, :], (1, 0, 2)) |
||||
|
|
||||
|
|
||||
|
def reformat_adc(data): |
||||
|
data = np.reshape(data, (radarCfg['Np'], len(radarCfg['TxSeq']), radarCfg['N'], radarCfg['NrChn'])) |
||||
|
chirpLevelData = data[:, 0, :, :] |
||||
|
for j in range(1, len(radarCfg['TxSeq'])): |
||||
|
chirpLevelData = np.concatenate((chirpLevelData, data[:, j, :, :]), axis=-1) |
||||
|
return np.transpose(chirpLevelData[:, :, radarCfg['AntIdx']], (1, 0, 2)) |
||||
|
|
||||
|
|
||||
|
def read_radar_data(frame): |
||||
|
radarData = np.load(savePath['radarRaw'] + f'{frame:04}.npy') |
||||
|
chirpLevelData = reformat_adc(radarData) |
||||
|
return chirpLevelData |
||||
|
|
||||
|
#read radar data shenron |
||||
|
def read_radar_data_shenron(frame): |
||||
|
radarData = np.load(savePath['radarRaw'] + f'{frame:04}.npy') |
||||
|
chirpLevelData = reformat_adc_shenron(radarData) |
||||
|
return chirpLevelData |
||||
|
|
||||
|
|
||||
|
def read_radar_pcd(frame): |
||||
|
points = np.load(savePath['radarPCD'] + f'{frame:04}.npy') |
||||
|
return points |
||||
|
|
||||
|
|
||||
|
def read_camera_img(frame): |
||||
|
rgbImg = np.asarray(Image.open(savePath['cameraRGB'] + f'{frame:04}.png')) |
||||
|
depthImg = np.asarray(Image.open(savePath['cameraDepth'] + f'{frame:04}.png')) |
||||
|
return rgbImg, depthImg |
||||
|
|
||||
|
|
||||
|
def read_lidar_pcd(frame): |
||||
|
pcd = PyntCloud.from_file(savePath['lidarPCD'] + f'{frame:04}.pts') |
||||
|
return pcd.xyz |
||||
|
|
||||
|
|
||||
|
def plot_range_doppler(vis, dopplerProfile, rangeAxis, velAxis): |
||||
|
dopplerProfiledB = 20*np.log10(np.abs(dopplerProfile[:, :, 0])) |
||||
|
dopplerProfileMax = np.max(dopplerProfiledB) |
||||
|
dopplerProfileNorm = dopplerProfiledB - dopplerProfileMax |
||||
|
dopplerProfileNorm[dopplerProfileNorm < -30] = -30 |
||||
|
vis.plot_depth_fig( |
||||
|
data=dopplerProfileNorm, |
||||
|
pos=[velAxis[0], fftCfg['RMin']], |
||||
|
scale=[(velAxis[-1] - velAxis[0])/fftCfg['NFFTVel'], (fftCfg['RMax'] - fftCfg['RMin'])/len(rangeAxis)] |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def plot_range_aoa(vis, aoaProfile, rangeAxis): |
||||
|
aoaProfiledB = 20*np.log10(np.abs(aoaProfile[:, 1, :])) |
||||
|
aoaProfileMax = np.max(aoaProfiledB) |
||||
|
aoaProfileNorm = aoaProfiledB - aoaProfileMax |
||||
|
aoaProfileNorm[aoaProfileNorm < -30] = -30 |
||||
|
vis.plot_depth_fig( |
||||
|
data=aoaProfileNorm, |
||||
|
pos=[-1, fftCfg['RMin']], |
||||
|
scale=[2.0/fftCfg['NFFTAnt']/np.pi*180, (fftCfg['RMax'] - fftCfg['RMin'])/len(rangeAxis)] |
||||
|
) |
||||
@ -0,0 +1,95 @@ |
|||||
|
import os |
||||
|
import sys |
||||
|
import numpy as np |
||||
|
import tqdm |
||||
|
from pathlib import Path |
||||
|
|
||||
|
# Add project root and ISOLATE paths |
||||
|
project_root = Path(__file__).parent.parent |
||||
|
sys.path.append(str(project_root)) |
||||
|
sys.path.append(str(project_root / 'scripts' / 'ISOLATE')) |
||||
|
|
||||
|
# Import the model wrapper |
||||
|
try: |
||||
|
from scripts.ISOLATE.model_wrapper import ShenronRadarModel |
||||
|
except ImportError as e: |
||||
|
print(f"Error: Failed to import ShenronRadarModel. Ensure scripts/ISOLATE/model_wrapper.py exists. ({e})") |
||||
|
sys.exit(1) |
||||
|
|
||||
|
def process_session(session_path): |
||||
|
print(f"\n>>> Processing session: {session_path.name}") |
||||
|
|
||||
|
lidar_dir = session_path / "lidar" |
||||
|
if not lidar_dir.exists(): |
||||
|
print(f" [SKIP] No 'lidar' folder found.") |
||||
|
return |
||||
|
|
||||
|
# Find all .npy files in lidar/ |
||||
|
lidar_files = sorted(list(lidar_dir.glob("*.npy"))) |
||||
|
if not lidar_files: |
||||
|
print(f" [SKIP] No .npy files in 'lidar' folder.") |
||||
|
return |
||||
|
|
||||
|
output_dir = session_path / "shenron_radar" |
||||
|
output_dir.mkdir(exist_ok=True) |
||||
|
|
||||
|
# Initialize the model once per session |
||||
|
print(f" Initializing ShenronRadarModel...") |
||||
|
model = ShenronRadarModel(radar_type='radarbook') |
||||
|
|
||||
|
print(f" Generating Shenron Radar data for {len(lidar_files)} frames...") |
||||
|
|
||||
|
for lidar_file in tqdm.tqdm(lidar_files, desc=" Simulating Radar", unit="frame"): |
||||
|
try: |
||||
|
# 1. Load Semantic LiDAR data |
||||
|
# Expected raw: [x, y, z, cos, obj, tag] (6 cols) |
||||
|
# Expected Shenron input: [x, y, z, intensity, cos, obj, tag] (7 cols) |
||||
|
data = np.load(lidar_file) |
||||
|
|
||||
|
if data.shape[1] == 6: |
||||
|
# Pad with a dummy intensity column at index 3 |
||||
|
# This aligns 'tag' to index 6 as expected by our lidar.py mapping |
||||
|
padded_data = np.zeros((data.shape[0], 7)) |
||||
|
padded_data[:, 0:3] = data[:, 0:3] # x, y, z |
||||
|
padded_data[:, 4:7] = data[:, 3:6] # cos, obj, tag |
||||
|
data = padded_data |
||||
|
|
||||
|
# 2. Process through the physics-based model |
||||
|
# returns rich PCD: [M, 5] (x, y, z, velocity, magnitude) |
||||
|
rich_pcd = model.process(data) |
||||
|
|
||||
|
# 3. Save to disk |
||||
|
output_file = output_dir / lidar_file.name |
||||
|
np.save(output_file, rich_pcd) |
||||
|
|
||||
|
except Exception as e: |
||||
|
print(f"\n [ERROR] Failed to process {lidar_file.name}: {e}") |
||||
|
|
||||
|
def main(): |
||||
|
data_root = project_root / "data" |
||||
|
if not data_root.exists(): |
||||
|
print(f"Error: {data_root} not found.") |
||||
|
return |
||||
|
|
||||
|
# Get all session folders |
||||
|
sessions = sorted([d for d in data_root.iterdir() if d.is_dir()]) |
||||
|
|
||||
|
if not sessions: |
||||
|
print("No simulation sessions found in data/.") |
||||
|
return |
||||
|
|
||||
|
print(f"Found {len(sessions)} sessions.") |
||||
|
|
||||
|
for session in sessions: |
||||
|
# Check if the session has frames.jsonl to confirm it's a valid data folder |
||||
|
if (session / "frames.jsonl").exists(): |
||||
|
process_session(session) |
||||
|
else: |
||||
|
print(f"Skipping {session.name} (no frames.jsonl found).") |
||||
|
|
||||
|
print("\n" + "="*50) |
||||
|
print("SHENRON BATCH PROCESSING COMPLETE!") |
||||
|
print("="*50) |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
||||
@ -0,0 +1,98 @@ |
|||||
|
import sys |
||||
|
import os |
||||
|
import numpy as np |
||||
|
import time |
||||
|
|
||||
|
# Add project root and ISOLATE paths to sys.path |
||||
|
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
||||
|
sys.path.append(project_root) |
||||
|
|
||||
|
# Import the model wrapper |
||||
|
try: |
||||
|
from scripts.ISOLATE.model_wrapper import ShenronRadarModel |
||||
|
print(">>> Successfully imported ShenronRadarModel") |
||||
|
except ImportError as e: |
||||
|
print(f">>> Failed to import ShenronRadarModel: {e}") |
||||
|
sys.exit(1) |
||||
|
|
||||
|
def run_smoke_test(): |
||||
|
print("\n" + "="*50) |
||||
|
print("SHENRON MODEL SMOKE TEST") |
||||
|
print("="*50) |
||||
|
|
||||
|
success = True |
||||
|
|
||||
|
try: |
||||
|
# 1. Initialize the Model |
||||
|
print("\n[Step 1] Initializing ShenronRadarModel...") |
||||
|
start_time = time.time() |
||||
|
model = ShenronRadarModel(radar_type='radarbook') |
||||
|
print(f"Done in {time.time() - start_time:.2f}s") |
||||
|
|
||||
|
# 2. Prepare Synthetic Semantic LiDAR Data |
||||
|
# Format: [x, y, z, intensity, cos_inc_angle, object_idx, semantic_tag] |
||||
|
print("\n[Step 2] Preparing synthetic semantic LiDAR data...") |
||||
|
# Create a "vehicle" (tag 10) 15 meters ahead, slightly to the left |
||||
|
num_points = 100 |
||||
|
data = np.zeros((num_points, 7)) |
||||
|
|
||||
|
# Geometry |
||||
|
data[:, 0] = np.random.uniform(14.5, 15.5, num_points) # x (forward) |
||||
|
data[:, 1] = np.random.uniform(-2.0, -1.0, num_points) # y (left) |
||||
|
data[:, 2] = np.random.uniform(0.5, 1.5, num_points) # z (up) |
||||
|
|
||||
|
# Physical properties |
||||
|
data[:, 3] = 0.5 # intensity |
||||
|
data[:, 4] = 0.9 # cos_inc_angle |
||||
|
data[:, 5] = 123 # object_idx |
||||
|
data[:, 6] = 10 # semantic_tag (Vehicle) |
||||
|
|
||||
|
print(f"Created {num_points} points representing a vehicle at 15m.") |
||||
|
|
||||
|
# 3. Process data through the model |
||||
|
print("\n[Step 3] Running Shenron Radar Simulation (FMCW + DSP)...") |
||||
|
start_time = time.time() |
||||
|
rich_pcd = model.process(data) |
||||
|
process_time = time.time() - start_time |
||||
|
print(f"Done in {process_time:.2f}s") |
||||
|
|
||||
|
# 4. Validate output |
||||
|
print("\n[Step 4] Validating output...") |
||||
|
print(f"Output shape: {rich_pcd.shape}") |
||||
|
|
||||
|
if rich_pcd.shape[0] > 0: |
||||
|
print(f"Detected {rich_pcd.shape[0]} radar targets.") |
||||
|
print("Format: [x, y, z, velocity, magnitude]") |
||||
|
# Check for expected coordinates (roughly 15m ahead) |
||||
|
avg_x = np.mean(rich_pcd[:, 0]) |
||||
|
avg_y = np.mean(rich_pcd[:, 1]) |
||||
|
print(f"Average Detection Position: x={avg_x:.2f}, y={avg_y:.2f}") |
||||
|
|
||||
|
if 10 < avg_x < 20: |
||||
|
print("SUCCESS: Detections are in the expected range.") |
||||
|
else: |
||||
|
print("WARNING: Detections are outside expected range.") |
||||
|
|
||||
|
print("\nSample Detections:") |
||||
|
print(rich_pcd[:5]) |
||||
|
else: |
||||
|
print("ERROR: No detections found from valid input data.") |
||||
|
success = False |
||||
|
|
||||
|
except Exception as e: |
||||
|
print(f"\n!!! CRITICAL ERROR during smoke test: {e}") |
||||
|
import traceback |
||||
|
traceback.print_exc() |
||||
|
success = False |
||||
|
|
||||
|
print("\n" + "="*50) |
||||
|
if success: |
||||
|
print("RESULT: SMOKE TEST PASSED") |
||||
|
else: |
||||
|
print("RESULT: SMOKE TEST FAILED") |
||||
|
print("="*50) |
||||
|
|
||||
|
return success |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
run_smoke_test() |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue