imu_delay_tester.py
Go to the documentation of this file.
1 """
2 Command line tool to for IMU delay Test
3 """
4 import numpy as np
5 import matplotlib.pyplot as plt
6 from scipy.interpolate import interp1d
7 from statsmodels.graphics.tsaplots import plot_acf
8 
9 import argparse
10 from argparse import RawTextHelpFormatter # to preserve line breaks in description
11 # see https://stackoverflow.com/questions/3853722/how-to-insert-newlines-on-argparse-help-text
12 
13 def binning_decay(timestamp_way_offset, decay_microsec):
14  """
15  binning of azimuth angles in 1, 2 or 3 lidar segments with decay time ca. 10 milliseconds
16  """
17  timestamp_azimuth_pairs = np.zeros([0,2])
18  last_timestamp = 0
19  for src_row in range(len(timestamp_way_offset)):
20  timestamp = timestamp_way_offset[src_row, 0]
21  azimuth = timestamp_way_offset[src_row, 1]
22  if timestamp - last_timestamp > decay_microsec: # append new measurement
23  timestamp_azimuth_pairs = np.vstack((timestamp_azimuth_pairs,np.array(([timestamp, azimuth]))))
24  last_timestamp = timestamp
25  else: # add azimuth to last measurement
26  dst_row = len(timestamp_azimuth_pairs) - 1
27  timestamp_azimuth_pairs[dst_row, 1] = timestamp_azimuth_pairs[dst_row, 1] + azimuth
28  return timestamp_azimuth_pairs
29 
30 def filter_azimuth(timestamp_way_offset, min_azimuth):
31  """
32  remove azimuth angles less than e.g. 16 degree (other segments or outliers)
33  """
34  timestamp_azimuth_pairs = np.zeros([0,2])
35  for row in range(len(timestamp_way_offset)):
36  timestamp = timestamp_way_offset[row, 0]
37  azimuth = timestamp_way_offset[row, 1]
38  if azimuth > min_azimuth:
39  timestamp_azimuth_pairs = np.vstack((timestamp_azimuth_pairs,np.array(([timestamp, azimuth]))))
40  return timestamp_azimuth_pairs
41 
42 def calc_azimuth_gradient(timestamp_way_offset):
43  """
44  calculates the gradients of azimuth angles
45  """
46  timestamp_azi_gradient = np.zeros([0,2])
47  for row in range(1, len(timestamp_way_offset)):
48  time1 = timestamp_way_offset[row - 1, 0]
49  azi1 = timestamp_way_offset[row - 1, 1]
50  time2 = timestamp_way_offset[row, 0]
51  azi2 = timestamp_way_offset[row, 1]
52  timestamp = 0.5 * (time1 + time2)
53  gradient = (azi2 - azi1) / (time2 - time1)
54  timestamp_azi_gradient = np.vstack((timestamp_azi_gradient,np.array(([timestamp, gradient]))))
55  return timestamp_azi_gradient
56 
57 def filter_local_minima(timestamp_way_offset):
58  """
59  returns the array of (timestamp, value) of all local minima
60  """
61  timestamp_minima = np.zeros([0,2])
62  for row in range(1, len(timestamp_way_offset) - 1):
63  timestamp = timestamp_way_offset[row, 0]
64  value = timestamp_way_offset[row, 1]
65  if value < timestamp_way_offset[row - 1, 1] and value < timestamp_way_offset[row + 1, 1]:
66  timestamp_minima = np.vstack((timestamp_minima,np.array(([timestamp, value]))))
67  return timestamp_minima
68 
69 def filter_local_maxima(timestamp_way_offset):
70  """
71  returns the array of (timestamp, value) of all local maxim
72  """
73  timestamp_minima = np.zeros([0,2])
74  for row in range(1, len(timestamp_way_offset) - 1):
75  timestamp = timestamp_way_offset[row, 0]
76  value = timestamp_way_offset[row, 1]
77  if value > timestamp_way_offset[row - 1, 1] and value > timestamp_way_offset[row + 1, 1]:
78  timestamp_minima = np.vstack((timestamp_minima,np.array(([timestamp, value]))))
79  return timestamp_minima
80 
81 def get_closest_timestamp(timestamp_velocity, imu_timestamp):
82  best_lidar_timestamp = timestamp_velocity[0, 0]
83  best_dt = abs(timestamp_velocity[0, 0] - imu_timestamp)
84  for row in range(1, len(timestamp_velocity)):
85  lidar_timestamp = timestamp_velocity[row, 0]
86  if abs(lidar_timestamp - imu_timestamp) < best_dt:
87  best_lidar_timestamp = lidar_timestamp
88  best_dt = abs(lidar_timestamp - imu_timestamp)
89  return best_lidar_timestamp, best_dt
90 
91 def calc_2nd_derivate_from_xy(xaxis, yaxis):
92  """
93  Idea taken from https://mathformeremortals.wordpress.com/2013/01/12/a-numerical-second-derivative-from-three-points/
94  """
95  num_pairs = len(xaxis)
96  y_2nd_derivate = np.zeros(num_pairs)
97 
98  for idx in range(1,num_pairs-1):
99  y_part = yaxis[idx-1:idx+2]
100  x_part = xaxis[idx-1:idx+2]
101 
102  x1 = x_part[0]
103  x2 = x_part[1]
104  x3 = x_part[2]
105 
106  x_diff = np.array([2.0/((x2-x1)*(x3-x1)), -2.0/((x3 - x2)*(x2 - x1)), 2.0/((x3 - x2)*(x3 - x1))])
107  y_2nd_derivate[idx] = np.dot(x_diff, y_part)
108 
109  return y_2nd_derivate
110 
111 
112 """
113 main
114 """
115 
116 parser = argparse.ArgumentParser(
117  prog='IMU Delay Tester',
118  formatter_class=RawTextHelpFormatter,
119  description="Calculates delay between LIDAR and IMU\n"
120  )
121 
122 csv_filename = '../test/20231024_multiscan_timestamp_azimuth_imuacceleration.csv'
123 parser.add_argument('--csv_filename', help='CSV data file with timestamp', default=csv_filename, type=str)
124 args = parser.parse_args()
125 
126 csv_filename = args.csv_filename
127 # read csv file
128 with open(csv_filename) as file:
129  lines = [line.rstrip() for line in file]
130 
131 timestamp_way_offset = np.zeros([0,2])
132 timestamp_imu_z = np.zeros([0,2])
133 timestamp_start = -1.0
134 for line in lines:
135  line = line.replace(',', '.') # replace german "," with "."
136  token_list = line.split(";")
137  if len(token_list) == 3: # valid data line holding <timestamp>, <way offset>, <imu Z-data>
138  for idx, token in enumerate(token_list):
139  if token == "":
140  continue
141  if idx == 0:
142  timestamp = float(token)
143  # convert timestamp to milliseconds from start
144  if timestamp_start < 0:
145  timestamp_start = timestamp
146  timestamp = 0.001 * (timestamp - timestamp_start)
147  if idx == 1:
148  way_offset = float(token)
149  timestamp_way_offset = np.vstack((timestamp_way_offset,np.array(([timestamp, way_offset]))))
150  if idx == 2:
151  imu_value = float(token)
152  timestamp_imu_z = np.vstack((timestamp_imu_z,np.array(([timestamp, imu_value]))))
153 
154 # binning of azimuth angles in 1, 2 or 3 lidar segments with decay time ca. 10 milliseconds
155 # timestamp_way_offset = binning_decay(timestamp_way_offset, 10000)
156 
157 # remove azimuth angles less than 16 degree (other lidar segments)
158 timestamp_way_offset = filter_azimuth(timestamp_way_offset, 16)
159 
160 # X axis parameter:
161 xaxis_azi_org = np.array(timestamp_way_offset[:, 0])
162 # Y axis parameter:
163 yaxis_azi_org = np.array(timestamp_way_offset[:, 1])
164 
165 xaxis_imu_org = np.array(timestamp_imu_z[:,0])
166 yaxis_imu_org = np.array(timestamp_imu_z[:,1])
167 
168 timestamp_azi_gradient = calc_azimuth_gradient(timestamp_way_offset)
169 xaxis_azi_grad = np.array(timestamp_azi_gradient[:,0])
170 yaxis_azi_grad = -np.array(timestamp_azi_gradient[:,1])
171 
172 compute_derivates = False
173 if compute_derivates:
174 
175  # Min imu acceleration at point of free fall => max azimuth gradient, max velocity
176  timestamp_min_imu_accel = filter_local_minima(timestamp_imu_z)
177  timestamp_max_velocity = filter_local_maxima(timestamp_azi_gradient)
178 
179  # Sort timestamp_min_imu_accel by ascending values and get 9 minima for the 9 periods
180  timestamp_min_imu_accel_sorted = timestamp_min_imu_accel[timestamp_min_imu_accel[:, 1].argsort()] # sort by acceleration
181  timestamp_min_imu_accel = timestamp_min_imu_accel_sorted[0:8,:] # 9 minima for 9 periods
182  timestamp_min_imu_accel = timestamp_min_imu_accel[timestamp_min_imu_accel[:, 0].argsort()] # resort by timestamp
183  xaxis_min_imu_accel = np.array(timestamp_min_imu_accel[:,0])
184  yaxis_min_imu_accel = np.array(timestamp_min_imu_accel[:,1])
185  xaxis_max_velocity = np.array(timestamp_max_velocity[:,0])
186  yaxis_max_velocity = np.array(timestamp_max_velocity[:,1])
187 
188  # Find max velocity closest to min acceleration
189  min_imu_latency_sec = 1.0e6
190  for row in range(len(timestamp_min_imu_accel)):
191  imu_timestamp = timestamp_min_imu_accel[row, 0]
192  lidar_timestamp, dt = get_closest_timestamp(timestamp_max_velocity, imu_timestamp)
193  imu_latency_sec = dt * 1.0e-6 # delta timestamp in micro seconds to latency in second
194  min_imu_latency_sec = min(min_imu_latency_sec, imu_latency_sec)
195  print(f"imu latency: {imu_latency_sec:.6} sec")
196 
197  print(f"\nimu_delay_tester ({csv_filename}): min imu latency = {min_imu_latency_sec:.6} sec")
198 
199  # Compute lidar acceleration, i.e. 2.nd derivate
200  y_2nd_derivate = calc_2nd_derivate_from_xy(xaxis_azi_org, yaxis_azi_org)
201 
202 show_plot = True
203 if show_plot:
204 
205  fig, (ax1, ax2, ax3) = plt.subplots(3)
206  # fig, (ax1, ax2) = plt.subplots(2)
207 
208  ax1.set_title('azimuth over time')
209  ax1.scatter(xaxis_azi_org, yaxis_azi_org)
210  ax1.plot(xaxis_azi_org, yaxis_azi_org)
211 
212  ax2.set_title('imu acceleration over time')
213  ax2.scatter(xaxis_imu_org, yaxis_imu_org)
214  ax2.plot(xaxis_imu_org, yaxis_imu_org)
215 
216  ax3.set_title('azimuth gradient over time')
217  ax3.scatter(xaxis_azi_grad, yaxis_azi_grad)
218  ax3.plot(xaxis_azi_grad, yaxis_azi_grad)
219 
220  fig.set_figheight(10) # height = 1000 px
221  fig.set_figwidth(20) # width = 2000 px
222  fig.show()
223  plt.savefig(fname=csv_filename[:-4]+'.png')
224 
225  # fig, (ax1, ax2) = plt.subplots(2)
226  # ax1.set_title('filtered maxima velocity over time')
227  # ax1.scatter(xaxis_max_velocity, yaxis_max_velocity)
228  # ax1.plot(xaxis_max_velocity, yaxis_max_velocity)
229  # ax2.set_title('filtered minima imu acceleration over time')
230  # ax2.scatter(xaxis_min_imu_accel, yaxis_min_imu_accel)
231  # ax2.plot(xaxis_min_imu_accel, yaxis_min_imu_accel)
232  # fig.set_figheight(10) # height = 1000 px
233  # fig.set_figwidth(20) # width = 2000 px
234  # fig.show()
235 
236  # show_acceleration = True
237  # f = plt.figure(1)
238  # plt.scatter(xaxis_azi_org, y_2nd_derivate)
239  # plt.plot(xaxis_azi_org, y_2nd_derivate)
240  # f.show()
241  # g = plt.figure(2)
242  # plt.scatter(xaxis_azi_org, yaxis_azi_org)
243  # plt.plot(xaxis_azi_org, yaxis_azi_org)
244  # g.show()
245  # h = plt.figure(3)
246  # plt.scatter(xaxis_imu_org, yaxis_imu_org)
247  # plt.plot(xaxis_imu_org, yaxis_imu_org)
248  # h.show()
249 
250  plt.show()
251  plt.close()
252 
253 pass
254 
imu_delay_tester.filter_local_maxima
def filter_local_maxima(timestamp_way_offset)
Definition: imu_delay_tester.py:69
roswrap::console::print
ROSCONSOLE_DECL void print(FilterBase *filter, void *logger, Level level, const char *file, int line, const char *function, const char *fmt,...) ROSCONSOLE_PRINTF_ATTRIBUTE(7
Don't call this directly. Use the ROS_LOG() macro instead.
imu_delay_tester.calc_azimuth_gradient
def calc_azimuth_gradient(timestamp_way_offset)
Definition: imu_delay_tester.py:42
imu_delay_tester.filter_azimuth
def filter_azimuth(timestamp_way_offset, min_azimuth)
Definition: imu_delay_tester.py:30
imu_delay_tester.filter_local_minima
def filter_local_minima(timestamp_way_offset)
Definition: imu_delay_tester.py:57
imu_delay_tester.calc_2nd_derivate_from_xy
def calc_2nd_derivate_from_xy(xaxis, yaxis)
Definition: imu_delay_tester.py:91
imu_delay_tester.get_closest_timestamp
def get_closest_timestamp(timestamp_velocity, imu_timestamp)
Definition: imu_delay_tester.py:81
imu_delay_tester.binning_decay
def binning_decay(timestamp_way_offset, decay_microsec)
Definition: imu_delay_tester.py:13


sick_scan_xd
Author(s): Michael Lehning , Jochen Sprickerhof , Martin Günther
autogenerated on Fri Oct 25 2024 02:47:08