Skip to content

Commit 356f7eb

Browse files
authored
Merge pull request opencv#86 from helenol/feature/fix_transforms
Fix entire transformation tree to match physical sensor + calibration
2 parents 8a2e8cd + fa7c550 commit 356f7eb

File tree

4 files changed

+110
-258
lines changed

4 files changed

+110
-258
lines changed

include/azure_kinect_ros_driver/k4a_calibration_transform_data.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,12 @@ class K4ACalibrationTransformData
5757
void printCameraCalibration(k4a_calibration_camera_t& calibration);
5858
void printExtrinsics(k4a_calibration_extrinsics_t& extrinsics);
5959

60-
void publishRgbToBaseTf();
61-
void publishImuToBaseTf();
60+
void publishRgbToDepthTf();
61+
void publishImuToDepthTf();
6262
void publishDepthToBaseTf();
6363

6464
tf2::Quaternion getDepthToBaseRotationCorrection();
6565
tf2::Vector3 getDepthToBaseTranslationCorrection();
66-
tf2::Quaternion getImuToDepthRotationCorrection();
67-
tf2::Quaternion getColorToDepthRotationCorrection();
6866

6967
tf2_ros::StaticTransformBroadcaster static_broadcaster_;
7068
};

launch/driver.launch

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!--
1+
<!--
22
Copyright (c) Microsoft Corporation. All rights reserved.
33
Licensed under the MIT License.
44
-->
@@ -28,14 +28,14 @@ Licensed under the MIT License.
2828
<arg name="body_tracking_smoothing_factor" default="0.0" /> <!-- Set between 0 for no smoothing and 1 for full smoothing -->
2929

3030
<node pkg="azure_kinect_ros_driver" type="node" name="node" output="screen" required="$(arg required)">
31-
<param name="depth_enabled" type="bool" value="$(arg depth_enabled)" />
32-
<param name="depth_mode" type="string" value="$(arg depth_mode)" />
33-
<param name="color_enabled" type="bool" value="$(arg color_enabled)" />
31+
<param name="depth_enabled" type="bool" value="$(arg depth_enabled)" />
32+
<param name="depth_mode" type="string" value="$(arg depth_mode)" />
33+
<param name="color_enabled" type="bool" value="$(arg color_enabled)" />
3434
<param name="color_format" type="string" value="$(arg color_format)" />
35-
<param name="color_resolution" type="string" value="$(arg color_resolution)" />
36-
<param name="fps" type="int" value="$(arg fps)" />
37-
<param name="point_cloud" type="bool" value="$(arg point_cloud)" />
38-
<param name="rgb_point_cloud" type="bool" value="$(arg rgb_point_cloud)" />
35+
<param name="color_resolution" type="string" value="$(arg color_resolution)" />
36+
<param name="fps" type="int" value="$(arg fps)" />
37+
<param name="point_cloud" type="bool" value="$(arg point_cloud)" />
38+
<param name="rgb_point_cloud" type="bool" value="$(arg rgb_point_cloud)" />
3939
<param name="sensor_sn" type="string" value="$(arg sensor_sn)" />
4040
<param name="tf_prefix" type="string" value="$(arg tf_prefix)" />
4141
<param name="recording_file" type="string" value="$(arg recording_file)" />

src/k4a_calibration_transform_data.cpp

Lines changed: 45 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
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

9794
int 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

281216
void 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

311247
tf2::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

316253
tf2::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-
367265
void 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

Comments
 (0)