00001 #include <apriltags_ros/apriltag_detector.h>
00002 #include <cv_bridge/cv_bridge.h>
00003 #include <sensor_msgs/image_encodings.h>
00004 #include <boost/foreach.hpp>
00005 #include <geometry_msgs/PoseStamped.h>
00006 #include <geometry_msgs/PoseArray.h>
00007 #include <apriltags_ros/AprilTagDetection.h>
00008 #include <apriltags_ros/AprilTagDetectionArray.h>
00009 #include <AprilTags/Tag16h5.h>
00010 #include <AprilTags/Tag25h7.h>
00011 #include <AprilTags/Tag25h9.h>
00012 #include <AprilTags/Tag36h9.h>
00013 #include <AprilTags/Tag36h11.h>
00014 #include <XmlRpcException.h>
00015
00016 namespace apriltags_ros{
00017
00018 AprilTagDetector::AprilTagDetector(ros::NodeHandle& nh, ros::NodeHandle& pnh): it_(nh){
00019 XmlRpc::XmlRpcValue april_tag_descriptions;
00020 if(!pnh.getParam("tag_descriptions", april_tag_descriptions)){
00021 ROS_WARN("No april tags specified");
00022 }
00023 else{
00024 try{
00025 descriptions_ = parse_tag_descriptions(april_tag_descriptions);
00026 } catch(XmlRpc::XmlRpcException e){
00027 ROS_ERROR_STREAM("Error loading tag descriptions: "<<e.getMessage());
00028 }
00029 }
00030
00031 if(!pnh.getParam("sensor_frame_id", sensor_frame_id_)){
00032 sensor_frame_id_ = "";
00033 }
00034
00035 std::string tag_family;
00036 pnh.param<std::string>("tag_family", tag_family, "36h11");
00037
00038 pnh.param<bool>("projected_optics", projected_optics_, false);
00039
00040 const AprilTags::TagCodes* tag_codes;
00041 if(tag_family == "16h5"){
00042 tag_codes = &AprilTags::tagCodes16h5;
00043 }
00044 else if(tag_family == "25h7"){
00045 tag_codes = &AprilTags::tagCodes25h7;
00046 }
00047 else if(tag_family == "25h9"){
00048 tag_codes = &AprilTags::tagCodes25h9;
00049 }
00050 else if(tag_family == "36h9"){
00051 tag_codes = &AprilTags::tagCodes36h9;
00052 }
00053 else if(tag_family == "36h11"){
00054 tag_codes = &AprilTags::tagCodes36h11;
00055 }
00056 else{
00057 ROS_WARN("Invalid tag family specified; defaulting to 36h11");
00058 tag_codes = &AprilTags::tagCodes36h11;
00059 }
00060
00061 tag_detector_= boost::shared_ptr<AprilTags::TagDetector>(new AprilTags::TagDetector(*tag_codes));
00062 image_sub_ = it_.subscribeCamera("image_rect", 1, &AprilTagDetector::imageCb, this);
00063 image_pub_ = it_.advertise("tag_detections_image", 1);
00064 detections_pub_ = nh.advertise<AprilTagDetectionArray>("tag_detections", 1);
00065 pose_pub_ = nh.advertise<geometry_msgs::PoseArray>("tag_detections_pose", 1);
00066 }
00067 AprilTagDetector::~AprilTagDetector(){
00068 image_sub_.shutdown();
00069 }
00070
00071 void AprilTagDetector::imageCb(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cam_info){
00072 cv_bridge::CvImagePtr cv_ptr;
00073 try{
00074 cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
00075 }
00076 catch (cv_bridge::Exception& e){
00077 ROS_ERROR("cv_bridge exception: %s", e.what());
00078 return;
00079 }
00080 cv::Mat gray;
00081 cv::cvtColor(cv_ptr->image, gray, CV_BGR2GRAY);
00082 std::vector<AprilTags::TagDetection> detections = tag_detector_->extractTags(gray);
00083 ROS_DEBUG("%d tag detected", (int)detections.size());
00084
00085 double fx;
00086 double fy;
00087 double px;
00088 double py;
00089 if (projected_optics_) {
00090
00091
00092 fx = cam_info->P[0];
00093 fy = cam_info->P[5];
00094 px = cam_info->P[2];
00095 py = cam_info->P[6];
00096 } else {
00097
00098
00099 fx = cam_info->K[0];
00100 fy = cam_info->K[4];
00101 px = cam_info->K[2];
00102 py = cam_info->K[5];
00103 }
00104
00105 if(!sensor_frame_id_.empty())
00106 cv_ptr->header.frame_id = sensor_frame_id_;
00107
00108 AprilTagDetectionArray tag_detection_array;
00109 geometry_msgs::PoseArray tag_pose_array;
00110 tag_pose_array.header = cv_ptr->header;
00111
00112 BOOST_FOREACH(AprilTags::TagDetection detection, detections){
00113 std::map<int, AprilTagDescription>::const_iterator description_itr = descriptions_.find(detection.id);
00114 if(description_itr == descriptions_.end()){
00115 ROS_WARN_THROTTLE(10.0, "Found tag: %d, but no description was found for it", detection.id);
00116 continue;
00117 }
00118 AprilTagDescription description = description_itr->second;
00119 double tag_size = description.size();
00120
00121 detection.draw(cv_ptr->image);
00122 Eigen::Matrix4d transform = detection.getRelativeTransform(tag_size, fx, fy, px, py);
00123 Eigen::Matrix3d rot = transform.block(0, 0, 3, 3);
00124 Eigen::Quaternion<double> rot_quaternion = Eigen::Quaternion<double>(rot);
00125
00126 geometry_msgs::PoseStamped tag_pose;
00127 tag_pose.pose.position.x = transform(0, 3);
00128 tag_pose.pose.position.y = transform(1, 3);
00129 tag_pose.pose.position.z = transform(2, 3);
00130 tag_pose.pose.orientation.x = rot_quaternion.x();
00131 tag_pose.pose.orientation.y = rot_quaternion.y();
00132 tag_pose.pose.orientation.z = rot_quaternion.z();
00133 tag_pose.pose.orientation.w = rot_quaternion.w();
00134 tag_pose.header = cv_ptr->header;
00135
00136 AprilTagDetection tag_detection;
00137 tag_detection.pose = tag_pose;
00138 tag_detection.id = detection.id;
00139 tag_detection.size = tag_size;
00140 tag_detection_array.detections.push_back(tag_detection);
00141 tag_pose_array.poses.push_back(tag_pose.pose);
00142
00143 tf::Stamped<tf::Transform> tag_transform;
00144 tf::poseStampedMsgToTF(tag_pose, tag_transform);
00145 tf_pub_.sendTransform(tf::StampedTransform(tag_transform, tag_transform.stamp_, tag_transform.frame_id_, description.frame_name()));
00146 }
00147 detections_pub_.publish(tag_detection_array);
00148 pose_pub_.publish(tag_pose_array);
00149 image_pub_.publish(cv_ptr->toImageMsg());
00150 }
00151
00152
00153 std::map<int, AprilTagDescription> AprilTagDetector::parse_tag_descriptions(XmlRpc::XmlRpcValue& tag_descriptions){
00154 std::map<int, AprilTagDescription> descriptions;
00155 ROS_ASSERT(tag_descriptions.getType() == XmlRpc::XmlRpcValue::TypeArray);
00156 for (int32_t i = 0; i < tag_descriptions.size(); ++i) {
00157 XmlRpc::XmlRpcValue& tag_description = tag_descriptions[i];
00158 ROS_ASSERT(tag_description.getType() == XmlRpc::XmlRpcValue::TypeStruct);
00159 ROS_ASSERT(tag_description["id"].getType() == XmlRpc::XmlRpcValue::TypeInt);
00160 ROS_ASSERT(tag_description["size"].getType() == XmlRpc::XmlRpcValue::TypeDouble);
00161
00162 int id = (int)tag_description["id"];
00163 double size = (double)tag_description["size"];
00164
00165 std::string frame_name;
00166 if(tag_description.hasMember("frame_id")){
00167 ROS_ASSERT(tag_description["frame_id"].getType() == XmlRpc::XmlRpcValue::TypeString);
00168 frame_name = (std::string)tag_description["frame_id"];
00169 }
00170 else{
00171 std::stringstream frame_name_stream;
00172 frame_name_stream << "tag_" << id;
00173 frame_name = frame_name_stream.str();
00174 }
00175 AprilTagDescription description(id, size, frame_name);
00176 ROS_INFO_STREAM("Loaded tag config: "<<id<<", size: "<<size<<", frame_name: "<<frame_name);
00177 descriptions.insert(std::make_pair(id, description));
00178 }
00179 return descriptions;
00180 }
00181
00182
00183 }