Lidar Camera Callibration in ROS
Install ROS indigo for Ubuntu 14.04
Download .bag file
Start roscore
To see the contents of the bag:
$ rosbag info 2016-11-22-14-32-13_test.bag
Understanding ROS message types in the bag:
rosmsg show sensor_msgs/PointCloud2
rosmsg show sensor_msgs/Image
rosmsg show sensor_msgs/CameraInfo
Script read_bag.py
to see the values in the rosbag
To publish the topics in the bag, run in a new terminal:
$ rosbag play 2016-11-22-14-32-13_test.bag
To see published topics:
$ rostopic list -v
To export images from rosbag file, ran the export.launch script
$ roslaunch export.launch
It stores images in .ros folder in home directory
To see the input video stream from the bag file:
$ rosrun image_view image_view image:=/sensors/camera/image_color
Now, we know the contents of the ros-bag.
A ROS package was created to store the entire project.
Reference:
http://wiki.ros.org/ROS/Tutorials
http://wiki.ros.org/rosbag/Cookbook
http://wiki.ros.org/roslaunch/XML/node
Rosbag file is played in one terminal
Publishing Camera Calibration data with bag file using camera_calibration package:
$ rosrun camera_calibration cameracalibrator.py --size=7x5 --square=0.050 image:=/sensors/camera/image_color camera:=/sensors/camera/camera_info --no-service-check
Note: —no-service-check added to not wait for /sensors/camera/camera_info/set_camera_info
OUTPUT
Stored in ost.yaml file, udpated camera calibration matrices
image_width: 964
image_height: 724
camera_name: narrow_stereo
camera_matrix K:
rows: 3
cols: 3
data: [481.228482, 0.000000, 456.782531, 0.000000, 481.158298, 364.412635, 0.000000, 0.000000, 1.000000]
distortion_model : plumb_bob
distortion_coefficients D:
rows: 1
cols: 5
data: [-0.195875, 0.065588, 0.003400, 0.000218, 0.000000]
rectification_matrix R:
rows: 3
cols: 3
data: [1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000]
projection_matrix P:
rows: 3
cols: 4
data: [422.024750, 0.000000, 460.332694, 0.000000, 0.000000, 429.271149, 368.531509, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000]
Note: Camera Calibration matrices can also be calculated using opencv apis, once the images are collected from bag file using export.launch script.
change_camera_info.py
, create a new output ros bag with updated calibration matrices.
$ rosrun bag_tools change_camera_info.py ../2016-11-22-14-32-13_test_orig.bag ../2016-11-22-14-32-13_test_cameracalibrated_out.bag /sensors/camera/camera_info=../config/ost.yaml
Checked using read_bag.py
script that the values are updated.
To rectify images, image_proc is used. The image_proc_view.launch
file is used to read the calibrated bag file and rectify the stream
image_proc subscribes to image_color and camera_info and publishes image_rect_color
<node name="cam_image_proc" pkg="image_proc" type="image_proc" respawn="false" ns="/sensors/camera">
</node>
To check if image proc is publishing image_rect_color:
$ rostopic info /sensors/camera/image_rect_color
Type: sensor_msgs/Image
Publishers:
* /sensors/camera/cam_image_proc
Subscribers:
* /extract_rect_image
image_view subscribes to image_rect_color and extracts the rectified images from the stream
<node name="extract_rect_image" pkg="image_view" type="extract_images" respawn="false" required="true" output="screen" cwd="ROS_HOME">
<remap from="image" to="/sensors/camera/image_rect_color"></remap>
</node>
image_view stores file in home directory in .ros folder, few randomly chosen rectified images were stored in images directory, as it creates a lot of images for the whole bag file. Once such rectified image is shown below:
To find the 6DoF between Lidar and Camera, 2D-3D point correspondences methods are researched online.
One such reference: https://arxiv.org/pdf/1705.09785.pdf
The initial point set corresponding to Lidar and Camera is calculated in following steps:
a. rviz is used to run the input rosbag file with fixed frame set to velodyne. The config is saved as visualize_bags.rviz
b. The visualize.launch
file is used to start rviz with image_view node, to pause and collect points from lidar point cloud and image
c. So when a good correspondance between the point cloud and the image is seen, a frame is saved using image_view
and the corresponding lidar point selected is published using rviz:
$ rostopic echo /clicked_point
One such clicked point is :
header:
seq: 1
stamp:
secs: 1479853956
nsecs: 650794413
frame_id: velodyne
point:
x: 1.83453130722
y: 0.055240329355
z: -0.0346876457334
d. The corresponding point from image frame saved using image_view is collected using the get_image_points.py
(361, 334)
e. Six such points were collected between Lidar and Camera on the checker board.
Next step is to use these points to minimize the error to solve the equation 1
described in the paper https://arxiv.org/pdf/1705.09785.pdf
using Sequential Least SQuares Programming (SLSQP) from scipy for constrained optimization
lidar3D_camera2D_points.py
file is used to calculate the transformation vector between camera and velodyne frame of references
the constraints are defined in lidar_image_calibration_data.json
file along with the input points, it limits the translation between the corresponding points to be between +/- 4 meters and rotations to be a maximum of 360 degrees(2 pi)
calibrate-camera-lidar.launch
is used to lauch the script lidar3D_camera2D_points.py
Optimization techniques to get to global minima was researched online
Once the final state vector or static transform t+R is found, as shown below, it is used in the next step to create the lidar image overlay
Final static transform(tx, ty, tz, Ry, Rp, Rr):
[-0.211852 -0.09627462 -0.30093704 3.70629347 4.77717425 4.05932338]
lidar_overlay_image.py
script is used to generate the output image desired.The script subsribes to the nodes that it needs information from:
And it publishes -
The script has callbacks defined for each of the subscribe methods (except tf.TransformListener())
image_callback() listens to the input rectified image. It uses static tranform and velodyne frame data to calculate the rotated point in the camera frame, which in turn uses PinholeCameraModel to convert all the 3D rotated points in one frame to 2D - uv points. These points on the ros converted cv_image is then overlaid with lines to get the desired output.
Tutorials from Indigo API tf, Indigo API transformations
image_callback() then also publishes this lidar point overlaid output, which the image_view node listens to show the video
The output video viewed from image_view node seems laggy, due to too much processing. Some of it can be reduced by removing the image_proc node and instead creating a rectified rosbag and using it as input to the task2_image_overlay.launch
. The synchronization can definitely be further worked upon to improve.
For debugging purposes, the rqt_graph and view_frames shown below was used to understand the nodes connectivity and transform
$ rosrun tf view_frames