21 #include <unordered_map> 22 #include <unordered_set> 33 #include <dynamic_reconfigure/server.h> 34 #include <geometry_msgs/Vector3.h> 35 #include <geometry_msgs/Pose.h> 36 #include <geometry_msgs/PoseStamped.h> 37 #include <geometry_msgs/PoseWithCovarianceStamped.h> 38 #include <geometry_msgs/TransformStamped.h> 39 #include <visualization_msgs/Marker.h> 40 #include <visualization_msgs/MarkerArray.h> 42 #include <opencv2/opencv.hpp> 43 #include <opencv2/highgui.hpp> 45 #include <opencv2/imgproc/imgproc.hpp> 46 #include <opencv2/calib3d/calib3d.hpp> 48 #include <aruco_pose/Marker.h> 49 #include <aruco_pose/MarkerArray.h> 50 #include <aruco_pose/DetectorConfig.h> 61 std::unique_ptr<tf2_ros::TransformBroadcaster>
br_;
64 std::shared_ptr<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>
dyn_srv_;
92 dictionary = nh_priv_.
param(
"dictionary", 2);
93 estimate_poses_ = nh_priv_.
param(
"estimate_poses",
true);
94 send_tf_ = nh_priv_.
param(
"send_tf",
true);
95 if (estimate_poses_ && !nh_priv_.
getParam(
"length", length_)) {
96 NODELET_FATAL(
"can't estimate marker's poses as ~length parameter is not defined");
101 known_tilt_ = nh_priv_.
param<std::string>(
"known_tilt",
"");
102 auto_flip_ = nh_priv_.
param(
"auto_flip",
false);
104 frame_id_prefix_ = nh_priv_.
param<std::string>(
"frame_id_prefix",
"aruco_");
106 camera_matrix_ = cv::Mat::zeros(3, 3, CV_64F);
115 debug_pub_ = it_priv.
advertise(
"debug", 1);
116 markers_pub_ = nh_priv_.
advertise<aruco_pose::MarkerArray>(
"markers", 1);
117 vis_markers_pub_ = nh_priv_.
advertise<visualization_msgs::MarkerArray>(
"visualization", 1);
120 dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_);
121 dynamic_reconfigure::Server<aruco_pose::DetectorConfig>::CallbackType cb;
124 dyn_srv_->setCallback(cb);
130 void imageCallback(
const sensor_msgs::ImageConstPtr& msg,
const sensor_msgs::CameraInfoConstPtr &cinfo)
132 if (!enabled_)
return;
137 vector<vector<cv::Point2f>> corners, rejected;
139 vector<cv::Point3f> obj_points;
140 geometry_msgs::TransformStamped snap_to;
145 array_.header.stamp = msg->header.stamp;
146 array_.header.frame_id = msg->header.frame_id;
147 array_.markers.clear();
149 if (ids.size() != 0) {
153 if (estimate_poses_) {
158 if (!length_override_.empty()) {
159 for (
unsigned int i = 0; i < ids.size(); i++) {
161 auto item = length_override_.find(
id);
162 if (item != length_override_.end()) {
163 vector<cv::Vec3d> rvecs_current, tvecs_current;
164 vector<vector<cv::Point2f>> corners_current;
165 corners_current.push_back(corners[i]);
167 camera_matrix_, dist_coeffs_,
168 rvecs_current, tvecs_current);
169 rvecs[i] = rvecs_current[0];
170 tvecs[i] = tvecs_current[0];
175 if (!known_tilt_.empty()) {
177 snap_to = tf_buffer_->lookupTransform(msg->header.frame_id, known_tilt_,
185 array_.markers.reserve(ids.size());
186 aruco_pose::Marker marker;
187 geometry_msgs::TransformStamped transform;
188 transform.header.stamp = msg->header.stamp;
189 transform.header.frame_id = msg->header.frame_id;
191 for (
unsigned int i = 0; i < ids.size(); i++) {
196 if (estimate_poses_) {
197 fillPose(marker.pose, rvecs[i], tvecs[i]);
200 if (!known_tilt_.empty() && !snap_to.header.frame_id.empty()) {
201 snapOrientation(marker.pose.orientation, snap_to.transform.rotation, auto_flip_);
209 if (map_markers_ids_.find(ids[i]) == map_markers_ids_.end()) {
210 transform.transform.rotation = marker.pose.orientation;
212 br_->sendTransform(transform);
216 array_.markers.push_back(marker);
225 visualization_msgs::Marker vis_marker;
226 vis_marker.action = visualization_msgs::Marker::DELETEALL;
227 vis_array_.markers.clear();
228 vis_array_.markers.reserve(ids.size() + 1);
229 vis_array_.markers.push_back(vis_marker);
231 for (
unsigned int i = 0; i < ids.size(); i++)
232 pushVisMarkers(msg->header.frame_id, msg->header.stamp, array_.markers[i].pose,
235 vis_markers_pub_.
publish(vis_array_);
240 Mat debug = image.clone();
243 for (
unsigned int i = 0; i < ids.size(); i++)
248 out_msg.
header.frame_id = msg->header.frame_id;
249 out_msg.
header.stamp = msg->header.stamp;
251 out_msg.
image = debug;
256 inline void fillCorners(aruco_pose::Marker& marker,
const vector<cv::Point2f>& corners)
const 258 marker.c1.x = corners[0].x;
259 marker.c2.x = corners[1].x;
260 marker.c3.x = corners[2].x;
261 marker.c4.x = corners[3].x;
262 marker.c1.y = corners[0].y;
263 marker.c2.y = corners[1].y;
264 marker.c3.y = corners[2].y;
265 marker.c4.y = corners[3].y;
268 inline void fillPose(geometry_msgs::Pose& pose,
const cv::Vec3d& rvec,
const cv::Vec3d& tvec)
const 270 pose.position.x = tvec[0];
271 pose.position.y = tvec[1];
272 pose.position.z = tvec[2];
274 double angle = norm(rvec);
275 cv::Vec3d axis = rvec / angle;
278 q.
setRotation(tf2::Vector3(axis[0], axis[1], axis[2]), angle);
280 pose.orientation.w = q.w();
281 pose.orientation.x = q.x();
282 pose.orientation.y = q.y();
283 pose.orientation.z = q.z();
286 inline void fillTranslation(geometry_msgs::Vector3& translation,
const cv::Vec3d& tvec)
const 288 translation.x = tvec[0];
289 translation.y = tvec[1];
290 translation.z = tvec[2];
294 const geometry_msgs::Pose &pose,
double length,
int id,
int index)
296 visualization_msgs::Marker marker;
297 marker.header.frame_id = frame_id;
298 marker.header.stamp = stamp;
299 marker.action = visualization_msgs::Marker::ADD;
303 marker.ns =
"aruco_marker";
304 marker.type = visualization_msgs::Marker::CUBE;
307 marker.scale.z = 0.001;
311 marker.color.a = 0.9;
313 vis_array_.markers.push_back(marker);
316 marker.ns =
"aruco_marker_label";
317 marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
318 marker.scale.z = length * 0.6;
323 marker.text = std::to_string(
id);
325 vis_array_.markers.push_back(marker);
330 return frame_id_prefix_ + std::to_string(
id);
335 std::map<std::string, double> length_override;
336 nh.
getParam(
"length_override", length_override);
337 for (
auto const& item : length_override) {
338 length_override_[std::stoi(item.first)] = item.second;
344 auto item = length_override_.find(
id);
345 if (item != length_override_.end()) {
354 map_markers_ids_.clear();
355 for (
auto const& marker : msg.markers) {
356 map_markers_ids_.insert(marker.id);
362 enabled_ = config.enabled;
363 parameters_->adaptiveThreshConstant = config.adaptiveThreshConstant;
364 parameters_->adaptiveThreshWinSizeMin = config.adaptiveThreshWinSizeMin;
365 parameters_->adaptiveThreshWinSizeMax = config.adaptiveThreshWinSizeMax;
366 parameters_->adaptiveThreshWinSizeStep = config.adaptiveThreshWinSizeStep;
367 parameters_->cornerRefinementMaxIterations = config.cornerRefinementMaxIterations;
368 parameters_->cornerRefinementMethod = config.cornerRefinementMethod;
369 parameters_->cornerRefinementMinAccuracy = config.cornerRefinementMinAccuracy;
370 parameters_->cornerRefinementWinSize = config.cornerRefinementWinSize;
371 #if ((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 4) && (CV_VERSION_REVISION >= 7)) || (CV_VERSION_MAJOR > 3) 372 parameters_->detectInvertedMarker = config.detectInvertedMarker;
374 parameters_->errorCorrectionRate = config.errorCorrectionRate;
375 parameters_->minCornerDistanceRate = config.minCornerDistanceRate;
376 parameters_->markerBorderBits = config.markerBorderBits;
377 parameters_->maxErroneousBitsInBorderRate = config.maxErroneousBitsInBorderRate;
378 parameters_->minDistanceToBorder = config.minDistanceToBorder;
379 parameters_->minMarkerDistanceRate = config.minMarkerDistanceRate;
380 parameters_->minMarkerPerimeterRate = config.minMarkerPerimeterRate;
381 parameters_->maxMarkerPerimeterRate = config.maxMarkerPerimeterRate;
382 parameters_->minOtsuStdDev = config.minOtsuStdDev;
383 parameters_->perspectiveRemoveIgnoredMarginPerCell = config.perspectiveRemoveIgnoredMarginPerCell;
384 parameters_->perspectiveRemovePixelPerCell = config.perspectiveRemovePixelPerCell;
385 parameters_->polygonalApproxAccuracyRate = config.polygonalApproxAccuracyRate;
386 #if ((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 4) && (CV_VERSION_REVISION >= 2)) || (CV_VERSION_MAJOR > 3) 387 parameters_->aprilTagQuadDecimate = config.aprilTagQuadDecimate;
388 parameters_->aprilTagQuadSigma = config.aprilTagQuadSigma;
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
void fillCorners(aruco_pose::Marker &marker, const vector< cv::Point2f > &corners) const
aruco_pose::MarkerArray array_
void snapOrientation(geometry_msgs::Quaternion &to, const geometry_msgs::Quaternion &from, bool auto_flip=false)
std::unique_ptr< tf2_ros::TransformBroadcaster > br_
visualization_msgs::MarkerArray vis_array_
void imageCallback(const sensor_msgs::ImageConstPtr &msg, const sensor_msgs::CameraInfoConstPtr &cinfo)
std::string frame_id_prefix_
image_transport::Publisher debug_pub_
ros::Publisher markers_pub_
double getMarkerLength(int id)
uint32_t getNumSubscribers() const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
CameraSubscriber subscribeCamera(const std::string &base_topic, uint32_t queue_size, const CameraSubscriber::Callback &callback, const ros::VoidPtr &tracked_object=ros::VoidPtr(), const TransportHints &transport_hints=TransportHints())
cv::Ptr< cv::aruco::Dictionary > dictionary_
ros::NodeHandle & getNodeHandle() const
cv::Ptr< cv::aruco::DetectorParameters > parameters_
Publisher advertise(const std::string &base_topic, uint32_t queue_size, bool latch=false)
ros::NodeHandle & getPrivateNodeHandle() const
static void parseCameraInfo(const sensor_msgs::CameraInfoConstPtr &cinfo, cv::Mat &matrix, cv::Mat &dist)
void pushVisMarkers(const std::string &frame_id, const ros::Time &stamp, const geometry_msgs::Pose &pose, double length, int id, int index)
void fillTranslation(geometry_msgs::Vector3 &translation, const cv::Vec3d &tvec) const
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
CV_EXPORTS Ptr< Dictionary > getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME name)
Returns one of the predefined dictionaries defined in PREDEFINED_DICTIONARY_NAME. ...
std::unordered_map< int, double > length_override_
sensor_msgs::ImagePtr toImageMsg() const
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val) const
void readLengthOverride(ros::NodeHandle &nh)
void publish(const boost::shared_ptr< M > &message) const
#define NODELET_WARN_THROTTLE(rate,...)
bool getParam(const std::string &key, std::string &s) const
void mapMarkersCallback(const aruco_pose::MarkerArray &msg)
void fillPose(geometry_msgs::Pose &pose, const cv::Vec3d &rvec, const cv::Vec3d &tvec) const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::unordered_set< int > map_markers_ids_
void publish(const sensor_msgs::Image &message) const
ros::Subscriber map_markers_sub_
void setRotation(const Vector3 &axis, const tf2Scalar &angle)
ros::Publisher vis_markers_pub_
std::shared_ptr< dynamic_reconfigure::Server< aruco_pose::DetectorConfig > > dyn_srv_
#define NODELET_INFO(...)
std::string getChildFrameId(int id) const
image_transport::CameraSubscriber img_sub_
CV_EXPORTS_W void drawDetectedMarkers(InputOutputArray image, InputArrayOfArrays corners, InputArray ids=noArray(), Scalar borderColor=Scalar(0, 255, 0))
Draw detected markers in image.
InputArrayOfArrays const Ptr< CharucoBoard > Size InputOutputArray InputOutputArray OutputArrayOfArrays rvecs
CV_EXPORTS_W void estimatePoseSingleMarkers(InputArrayOfArrays corners, float markerLength, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvecs, OutputArray tvecs, OutputArray _objPoints=noArray())
Pose estimation for single markers.
static CV_WRAP Ptr< DetectorParameters > create()
Create a new set of DetectorParameters with default values.
ROSCPP_DECL void shutdown()
#define NODELET_FATAL(...)
InputArrayOfArrays const Ptr< CharucoBoard > Size InputOutputArray InputOutputArray OutputArrayOfArrays OutputArrayOfArrays tvecs
uint32_t getNumSubscribers() const
CV_EXPORTS_W void detectMarkers(InputArray image, const Ptr< Dictionary > &dictionary, OutputArrayOfArrays corners, OutputArray ids, const Ptr< DetectorParameters > ¶meters=DetectorParameters::create(), OutputArrayOfArrays rejectedImgPoints=noArray(), InputArray cameraMatrix=noArray(), InputArray distCoeff=noArray())
Basic marker detection.
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
std::unique_ptr< tf2_ros::TransformListener > tf_listener_
void paramCallback(aruco_pose::DetectorConfig &config, uint32_t level)
std::unique_ptr< tf2_ros::Buffer > tf_buffer_
CV_EXPORTS_W void drawAxis(InputOutputArray image, InputArray cameraMatrix, InputArray distCoeffs, InputArray rvec, InputArray tvec, float length)
Draw coordinate system axis from pose estimation.