(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Xtion/Kinect Camera TF Calibration

Description: Explains multiple ways of vision-based calibration for coordinates between head-mount camera and hands. With an advanced method you can even fine-tune joint offsets.

Tutorial Level: ADVANCED

Next Tutorial: Install or update software on ControllerBox

NOTE: All the tutorials available under the URL http://wiki.ros.org/rtmros_nextage/Tutorials are applicable to the multiple products of Kawada Industries; Hiro (only with the one that opensource software is installed) and NEXTAGE OPEN. To simplify the notation in the rest of the tutorials, we use HiroNXO to appoint the aforementioned robots.


Two different approaches are discussed in this page for HiroNXO's whole-body calibration (i.e. joint-level calibration is done at proprietary module level).

As always, we recommend any new feature to be tried on simulator first before the real robot. Use Gazebo to confirm it works then try it on the real robot.

Large part of this tutorial page uses NEXTAGE as an example. Replace as needed as always.

Xtion/Kinect Camera TF Calibration

Note that the work progress while writing this section can be seen in this ticket.

Xtion/Kinect on Gazebo Simulation

Since Xtion/Kinect is well intergrated to ROS/Gazebo, it is also easy for HiroNXO to incorporate those sensors on the simulation.

Launch Gazebo with Nextage robot model

$ roslaunch nextage_gazebo nextage_world.launch

Spawn models of Kinect and chekcerboard

After the robot finished the initial motion in Gazebo, launch gazebo_kinect_checkerboard_chest.launch or gazebo_kinect_checkerboard_chest.launch to spawn Kinect and the checkerborad models in Gazebo.

$ roslaunch nextage_calibration gazebo_kinect_checkerboard_chest.launch   #chest
$ roslaunch nextage_calibration gazebo_kinect_checkerboard_waist.launch   #waist


Run checkerboard detector

$ roslaunch nextage_calibration camera_checkerboard_chest.launch launch_openni2:=false
$ roslaunch nextage_calibration camera_checkerboard_waist.launch launch_openni2:=false


Run RViz

$ rviz -d `rospack find nextage_calibration`/conf/calibration.rviz


Run tf_echo to get TF between camera link and parent link.

tf_echo for TF from camera_link to HEAD_JOINT1_Link:

$ rosrun tf tf_echo HEAD_JOINT1_Link camera_link

Example of tf_echo result:

At time 93.641
- Translation: [0.101, -0.014, 0.199]
- Rotation: in Quaternion [-0.001, 0.704, 0.002, 0.710]
            in RPY (radian) [0.126, 1.563, 0.130]
            in RPY (degree) [7.223, 89.561, 7.477]

After everything finished, press Ctrl-C to quit all nodes.

Xtion/Kinect on a real robot

Get the robot ready

Since we use ROS, run rtm_ros_bridge on your PC (tutorial).

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%

Run joint-level calibration. After the encoders check has finished, move the arms to the off-pose since we don't need them for now.

$ ipython -i `rospack find nextage_ros_bridge`/script/nextage.py -- --host nextage

(many connection messages)

[hrpsys.py] initialized successfully


In [1]: robot.checkEncoders()
In [2]: robot.goOffPose()
In [3]: robot.servoOff()

Install checkerboard on the robot

First, you must confirm the servo has turned off.

And then install a checkerboard on chest or waist of Nextage robot as shown below.

Checkerboard on chest



Checkerboard on waist


Turn on servo to keep the angle of CHEST_JOINT0 as 0

You should keep the angle of CHEST_JOINT0 as 0 [deg] when you use the checkerboard on chest.

In [1]: robot.servoOn()
In [2]: robot.goInitial()

Turn the camera to the checkerboard

Turn the camera so that the checkerboard appears using joint control.

Example of commands for using joint control

In [1]: robot.setJointAnglesOfGroup( 'head', [ 0.0, 45.0 ], 5.0 )
In [2]: robot.getJointAngles()

Run checkerboard detector


$ roslaunch nextage_calibration camera_checkerboard_chest.launch  #chest
$ roslaunch nextage_calibration camera_checkerboard_waist.launch  #waist


$ roslaunch nextage_calibration camera_checkerboard_chest.launch launch_openni2:=false  #chest
$ roslaunch nextage_calibration camera_checkerboard_waist.launch launch_openni2:=false  #waist

Checkerboard on chest


Checkerboard on waist


Run RViz

Run RViz for checking the operation status of the each functions.

$ rviz -d `rospack find nextage_calibration`/conf/calibration.rviz

RViz with checkerboard on chest


RViz with checkerboard on waist


Run tf_echo to get TF between camera link and parent link.

tf_echo for TF from camera_link to HEAD_JOINT1_Link

$ rosrun tf tf_echo HEAD_JOINT1_Link camera_link

Example of tf_echo result

At time 93.641
- Translation: [0.101, -0.014, 0.199]
- Rotation: in Quaternion [-0.001, 0.704, 0.002, 0.710]
            in RPY (radian) [0.126, 1.563, 0.130]
            in RPY (degree) [7.223, 89.561, 7.477]

Go off-pose and Turn off the servo

Move the arms to the off-pose and turn off the servo.

In [1]: robot.goOffPose()
In [2]: robot.servoOff()

Quit all nodes

Ctrl-C to quit all nodes.

Finally, this step is the reason why we've gone through all the steps above -- let's apply the extrinsic camera parameters!

Apply the TF values of the real robot Xtion/Kinect camera through xtion_kinect.launch as arguments as below.

( Units: TF_COORD_XYZ [m], TF_COORD_RPY [rad] )

First don't forget to run rtm_ros_bridge on your Ubuntu PC.

$ roslaunch nextage_ros_bridge nextage_ros_bridge_real.launch nameserver:=%HOSTNAME%

For Xtion:

$ roslaunch nextage_calibration xtion_kinect.launch TF_COORD_XYZ:='0.101 -0.014 0.199' TF_COORD_RPY:='0.126 1.563 0.130'

For Kinect, only difference is that the argument launch_openni2 needs to be false:

$ roslaunch nextage_calibration xtion_kinect.launch TF_COORD_XYZ:='0.101 -0.014 0.199' TF_COORD_RPY:='0.126 1.563 0.130' launch_openni2:=false

Optional calibration for Xtion camera

When your camera has no calibration infomation, you can still run camera calibration using the method in this page.

Calibrate Head-mount Camera and Hands coordinates (Work in progess)


(Feb 2017 update There's a claim that the methods described in this page is a bit too advanced. There's an ongoing work to provide calibration package so that users of HiroNXO wouldn't have to bother the same task.)

You'll learn/create:

  • how to run head-mount camera ROS nodes that return pointclouds.

  • Hand-eye calibration
  • a modified URDF with links supporting cameras. And if you wish, you can put the calibration data into a new dedicated package.

Get the image and pointcloud from head-mount camera

You can get the output from HiroNXO's default head-mount camera via ROS by the following.


Hand-eye only calibration


More elaborated calibration

Before Calibration

Manually edit the robot's URDF (hironx_moveit_config/models/HiroNX.urdf for Hironx | nextage_description/urdf/NextageOpen.urdf for NEXTAGE Open) temporarily to add camera frames, joints and links.

Here's an example of HiroNX.urdf. The coordinate depends on your robot's setting.

+++ b/hironx_moveit_config/models/HiroNX.urdf
        @@ -652,4 +652,37 @@
             <limit lower="-0.523599" upper="0.523599" effort="100"
        velocity="0.5" />
             <dynamics damping="0.2" friction="0" />
        +  <link name="camera_link"/>
        +  <link name="camera_depth_frame"/>
        +  <link name="camera_rgb_frame"/>
        +  <link name="camera_depth_optical_frame"/>
        +  <link name="camera_rgb_optical_frame"/>
        +  <joint name="head_to_kinect_joint" type="fixed">
        +    <origin rpy="0 0 0" xyz="0.05 0.08 0.1"/>
        +    <parent link="HEAD_JOINT1_Link" />
        +    <child link="camera_link"/>
        +  </joint>
        +  <joint name="camera_base_link" type="fixed">
        +    <origin rpy="0 0 0" xyz="0 -0.02 0"/>
        +    <parent link="camera_link"/>
        +    <child link="camera_depth_frame"/>
        +  </joint>
        +  <joint name="camera_base_link1" type="fixed">
        +    <origin rpy="0 0 0" xyz="0 -0.045 0"/>
        +    <parent link="camera_link"/>
        +    <child link="camera_rgb_frame"/>

Do calibration

Collect Calibration Data

1. Make sure your hironx_ros_bridge node is running (hironx_ros_bridge_sim.launch or hironx_ros_bridge_real.launch).

2. Upload URDF to robot_description to the Parameter Server.

$ roslaunch hironx_calibration upload_urdf.launch

This launch will simply overwrite default robot_description parameter (generated from the whole-body COLLADA file without kinect frames) .

3. Since this changes default URDF model, we need to run robot_state_publisher to publish tf with the updated URDF. We also need to run kinect "without" tf publisher (TODO why?):

$ rosrun robot_state_publisher robot_state_publisher __name:=hrpsys_state_publisher
$ roslaunch hironx_kinect.launch publish_tf:=false

4. Finally, run calibration node. /!\ This will move robot arm and be careful, and this motion may cause self collision. Please watch the robot carefully and be ready to push emergency stop button.

$ roslaunch hironx_calibration capture_data.launch

Run calibration step

Run calibration program, this program uses a calibration data file generated during the calibration process and stored under tmp (ie. /tmp/hironx_calibration/cal_measurements.bag), meaning that if you reboot the computer, then you have to restart from the previous step.

$ roslaunch hironx_calibration calibrate_hironx.launch

Finally we get following results (in this example, errors in head_camera is no small enough...)

        Computing Jacobian.. (cycle #7)         RMS error: 0.962
        Errors Breakdown:
          head_camera: 9.528937
          arm_chain: 0.007444
        [INFO] [WallTime: 1411546682.199861] Writing original urdf to
        Joint removed: arm_chain_cb
         xyz: [-0.08738123167896214, 0.0, -0.001496522196753334]
         rpy: (-1.6950600508827331, 0.29224067030639833, -0.057327689173656195)
        Updating xyz for head_to_kinect_joint
         old: [0.0, 0.0, 0.1]
         new: [0.062367406594775215, 0.07536259323525744, 0.12330820494245931]
        Updating rpy for head_to_kinect_joint
         old: [0.0, 0.0, 0.0]
         new: (-0.03346088258363324, 0.1383150116022393, -0.014468436806528857)
        The following joints weren't updated:

And change arguments of static_tf_publisher at https://github.com/start-jsk/rtmros_hironx/pull/252/files#diff-921a60a71a65fcd10fcb361bcd0971b2R8

Scenes from calibration with the robot

Checker board is held like this through the entire calibration process. NOTE that in this picture the head is replaced with xtion with a custom cover. https://cloud.githubusercontent.com/assets/493276/4386658/b3145a82-43d2-11e4-96ca-34b6bea430e4.JPG

The image below is before the calibration (with no offset between head and camera): https://cloud.githubusercontent.com/assets/493276/4385494/3abf7d4e-43c5-11e4-9b90-be205e128898.png

Post calibration (not accurate enough maybe due to using xtion (carmine), instead of Kinect...) https://cloud.githubusercontent.com/assets/493276/4385505/4db15774-43c5-11e4-87d2-2648b9e34796.png

Restart everything

It is safe to restart everything after calibration since we have changed robot_description and killed default robot_state_publisher).

(Advanced) Create calibration package

The calibration package hironx_calibration we've just gone through is created by following sets of commands and operations. For Hironx robot you can just utilize hironx_calibration package for your own robot as well, but for NEXTAGE Open you may create a similar package by following this.

Use calibration_setup_helper package to generate a package (example with Hironx:

$ rosrun calibration_setup_helper calibration_setup_helper.py `rospack find hironx_moveit_config`/config/HiroNX.urdf --base-link CHEST_JOINT0_Link --arm-tip-link RARM_JOINT5_Link --head-tip-link HEAD_JOINT1_Link  --arm-controller=rarm_controller/command  --head-controller=head_controller/command --head-camera-frame camera_rgb_optical_frame  --head-camera-joint head_to_kinect_joint

Make samples. At first, make sure turning off servo for all joints except CHEST_JOINT0 (in ipython terminal below):

IN[1]: robot.servoOff()
IN[2]: robot.servoOn('CHEST_JOINT0')

And move arm and press enter to store samples, usually we take 15-30 samples but not sure about appropriate number of samples.

$ rosrun hironx_calibration make_samples.py

Wiki: rtmros_nextage/Tutorials/CalibrateKinect (last edited 2017-09-21 08:08:38 by Yosuke Yamamoto)