1414#include < angles/angles.h>
1515#include < sensor_msgs/distortion_models.h>
1616#include < tf2/convert.h>
17+ #include < tf2/LinearMath/Transform.h>
18+ #include < tf2_geometry_msgs/tf2_geometry_msgs.h>
1719
1820// Project headers
1921//
@@ -80,18 +82,13 @@ void K4ACalibrationTransformData::initialize(const K4AROSDeviceParams params)
8082 getColorWidth () * (int )sizeof (DepthPixel));
8183 }
8284
83- // Publish various transforms needed by ROS
84- publishImuToBaseTf ();
85-
86- if (depthEnabled)
87- {
88- publishDepthToBaseTf ();
89- }
90-
91- if (colorEnabled)
92- {
93- publishRgbToBaseTf ();
94- }
85+ // Publish various transforms needed by ROS.
86+ // We publish all TFs all the time, even if the respective sensor data
87+ // output is off. This allows us to just use the SDK calibrations, and one
88+ // cosmetic TF output between the depth and the base.
89+ publishDepthToBaseTf ();
90+ publishImuToDepthTf ();
91+ publishRgbToDepthTf ();
9592}
9693
9794int K4ACalibrationTransformData::getDepthWidth ()
@@ -176,110 +173,49 @@ void K4ACalibrationTransformData::printExtrinsics(k4a_calibration_extrinsics_t&
176173 ROS_INFO_STREAM (" \t\t\t Rotation[2]: " << extrinsics.rotation [6 ] << " , " << extrinsics.rotation [7 ] << " , " << extrinsics.rotation [8 ]);
177174}
178175
179- void K4ACalibrationTransformData::publishRgbToBaseTf ()
176+ void K4ACalibrationTransformData::publishRgbToDepthTf ()
180177{
181- k4a_float3_t origin = {0 .0f , 0 .0f , 0 .0f };
182- k4a_float3_t target = {0 .0f , 0 .0f , 0 .0f };
183-
184- // Compute the offset of the RGB camera assembly from the depth camera origin
185- target = k4a_calibration_.convert_3d_to_3d (
186- origin,
187- K4A_CALIBRATION_TYPE_DEPTH,
188- K4A_CALIBRATION_TYPE_COLOR);
189-
190- ROS_INFO_STREAM (" Depth -> RGB offset: ( " << target.xyz .x << " , " << target.xyz .y << " , " << target.xyz .z << " )" );
178+ k4a_calibration_extrinsics_t * rgb_extrinsics = &k4a_calibration_.extrinsics [K4A_CALIBRATION_TYPE_DEPTH][K4A_CALIBRATION_TYPE_COLOR];
179+ tf2::Vector3 depth_to_rgb_translation (rgb_extrinsics->translation [0 ] / 1000 .0f , rgb_extrinsics->translation [1 ] / 1000 .0f ,
180+ rgb_extrinsics->translation [2 ] / 1000 .0f );
181+ tf2::Matrix3x3 depth_to_rgb_rotation (rgb_extrinsics->rotation [0 ], rgb_extrinsics->rotation [1 ], rgb_extrinsics->rotation [2 ],
182+ rgb_extrinsics->rotation [3 ], rgb_extrinsics->rotation [4 ], rgb_extrinsics->rotation [5 ],
183+ rgb_extrinsics->rotation [6 ], rgb_extrinsics->rotation [7 ], rgb_extrinsics->rotation [8 ]);
184+ tf2::Transform depth_to_rgb_transform (depth_to_rgb_rotation, depth_to_rgb_translation);
191185
192186 geometry_msgs::TransformStamped static_transform;
187+ static_transform.transform = tf2::toMsg (depth_to_rgb_transform.inverse ());
193188
194189 static_transform.header .stamp = ros::Time::now ();
195- static_transform.header .frame_id = tf_prefix_ + camera_base_frame_ ;
190+ static_transform.header .frame_id = tf_prefix_ + depth_camera_frame_ ;
196191 static_transform.child_frame_id = tf_prefix_ + rgb_camera_frame_;
197192
198- tf2::Vector3 extrinsic_translation ((target.xyz .z / -1000 .0f ), (target.xyz .x / 1000 .0f ), (target.xyz .y / 1000 .0f ));
199- extrinsic_translation += getDepthToBaseTranslationCorrection ();
200-
201- static_transform.transform .translation .x = extrinsic_translation.x ();
202- static_transform.transform .translation .y = extrinsic_translation.y ();
203- static_transform.transform .translation .z = extrinsic_translation.z ();
204-
205- k4a_calibration_extrinsics_t * color_extrinsics = &k4a_calibration_.extrinsics [K4A_CALIBRATION_TYPE_DEPTH][K4A_CALIBRATION_TYPE_COLOR];
206-
207- tf2::Matrix3x3 color_matrix (color_extrinsics->rotation [0 ], color_extrinsics->rotation [1 ], color_extrinsics->rotation [2 ],
208- color_extrinsics->rotation [3 ], color_extrinsics->rotation [4 ], color_extrinsics->rotation [5 ],
209- color_extrinsics->rotation [6 ], color_extrinsics->rotation [7 ], color_extrinsics->rotation [8 ]);
210-
211- double yaw, pitch, roll;
212- color_matrix.getEulerYPR (yaw, pitch, roll);
213- ROS_INFO_STREAM (" Color Roll Extrinsics (YPR): " << angles::to_degrees (yaw) << " , " << angles::to_degrees (pitch) << " , " << angles::to_degrees (roll));
214-
215- tf2::Quaternion color_rotation;
216- color_rotation.setRPY (
217- yaw,
218- roll,
219- pitch);
220-
221- color_rotation *= getColorToDepthRotationCorrection ();
222-
223- static_transform.transform .rotation .x = color_rotation.x ();
224- static_transform.transform .rotation .y = color_rotation.y ();
225- static_transform.transform .rotation .z = color_rotation.z ();
226- static_transform.transform .rotation .w = color_rotation.w ();
227-
228193 static_broadcaster_.sendTransform (static_transform);
229194}
230195
231- void K4ACalibrationTransformData::publishImuToBaseTf ()
196+ void K4ACalibrationTransformData::publishImuToDepthTf ()
232197{
233- k4a_float3_t origin = {0 .0f , 0 .0f , 0 .0f };
234- k4a_float3_t target = {0 .0f , 0 .0f , 0 .0f };
235-
236- target = k4a_calibration_.convert_3d_to_3d (
237- origin,
238- K4A_CALIBRATION_TYPE_DEPTH,
239- K4A_CALIBRATION_TYPE_ACCEL);
240-
241- ROS_INFO_STREAM (" Depth -> IMU offset: ( " << target.xyz .x << " , " << target.xyz .y << " , " << target.xyz .z << " )" );
198+ k4a_calibration_extrinsics_t * imu_extrinsics = &k4a_calibration_.extrinsics [K4A_CALIBRATION_TYPE_DEPTH][K4A_CALIBRATION_TYPE_ACCEL];
199+ tf2::Vector3 depth_to_imu_translation (imu_extrinsics->translation [0 ] / 1000 .0f , imu_extrinsics->translation [1 ] / 1000 .0f ,
200+ imu_extrinsics->translation [2 ] / 1000 .0f );
201+ tf2::Matrix3x3 depth_to_imu_rotation (imu_extrinsics->rotation [0 ], imu_extrinsics->rotation [1 ], imu_extrinsics->rotation [2 ],
202+ imu_extrinsics->rotation [3 ], imu_extrinsics->rotation [4 ], imu_extrinsics->rotation [5 ],
203+ imu_extrinsics->rotation [6 ], imu_extrinsics->rotation [7 ], imu_extrinsics->rotation [8 ]);
204+ tf2::Transform depth_to_imu_transform (depth_to_imu_rotation, depth_to_imu_translation);
242205
243206 geometry_msgs::TransformStamped static_transform;
207+ static_transform.transform = tf2::toMsg (depth_to_imu_transform.inverse ());
244208
245209 static_transform.header .stamp = ros::Time::now ();
246- static_transform.header .frame_id = tf_prefix_ + camera_base_frame_ ;
210+ static_transform.header .frame_id = tf_prefix_ + depth_camera_frame_ ;
247211 static_transform.child_frame_id = tf_prefix_ + imu_frame_;
248212
249- tf2::Vector3 extrinsic_translation ((target.xyz .x / 1000 .0f ), (target.xyz .y / -1000 .0f ), (target.xyz .z / -1000 .0f ));
250- extrinsic_translation += getDepthToBaseTranslationCorrection ();
251-
252- static_transform.transform .translation .x = extrinsic_translation.x ();
253- static_transform.transform .translation .y = extrinsic_translation.y ();
254- static_transform.transform .translation .z = extrinsic_translation.z ();
255-
256- k4a_calibration_extrinsics_t * imu_extrinsics = &k4a_calibration_.extrinsics [K4A_CALIBRATION_TYPE_DEPTH][K4A_CALIBRATION_TYPE_ACCEL];
257-
258- tf2::Matrix3x3 imu_matrix (imu_extrinsics->rotation [0 ], imu_extrinsics->rotation [1 ], imu_extrinsics->rotation [2 ],
259- imu_extrinsics->rotation [3 ], imu_extrinsics->rotation [4 ], imu_extrinsics->rotation [5 ],
260- imu_extrinsics->rotation [6 ], imu_extrinsics->rotation [7 ], imu_extrinsics->rotation [8 ]);
261-
262- double yaw, pitch, roll;
263- imu_matrix.getEulerYPR (yaw, pitch, roll);
264- ROS_INFO_STREAM (" IMU Roll Extrinsics (YPR): " << angles::to_degrees (yaw) << " , " << angles::to_degrees (pitch) << " , " << angles::to_degrees (roll));
265- ROS_INFO_STREAM (" IMU Roll Extrinsics Corrected (YPR): " << angles::to_degrees (yaw) + 90 << " , " << angles::to_degrees (pitch) << " , " << angles::to_degrees (roll) - 84 );
266-
267- tf2::Quaternion imu_rotation;
268- imu_rotation.setRPY (
269- yaw + angles::from_degrees (90 ),
270- roll + angles::from_degrees (-84 ), // Rotation around the Y axis must include a 6 degree correction for a static offset collected during calibration
271- pitch + angles::from_degrees (0 ));
272-
273- static_transform.transform .rotation .x = imu_rotation.x ();
274- static_transform.transform .rotation .y = imu_rotation.y ();
275- static_transform.transform .rotation .z = imu_rotation.z ();
276- static_transform.transform .rotation .w = imu_rotation.w ();
277-
278213 static_broadcaster_.sendTransform (static_transform);
279214}
280215
281216void K4ACalibrationTransformData::publishDepthToBaseTf ()
282217{
218+ // This is a purely cosmetic transform to make the base model of the URDF look good.
283219 geometry_msgs::TransformStamped static_transform;
284220
285221 static_transform.header .stamp = ros::Time::now ();
@@ -310,60 +246,22 @@ void K4ACalibrationTransformData::publishDepthToBaseTf()
310246
311247tf2::Vector3 K4ACalibrationTransformData::getDepthToBaseTranslationCorrection ()
312248{
249+ // These are purely cosmetic tranformations for the URDF drawing!!
313250 return tf2::Vector3 (DEPTH_CAMERA_OFFSET_MM_X / 1000 .0f , DEPTH_CAMERA_OFFSET_MM_Y / 1000 .0f , DEPTH_CAMERA_OFFSET_MM_Z / 1000 .0f );
314251}
315252
316253tf2::Quaternion K4ACalibrationTransformData::getDepthToBaseRotationCorrection ()
317254{
255+ // These are purely cosmetic tranformations for the URDF drawing!!
318256 tf2::Quaternion ros_camera_rotation; // ROS camera co-ordinate system requires rotating the entire camera relative to camera_base
319- tf2::Quaternion depth_rotation; // K4A depth camera has different declinations based on the operating mode (NFOV or WFOV)
320-
321- // WFOV modes are rotated down by 7.3 degrees
322- if (k4a_calibration_.depth_mode == K4A_DEPTH_MODE_WFOV_UNBINNED || k4a_calibration_.depth_mode == K4A_DEPTH_MODE_WFOV_2X2BINNED)
323- {
324- depth_rotation.setEuler (0 , angles::from_degrees (-7.3 ), 0 );
325- }
326- // NFOV modes are rotated down by 6 degrees
327- else if (k4a_calibration_.depth_mode == K4A_DEPTH_MODE_NFOV_UNBINNED || k4a_calibration_.depth_mode == K4A_DEPTH_MODE_NFOV_2X2BINNED)
328- {
329- depth_rotation.setEuler (0 , angles::from_degrees (-6.0 ), 0 );
330- }
331- // Passive IR mode doesn't have a rotation?
332- // TODO: verify that passive IR really doesn't have a rotation
333- else if (k4a_calibration_.depth_mode == K4A_DEPTH_MODE_PASSIVE_IR)
334- {
335- depth_rotation.setEuler (0 , 0 , 0 );
336- }
337- else
338- {
339- ROS_ERROR_STREAM (" Could not determine depth camera mode for rotation correction" );
340- depth_rotation.setEuler (0 , 0 , 0 );
341- }
257+ tf2::Quaternion depth_rotation; // K4A has one physical camera that is about 6 degrees downward facing.
342258
259+ depth_rotation.setEuler (0 , angles::from_degrees (-6.0 ), 0 );
343260 ros_camera_rotation.setEuler (M_PI / -2 .0f , M_PI, (M_PI / 2 .0f ));
344261
345262 return ros_camera_rotation * depth_rotation;
346263}
347264
348- tf2::Quaternion K4ACalibrationTransformData::getImuToDepthRotationCorrection ()
349- {
350- tf2::Quaternion ros_imu_rotation; // ROS IMU co-ordinate system requires rotating the entire IMU relative to camera_base
351- tf2::Quaternion imu_rotation; // K4A extrinsics all contain a 6 degree offset due to being captured in NFOV mode
352-
353- return ros_imu_rotation * imu_rotation;
354- }
355-
356- tf2::Quaternion K4ACalibrationTransformData::getColorToDepthRotationCorrection ()
357- {
358- tf2::Quaternion ros_camera_rotation; // ROS camera co-ordinate system requires rotating the entire camera relative to camera_base
359- tf2::Quaternion color_rotation; // K4A extrinsics all contain a 6 degree offset due to being captured in NFOV mode
360-
361- color_rotation.setEuler (0 , angles::from_degrees (-6.0 ), 0 );
362- ros_camera_rotation.setEuler (M_PI / -2 .0f , M_PI, (M_PI / 2 .0f ));
363-
364- return ros_camera_rotation * color_rotation;
365- }
366-
367265void K4ACalibrationTransformData::getDepthCameraInfo (sensor_msgs::CameraInfo &camera_info)
368266{
369267 camera_info.header .frame_id = tf_prefix_ + depth_camera_frame_;
@@ -374,7 +272,7 @@ void K4ACalibrationTransformData::getDepthCameraInfo(sensor_msgs::CameraInfo &ca
374272 k4a_calibration_intrinsic_parameters_t * parameters = &k4a_calibration_.depth_camera_calibration .intrinsics .parameters ;
375273
376274 // The distortion parameters, size depending on the distortion model.
377- // For "rational_polynomial", the 5 parameters are: (k1, k2, p1, p2, k3, k4, k5, k6).
275+ // For "rational_polynomial", the 8 parameters are: (k1, k2, p1, p2, k3, k4, k5, k6).
378276 camera_info.D = {parameters->param .k1 , parameters->param .k2 , parameters->param .p1 , parameters->param .p2 , parameters->param .k3 , parameters->param .k4 , parameters->param .k5 , parameters->param .k6 };
379277
380278 // Intrinsic camera matrix for the raw (distorted) images.
@@ -384,8 +282,8 @@ void K4ACalibrationTransformData::getDepthCameraInfo(sensor_msgs::CameraInfo &ca
384282 // Projects 3D points in the camera coordinate frame to 2D pixel
385283 // coordinates using the focal lengths (fx, fy) and principal point
386284 // (cx, cy).
387- camera_info.K = {parameters->param .fx , 0 .0f , parameters->param .cx ,
388- 0 .0f , parameters->param .fy , parameters->param .cy ,
285+ camera_info.K = {parameters->param .fx , 0 .0f , parameters->param .cx ,
286+ 0 .0f , parameters->param .fy , parameters->param .cy ,
389287 0 .0f , 0.0 , 1 .0f };
390288
391289 // Projection/camera matrix
@@ -408,8 +306,8 @@ void K4ACalibrationTransformData::getDepthCameraInfo(sensor_msgs::CameraInfo &ca
408306 // A rotation matrix aligning the camera coordinate system to the ideal
409307 // stereo image plane so that epipolar lines in both stereo images are
410308 // parallel.
411- camera_info.R = {1 .0f , 0 .0f , 0 .0f ,
412- 0 .0f , 1 .0f , 0 .0f ,
309+ camera_info.R = {1 .0f , 0 .0f , 0 .0f ,
310+ 0 .0f , 1 .0f , 0 .0f ,
413311 0 .0f , 0 .0f , 1 .0f };
414312}
415313
@@ -423,7 +321,7 @@ void K4ACalibrationTransformData::getRgbCameraInfo(sensor_msgs::CameraInfo &came
423321 k4a_calibration_intrinsic_parameters_t * parameters = &k4a_calibration_.color_camera_calibration .intrinsics .parameters ;
424322
425323 // The distortion parameters, size depending on the distortion model.
426- // For "rational_polynomial", the 5 parameters are: (k1, k2, p1, p2, k3, k4, k5, k6).
324+ // For "rational_polynomial", the 8 parameters are: (k1, k2, p1, p2, k3, k4, k5, k6).
427325 camera_info.D = {parameters->param .k1 , parameters->param .k2 , parameters->param .p1 , parameters->param .p2 , parameters->param .k3 , parameters->param .k4 , parameters->param .k5 , parameters->param .k6 };
428326
429327 // Intrinsic camera matrix for the raw (distorted) images.
@@ -433,8 +331,8 @@ void K4ACalibrationTransformData::getRgbCameraInfo(sensor_msgs::CameraInfo &came
433331 // Projects 3D points in the camera coordinate frame to 2D pixel
434332 // coordinates using the focal lengths (fx, fy) and principal point
435333 // (cx, cy).
436- camera_info.K = {parameters->param .fx , 0 .0f , parameters->param .cx ,
437- 0 .0f , parameters->param .fy , parameters->param .cy ,
334+ camera_info.K = {parameters->param .fx , 0 .0f , parameters->param .cx ,
335+ 0 .0f , parameters->param .fy , parameters->param .cy ,
438336 0 .0f , 0.0 , 1 .0f };
439337
440338 // Projection/camera matrix
@@ -457,7 +355,7 @@ void K4ACalibrationTransformData::getRgbCameraInfo(sensor_msgs::CameraInfo &came
457355 // A rotation matrix aligning the camera coordinate system to the ideal
458356 // stereo image plane so that epipolar lines in both stereo images are
459357 // parallel.
460- camera_info.R = {1 .0f , 0 .0f , 0 .0f ,
461- 0 .0f , 1 .0f , 0 .0f ,
358+ camera_info.R = {1 .0f , 0 .0f , 0 .0f ,
359+ 0 .0f , 1 .0f , 0 .0f ,
462360 0 .0f , 0 .0f , 1 .0f };
463- }
361+ }
0 commit comments