36 #include <geometry_msgs/TransformStamped.h> 37 #include <geometry_msgs/PoseWithCovarianceStamped.h> 38 #include <sensor_msgs/Image.h> 39 #include <visualization_msgs/Marker.h> 40 #include <visualization_msgs/MarkerArray.h> 42 #include <aruco_pose/MarkerArray.h> 43 #include <aruco_pose/Marker.h> 45 #include <opencv2/opencv.hpp> 53 using sensor_msgs::Image;
54 using sensor_msgs::CameraInfo;
55 using aruco_pose::MarkerArray;
70 geometry_msgs::PoseWithCovarianceStamped
pose_;
91 img_pub_ = nh_priv_.
advertise<sensor_msgs::Image>(
"image", 1,
true);
92 markers_pub_ = nh_priv_.
advertise<aruco_pose::MarkerArray>(
"markers", 1,
true);
94 board_ = cv::makePtr<cv::aruco::Board>();
96 static_cast<cv::aruco::PREDEFINED_DICTIONARY_NAME>(nh_priv_.
param(
"dictionary", 2)));
97 camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
99 std::string type, map;
100 type = nh_priv_.
param<std::string>(
"type",
"map");
101 transform_.child_frame_id = nh_priv_.
param<std::string>(
"frame_id",
"aruco_map");
102 known_tilt_ = nh_priv_.
param<std::string>(
"known_tilt",
"");
103 auto_flip_ = nh_priv_.
param(
"auto_flip",
false);
104 image_width_ = nh_priv_.
param(
"image_width" , 2000);
105 image_height_ = nh_priv_.
param(
"image_height", 2000);
106 image_margin_ = nh_priv_.
param(
"image_margin", 200);
107 image_axis_ = nh_priv_.
param(
"image_axis",
true);
108 markers_parent_frame_ = nh_priv_.
param<std::string>(
"markers/frame_id", transform_.child_frame_id);
109 markers_frame_ = nh_priv_.
param<std::string>(
"markers/child_frame_id_prefix",
"");
114 param(nh_priv_,
"map", map);
116 }
else if (type ==
"gridboard") {
123 pose_pub_ = nh_priv_.
advertise<geometry_msgs::PoseWithCovarianceStamped>(
"pose", 1);
124 vis_markers_pub_ = nh_priv_.
advertise<visualization_msgs::MarkerArray>(
"visualization", 1,
true);
125 debug_pub_ = it_priv.
advertise(
"debug", 1);
127 image_sub_.
subscribe(nh_,
"image_raw", 1);
128 info_sub_.
subscribe(nh_,
"camera_info", 1);
129 markers_sub_.
subscribe(nh_,
"markers", 1);
137 vis_markers_pub_.publish(vis_array_);
142 void callback(
const sensor_msgs::ImageConstPtr& image,
143 const sensor_msgs::CameraInfoConstPtr& cinfo,
144 const aruco_pose::MarkerArrayConstPtr& markers)
147 int count = markers->markers.size();
148 std::vector<int>
ids;
149 std::vector<std::vector<cv::Point2f>> corners;
150 cv::Vec3d rvec, tvec;
153 if (markers->markers.empty())
goto publish_debug;
156 corners.reserve(count);
158 for(
auto const &marker : markers->markers) {
159 ids.push_back(marker.id);
160 std::vector<cv::Point2f> marker_corners = {
161 cv::Point2f(marker.c1.x, marker.c1.y),
162 cv::Point2f(marker.c2.x, marker.c2.y),
163 cv::Point2f(marker.c3.x, marker.c3.y),
164 cv::Point2f(marker.c4.x, marker.c4.y)
166 corners.push_back(marker_corners);
169 if (known_tilt_.empty()) {
173 if (!valid)
goto publish_debug;
175 transform_.header.stamp = markers->header.stamp;
176 transform_.header.frame_id = markers->header.frame_id;
177 pose_.header = transform_.header;
178 fillPose(pose_.pose.pose, rvec, tvec);
182 Mat obj_points, img_points;
185 if (obj_points.empty())
goto publish_debug;
187 double center_x = 0, center_y = 0, center_z = 0;
190 valid = solvePnP(obj_points, img_points, camera_matrix_, dist_coeffs_, rvec, tvec,
false);
191 if (!valid)
goto publish_debug;
195 geometry_msgs::TransformStamped snap_to = tf_buffer_.
lookupTransform(markers->header.frame_id,
197 snapOrientation(transform_.transform.rotation, snap_to.transform.rotation, auto_flip_);
202 geometry_msgs::TransformStamped shift;
203 shift.transform.translation.x = -center_x;
204 shift.transform.translation.y = -center_y;
205 shift.transform.translation.z = -center_z;
206 shift.transform.rotation.w = 1;
210 tvec[0] = transform_.transform.translation.x;
211 tvec[1] = transform_.transform.translation.y;
212 tvec[2] = transform_.transform.translation.z;
214 transform_.header.stamp = markers->header.stamp;
215 transform_.header.frame_id = markers->header.frame_id;
216 pose_.header = transform_.header;
220 if (!transform_.child_frame_id.empty()) {
231 _drawAxis(mat, camera_matrix_, dist_coeffs_, rvec, tvec, 1.0);
234 out_msg.
header.frame_id = image->header.frame_id;
235 out_msg.
header.stamp = image->header.stamp;
249 for (
int i = 0; i < obj_points.rows; i++) {
250 sum_x += obj_points.at<
float>(i, 0);
251 sum_y += obj_points.at<
float>(i, 1);
252 sum_z += obj_points.at<
float>(i, 2);
255 center_x = sum_x / obj_points.rows;
256 center_y = sum_y / obj_points.rows;
257 center_z = sum_z / obj_points.rows;
259 for (
int i = 0; i < obj_points.rows; i++) {
260 obj_points.at<
float>(i, 0) -= center_x;
261 obj_points.at<
float>(i, 1) -= center_y;
262 obj_points.at<
float>(i, 2) -= center_z;
268 std::ifstream
f(filename);
276 while (std::getline(f, line)) {
278 double length, x, y, z, yaw, pitch, roll;
280 std::istringstream
s(line);
290 NODELET_DEBUG(
"Skipping line as a comment: %s", line.c_str());
292 }
else if (isdigit(first)) {
301 throw std::runtime_error(
"Malformed input");
304 if (!(s >>
id >> length >> x >> y)) {
306 "Each marker must have at least id, length, x, y fields", line.c_str());
311 NODELET_DEBUG(
"No z coordinate provided for marker %d, assuming 0",
id);
315 NODELET_DEBUG(
"No yaw provided for marker %d, assuming 0",
id);
319 NODELET_DEBUG(
"No pitch provided for marker %d, assuming 0",
id);
323 NODELET_DEBUG(
"No roll provided for marker %d, assuming 0",
id);
326 addMarker(
id, length, x, y, z, yaw, pitch, roll);
329 NODELET_INFO(
"loading %s complete (%d markers)", filename.c_str(),
static_cast<int>(board_->ids.size()));
338 double markers_side, markers_sep_x, markers_sep_y;
339 std::vector<int> marker_ids;
340 markers_x = nh.
param(
"markers_x", 10);
341 markers_y = nh.
param(
"markers_y", 10);
342 first_marker = nh.
param(
"first_marker", 0);
344 param(nh,
"markers_side", markers_side);
345 param(nh,
"markers_sep_x", markers_sep_x);
346 param(nh,
"markers_sep_y", markers_sep_y);
348 if (nh.
getParam(
"marker_ids", marker_ids)) {
349 if ((
unsigned int)(markers_x *
markers_y) != marker_ids.size()) {
350 NODELET_FATAL(
"~marker_ids length should be equal to ~markers_x * ~markers_y");
355 marker_ids.resize(markers_x * markers_y);
356 for (
int i = 0; i < markers_x *
markers_y; i++)
358 marker_ids.at(i) = first_marker++;
362 double max_y = markers_y * markers_side + (markers_y - 1) * markers_sep_y;
365 double x_pos = x * (markers_side + markers_sep_x);
366 double y_pos = max_y - y * (markers_side + markers_sep_y) - markers_side;
367 NODELET_INFO(
"add marker %d %g %g", marker_ids[y * markers_y + x], x_pos, y_pos);
368 addMarker(marker_ids[y * markers_y + x], markers_side, x_pos, y_pos, 0, 0, 0, 0);
389 double yaw,
double pitch,
double roll)
392 int num_markers = board_->dictionary->bytesList.rows;
393 if (num_markers <=
id) {
394 NODELET_ERROR(
"Marker id %d is not in dictionary; current dictionary contains %d markers. " 395 "Please see https://github.com/CopterExpress/clover/blob/master/aruco_pose/README.md#parameters for details",
400 if (std::count(board_->ids.begin(), board_->ids.end(), id) > 0) {
406 q.
setRPY(roll, pitch, yaw);
413 double halflen = length / 2;
423 vector<cv::Point3f> obj_points = {
424 cv::Point3f(p0.x(), p0.y(), p0.z()),
425 cv::Point3f(p1.x(), p1.y(), p1.z()),
426 cv::Point3f(p2.x(), p2.y(), p2.z()),
427 cv::Point3f(p3.x(), p3.y(), p3.z())
430 board_->ids.push_back(
id);
431 board_->objPoints.push_back(obj_points);
434 if (!markers_frame_.empty()) {
435 geometry_msgs::TransformStamped marker_transform;
437 marker_transform.child_frame_id = markers_frame_ + std::to_string(
id);
439 markers_transforms_.push_back(marker_transform);
443 aruco_pose::Marker marker;
446 marker.pose.position.x = x;
447 marker.pose.position.y = y;
448 marker.pose.position.z = z;
450 markers_.markers.push_back(marker);
453 visualization_msgs::Marker vis_marker;
454 vis_marker.header.frame_id = transform_.child_frame_id;
455 vis_marker.action = visualization_msgs::Marker::ADD;
456 vis_marker.id = vis_array_.markers.size();
457 vis_marker.ns =
"aruco_map_marker";
458 vis_marker.type = visualization_msgs::Marker::CUBE;
459 vis_marker.scale.x =
length;
460 vis_marker.scale.y =
length;
461 vis_marker.scale.z = 0.001;
462 vis_marker.color.r = 1;
463 vis_marker.color.g = 0.5;
464 vis_marker.color.b = 0.5;
465 vis_marker.color.a = 0.8;
466 vis_marker.pose.position.x = x;
467 vis_marker.pose.position.y = y;
468 vis_marker.pose.position.z = z;
470 vis_marker.frame_locked =
true;
471 vis_array_.markers.push_back(vis_marker);
483 if (!markers_transforms_.empty()) {
490 markers_pub_.
publish(markers_);
495 cv::Size size(image_width_, image_height_);
499 if (!board_->ids.empty()) {
504 image.create(size, CV_8UC1);
505 image.setTo(cv::Scalar::all(255));
void loadMap(std::string filename)
CV_EXPORTS_W void getBoardObjectAndImagePoints(const Ptr< Board > &board, InputArrayOfArrays detectedCorners, InputArray detectedIds, OutputArray objPoints, OutputArray imgPoints)
Given a board configuration and a set of detected markers, returns the corresponding image points and...
void snapOrientation(geometry_msgs::Quaternion &to, const geometry_msgs::Quaternion &from, bool auto_flip=false)
#define NODELET_ERROR(...)
aruco_pose::MarkerArray markers_
void createGridBoard(ros::NodeHandle &nh)
#define NODELET_WARN(...)
static void param(ros::NodeHandle nh, const std::string ¶m_name, T ¶m_val)
uint32_t getNumSubscribers() const
void callback(const sensor_msgs::ImageConstPtr &image, const sensor_msgs::CameraInfoConstPtr &cinfo, const aruco_pose::MarkerArrayConstPtr &markers)
ros::NodeHandle & getNodeHandle() const
void fillPose(geometry_msgs::Pose &pose, const cv::Vec3d &rvec, const cv::Vec3d &tvec)
void addMarker(int id, double length, double x, double y, double z, double yaw, double pitch, double roll)
Publisher advertise(const std::string &base_topic, uint32_t queue_size, bool latch=false)
message_filters::Subscriber< Image > image_sub_
ros::NodeHandle & getPrivateNodeHandle() const
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
static void parseCameraInfo(const sensor_msgs::CameraInfoConstPtr &cinfo, cv::Mat &matrix, cv::Mat &dist)
tf2_ros::Buffer tf_buffer_
CV_EXPORTS Ptr< Dictionary > getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME name)
Returns one of the predefined dictionaries defined in PREDEFINED_DICTIONARY_NAME. ...
sensor_msgs::ImagePtr toImageMsg() const
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val) const
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
void publish(const boost::shared_ptr< M > &message) const
void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec, InputArray _tvec, float length)
#define NODELET_WARN_THROTTLE(rate,...)
void setRPY(const tfScalar &roll, const tfScalar &pitch, const tfScalar &yaw)
bool getParam(const std::string &key, std::string &s) const
tf2_ros::TransformBroadcaster br_
static void quaternionTFToMsg(const Quaternion &bt, geometry_msgs::Quaternion &msg)
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
geometry_msgs::TransformStamped transform_
image_transport::Publisher debug_pub_
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void publish(const sensor_msgs::Image &message) const
vector< geometry_msgs::TransformStamped > markers_transforms_
std::string markers_parent_frame_
#define NODELET_INFO(...)
void transformToPose(const geometry_msgs::Transform &transform, geometry_msgs::Pose &pose)
cv::Ptr< cv::aruco::Board > board_
CV_EXPORTS_W void drawDetectedMarkers(InputOutputArray image, InputArrayOfArrays corners, InputArray ids=noArray(), Scalar borderColor=Scalar(0, 255, 0))
Draw detected markers in image.
boost::shared_ptr< message_filters::Synchronizer< SyncPolicy > > sync_
void subscribe(ros::NodeHandle &nh, const std::string &topic, uint32_t queue_size, const ros::TransportHints &transport_hints=ros::TransportHints(), ros::CallbackQueueInterface *callback_queue=0)
ros::Publisher markers_pub_
message_filters::Subscriber< MarkerArray > markers_sub_
CV_EXPORTS_W int estimatePoseBoard(InputArrayOfArrays corners, InputArray ids, const Ptr< Board > &board, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess=false)
Pose estimation for a board of markers.
message_filters::Subscriber< CameraInfo > info_sub_
ROSCPP_DECL void shutdown()
geometry_msgs::PoseWithCovarianceStamped pose_
void publishMarkersFrames()
void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize, int borderBits, bool drawAxis)
#define NODELET_FATAL(...)
tf2_ros::TransformListener tf_listener_
std::string markers_frame_
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
#define NODELET_DEBUG(...)
static void transformTFToMsg(const Transform &bt, geometry_msgs::Transform &msg)
tf2_ros::StaticTransformBroadcaster static_br_
void fillTransform(geometry_msgs::Transform &transform, const cv::Vec3d &rvec, const cv::Vec3d &tvec)
void alignObjPointsToCenter(Mat &obj_points, double ¢er_x, double ¢er_y, double ¢er_z) const
message_filters::sync_policies::ExactTime< Image, CameraInfo, MarkerArray > SyncPolicy
visualization_msgs::MarkerArray vis_array_
ros::Publisher vis_markers_pub_