35 import roslib; roslib.load_manifest(
'calibration_estimation')
40 from numpy
import array, matrix, zeros, cumsum, concatenate, reshape
44 from visualization_msgs.msg
import Marker, MarkerArray
45 import geometry_msgs.msg
49 Helpers for computing errors and jacobians 51 def __init__(self, robot_params, free_dict, multisensors, use_cov):
58 self.
marker_pub = rospy.Publisher(
"pose_guesses", Marker)
63 Take the set of optimization params, and expand it into the bigger param vec 65 full_param_vec = self._expanded_params.copy()
66 full_param_vec[numpy.where(self.
_free_list), 0] = opt_param_vec
71 opt_param_vec, full_pose_arr = self.
split_all(opt_all_vec)
76 self._robot_params.inflate(full_param_vec)
84 for multisensor, cb_pose_vec
in zip(self.
_multisensors, list(full_pose_arr)):
86 cb_points =
SingleTransform(cb_pose_vec).transform * self._robot_params.checkerboards[multisensor.checkerboard].generate_points()
88 r_list.append(multisensor.compute_residual_scaled(cb_points))
90 r_list.append(multisensor.compute_residual(cb_points))
92 cb_points_msgs = [ geometry_msgs.msg.Point(cur_pt[0,0], cur_pt[0,1], cur_pt[0,2])
for cur_pt
in cb_points.T]
95 m.header.frame_id = self._robot_params.base_link
98 m.type = Marker.SPHERE_LIST
99 m.action = Marker.MODIFY
100 m.points = cb_points_msgs
108 self.marker_pub.publish(m)
110 cur_pt = cb_points.T[0]
113 m_id.header.frame_id = self._robot_params.base_link
114 m_id.ns =
"points_ids" 116 m_id.type = Marker.TEXT_VIEW_FACING
117 m_id.action = Marker.MODIFY
126 m_id.pose.position.x = cur_pt[0,0];
127 m_id.pose.position.y = cur_pt[0,1];
128 m_id.pose.position.z = cur_pt[0,2] + 0.05;
129 m_id.pose.orientation.x = 0.0;
130 m_id.pose.orientation.y = 0.0;
131 m_id.pose.orientation.z = 0.0;
132 m_id.pose.orientation.w = 1.0;
133 self.marker_pub.publish(m_id)
137 r_vec = concatenate(r_list)
139 rms_error = numpy.sqrt( numpy.mean(r_vec**2) )
141 print "\t\t\t\t\tRMS error: %.3f \r" % rms_error,
149 The resulting returned jacobian can be thought of as a set of several concatenated jacobians as follows: 152 J = [ J_multisensor_1 ] 156 where M is the number of multisensors, which is generally the number of calibration samples collected. 158 Multisensor Jacobian: 159 The Jacobian for a given multisensor is created by concatenating jacobians for each sensor 162 J_multisensor_m = [ J_sensor_m_1 ] 166 where S is the number of sensors in this multisensor. 169 A single sensor jacobian is created by concatenating jacobians for the system parameters and checkerboards 171 J_sensor_m_s = [ J_params_m_s | J_m_s_pose0 | J_m_s_pose1 | --- | J_m_s_CB_poseC ] 173 The number of rows in J_sensor_m_s is equal to the number of rows in this sensor's residual. 174 J_params_m_s shows how modifying the free system parameters affects the residual. 175 Each J_m_s_pose_c is 6 columns wide and shows how moving a certain target affects the residual. All 176 of the J_m_s_pose blocks are zero, except J_sensor_pose_m, since target m is the only target that 177 was viewed by the sensors in this multisensor. 180 sys.stdout.write(
" \r")
181 sys.stdout.write(
"Computing Jacobian.. (cycle #%d)\r" % self.
_j_count)
186 opt_param_vec, full_pose_arr = self.
split_all(opt_all_vec)
189 ms_r_len = [ms.get_residual_length()
for ms
in self.
_multisensors]
190 J = zeros([sum(ms_r_len), len(opt_all_vec)])
193 ms_end_row = list(cumsum(ms_r_len))
194 ms_start_row = [0] + ms_end_row[:-1]
197 ms_end_col = list(cumsum([6]*len(self.
_multisensors)) + len(opt_param_vec))
198 ms_start_col = [x-6
for x
in ms_end_col]
203 J_ms_params = J[ ms_start_row[i]:ms_end_row[i],
204 0:len(opt_param_vec) ]
205 s_r_len = [s.get_residual_length()
for s
in ms.sensors]
206 s_end_row = list(cumsum(s_r_len))
207 s_start_row = [0] + s_end_row[:-1]
210 for k,s
in enumerate(ms.sensors):
211 J_s_params = J_ms_params[ s_start_row[k]:s_end_row[k], :]
215 J_ms_pose = J[ ms_start_row[i]:ms_end_row[i],
216 ms_start_col[i]:ms_end_col[i]]
217 assert(J_ms_pose.shape[1] == 6)
226 Splits the input vector into two parts: 227 1) opt_param_vec: The system parameters that we're optimizing, 228 2) full_pose_arr: A Nx6 array where each row is a checkerboard pose 231 opt_param_vec = opt_all_vec[0:opt_param_len]
233 full_pose_vec = opt_all_vec[opt_param_len:]
234 full_pose_arr = numpy.reshape(full_pose_vec, [-1,6])
235 return opt_param_vec, full_pose_arr
239 Computes the jacobian from the free system parameters to a single sensor 241 - opt_param_vec: Vector of the free system parameters (the parameters we're actuall optimizing) 242 - target_pose_T: The pose of the target that this sensor is viewing 243 - target_id: String name of the target that this sensor is viewing. This is necessary to generate the 244 set of feature points on the target 245 - sensor: The actual sensor definition 247 - J_scaled: An mxn jacobian, where m is the length of sensor's residual and n is the length of opt_param_vec. 248 If covariance calculations are enabled, then the Jacobian is scaled by sqrt(Gamma), where Gamma 249 is the information matrix for this measurement. 251 sparsity_dict = sensor.build_sparsity_dict()
252 required_keys = [
'chains',
'tilting_lasers',
'transforms',
'rectified_cams',
'checkerboards']
253 for cur_key
in required_keys:
254 if cur_key
not in sparsity_dict.keys():
255 sparsity_dict[cur_key] = {}
257 full_sparsity_list = self._robot_params.calc_free(sparsity_dict)
258 full_sparsity_vec = numpy.array(full_sparsity_list)
262 opt_sparsity_vec = full_sparsity_vec[numpy.where(self.
_free_list)].copy()
266 self._robot_params.inflate(full_param_vec)
272 target_points = target_pose_T * self._robot_params.checkerboards[target_id].generate_points()
273 f0 = sensor.compute_residual(target_points)
275 gamma_sqrt = sensor.compute_marginal_gamma_sqrt(target_points)
276 Jt = numpy.zeros([len(x0),len(f0)])
277 dx = numpy.zeros(len(x0))
278 for i
in numpy.where(opt_sparsity_vec)[0]:
280 opt_test_param_vec = x0 + dx
282 self._robot_params.inflate(full_test_param_vec)
284 target_points = target_pose_T * self._robot_params.checkerboards[target_id].generate_points()
285 Jt[i] = (sensor.compute_residual(target_points) - f0)/epsilon
289 J_scaled = gamma_sqrt * J
296 Generates the jacobian from a target pose to the multisensor's residual. 299 - opt_param_vec: Vector of the free system parameters (the parameters we're actuall optimizing) 300 - pose_param_vec: Vector of length 6 encoding the target's pose 0:3=translation 3:6=rotation_axis 301 - multisensor: The actual multisensor definition. 303 - J_scaled: An mx6 jacobian, where m is the length of multisensor's residual. There are 6 columns since the 304 target's pose is encoded using 6 parameters. 305 If covariance calculations are enabled, then the Jacobian is scaled by sqrt(Gamma), where Gamma 306 is the information matrix for this measurement. 310 self._robot_params.inflate(full_param_vec)
312 cb_model = self._robot_params.checkerboards[multisensor.checkerboard]
313 local_cb_points = cb_model.generate_points()
319 f0 = multisensor.compute_residual(world_pts)
321 gamma_sqrt = multisensor.compute_marginal_gamma_sqrt(world_pts)
323 Jt = numpy.zeros([len(x0),len(f0)])
324 dx = numpy.zeros(len(x0))
325 for i
in range(len(x0)):
328 fTest = multisensor.compute_residual(
SingleTransform(test_vec).transform * local_cb_points)
329 Jt[i] = (fTest - f0)/epsilon
334 J_scaled = gamma_sqrt * J
341 Construct vector of all the parameters that we're optimizing over. This includes 342 both the free system parameters and the checkerboard poses 344 - robot_params: Dictionary with all of the system parameters 345 - free_dict: Dictionary that specifies which system parameters are free 346 - pose_guess_arr: Mx6 array storing the current checkerboard poses, where M is 347 the number of calibration samples 348 Returns: Vector of length (F + Mx6), where F is the number of 1's in the free_dict 352 expanded_param_vec = robot_params.deflate()
353 free_list = robot_params.calc_free(free_dict)
356 opt_param_vec = array(expanded_param_vec[numpy.where(free_list)].T)[0]
358 assert(pose_guess_arr.shape[1] == 6)
359 opt_pose_vec = reshape(pose_guess_arr, [-1])
361 return numpy.concatenate([opt_param_vec, opt_pose_vec])
366 for ms, k
in zip(multisensors, range(len(multisensors))):
367 cb = error_calc._robot_params.checkerboards[ms.checkerboard]
368 cb_pose_vec = opt_pose_arr[k]
369 target_pts =
SingleTransform(cb_pose_vec).transform * cb.generate_points()
370 for sensor
in ms.sensors:
371 r_sensor = sensor.compute_residual(target_pts) * numpy.sqrt(sensor.terms_per_sample)
372 if sensor.sensor_id
not in errors_dict.keys():
373 errors_dict[sensor.sensor_id] = []
374 errors_dict[sensor.sensor_id].append(r_sensor)
377 def opt_runner(robot_params, pose_guess_arr, free_dict, multisensors, use_cov):
379 Runs a single optimization step for the calibration optimization. 380 robot_params - Instance of UrdfParams 381 free_dict - Dictionary storing which parameters are free 382 multisensor - list of list of measurements. Each multisensor corresponds to a single checkerboard pose 383 pose_guesses - List of guesses as to where all the checkerboard are. This is used to initialze the optimization 385 error_calc =
ErrorCalc(robot_params, free_dict, multisensors, use_cov)
390 x, cov_x, infodict, mesg, iter = scipy.optimize.leastsq(error_calc.calculate_error, opt_all, Dfun=error_calc.calculate_jacobian, full_output=1)
392 J = error_calc.calculate_jacobian(x)
395 opt_param_vec, pose_vec = error_calc.split_all(x)
396 expanded_param_vec = error_calc.calculate_full_param_vec(opt_param_vec)
397 opt_pose_arr = reshape(pose_vec, [-1, 6])
399 output_dict = error_calc._robot_params.params_to_config(expanded_param_vec)
404 print "Errors Breakdown:" 405 for sensor_id, error_list
in errors_dict.items():
406 error_cat = numpy.concatenate(error_list)
407 rms_error = numpy.sqrt( numpy.mean(error_cat**2) )
408 print " %s: %.6f" % (sensor_id, rms_error)
410 return output_dict, opt_pose_arr, J
def single_sensor_params_jacobian(self, opt_param_vec, target_pose_T, target_id, sensor)
def calculate_error(self, opt_all_vec)
def multisensor_pose_jacobian(self, opt_param_vec, pose_param_vec, multisensor)
def build_opt_vector(robot_params, free_dict, pose_guess_arr)
def __init__(self, robot_params, free_dict, multisensors, use_cov)
def calculate_full_param_vec(self, opt_param_vec)
def split_all(self, opt_all_vec)
def calculate_jacobian(self, opt_all_vec)
def opt_runner(robot_params, pose_guess_arr, free_dict, multisensors, use_cov)
def compute_errors_breakdown(error_calc, multisensors, opt_pose_arr)