00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include <ros/ros.h>
00036 #include <ros/package.h>
00037
00038 #include <std_msgs/Header.h>
00039 #include <geometry_msgs/PointStamped.h>
00040 #include <geometry_msgs/Pose2D.h>
00041 #include <geometry_msgs/PoseStamped.h>
00042 #include <sensor_msgs/Image.h>
00043 #include <sensor_msgs/PointCloud2.h>
00044 #include <sensor_msgs/CameraInfo.h>
00045
00046 #include <message_filters/subscriber.h>
00047 #include <message_filters/synchronizer.h>
00048 #include <message_filters/sync_policies/approximate_time.h>
00049 #include <cv_bridge/cv_bridge.h>
00050 #include <sensor_msgs/image_encodings.h>
00051 #include <actionlib/server/simple_action_server.h>
00052
00053 #include <pr2_manipulation_controllers/JTTaskControllerState.h>
00054 #include <pr2_manipulation_controllers/JinvTeleopControllerState.h>
00055
00056
00057 #include <tf/transform_listener.h>
00058
00059
00060 #include <pcl16/point_cloud.h>
00061 #include <pcl16/point_types.h>
00062 #include <pcl16/common/common.h>
00063 #include <pcl16/common/eigen.h>
00064 #include <pcl16/common/centroid.h>
00065 #include <pcl16/io/io.h>
00066 #include <pcl16/io/pcd_io.h>
00067 #include <pcl16_ros/transforms.h>
00068 #include <pcl16/ros/conversions.h>
00069 #include <pcl16/ModelCoefficients.h>
00070 #include <pcl16/registration/transformation_estimation_svd.h>
00071 #include <pcl16/sample_consensus/method_types.h>
00072 #include <pcl16/sample_consensus/model_types.h>
00073 #include <pcl16/segmentation/sac_segmentation.h>
00074 #include <pcl16/filters/extract_indices.h>
00075
00076
00077 #include <opencv2/core/core.hpp>
00078 #include <opencv2/imgproc/imgproc.hpp>
00079 #include <opencv2/highgui/highgui.hpp>
00080
00081
00082 #include <boost/shared_ptr.hpp>
00083
00084
00085 #include <cpl_visual_features/helpers.h>
00086 #include <cpl_visual_features/comp_geometry.h>
00087 #include <cpl_visual_features/features/shape_context.h>
00088
00089
00090 #include <tabletop_pushing/LearnPush.h>
00091 #include <tabletop_pushing/LocateTable.h>
00092 #include <tabletop_pushing/VisFeedbackPushTrackingAction.h>
00093 #include <tabletop_pushing/point_cloud_segmentation.h>
00094 #include <tabletop_pushing/shape_features.h>
00095 #include <tabletop_pushing/object_tracker_25d.h>
00096 #include <tabletop_pushing/push_primitives.h>
00097 #include <tabletop_pushing/arm_obj_segmentation.h>
00098 #include <tabletop_pushing/extern/Timer.hpp>
00099
00100
00101 #include <libsvm/svm.h>
00102
00103
00104 #include <vector>
00105 #include <queue>
00106 #include <set>
00107 #include <string>
00108 #include <sstream>
00109 #include <iostream>
00110 #include <utility>
00111 #include <float.h>
00112 #include <math.h>
00113 #include <cmath>
00114 #include <time.h>
00115 #include <cstdlib>
00116
00117
00118
00119
00120 #define DISPLAY_WAIT 1
00121
00122
00123
00124 using boost::shared_ptr;
00125
00126 using tabletop_pushing::LearnPush;
00127 using tabletop_pushing::LocateTable;
00128 using tabletop_pushing::PushVector;
00129 using tabletop_pushing::PointCloudSegmentation;
00130 using tabletop_pushing::ProtoObject;
00131 using tabletop_pushing::ProtoObjects;
00132 using tabletop_pushing::ShapeLocation;
00133 using tabletop_pushing::ShapeLocations;
00134 using tabletop_pushing::ObjectTracker25D;
00135 using tabletop_pushing::ArmObjSegmentation;
00136
00137 using geometry_msgs::PoseStamped;
00138 using geometry_msgs::PointStamped;
00139 using geometry_msgs::Pose2D;
00140 using geometry_msgs::Twist;
00141 typedef pcl16::PointCloud<pcl16::PointXYZ> XYZPointCloud;
00142 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image,
00143 sensor_msgs::Image,
00144 sensor_msgs::Image,
00145 sensor_msgs::PointCloud2> MySyncPolicy;
00146 using cpl_visual_features::upSample;
00147 using cpl_visual_features::downSample;
00148 using cpl_visual_features::subPIAngle;
00149 using cpl_visual_features::lineSegmentIntersection2D;
00150 using cpl_visual_features::ShapeDescriptors;
00151 using cpl_visual_features::ShapeDescriptor;
00152
00153 typedef tabletop_pushing::VisFeedbackPushTrackingFeedback PushTrackerState;
00154 typedef tabletop_pushing::VisFeedbackPushTrackingGoal PushTrackerGoal;
00155 typedef tabletop_pushing::VisFeedbackPushTrackingResult PushTrackerResult;
00156 typedef tabletop_pushing::VisFeedbackPushTrackingAction PushTrackerAction;
00157
00158 #define FOOTPRINT_XY_RES 0.001
00159
00160 inline int objLocToIdx(double val, double min_val, double max_val)
00161 {
00162 return round((val-min_val)/FOOTPRINT_XY_RES);
00163 }
00164
00165 struct ScoredIdx
00166 {
00167 double score;
00168 int idx;
00169 };
00170
00171 class ScoredIdxComparison
00172 {
00173 public:
00174 ScoredIdxComparison(const bool& descend=false) : descend_(descend) {}
00175 bool operator() (const ScoredIdx& lhs, const ScoredIdx&rhs) const
00176 {
00177 if (descend_)
00178 {
00179 return (lhs.score < rhs.score);
00180 }
00181 else
00182 {
00183 return (lhs.score > rhs.score);
00184 }
00185 }
00186 protected:
00187 bool descend_;
00188 };
00189
00190 class TabletopPushingPerceptionNode
00191 {
00192 public:
00193 TabletopPushingPerceptionNode(ros::NodeHandle &n) :
00194 n_(n), n_private_("~"),
00195 image_sub_(n, "color_image_topic", 1),
00196 depth_sub_(n, "depth_image_topic", 1),
00197 mask_sub_(n, "mask_image_topic", 1),
00198 cloud_sub_(n, "point_cloud_topic", 1),
00199 sync_(MySyncPolicy(15), image_sub_, depth_sub_, mask_sub_, cloud_sub_),
00200 as_(n, "push_tracker", false),
00201 have_depth_data_(false),
00202 camera_initialized_(false), recording_input_(false), record_count_(0),
00203 learn_callback_count_(0), goal_out_count_(0), goal_heading_count_(0),
00204 frame_callback_count_(0), frame_set_count_(0),
00205 just_spun_(false), major_axis_spin_pos_scale_(0.75), object_not_moving_thresh_(0),
00206 object_not_moving_count_(0), object_not_moving_count_limit_(10),
00207 gripper_not_moving_thresh_(0), gripper_not_moving_count_(0),
00208 gripper_not_moving_count_limit_(10), current_file_id_(""), force_swap_(false),
00209 num_position_failures_(0), footprint_count_(0)
00210 {
00211 tf_ = shared_ptr<tf::TransformListener>(new tf::TransformListener());
00212 pcl_segmenter_ = shared_ptr<PointCloudSegmentation>(
00213 new PointCloudSegmentation(tf_));
00214
00215 n_private_.param("display_wait_ms", display_wait_ms_, 3);
00216 n_private_.param("use_displays", use_displays_, false);
00217 n_private_.param("write_input_to_disk", write_input_to_disk_, false);
00218 n_private_.param("write_to_disk", write_to_disk_, false);
00219
00220 n_private_.param("min_workspace_x", pcl_segmenter_->min_workspace_x_, 0.0);
00221 n_private_.param("min_workspace_z", pcl_segmenter_->min_workspace_z_, 0.0);
00222 n_private_.param("max_workspace_x", pcl_segmenter_->max_workspace_x_, 0.0);
00223 n_private_.param("max_workspace_z", pcl_segmenter_->max_workspace_z_, 0.0);
00224 n_private_.param("min_table_z", pcl_segmenter_->min_table_z_, -0.5);
00225 n_private_.param("max_table_z", pcl_segmenter_->max_table_z_, 1.5);
00226
00227
00228 n_private_.param("min_goal_x", min_goal_x_, 0.425);
00229 n_private_.param("max_goal_x", max_goal_x_, 0.8);
00230 n_private_.param("min_goal_y", min_goal_y_, -0.3);
00231 n_private_.param("max_goal_y", max_goal_y_, 0.3);
00232
00233 n_private_.param("mps_min_inliers", pcl_segmenter_->mps_min_inliers_, 10000);
00234 n_private_.param("mps_min_angle_thresh", pcl_segmenter_->mps_min_angle_thresh_, 2.0);
00235 n_private_.param("mps_min_dist_thresh", pcl_segmenter_->mps_min_dist_thresh_, 0.02);
00236
00237 std::string default_workspace_frame = "torso_lift_link";
00238 n_private_.param("workspace_frame", workspace_frame_,
00239 default_workspace_frame);
00240 std::string default_camera_frame = "head_mount_kinect_rgb_optical_frame";
00241 n_private_.param("camera_frame", camera_frame_, default_camera_frame);
00242
00243 std::string output_path_def = "~";
00244 n_private_.param("img_output_path", base_output_path_, output_path_def);
00245
00246 n_private_.param("start_tracking_on_push_call", start_tracking_on_push_call_, false);
00247
00248 n_private_.param("num_downsamples", num_downsamples_, 2);
00249 pcl_segmenter_->num_downsamples_ = num_downsamples_;
00250
00251 std::string cam_info_topic_def = "/kinect_head/rgb/camera_info";
00252 n_private_.param("cam_info_topic", cam_info_topic_, cam_info_topic_def);
00253
00254
00255 n_private_.param("table_ransac_thresh", pcl_segmenter_->table_ransac_thresh_, 0.01);
00256 n_private_.param("table_ransac_angle_thresh", pcl_segmenter_->table_ransac_angle_thresh_, 30.0);
00257 n_private_.param("cylinder_ransac_thresh", pcl_segmenter_->cylinder_ransac_thresh_, 0.03);
00258 n_private_.param("cylinder_ransac_angle_thresh", pcl_segmenter_->cylinder_ransac_angle_thresh_, 1.5);
00259 n_private_.param("optimize_cylinder_coefficients", pcl_segmenter_->optimize_cylinder_coefficients_, false);
00260 n_private_.param("sphere_ransac_thresh", pcl_segmenter_->sphere_ransac_thresh_, 0.01);
00261 n_private_.param("pcl_cluster_tolerance", pcl_segmenter_->cluster_tolerance_, 0.25);
00262 n_private_.param("pcl_difference_thresh", pcl_segmenter_->cloud_diff_thresh_, 0.01);
00263 n_private_.param("pcl_min_cluster_size", pcl_segmenter_->min_cluster_size_, 100);
00264 n_private_.param("pcl_max_cluster_size", pcl_segmenter_->max_cluster_size_, 2500);
00265 n_private_.param("pcl_voxel_downsample_res", pcl_segmenter_->voxel_down_res_, 0.005);
00266 n_private_.param("pcl_cloud_intersect_thresh", pcl_segmenter_->cloud_intersect_thresh_, 0.005);
00267 n_private_.param("pcl_table_hull_alpha", pcl_segmenter_->hull_alpha_, 0.1);
00268 n_private_.param("use_pcl_voxel_downsample", pcl_segmenter_->use_voxel_down_, true);
00269 n_private_.param("icp_max_iters", pcl_segmenter_->icp_max_iters_, 100);
00270 n_private_.param("icp_transform_eps", pcl_segmenter_->icp_transform_eps_, 0.0);
00271 n_private_.param("icp_max_cor_dist", pcl_segmenter_->icp_max_cor_dist_, 1.0);
00272 n_private_.param("icp_ransac_thresh", pcl_segmenter_->icp_ransac_thresh_, 0.015);
00273
00274 n_private_.param("push_tracker_dist_thresh", tracker_dist_thresh_, 0.05);
00275 n_private_.param("push_tracker_angle_thresh", tracker_angle_thresh_, 0.01);
00276 n_private_.param("major_axis_spin_pos_scale", major_axis_spin_pos_scale_, 0.75);
00277 bool use_cv_ellipse;
00278 n_private_.param("use_cv_ellipse", use_cv_ellipse, false);
00279 n_private_.param("use_mps_segmentation", use_mps_segmentation_, false);
00280
00281 n_private_.param("max_object_gripper_dist", max_object_gripper_dist_, 0.10);
00282 n_private_.param("gripper_not_moving_thresh", gripper_not_moving_thresh_, 0.005);
00283 n_private_.param("object_not_moving_thresh", object_not_moving_thresh_, 0.005);
00284 n_private_.param("gripper_not_moving_count_limit", gripper_not_moving_count_limit_, 100);
00285 n_private_.param("object_not_moving_count_limit", object_not_moving_count_limit_, 100);
00286 n_private_.param("object_not_detected_count_limit", object_not_detected_count_limit_, 5);
00287 n_private_.param("object_too_far_count_limit", object_too_far_count_limit_, 5);
00288 n_private_.param("object_not_between_count_limit", object_not_between_count_limit_, 5);
00289 n_private_.param("object_not_between_epsilon", object_not_between_epsilon_, 0.01);
00290 n_private_.param("start_loc_push_time_limit", start_loc_push_time_, 5.0);
00291 n_private_.param("start_loc_push_dist", start_loc_push_dist_, 0.30);
00292 n_private_.param("use_center_pointing_shape_context", use_center_pointing_shape_context_, true);
00293 n_private_.param("self_mask_dilate_size", mask_dilate_size_, 5);
00294 n_private_.param("point_cloud_hist_res", point_cloud_hist_res_, 0.005);
00295 n_private_.param("boundary_hull_alpha", hull_alpha_, 0.01);
00296 n_private_.param("hull_gripper_spread", gripper_spread_, 0.05);
00297
00298 n_.param("start_loc_use_fixed_goal", start_loc_use_fixed_goal_, false);
00299 n_.param("use_graphcut_arm_seg", use_graphcut_arm_seg_, false);
00300
00301
00302 std::string arm_color_model_name;
00303 n_private_.param("arm_color_model_name", arm_color_model_name, std::string(""));
00304
00305 #ifdef DEBUG_POSE_ESTIMATION
00306 pose_est_stream_.open("/u/thermans/data/new/pose_ests.txt");
00307 #endif // DEBUG_POSE_ESTIMATION
00308
00309
00310 arm_obj_segmenter_ = shared_ptr<ArmObjSegmentation>(new ArmObjSegmentation());
00311 if (arm_color_model_name.length() > 0)
00312 {
00313 std::stringstream arm_color_model_path;
00314 arm_color_model_path << ros::package::getPath("tabletop_pushing") << "/cfg/" << arm_color_model_name;
00315 arm_obj_segmenter_->loadArmColorModel(arm_color_model_path.str());
00316 }
00317 obj_tracker_ = shared_ptr<ObjectTracker25D>(
00318 new ObjectTracker25D(pcl_segmenter_, arm_obj_segmenter_, num_downsamples_, use_displays_,
00319 write_to_disk_, base_output_path_, camera_frame_, use_cv_ellipse,
00320 use_mps_segmentation_, use_graphcut_arm_seg_, hull_alpha_));
00321
00322
00323 sync_.registerCallback(&TabletopPushingPerceptionNode::sensorCallback,
00324 this);
00325 push_pose_server_ = n_.advertiseService(
00326 "get_learning_push_vector",
00327 &TabletopPushingPerceptionNode::learnPushCallback, this);
00328 table_location_server_ = n_.advertiseService(
00329 "get_table_location", &TabletopPushingPerceptionNode::getTableLocation,
00330 this);
00331
00332
00333 jtteleop_l_arm_subscriber_ = n_.subscribe("/l_cart_transpose_push/state", 1,
00334 &TabletopPushingPerceptionNode::lArmStateCartCB,
00335 this);
00336 jtteleop_r_arm_subscriber_ = n_.subscribe("/r_cart_transpose_push/state", 1,
00337 &TabletopPushingPerceptionNode::rArmStateCartCB,
00338 this);
00339 jinv_l_arm_subscriber_ = n_.subscribe("/l_cart_jinv_push/state", 1,
00340 &TabletopPushingPerceptionNode::lArmStateVelCB,
00341 this);
00342 jinv_r_arm_subscriber_ = n_.subscribe("/r_cart_jinv_push/state", 1,
00343 &TabletopPushingPerceptionNode::rArmStateVelCB,
00344 this);
00345
00346 as_.registerGoalCallback(
00347 boost::bind(&TabletopPushingPerceptionNode::pushTrackerGoalCB, this));
00348 as_.registerPreemptCallback(
00349 boost::bind(&TabletopPushingPerceptionNode::pushTrackerPreemptCB,this));
00350 as_.start();
00351 }
00352
00353 void sensorCallback(const sensor_msgs::ImageConstPtr& img_msg,
00354 const sensor_msgs::ImageConstPtr& depth_msg,
00355 const sensor_msgs::ImageConstPtr& mask_msg,
00356 const sensor_msgs::PointCloud2ConstPtr& cloud_msg)
00357 {
00358 #ifdef PROFILE_CB_TIME
00359 long long cb_start_time = Timer::nanoTime();
00360 #endif
00361
00362 if (!camera_initialized_)
00363 {
00364 cam_info_ = *ros::topic::waitForMessage<sensor_msgs::CameraInfo>(
00365 cam_info_topic_, n_, ros::Duration(5.0));
00366 camera_initialized_ = true;
00367 pcl_segmenter_->cam_info_ = cam_info_;
00368 ROS_DEBUG_STREAM("Cam info: " << cam_info_);
00369 }
00370
00371 cv::Mat color_frame;
00372 cv::Mat depth_frame;
00373 cv::Mat self_mask;
00374 cv_bridge::CvImagePtr color_cv_ptr = cv_bridge::toCvCopy(img_msg);
00375 cv_bridge::CvImagePtr depth_cv_ptr = cv_bridge::toCvCopy(depth_msg);
00376 cv_bridge::CvImagePtr mask_cv_ptr = cv_bridge::toCvCopy(mask_msg);
00377 color_frame = color_cv_ptr->image;
00378 depth_frame = depth_cv_ptr->image;
00379 self_mask = mask_cv_ptr->image;
00380
00381
00382
00383 #ifdef PROFILE_CB_TIME
00384 long long grow_mask_start_time = Timer::nanoTime();
00385 #endif
00386
00387 if (mask_dilate_size_ > 0)
00388 {
00389 cv::Mat morph_element(mask_dilate_size_, mask_dilate_size_, CV_8UC1, cv::Scalar(255));
00390 cv::erode(self_mask, self_mask, morph_element);
00391 }
00392 #ifdef PROFILE_CB_TIME
00393 double grow_mask_elapsed_time = (((double)(Timer::nanoTime() - grow_mask_start_time)) /
00394 Timer::NANOSECONDS_PER_SECOND);
00395 long long transform_start_time = Timer::nanoTime();
00396 #endif
00397
00398 XYZPointCloud cloud;
00399 pcl16::fromROSMsg(*cloud_msg, cloud);
00400 tf_->waitForTransform(workspace_frame_, cloud.header.frame_id, cloud.header.stamp, ros::Duration(0.5));
00401 pcl16_ros::transformPointCloud(workspace_frame_, cloud, cloud, *tf_);
00402 #ifdef PROFILE_CB_TIME
00403 double transform_elapsed_time = (((double)(Timer::nanoTime() - transform_start_time)) /
00404 Timer::NANOSECONDS_PER_SECOND);
00405 long long filter_start_time = Timer::nanoTime();
00406 #endif
00407
00408
00409
00410
00411 for (int r = 0; r < depth_frame.rows; ++r)
00412 {
00413 float* depth_row = depth_frame.ptr<float>(r);
00414 for (int c = 0; c < depth_frame.cols; ++c)
00415 {
00416 float cur_d = depth_row[c];
00417 if (isnan(cur_d))
00418 {
00419 depth_row[c] = 0.0;
00420 }
00421 }
00422 }
00423
00424 XYZPointCloud cloud_self_filtered(cloud);
00425 pcl16::PointXYZ nan_point;
00426 nan_point.x = numeric_limits<float>::quiet_NaN();
00427 nan_point.y = numeric_limits<float>::quiet_NaN();
00428 nan_point.z = numeric_limits<float>::quiet_NaN();
00429 for (unsigned int x = 0; x < cloud.width; ++x)
00430 {
00431 for (unsigned int y = 0; y < cloud.height; ++y)
00432 {
00433 if (self_mask.at<uchar>(y,x) == 0)
00434 {
00435 cloud_self_filtered.at(x,y) = nan_point;
00436 }
00437 }
00438 }
00439 #ifdef PROFILE_CB_TIME
00440 double filter_elapsed_time = (((double)(Timer::nanoTime() - filter_start_time)) /
00441 Timer::NANOSECONDS_PER_SECOND);
00442 long long downsample_start_time = Timer::nanoTime();
00443 #endif
00444
00445
00446 cv::Mat color_frame_down = downSample(color_frame, num_downsamples_);
00447 cv::Mat depth_frame_down = downSample(depth_frame, num_downsamples_);
00448 cv::Mat self_mask_down = downSample(self_mask, num_downsamples_);
00449 cv::Mat arm_mask_crop;
00450 color_frame_down.copyTo(arm_mask_crop, self_mask_down);
00451
00452 #ifdef PROFILE_CB_TIME
00453 double downsample_elapsed_time = (((double)(Timer::nanoTime() - downsample_start_time)) /
00454 Timer::NANOSECONDS_PER_SECOND);
00455 long long copy_start_time = Timer::nanoTime();
00456 #endif
00457
00458
00459
00460
00461
00462
00463
00464
00465 cur_color_frame_ = color_frame_down.clone();
00466 cur_depth_frame_ = depth_frame_down.clone();
00467 cur_self_mask_ = self_mask_down.clone();
00468 cur_point_cloud_ = cloud;
00469 cur_self_filtered_cloud_ = cloud_self_filtered;
00470 have_depth_data_ = true;
00471 cur_camera_header_ = img_msg->header;
00472 pcl_segmenter_->cur_camera_header_ = cur_camera_header_;
00473
00474 #ifdef PROFILE_CB_TIME
00475 double copy_elapsed_time = (((double)(Timer::nanoTime() - copy_start_time)) /
00476 Timer::NANOSECONDS_PER_SECOND);
00477 double update_tracks_elapsed_time = 0.0;
00478 double copy_tracks_elapsed_time = 0.0;
00479 double display_tracks_elapsed_time = 0.0;
00480 double publish_feedback_elapsed_time = 0.0;
00481 double evaluate_goal_elapsed_time = 0.0;
00482 long long tracker_start_time = Timer::nanoTime();
00483 #endif
00484
00485 if (obj_tracker_->isInitialized() && !obj_tracker_->isPaused())
00486 {
00487 PoseStamped arm_pose;
00488 if (pushing_arm_ == "l")
00489 {
00490 arm_pose = l_arm_pose_;
00491 }
00492 else
00493 {
00494 arm_pose = r_arm_pose_;
00495 }
00496 #ifdef PROFILE_CB_TIME
00497 long long update_tracks_start_time = Timer::nanoTime();
00498 #endif
00499
00500 PushTrackerState tracker_state;
00501 if (use_graphcut_arm_seg_)
00502 {
00503 obj_tracker_->updateTracks(cur_color_frame_, cur_depth_frame_, cur_self_mask_, cur_point_cloud_,
00504 proxy_name_, tracker_state);
00505 }
00506 else
00507 {
00508 obj_tracker_->updateTracks(cur_color_frame_, cur_depth_frame_, cur_self_mask_, cur_self_filtered_cloud_,
00509 proxy_name_, tracker_state);
00510 }
00511
00512 #ifdef DEBUG_POSE_ESTIMATION
00513 pose_est_stream_ << tracker_state.x.x << " " << tracker_state.x.y << " " << tracker_state.z << " "
00514 << tracker_state.x.theta << "\n";
00515 #endif // DEBUG_POSE_ESTIMATION
00516
00517 #ifdef PROFILE_CB_TIME
00518 update_tracks_elapsed_time = (((double)(Timer::nanoTime() - update_tracks_start_time)) /
00519 Timer::NANOSECONDS_PER_SECOND);
00520 long long copy_tracks_start_time = Timer::nanoTime();
00521 #endif
00522 ProtoObject cur_obj = obj_tracker_->getMostRecentObject();
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563 tracker_state.proxy_name = proxy_name_;
00564 tracker_state.controller_name = controller_name_;
00565 tracker_state.behavior_primitive = behavior_primitive_;
00566
00567 PointStamped start_point;
00568 PointStamped end_point;
00569 start_point.header.frame_id = workspace_frame_;
00570 end_point.header.frame_id = workspace_frame_;
00571 start_point.point.x = tracker_state.x.x;
00572 start_point.point.y = tracker_state.x.y;
00573 start_point.point.z = tracker_state.z;
00574 end_point.point.x = tracker_goal_pose_.x;
00575 end_point.point.y = tracker_goal_pose_.y;
00576 end_point.point.z = start_point.point.z;
00577 #ifdef PROFILE_CB_TIME
00578 copy_tracks_elapsed_time = (((double)(Timer::nanoTime() - copy_tracks_start_time)) /
00579 Timer::NANOSECONDS_PER_SECOND);
00580 long long display_tracks_start_time = Timer::nanoTime();
00581 #endif
00582
00583 displayPushVector(cur_color_frame_, start_point, end_point);
00584
00585 displayGoalHeading(cur_color_frame_, start_point, tracker_state.x.theta,
00586 tracker_goal_pose_.theta);
00587 #ifdef PROFILE_CB_TIME
00588 display_tracks_elapsed_time = (((double)(Timer::nanoTime() - display_tracks_start_time)) /
00589 Timer::NANOSECONDS_PER_SECOND);
00590 long long evaluate_goals_start_time = Timer::nanoTime();
00591 #endif
00592
00593
00594 if (as_.isActive())
00595 {
00596 #ifdef PROFILE_CB_TIME
00597 long long publish_feedback_start_time = Timer::nanoTime();
00598 #endif
00599 as_.publishFeedback(tracker_state);
00600 #ifdef PROFILE_CB_TIME
00601 publish_feedback_elapsed_time = (((double)(Timer::nanoTime() - publish_feedback_start_time)) /
00602 Timer::NANOSECONDS_PER_SECOND);
00603 long long evaluate_goal_start_time = Timer::nanoTime();
00604 #endif
00605 evaluateGoalAndAbortConditions(tracker_state);
00606 #ifdef PROFILE_CB_TIME
00607 evaluate_goal_elapsed_time = (((double)(Timer::nanoTime() - evaluate_goal_start_time)) /
00608 Timer::NANOSECONDS_PER_SECOND);
00609 #endif
00610 }
00611 }
00612 else if (obj_tracker_->isInitialized() && obj_tracker_->isPaused())
00613 {
00614 obj_tracker_->pausedUpdate(cur_color_frame_);
00615 PointStamped start_point;
00616 PointStamped end_point;
00617 PushTrackerState tracker_state = obj_tracker_->getMostRecentState();
00618 start_point.header.frame_id = workspace_frame_;
00619 end_point.header.frame_id = workspace_frame_;
00620 start_point.point.x = tracker_state.x.x;
00621 start_point.point.y = tracker_state.x.y;
00622 start_point.point.z = tracker_state.z;
00623 end_point.point.x = tracker_goal_pose_.x;
00624 end_point.point.y = tracker_goal_pose_.y;
00625 end_point.point.z = start_point.point.z;
00626 displayPushVector(cur_color_frame_, start_point, end_point, "goal_vector", true);
00627
00628 displayGoalHeading(cur_color_frame_, start_point, tracker_state.x.theta,
00629 tracker_goal_pose_.theta, true);
00630 }
00631 #ifdef PROFILE_CB_TIME
00632 double tracker_elapsed_time = (((double)(Timer::nanoTime() - tracker_start_time)) /
00633 Timer::NANOSECONDS_PER_SECOND);
00634 #endif
00635
00636
00637
00638 #ifdef DISPLAY_INPUT_COLOR
00639 if (use_displays_)
00640 {
00641 cv::imshow("color", cur_color_frame_);
00642
00643 cv::imshow("arm_mask_crop", arm_mask_crop);
00644 }
00645
00646 if (write_input_to_disk_ && recording_input_)
00647 {
00648 std::stringstream out_name;
00649 if (current_file_id_.size() > 0)
00650 {
00651 std::stringstream cloud_out_name;
00652 out_name << base_output_path_ << current_file_id_ << "_input_" << record_count_ << ".png";
00653
00654
00655
00656 }
00657 else
00658 {
00659 out_name << base_output_path_ << "input" << record_count_ << ".png";
00660 }
00661 cv::imwrite(out_name.str(), cur_color_frame_);
00662
00663
00664
00665 record_count_++;
00666 }
00667 #endif // DISPLAY_INPUT_COLOR
00668 #ifdef DISPLAY_INPUT_DEPTH
00669 if (use_displays_)
00670 {
00671 double depth_max = 1.0;
00672 cv::minMaxLoc(cur_depth_frame_, NULL, &depth_max);
00673 cv::Mat depth_display = cur_depth_frame_.clone();
00674 depth_display /= depth_max;
00675 cv::imshow("input_depth", depth_display);
00676 }
00677 #endif // DISPLAY_INPUT_DEPTH
00678 #ifdef DISPLAY_WAIT
00679 if (use_displays_)
00680 {
00681 cv::waitKey(display_wait_ms_);
00682 }
00683 #endif // DISPLAY_WAIT
00684 ++frame_callback_count_;
00685 #ifdef PROFILE_CB_TIME
00686 double cb_elapsed_time = (((double)(Timer::nanoTime() - tracker_start_time)) /
00687 Timer::NANOSECONDS_PER_SECOND);
00688 if (obj_tracker_->isInitialized() && !obj_tracker_->isPaused())
00689 {
00690 ROS_INFO_STREAM("cb_elapsed_time " << cb_elapsed_time);
00691 ROS_INFO_STREAM("\t grow_mask_elapsed_time " << grow_mask_elapsed_time);
00692 ROS_INFO_STREAM("\t transform_elapsed_time " << transform_elapsed_time);
00693 ROS_INFO_STREAM("\t filter_elapsed_time " << filter_elapsed_time);
00694 ROS_INFO_STREAM("\t downsample_elapsed_time " << downsample_elapsed_time);
00695 ROS_INFO_STREAM("\t copy_elapsed_time " << copy_elapsed_time);
00696 ROS_INFO_STREAM("\t tracker_elapsed_time " << tracker_elapsed_time);
00697 ROS_INFO_STREAM("\t\t update_tracks_elapsed_time " << update_tracks_elapsed_time);
00698 ROS_INFO_STREAM("\t\t copy_tracks_elapsed_time " << copy_tracks_elapsed_time);
00699 ROS_INFO_STREAM("\t\t display_tracks_elapsed_time " << display_tracks_elapsed_time);
00700 ROS_INFO_STREAM("\t\t publish_feedback_elapsed_time " << publish_feedback_elapsed_time);
00701 ROS_INFO_STREAM("\t\t evaluate_goal_elapsed_time " << evaluate_goal_elapsed_time);
00702 }
00703 #endif
00704 }
00705
00706 void evaluateGoalAndAbortConditions(PushTrackerState& tracker_state)
00707 {
00708
00709 float x_error = tracker_goal_pose_.x - tracker_state.x.x;
00710 float y_error = tracker_goal_pose_.y - tracker_state.x.y;
00711 float theta_error = subPIAngle(tracker_goal_pose_.theta - tracker_state.x.theta);
00712
00713 float x_dist = fabs(x_error);
00714 float y_dist = fabs(y_error);
00715 float theta_dist = fabs(theta_error);
00716
00717 if (timing_push_)
00718 {
00719 if (pushingTimeUp())
00720 {
00721 abortPushingGoal("Pushing time up");
00722 }
00723 return;
00724 }
00725
00726 if (controller_name_ == "rotate_to_heading")
00727 {
00728 if (theta_dist < tracker_angle_thresh_)
00729 {
00730 ROS_INFO_STREAM("Cur state: (" << tracker_state.x.x << ", " <<
00731 tracker_state.x.y << ", " << tracker_state.x.theta << ")");
00732 ROS_INFO_STREAM("Desired goal: (" << tracker_goal_pose_.x << ", " <<
00733 tracker_goal_pose_.y << ", " << tracker_goal_pose_.theta << ")");
00734 ROS_INFO_STREAM("Goal error: (" << x_dist << ", " << y_dist << ", "
00735 << theta_dist << ")");
00736 PushTrackerResult res;
00737 res.aborted = false;
00738 as_.setSucceeded(res);
00739 obj_tracker_->pause();
00740 }
00741 return;
00742 }
00743
00744 if (x_dist < tracker_dist_thresh_ && y_dist < tracker_dist_thresh_)
00745 {
00746 ROS_INFO_STREAM("Cur state: (" << tracker_state.x.x << ", " <<
00747 tracker_state.x.y << ", " << tracker_state.x.theta << ")");
00748 ROS_INFO_STREAM("Desired goal: (" << tracker_goal_pose_.x << ", " <<
00749 tracker_goal_pose_.y << ", " << tracker_goal_pose_.theta << ")");
00750 ROS_INFO_STREAM("Goal error: (" << x_dist << ", " << y_dist << ", "
00751 << theta_dist << ")");
00752 PushTrackerResult res;
00753 res.aborted = false;
00754 as_.setSucceeded(res);
00755 obj_tracker_->pause();
00756 return;
00757 }
00758
00759 if (objectNotMoving(tracker_state))
00760 {
00761 abortPushingGoal("Object is not moving");
00762 }
00763 else if (gripperNotMoving())
00764 {
00765 abortPushingGoal("Gripper is not moving");
00766 }
00767 else if (objectDisappeared(tracker_state))
00768 {
00769 abortPushingGoal("Object disappeared");
00770 }
00771 else
00772 {
00773 if (objectTooFarFromGripper(tracker_state.x))
00774 {
00775 abortPushingGoal("Object is too far from gripper.");
00776 }
00777 else if (behavior_primitive_ != "gripper_pull")
00778 {
00779 abortPushingGoal("Object is not between gripper and goal.");
00780 }
00781 }
00782 }
00783
00784 void abortPushingGoal(std::string msg)
00785 {
00786 ROS_WARN_STREAM(msg << " Aborting.");
00787 PushTrackerResult res;
00788 res.aborted = true;
00789 as_.setAborted(res);
00790 obj_tracker_->pause();
00791 }
00792
00802 bool learnPushCallback(LearnPush::Request& req, LearnPush::Response& res)
00803 {
00804 if ( have_depth_data_ )
00805 {
00806 if (!req.analyze_previous)
00807 {
00808 controller_name_ = req.controller_name;
00809 proxy_name_ = req.proxy_name;
00810 behavior_primitive_ = req.behavior_primitive;
00811 }
00812
00813 if (req.initialize)
00814 {
00815 num_position_failures_ = 0;
00816 ROS_INFO_STREAM("Initializing");
00817 record_count_ = 0;
00818 learn_callback_count_ = 0;
00819 res.no_push = true;
00820 ROS_INFO_STREAM("Stopping input recording");
00821 recording_input_ = false;
00822 obj_tracker_->stopTracking();
00823 }
00824 else if (req.analyze_previous || req.get_pose_only)
00825 {
00826 ROS_INFO_STREAM("Getting current object pose");
00827 getObjectPose(res);
00828 res.no_push = true;
00829 ROS_INFO_STREAM("Stopping input recording");
00830 recording_input_ = false;
00831 ProtoObject cur_obj = obj_tracker_->getMostRecentObject();
00832 if (cur_obj.cloud.header.frame_id.size() == 0)
00833 {
00834 ROS_INFO_STREAM("cur_obj.cloud.header.frame_id is blank, setting to workspace_frame_");
00835 cur_obj.cloud.header.frame_id = workspace_frame_;
00836 }
00837 PushTrackerState cur_state;
00838 cur_state.x.x = res.centroid.x;
00839 cur_state.x.y = res.centroid.y;
00840 cur_state.x.theta = res.theta;
00841 cur_state.z = res.centroid.z;
00842 if (req.analyze_previous)
00843 {
00844
00845 obj_tracker_->trackerDisplay(cur_color_frame_, cur_state, cur_obj, true);
00846 ROS_INFO_STREAM("Current theta: " << cur_state.x.theta);
00847 if (use_displays_)
00848 {
00849 ROS_INFO_STREAM("Presss 's' to swap orientation: ");
00850 char key_press = cv::waitKey(2000);
00851 if (key_press == 's')
00852 {
00853
00854 force_swap_ = !obj_tracker_->getSwapState();
00855 startTracking(cur_state, force_swap_);
00856 ROS_INFO_STREAM("Swapped theta: " << cur_state.x.theta);
00857 res.theta = cur_state.x.theta;
00858 obj_tracker_->stopTracking();
00859 obj_tracker_->trackerDisplay(cur_color_frame_, cur_state, cur_obj);
00860
00861 cv::waitKey(3);
00862 }
00863 }
00864 }
00865 else
00866 {
00867 ROS_INFO_STREAM("Calling tracker display");
00868 obj_tracker_->trackerDisplay(cur_color_frame_, cur_state, cur_obj);
00869 obj_tracker_->stopTracking();
00870 }
00871 ROS_INFO_STREAM("Done getting current pose\n");
00872 }
00873 else
00874 {
00875 ROS_INFO_STREAM("Determining push start pose");
00876 res = getPushStartPose(req);
00877 recording_input_ = !res.no_objects;
00878 if (recording_input_)
00879 {
00880 ROS_INFO_STREAM("Starting input recording");
00881 ROS_INFO_STREAM("current_file_id: " << current_file_id_);
00882 }
00883 else
00884 {
00885 ROS_INFO_STREAM("Stopping input recording");
00886 }
00887 res.no_push = res.no_objects;
00888 }
00889 }
00890 else
00891 {
00892 ROS_ERROR_STREAM("Calling getStartPose prior to receiving sensor data.");
00893 recording_input_ = false;
00894 res.no_push = true;
00895 return false;
00896 }
00897 return true;
00898 }
00899
00900 LearnPush::Response getPushStartPose(LearnPush::Request& req)
00901 {
00902 LearnPush::Response res;
00903 PushTrackerState cur_state;
00904 startTracking(cur_state, force_swap_);
00905 ProtoObject cur_obj = obj_tracker_->getMostRecentObject();
00906 if (req.learn_start_loc)
00907 {
00908 obj_tracker_->trackerDisplay(cur_color_frame_, cur_state, cur_obj, true);
00909 if (use_displays_)
00910 {
00911 ROS_INFO_STREAM("Current theta: " << cur_state.x.theta);
00912 ROS_INFO_STREAM("Presss 's' to swap orientation: ");
00913 char key_press = cv::waitKey(2000);
00914 if (key_press == 's')
00915 {
00916 force_swap_ = !force_swap_;
00917 obj_tracker_->toggleSwap();
00918 startTracking(cur_state, force_swap_);
00919 obj_tracker_->trackerDisplay(cur_color_frame_, cur_state, cur_obj);
00920
00921 cv::waitKey(3);
00922 ROS_INFO_STREAM("Swapped theta: " << cur_state.x.theta);
00923 }
00924 }
00925 }
00926
00927 if (!start_tracking_on_push_call_)
00928 {
00929 obj_tracker_->pause();
00930 }
00931
00932 if (cur_state.no_detection)
00933 {
00934 ROS_WARN_STREAM("No objects found");
00935 res.centroid.x = 0.0;
00936 res.centroid.y = 0.0;
00937 res.centroid.z = 0.0;
00938 res.theta = 0.0;
00939 res.no_objects = true;
00940 return res;
00941 }
00942 res.no_objects = false;
00943 res.centroid.x = cur_obj.centroid[0];
00944 res.centroid.y = cur_obj.centroid[1];
00945 res.centroid.z = cur_obj.centroid[2];
00946 res.theta = cur_state.x.theta;
00947
00948
00949 PushVector p;
00950 p.header.frame_id = workspace_frame_;
00951 bool pull_start = (req.behavior_primitive == "gripper_pull");
00952 bool rotate_push = (req.controller_name == "rotate_to_heading");
00953
00954
00955 if (req.learn_start_loc)
00956 {
00957
00958 ShapeLocation chosen_loc;
00959 float predicted_score = -1;
00960 if (req.start_loc_param_path.length() > 0)
00961 {
00962 ROS_INFO_STREAM("Finding learned push start loc");
00963 ROS_INFO_STREAM("Using param path "<< req.start_loc_param_path);
00964
00965
00966 if (req.start_loc_param_path.compare("rand") == 0)
00967 {
00968 chosen_loc = chooseRandomPushStartLoc(cur_obj, cur_state, rotate_push);
00969 }
00970 else
00971 {
00972 chosen_loc = chooseLearnedPushStartLoc(cur_obj, cur_state, req.start_loc_param_path, predicted_score,
00973 req.previous_position_worked, rotate_push);
00974 }
00975 }
00976 else if (start_loc_use_fixed_goal_)
00977 {
00978 chosen_loc = chooseFixedGoalPushStartLoc(cur_obj, cur_state, req.new_object,
00979 req.num_start_loc_pushes_per_sample, req.num_start_loc_sample_locs,
00980 req.trial_id);
00981 }
00982 else
00983 {
00984 chosen_loc = choosePushStartLoc(cur_obj, cur_state, req.new_object, req.num_start_loc_clusters);
00985 }
00986 ROS_INFO_STREAM("Chosen loc is: (" << chosen_loc.boundary_loc_.x << ", " << chosen_loc.boundary_loc_.y << ")");
00987 res.shape_descriptor.assign(chosen_loc.descriptor_.begin(), chosen_loc.descriptor_.end());
00988 res.predicted_score = predicted_score;
00989 float new_push_angle;
00990 if (rotate_push)
00991 {
00992
00993 new_push_angle = getRotatePushHeading(cur_state, chosen_loc, cur_obj);
00994 res.goal_pose.x = cur_state.x.x;
00995 res.goal_pose.y = cur_state.x.y;
00996
00997
00998
00999 res.goal_pose.theta = subPIAngle(cur_state.x.theta+M_PI);
01000 }
01001 else
01002 {
01003
01004 new_push_angle = atan2(res.centroid.y - chosen_loc.boundary_loc_.y,
01005 res.centroid.x - chosen_loc.boundary_loc_.x);
01006 res.goal_pose.x = res.centroid.x+cos(new_push_angle)*start_loc_push_dist_;
01007 res.goal_pose.y = res.centroid.y+sin(new_push_angle)*start_loc_push_dist_;
01008 }
01009
01010 p.start_point.x = chosen_loc.boundary_loc_.x;
01011 p.start_point.y = chosen_loc.boundary_loc_.y;
01012 p.start_point.z = chosen_loc.boundary_loc_.z;
01013 p.push_angle = new_push_angle;
01014 p.push_dist = start_loc_push_dist_;
01015
01016
01017 timing_push_ = true;
01018
01019
01020
01021 if (write_to_disk_)
01022 {
01023 std::stringstream cloud_file_name;
01024 cloud_file_name << base_output_path_ << req.trial_id << "_obj_cloud.pcd";
01025 std::stringstream color_file_name;
01026 color_file_name << base_output_path_ << req.trial_id << "_color.png";
01027 current_file_id_ = req.trial_id;
01028 pcl16::io::savePCDFile(cloud_file_name.str(), cur_obj.cloud);
01029 cv::imwrite(color_file_name.str(), cur_color_frame_);
01030 }
01031 }
01032 else
01033 {
01034 res.goal_pose.x = req.goal_pose.x;
01035 res.goal_pose.y = req.goal_pose.y;
01036
01037
01038 if (pull_start)
01039 {
01040
01041 p.push_angle = atan2(res.centroid.y - res.goal_pose.y, res.centroid.x - res.goal_pose.x);
01042 }
01043 else if (rotate_push)
01044 {
01045
01046 }
01047 else
01048 {
01049 p.push_angle = atan2(res.goal_pose.y - res.centroid.y, res.goal_pose.x - res.centroid.x);
01050 }
01051
01052 Eigen::Vector3f push_unit_vec(std::cos(p.push_angle), std::sin(p.push_angle), 0.0f);
01053 std::vector<pcl16::PointXYZ> end_points;
01054 pcl_segmenter_->lineCloudIntersectionEndPoints(cur_obj.cloud, push_unit_vec, cur_obj.centroid, end_points);
01055 p.start_point.x = end_points[0].x;
01056 p.start_point.y = end_points[0].y;
01057 p.start_point.z = end_points[0].z;
01058
01059 p.push_dist = hypot(res.centroid.x - res.goal_pose.x, res.centroid.y - res.goal_pose.y);
01060 timing_push_ = false;
01061 }
01062
01063 PointStamped obj_centroid;
01064 obj_centroid.header.frame_id = workspace_frame_;
01065 obj_centroid.point = res.centroid;
01066 if (rotate_push)
01067 {
01068 displayGoalHeading(cur_color_frame_, obj_centroid, cur_state.x.theta, res.goal_pose.theta);
01069 }
01070 else
01071 {
01072 PointStamped start_point;
01073 start_point.header.frame_id = workspace_frame_;
01074 start_point.point = p.start_point;
01075 PointStamped end_point;
01076 end_point.header.frame_id = workspace_frame_;
01077 end_point.point.x = res.goal_pose.x;
01078 end_point.point.y = res.goal_pose.y;
01079 end_point.point.z = start_point.point.z;
01080 displayPushVector(cur_color_frame_, start_point, end_point);
01081 if (use_displays_)
01082 {
01083 displayInitialPushVector(cur_color_frame_, start_point, end_point, obj_centroid);
01084 }
01085 }
01086
01087
01088 ROS_INFO_STREAM("Chosen push start point: (" << p.start_point.x << ", " << p.start_point.y << ", " <<
01089 p.start_point.z << ")");
01090 ROS_INFO_STREAM("Push dist: " << p.push_dist);
01091 ROS_INFO_STREAM("Push angle: " << p.push_angle << "\n");
01092 learn_callback_count_++;
01093 res.push = p;
01094 tracker_goal_pose_ = res.goal_pose;
01095 return res;
01096 }
01097
01108 ShapeLocation choosePushStartLoc(ProtoObject& cur_obj, PushTrackerState& cur_state, bool new_object, int num_clusters)
01109 {
01110 if (new_object)
01111 {
01112 start_loc_history_.clear();
01113 }
01114
01115 ShapeLocations locs = tabletop_pushing::extractObjectShapeContext(cur_obj, use_center_pointing_shape_context_);
01116
01117
01118
01119 int loc_idx = 0;
01120 if (start_loc_history_.size() == 0)
01121 {
01122
01123 loc_idx = rand() % locs.size();
01124 }
01125 else
01126 {
01127
01128 std::vector<int> cluster_ids;
01129 ShapeDescriptors centers;
01130 double min_err_change = 0.001;
01131 int max_iter = 1000;
01132 tabletop_pushing::clusterShapeFeatures(locs, num_clusters, cluster_ids, centers, min_err_change, max_iter);
01133
01134
01135 cv::Mat boundary_disp_img(cur_color_frame_.size(), CV_32FC3, cv::Scalar(0,0,0));
01136 for (unsigned int i = 0; i < locs.size(); ++i)
01137 {
01138 const cv::Point2f img_idx = pcl_segmenter_->projectPointIntoImage(
01139 locs[i].boundary_loc_, cur_obj.cloud.header.frame_id, camera_frame_);
01140 boundary_disp_img.at<cv::Vec3f>(img_idx.y, img_idx.x) = pcl_segmenter_->colors_[cluster_ids[i]];
01141 }
01142 cv::imshow("Cluster colors", boundary_disp_img);
01143
01144
01145
01146 std::vector<int> used_clusters;
01147 for (int i = 0; i < start_loc_history_.size(); ++i)
01148 {
01149 double cluster_dist = 0;
01150 int closest = tabletop_pushing::closestShapeFeatureCluster(start_loc_history_[i].descriptor_, centers,
01151 cluster_dist);
01152 used_clusters.push_back(closest);
01153 }
01154 bool done = false;
01155 int rand_cluster = -1;
01156 while (!done)
01157 {
01158 rand_cluster = rand() % num_clusters;
01159 done = true;
01160 for (int i = 0; i < used_clusters.size(); ++i)
01161 {
01162 if (used_clusters[i] == rand_cluster)
01163 {
01164 done = false;
01165 }
01166 }
01167 }
01168 ROS_INFO_STREAM("Chose cluster " << rand_cluster);
01169
01170 std::vector<int> loc_choices;
01171 for (int l = 0; l < locs.size(); ++l)
01172 {
01173 if (cluster_ids[l] == rand_cluster)
01174 {
01175 loc_choices.push_back(l);
01176 }
01177 }
01178 int choice_idx = rand()%loc_choices.size();
01179 loc_idx = loc_choices[choice_idx];
01180 }
01181
01182 ShapeLocation s(worldPointInObjectFrame(locs[loc_idx].boundary_loc_, cur_state),
01183 locs[loc_idx].descriptor_);
01184 start_loc_history_.push_back(s);
01185 return locs[loc_idx];
01186 }
01187
01200 ShapeLocation chooseFixedGoalPushStartLoc(ProtoObject& cur_obj, PushTrackerState& cur_state, bool new_object,
01201 int num_start_loc_pushes_per_sample, int num_start_loc_sample_locs,
01202 std::string trial_id)
01203 {
01204 XYZPointCloud hull_cloud = tabletop_pushing::getObjectBoundarySamples(cur_obj, hull_alpha_);
01205
01206 int rot_idx = -1;
01207 if (new_object)
01208 {
01209
01210 start_loc_obj_ = cur_obj;
01211
01212 start_loc_arc_length_percent_ = 0.0;
01213 start_loc_push_sample_count_ = 0;
01214 start_loc_history_.clear();
01215
01216
01217 ROS_INFO_STREAM("Current state theta is: " << cur_state.x.theta);
01218 double min_angle_dist = FLT_MAX;
01219 for (int i = 0; i < hull_cloud.size(); ++i)
01220 {
01221 double theta_i = atan2(hull_cloud.at(i).y - cur_state.x.y, hull_cloud.at(i).x - cur_state.x.x);
01222 double angle_dist_i = fabs(subPIAngle(theta_i - cur_state.x.theta));
01223 if (angle_dist_i < min_angle_dist)
01224 {
01225 min_angle_dist = angle_dist_i;
01226 rot_idx = i;
01227 }
01228 }
01229 }
01230 else
01231 {
01232
01233 if (start_loc_history_.size() % num_start_loc_pushes_per_sample == 0)
01234 {
01235 start_loc_arc_length_percent_ += 1.0/num_start_loc_sample_locs;
01236 ROS_INFO_STREAM("Incrementing arc length percent based on: " << num_start_loc_pushes_per_sample);
01237 }
01238
01239
01240 ROS_INFO_STREAM("init_obj_point: " << start_loc_history_[0].boundary_loc_);
01241 pcl16::PointXYZ init_loc_point = objectPointInWorldFrame(start_loc_history_[0].boundary_loc_, cur_state);
01242 ROS_INFO_STREAM("init_loc_point: " << init_loc_point);
01243
01244
01245 double min_dist = FLT_MAX;
01246 for (int i = 0; i < hull_cloud.size(); ++i)
01247 {
01248 double dist_i = pcl_segmenter_->sqrDist(init_loc_point, hull_cloud.at(i));
01249 if (dist_i < min_dist)
01250 {
01251 min_dist = dist_i;
01252 rot_idx = i;
01253 }
01254 }
01255 }
01256
01257 double pt0_theta = atan2(hull_cloud[rot_idx].y - cur_state.x.y, hull_cloud[rot_idx].x - cur_state.x.x);
01258 int pt1_idx = (rot_idx+1) % hull_cloud.size();
01259 double pt1_theta = atan2(hull_cloud[pt1_idx].y - cur_state.x.y, hull_cloud[pt1_idx].x - cur_state.x.x);
01260 bool reverse_data = false;
01261 if (subPIAngle(pt1_theta - pt0_theta) < 0)
01262 {
01263 reverse_data = true;
01264 ROS_INFO_STREAM("Reversing data for boundaries");
01265 }
01266
01267
01268 std::vector<double> boundary_dists(hull_cloud.size(), 0.0);
01269 double boundary_length = 0.0;
01270 ROS_INFO_STREAM("rot_idx is " << rot_idx);
01271 for (int i = 1; i <= hull_cloud.size(); ++i)
01272 {
01273 int idx0 = (rot_idx+i-1) % hull_cloud.size();
01274 int idx1 = (rot_idx+i) % hull_cloud.size();
01275 if (reverse_data)
01276 {
01277 idx0 = (hull_cloud.size()+rot_idx-i+1) % hull_cloud.size();
01278 idx1 = (hull_cloud.size()+rot_idx-i) % hull_cloud.size();
01279 }
01280
01281 boundary_dists[idx0] = boundary_length;
01282 double loc_dist = pcl_segmenter_->dist(hull_cloud[idx0], hull_cloud[idx1]);
01283 boundary_length += loc_dist;
01284 }
01285
01286
01287 double desired_boundary_dist = start_loc_arc_length_percent_*boundary_length;
01288 ROS_INFO_STREAM("Finding location at dist " << desired_boundary_dist << " ~= " << start_loc_arc_length_percent_*100 <<
01289 "\% of " << boundary_length);
01290 int boundary_loc_idx;
01291 double min_boundary_dist_diff = FLT_MAX;
01292 for (int i = 0; i < hull_cloud.size(); ++i)
01293 {
01294 double boundary_dist_diff_i = fabs(desired_boundary_dist - boundary_dists[i]);
01295 if (boundary_dist_diff_i < min_boundary_dist_diff)
01296 {
01297 min_boundary_dist_diff = boundary_dist_diff_i;
01298 boundary_loc_idx = i;
01299 }
01300 }
01301 ROS_INFO_STREAM("Chose location at idx: " << boundary_loc_idx << " with diff " << min_boundary_dist_diff);
01302
01303
01304
01305 pcl16::PointXYZ boundary_loc = hull_cloud[boundary_loc_idx];
01306 ShapeDescriptor sd = tabletop_pushing::extractLocalAndGlobalShapeFeatures(hull_cloud, cur_obj, boundary_loc,
01307 boundary_loc_idx, gripper_spread_,
01308 hull_alpha_, point_cloud_hist_res_);
01309
01310
01311
01312
01313 ShapeLocation s_obj(worldPointInObjectFrame(boundary_loc, cur_state), sd);
01314 start_loc_history_.push_back(s_obj);
01315
01316
01317 cv::Mat hull_img(cur_color_frame_.size(), CV_8UC1, cv::Scalar(0));
01318 pcl_segmenter_->projectPointCloudIntoImage(hull_cloud, hull_img);
01319 hull_img*=255;
01320 cv::Mat hull_disp_img(hull_img.size(), CV_8UC3, cv::Scalar(0,0,0));
01321 cv::cvtColor(hull_img, hull_disp_img, CV_GRAY2BGR);
01322 cv::Point2f img_rot_idx = pcl_segmenter_->projectPointIntoImage(hull_cloud[rot_idx], hull_cloud.header.frame_id,
01323 camera_frame_);
01324 cv::circle(hull_disp_img, img_rot_idx, 4, cv::Scalar(0,255,0), 3);
01325 cv::Point2f img_loc_idx = pcl_segmenter_->projectPointIntoImage(boundary_loc,
01326 hull_cloud.header.frame_id, camera_frame_);
01327 cv::circle(hull_disp_img, img_loc_idx, 4, cv::Scalar(0,0, 255));
01328 cv::imshow("object hull", hull_disp_img);
01329 if (write_to_disk_)
01330 {
01331 std::stringstream hull_file_name;
01332 hull_file_name << base_output_path_ << trial_id << "_obj_hull.png";
01333 std::stringstream hull_disp_file_name;
01334 hull_disp_file_name << base_output_path_ << trial_id << "_obj_hull_disp.png";
01335 cv::imwrite(hull_file_name.str(), hull_img);
01336 cv::imwrite(hull_disp_file_name.str(), hull_disp_img);
01337 }
01338 ShapeLocation s_world(boundary_loc, sd);
01339 return s_world;
01340
01341 }
01342
01343 ShapeLocation chooseLearnedPushStartLoc(ProtoObject& cur_obj, PushTrackerState& cur_state, std::string param_path,
01344 float& chosen_score, bool previous_position_worked,
01345 bool rotate_push)
01346 {
01347
01348
01349 int local_length = 36;
01350 int global_length = 60;
01351 XYZPointCloud hull_cloud = tabletop_pushing::getObjectBoundarySamples(cur_obj, hull_alpha_);
01352 ShapeDescriptors sds = tabletop_pushing::extractLocalAndGlobalShapeFeatures(hull_cloud, cur_obj,
01353 gripper_spread_, hull_alpha_,
01354 point_cloud_hist_res_);
01355
01356 svm_model* push_model;
01357 push_model = svm_load_model(param_path.c_str());
01358
01359 param_path.erase(param_path.size()-6, 6);
01360 std::stringstream train_feat_path;
01361 train_feat_path << param_path << "-feats.txt";
01362
01363
01364 double gamma_local = 2.5;
01365 double gamma_global = 2.0;
01366 double mixture_weight = 0.7;
01367 if (rotate_push)
01368 {
01369 gamma_local = 0.05;
01370 gamma_global = 2.5;
01371 mixture_weight = 0.7;
01372 }
01373 cv::Mat K = tabletop_pushing::computeChi2Kernel(sds, train_feat_path.str(), local_length, global_length,
01374 gamma_local, gamma_global, mixture_weight);
01375
01376 std::vector<double> pred_push_scores;
01377 std::priority_queue<ScoredIdx, std::vector<ScoredIdx>, ScoredIdxComparison> pq(
01378 (ScoredIdxComparison(rotate_push)) );
01379 XYZPointCloud hull_cloud_obj;
01380 hull_cloud_obj.width = hull_cloud.size();
01381 hull_cloud_obj.height = 1;
01382 hull_cloud_obj.is_dense = false;
01383 hull_cloud_obj.resize(hull_cloud_obj.width*hull_cloud_obj.height);
01384
01385
01386 for (int i = 0; i < K.cols; ++i)
01387 {
01388 svm_node* x = new svm_node[K.rows+1];
01389 x[0].value = 0;
01390 x[0].index = 0;
01391 for (int j = 0; j < K.rows; ++j)
01392 {
01393 x[j+1].value = K.at<double>(j, i);
01394 x[j+1].index = 0;
01395 }
01396
01397 double raw_pred_score = svm_predict(push_model, x);
01398 double pred_score;
01399 if (rotate_push)
01400 {
01401 pred_score = exp(raw_pred_score);
01402 }
01403 else
01404 {
01405 pred_score = exp(raw_pred_score);
01406 }
01407 if (isnan(pred_score) || isinf(pred_score))
01408 {
01409 ROS_WARN_STREAM("Sample " << i << " has pred score: " << pred_score << "\traw pred score: " << raw_pred_score);
01410 }
01411 if (isinf(pred_score))
01412 {
01413 pred_score = raw_pred_score;
01414 }
01415 ScoredIdx scored_idx;
01416 scored_idx.score = pred_score;
01417 scored_idx.idx = i;
01418 pq.push(scored_idx);
01419 pred_push_scores.push_back(pred_score);
01420
01421 hull_cloud_obj[i].x = hull_cloud[i].x - cur_state.x.x;
01422 hull_cloud_obj[i].y = hull_cloud[i].y - cur_state.x.y;
01423 delete x;
01424 }
01425
01426
01427 svm_free_and_destroy_model(&push_model);
01428
01429
01430 writePredictedScoreToDisk(hull_cloud, sds, pred_push_scores);
01431
01432 ScoredIdx best_scored = pq.top();
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449 while (pq.size() > 0)
01450 {
01451 ScoredIdx chosen = pq.top();
01452 pq.pop();
01453
01454
01455 ShapeLocation loc;
01456 loc.boundary_loc_ = hull_cloud[chosen.idx];
01457 loc.descriptor_ = sds[chosen.idx];
01458 float new_push_angle;
01459 Pose2D goal_pose = generateStartLocLearningGoalPose(cur_state, cur_obj, loc, new_push_angle, rotate_push);
01460 if (rotate_push || goalPoseValid(goal_pose))
01461 {
01462 ROS_INFO_STREAM("Chose push location " << chosen.idx << " with score " << chosen.score);
01463 pcl16::PointXYZ selected(hull_cloud_obj[chosen.idx].x, hull_cloud_obj[chosen.idx].y, 0.0);
01464 displayLearnedPushLocScores(pred_push_scores, hull_cloud_obj, selected, rotate_push);
01465 chosen_score = chosen.score;
01466 return loc;
01467 }
01468 }
01469
01470 ShapeLocation best_loc;
01471 best_loc.boundary_loc_ = hull_cloud[best_scored.idx];
01472 best_loc.descriptor_ = sds[best_scored.idx];
01473 ROS_INFO_STREAM("Chose default push location " << best_scored.idx << " with score " << best_scored.score);
01474 chosen_score = best_scored.score;
01475 return best_loc;
01476 }
01477
01478 ShapeLocation chooseRandomPushStartLoc(ProtoObject& cur_obj, PushTrackerState& cur_state, bool rotate_push)
01479 {
01480
01481
01482 XYZPointCloud hull_cloud = tabletop_pushing::getObjectBoundarySamples(cur_obj, hull_alpha_);
01483 ShapeDescriptors sds = tabletop_pushing::extractLocalAndGlobalShapeFeatures(hull_cloud, cur_obj,
01484 gripper_spread_, hull_alpha_,
01485 point_cloud_hist_res_);
01486 std::vector<int> available_indices;
01487 for (int i = 0; i < hull_cloud.size(); ++i)
01488 {
01489 available_indices.push_back(i);
01490 }
01491
01492 while (available_indices.size() > 0)
01493 {
01494 int rand_idx = rand()%available_indices.size();
01495 int chosen_idx = available_indices[rand_idx];
01496
01497
01498 ShapeLocation loc;
01499 loc.boundary_loc_ = hull_cloud[chosen_idx];
01500 loc.descriptor_ = sds[chosen_idx];
01501 float new_push_angle;
01502 Pose2D goal_pose = generateStartLocLearningGoalPose(cur_state, cur_obj, loc, new_push_angle, rotate_push);
01503 if (rotate_push || goalPoseValid(goal_pose))
01504 {
01505
01506 ROS_INFO_STREAM("Choosing random idx: " << chosen_idx);
01507 return loc;
01508 }
01509 available_indices.erase(available_indices.begin()+rand_idx);
01510 }
01511
01512 int chosen_idx = rand()%hull_cloud.size();
01513 ShapeLocation chosen_loc;
01514 chosen_loc.boundary_loc_ = hull_cloud[chosen_idx];
01515 chosen_loc.descriptor_ = sds[chosen_idx];
01516 return chosen_loc;
01517 }
01518
01519 std::vector<pcl16::PointXYZ> findAxisAlignedBoundingBox(PushTrackerState& cur_state, ProtoObject& cur_obj)
01520 {
01521
01522 XYZPointCloud object_frame_cloud;
01523 worldPointsInObjectFrame(cur_obj.cloud, cur_state, object_frame_cloud);
01524
01525 double min_x = FLT_MAX;
01526 double max_x = -FLT_MAX;
01527 double min_y = FLT_MAX;
01528 double max_y = -FLT_MAX;
01529 for (int i = 0; i < object_frame_cloud.size(); ++i)
01530 {
01531 if (object_frame_cloud[i].x < min_x)
01532 {
01533 min_x = object_frame_cloud[i].x;
01534 }
01535 if (object_frame_cloud[i].x > max_x)
01536 {
01537 max_x = object_frame_cloud[i].x;
01538 }
01539 if (object_frame_cloud[i].y < min_y)
01540 {
01541 min_y = object_frame_cloud[i].y;
01542 }
01543 if (object_frame_cloud[i].y > max_y)
01544 {
01545 max_y = object_frame_cloud[i].y;
01546 }
01547 }
01548 std::vector<pcl16::PointXYZ> vertices;
01549 vertices.push_back(objectPointInWorldFrame(pcl16::PointXYZ(max_x, max_y, 0.0), cur_state));
01550 vertices.push_back(objectPointInWorldFrame(pcl16::PointXYZ(max_x, min_y, 0.0), cur_state));
01551 vertices.push_back(objectPointInWorldFrame(pcl16::PointXYZ(min_x, min_y, 0.0), cur_state));
01552 vertices.push_back(objectPointInWorldFrame(pcl16::PointXYZ(min_x, max_y, 0.0), cur_state));
01553 return vertices;
01554 }
01555
01556 float getRotatePushHeading(PushTrackerState& cur_state, ShapeLocation& chosen_loc, ProtoObject& cur_obj)
01557 {
01558
01559
01560
01561
01562 std::vector<pcl16::PointXYZ> vertices = findAxisAlignedBoundingBox(cur_state, cur_obj);
01563 double min_dist = FLT_MAX;
01564 int chosen_idx = 0;
01565 float push_angle_world_frame = 0.0;
01566
01567 cv::Point2f box_center;
01568 box_center.x = (vertices[0].x+vertices[2].x)*0.5;
01569 box_center.y = (vertices[0].y+vertices[2].y)*0.5;
01570 for (int i = 0; i < 4; ++i)
01571 {
01572 int j = (i+1)%4;
01573 double line_dist = cpl_visual_features::pointLineDistance2D(chosen_loc.boundary_loc_, vertices[i], vertices[j]);
01574 if (line_dist < min_dist)
01575 {
01576 min_dist = line_dist;
01577 chosen_idx = i;
01578
01579 push_angle_world_frame = atan2(box_center.y-(vertices[i].y + vertices[j].y)*0.5,
01580 box_center.x-(vertices[i].x + vertices[j].x)*0.5);
01581 }
01582 }
01583
01584 cv::Mat display_frame;
01585 cur_color_frame_.copyTo(display_frame);
01586 for (int i = 0; i < 4; ++i)
01587 {
01588 int j = (i+1)%4;
01589
01590 PointStamped a_world;
01591 a_world.header.frame_id = cur_obj.cloud.header.frame_id;
01592 a_world.point.x = vertices[i].x;
01593 a_world.point.y = vertices[i].y;
01594 a_world.point.z = vertices[i].z;
01595 PointStamped b_world;
01596 b_world.header.frame_id = cur_obj.cloud.header.frame_id;
01597 b_world.point.x = vertices[j].x;
01598 b_world.point.y = vertices[j].y;
01599 b_world.point.z = vertices[j].z;
01600 cv::Point a_img = pcl_segmenter_->projectPointIntoImage(a_world);
01601 cv::Point b_img = pcl_segmenter_->projectPointIntoImage(b_world);
01602 cv::line(display_frame, a_img, b_img, cv::Scalar(0,0,0),3);
01603 if (i == chosen_idx)
01604 {
01605
01606 cv::line(display_frame, a_img, b_img, cv::Scalar(0,255,255),1);
01607 }
01608 else
01609 {
01610 cv::line(display_frame, a_img, b_img, cv::Scalar(0,255,0),1);
01611 }
01612 }
01613 cv::imshow("footprint_box", display_frame);
01614
01615 return push_angle_world_frame;
01616 }
01617
01618 Pose2D generateStartLocLearningGoalPose(PushTrackerState& cur_state, ProtoObject& cur_obj,
01619 ShapeLocation& chosen_loc, float& new_push_angle, bool rotate_push=false)
01620 {
01621 Pose2D goal_pose;
01622 if (rotate_push)
01623 {
01624
01625 new_push_angle = getRotatePushHeading(cur_state, chosen_loc, cur_obj);
01626 goal_pose.x = cur_state.x.x;
01627 goal_pose.y = cur_state.x.y;
01628
01629
01630
01631
01632 goal_pose.theta = cur_state.x.theta+M_PI;
01633 }
01634 else
01635 {
01636
01637 new_push_angle = atan2(cur_state.x.y - chosen_loc.boundary_loc_.y,
01638 cur_state.x.x - chosen_loc.boundary_loc_.x);
01639 goal_pose.x = cur_state.x.x+cos(new_push_angle)*start_loc_push_dist_;
01640 goal_pose.y = cur_state.x.y+sin(new_push_angle)*start_loc_push_dist_;
01641 }
01642 return goal_pose;
01643 }
01644
01645 bool goalPoseValid(Pose2D goal_pose) const
01646 {
01647 return (goal_pose.x < max_goal_x_ && goal_pose.x > min_goal_x_ &&
01648 goal_pose.y < max_goal_y_ && goal_pose.y > min_goal_y_);
01649 }
01650
01651 ShapeLocation getStartLocDescriptor(ProtoObject& cur_obj, PushTrackerState& cur_state, geometry_msgs::Point start_pt)
01652 {
01653
01654 ShapeLocations locs = tabletop_pushing::extractObjectShapeContext(cur_obj, use_center_pointing_shape_context_);
01655
01656 float min_dist = FLT_MAX;
01657 unsigned int loc_idx = locs.size();
01658 for (unsigned int i = 0; i < locs.size(); ++i)
01659 {
01660 float loc_dist = pcl_segmenter_->dist(locs[i].boundary_loc_, start_pt);
01661 if (loc_dist < min_dist)
01662 {
01663 min_dist = loc_dist;
01664 loc_idx = i;
01665 }
01666 }
01667 ROS_WARN_STREAM("Chose loc " << locs[loc_idx].boundary_loc_ << " with distance " << min_dist << "m");
01668 return locs[loc_idx];
01669 }
01670
01671 void worldPointsInObjectFrame(XYZPointCloud& world_cloud, PushTrackerState& cur_state, XYZPointCloud& object_cloud)
01672 {
01673 object_cloud.width = world_cloud.size();
01674 object_cloud.height = 1;
01675 object_cloud.is_dense = false;
01676 object_cloud.resize(object_cloud.width);
01677 for (int i = 0; i < world_cloud.size(); ++i)
01678 {
01679 object_cloud[i] = worldPointInObjectFrame(world_cloud[i], cur_state);
01680 }
01681 }
01682
01683 pcl16::PointXYZ worldPointInObjectFrame(pcl16::PointXYZ world_pt, PushTrackerState& cur_state)
01684 {
01685
01686 pcl16::PointXYZ shifted_pt;
01687 shifted_pt.x = world_pt.x - cur_state.x.x;
01688 shifted_pt.y = world_pt.y - cur_state.x.y;
01689 shifted_pt.z = world_pt.z - cur_state.z;
01690 double ct = cos(cur_state.x.theta);
01691 double st = sin(cur_state.x.theta);
01692
01693 pcl16::PointXYZ obj_pt;
01694 obj_pt.x = ct*shifted_pt.x + st*shifted_pt.y;
01695 obj_pt.y = -st*shifted_pt.x + ct*shifted_pt.y;
01696 obj_pt.z = shifted_pt.z;
01697 return obj_pt;
01698 }
01699
01700 pcl16::PointXYZ objectPointInWorldFrame(pcl16::PointXYZ obj_pt, PushTrackerState& cur_state)
01701 {
01702
01703 pcl16::PointXYZ rotated_pt;
01704 double ct = cos(cur_state.x.theta);
01705 double st = sin(cur_state.x.theta);
01706 rotated_pt.x = ct*obj_pt.x - st*obj_pt.y;
01707 rotated_pt.y = st*obj_pt.x + ct*obj_pt.y;
01708 rotated_pt.z = obj_pt.z;
01709
01710 pcl16::PointXYZ world_pt;
01711 world_pt.x = rotated_pt.x + cur_state.x.x;
01712 world_pt.y = rotated_pt.y + cur_state.x.y;
01713 world_pt.z = rotated_pt.z + cur_state.z;
01714 return world_pt;
01715 }
01716
01717 void getObjectPose(LearnPush::Response& res)
01718 {
01719 bool no_objects = false;
01720
01721
01722
01723
01724 ProtoObject cur_obj = obj_tracker_->findTargetObject(cur_color_frame_, cur_point_cloud_, no_objects, true);
01725 if (no_objects)
01726 {
01727 ROS_WARN_STREAM("No objects found on analysis");
01728 res.centroid.x = 0.0;
01729 res.centroid.y = 0.0;
01730 res.centroid.z = 0.0;
01731 res.theta = 0.0;
01732 res.no_objects = true;
01733 return;
01734 }
01735 cv::RotatedRect obj_ellipse;
01736 obj_tracker_->fitObjectEllipse(cur_obj, obj_ellipse);
01737 res.no_objects = false;
01738 res.centroid.x = cur_obj.centroid[0];
01739 res.centroid.y = cur_obj.centroid[1];
01740 res.centroid.z = cur_obj.centroid[2];
01741 res.theta = obj_tracker_->getThetaFromEllipse(obj_ellipse);
01742 if(obj_tracker_->getSwapState())
01743 {
01744 if(res.theta > 0.0)
01745 res.theta += - M_PI;
01746 else
01747 res.theta += M_PI;
01748 }
01749 }
01750
01751 void startTracking(PushTrackerState& state, bool start_swap=false)
01752 {
01753 ROS_INFO_STREAM("Starting tracker");
01754 frame_set_count_++;
01755 goal_out_count_ = 0;
01756 goal_heading_count_ = 0;
01757 frame_callback_count_ = 0;
01758 if (use_graphcut_arm_seg_)
01759 {
01760 obj_tracker_->initTracks(cur_color_frame_, cur_depth_frame_, cur_self_mask_, cur_point_cloud_,
01761 proxy_name_, state, start_swap);
01762 }
01763 else
01764 {
01765 obj_tracker_->initTracks(cur_color_frame_, cur_depth_frame_, cur_self_mask_, cur_self_filtered_cloud_,
01766 proxy_name_, state, start_swap);
01767 }
01768
01769 }
01770
01771 void lArmStateCartCB(const pr2_manipulation_controllers::JTTaskControllerState l_arm_state)
01772 {
01773 l_arm_pose_ = l_arm_state.x;
01774 l_arm_vel_ = l_arm_state.xd;
01775 }
01776 void rArmStateCartCB(const pr2_manipulation_controllers::JTTaskControllerState r_arm_state)
01777 {
01778 r_arm_pose_ = r_arm_state.x;
01779 r_arm_vel_ = r_arm_state.xd;
01780 }
01781 void lArmStateVelCB(const pr2_manipulation_controllers::JinvTeleopControllerState l_arm_state)
01782 {
01783 l_arm_pose_ = l_arm_state.x;
01784 l_arm_vel_ = l_arm_state.xd;
01785 }
01786 void rArmStateVelCB(const pr2_manipulation_controllers::JinvTeleopControllerState r_arm_state)
01787 {
01788 r_arm_pose_ = r_arm_state.x;
01789 r_arm_vel_ = r_arm_state.xd;
01790 }
01791
01792 void pushTrackerGoalCB()
01793 {
01794 ROS_INFO_STREAM("Accepting goal");
01795 shared_ptr<const PushTrackerGoal> tracker_goal = as_.acceptNewGoal();
01796 tracker_goal_pose_ = tracker_goal->desired_pose;
01797 pushing_arm_ = tracker_goal->which_arm;
01798 controller_name_ = tracker_goal->controller_name;
01799 proxy_name_ = tracker_goal->proxy_name;
01800 behavior_primitive_ = tracker_goal->behavior_primitive;
01801 ROS_INFO_STREAM("Accepted goal of " << tracker_goal_pose_);
01802 gripper_not_moving_count_ = 0;
01803 object_not_moving_count_ = 0;
01804 object_not_detected_count_ = 0;
01805 object_too_far_count_ = 0;
01806 object_not_between_count_ = 0;
01807 push_start_time_ = ros::Time::now().toSec();
01808
01809 if (obj_tracker_->isInitialized())
01810 {
01811 obj_tracker_->unpause();
01812 }
01813 else
01814 {
01815 PushTrackerState state;
01816 startTracking(state);
01817 }
01818 }
01819
01820 void pushTrackerPreemptCB()
01821 {
01822 obj_tracker_->pause();
01823 ROS_INFO_STREAM("Preempted push tracker");
01824
01825 as_.setPreempted();
01826 }
01827
01828 bool objectNotMoving(PushTrackerState& tracker_state)
01829 {
01830 if (tracker_state.x_dot.x < object_not_moving_thresh_ &&
01831 tracker_state.x_dot.y < object_not_moving_thresh_)
01832 {
01833 ++object_not_moving_count_;
01834 }
01835 else
01836 {
01837 object_not_moving_count_ = 0;
01838 }
01839 return object_not_moving_count_ >= object_not_moving_count_limit_;
01840 }
01841
01842 bool objectDisappeared(PushTrackerState& tracker_state)
01843 {
01844 if (tracker_state.no_detection)
01845 {
01846 ++object_not_detected_count_;
01847 }
01848 else
01849 {
01850 object_not_detected_count_ = 0;
01851 }
01852 return object_not_detected_count_ >= object_not_detected_count_limit_;
01853 }
01854
01855 bool gripperNotMoving()
01856 {
01857 Twist gripper_vel;
01858 if ( pushing_arm_ == "l")
01859 {
01860 gripper_vel = l_arm_vel_;
01861 }
01862 else
01863 {
01864 gripper_vel = r_arm_vel_;
01865 }
01866 if (gripper_vel.linear.x < gripper_not_moving_thresh_ &&
01867 gripper_vel.linear.y < gripper_not_moving_thresh_)
01868 {
01869 ++gripper_not_moving_count_;
01870 }
01871 else
01872 {
01873 gripper_not_moving_count_ = 0;
01874 }
01875 return gripper_not_moving_count_ >= gripper_not_moving_count_limit_;
01876 }
01877
01878 bool objectNotBetweenGoalAndGripper(Pose2D& obj_state)
01879 {
01880 if (pushing_arm_ == "l")
01881 {
01882 if( pointIsBetweenOthers(l_arm_pose_.pose.position, obj_state, tracker_goal_pose_,
01883 object_not_between_epsilon_))
01884 {
01885 ++object_not_between_count_;
01886 }
01887 else
01888 {
01889 object_not_between_count_ = 0;
01890 }
01891 }
01892 else if (pushing_arm_ == "r")
01893 {
01894 if( pointIsBetweenOthers(r_arm_pose_.pose.position, obj_state, tracker_goal_pose_,
01895 object_not_between_epsilon_))
01896 {
01897 ++object_not_between_count_;
01898 }
01899 else
01900 {
01901 object_not_between_count_ = 0;
01902 }
01903 }
01904 return object_not_between_count_ >= object_not_between_count_limit_;
01905 }
01906
01907
01908 bool objectTooFarFromGripper(Pose2D& obj_state)
01909 {
01910 geometry_msgs::Point gripper_pt;
01911 if (pushing_arm_ == "l")
01912 {
01913 gripper_pt = l_arm_pose_.pose.position;
01914 }
01915 else
01916 {
01917 gripper_pt = r_arm_pose_.pose.position;
01918 }
01919 float gripper_dist = hypot(gripper_pt.x-obj_state.x,gripper_pt.y-obj_state.y);
01920 if (gripper_dist > max_object_gripper_dist_)
01921 {
01922 ++object_too_far_count_;
01923 }
01924 else
01925 {
01926 object_too_far_count_ = 0;
01927 }
01928 return object_too_far_count_ >= object_too_far_count_limit_;
01929 }
01930
01931 bool pushingTimeUp()
01932 {
01933 if (ros::Time::now().toSec() - push_start_time_ > start_loc_push_time_)
01934 {
01935 return true;
01936 }
01937 return false;
01938 }
01939
01940 bool pointIsBetweenOthers(geometry_msgs::Point pt, Pose2D& x1, Pose2D& x2, double epsilon=0.0)
01941 {
01942
01943 const float a_x = x2.x - pt.x;
01944 const float a_y = x2.y - pt.y;
01945 const float b_x = x2.x - x1.x;
01946 const float b_y = x2.y - x1.y;
01947 const float a_dot_b = a_x*b_x + a_y*b_y;
01948 const float b_dot_b = b_x*b_x + b_y*b_y;
01949 const float a_onto_b = a_dot_b/b_dot_b;
01950
01951
01952 const float d_1_x = a_onto_b*b_x;
01953 const float d_1_y = a_onto_b*b_y;
01954 const float d_1 = d_1_x*d_1_x + d_1_y*d_1_y;
01955 const float d_2 = b_x*b_x + b_y*b_y;
01956
01957
01958 return d_1+epsilon*epsilon < d_2;
01959 }
01960
01970 bool getTableLocation(LocateTable::Request& req, LocateTable::Response& res)
01971 {
01972 if ( have_depth_data_ )
01973 {
01974 getTablePlane(cur_point_cloud_, res.table_centroid);
01975 if ((res.table_centroid.pose.position.x == 0.0 &&
01976 res.table_centroid.pose.position.y == 0.0 &&
01977 res.table_centroid.pose.position.z == 0.0) ||
01978 res.table_centroid.pose.position.x < 0.0)
01979 {
01980 ROS_ERROR_STREAM("No plane found, leaving");
01981 res.found_table = false;
01982 return false;
01983 }
01984 res.found_table = true;
01985 res.table_centroid.header.stamp = ros::Time::now();
01986 }
01987 else
01988 {
01989 ROS_ERROR_STREAM("Calling getTableLocation prior to receiving sensor data.");
01990 res.found_table = false;
01991 return false;
01992 }
01993 return true;
01994 }
01995
02003 void getTablePlane(XYZPointCloud& cloud, PoseStamped& p)
02004 {
02005 XYZPointCloud obj_cloud, table_cloud;
02006
02007 Eigen::Vector4f table_centroid;
02008 pcl_segmenter_->getTablePlane(cloud, obj_cloud, table_cloud, table_centroid, false, true);
02009 p.pose.position.x = table_centroid[0];
02010 p.pose.position.y = table_centroid[1];
02011 p.pose.position.z = table_centroid[2];
02012 p.header = cloud.header;
02013 ROS_INFO_STREAM("Table centroid is: ("
02014 << p.pose.position.x << ", "
02015 << p.pose.position.y << ", "
02016 << p.pose.position.z << ")");
02017 table_centroid_ = p;
02018 }
02019
02020 void displayPushVector(cv::Mat& img, PointStamped& start_point, PointStamped& end_point,
02021 std::string display_name="goal_vector", bool force_no_write=false)
02022 {
02023 cv::Mat disp_img;
02024 img.copyTo(disp_img);
02025
02026 cv::Point img_start_point = pcl_segmenter_->projectPointIntoImage(start_point);
02027 cv::Point img_end_point = pcl_segmenter_->projectPointIntoImage(end_point);
02028 cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,0,0),3);
02029 cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,255,0));
02030 cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,0,0),3);
02031 cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,255,0));
02032 if (use_displays_)
02033 {
02034 cv::imshow(display_name, disp_img);
02035 }
02036 if (write_to_disk_ && !force_no_write)
02037 {
02038
02039 std::stringstream push_out_name;
02040 push_out_name << base_output_path_ << display_name << "_" << frame_set_count_ << "_"
02041 << goal_out_count_++ << ".png";
02042 cv::imwrite(push_out_name.str(), disp_img);
02043 }
02044 }
02045
02046 void displayInitialPushVector(cv::Mat& img, PointStamped& start_point, PointStamped& end_point,
02047 PointStamped& centroid)
02048 {
02049 cv::Mat disp_img;
02050 img.copyTo(disp_img);
02051
02052 cv::Point img_start_point = pcl_segmenter_->projectPointIntoImage(start_point);
02053 cv::Point img_end_point = pcl_segmenter_->projectPointIntoImage(end_point);
02054 cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,0,0),3);
02055 cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,255,0));
02056 cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,0,0),3);
02057 cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,255,0));
02058 cv::Point img_centroid_point = pcl_segmenter_->projectPointIntoImage(centroid);
02059 cv::circle(disp_img, img_centroid_point, 4, cv::Scalar(0,0,0),3);
02060 cv::circle(disp_img, img_centroid_point, 4, cv::Scalar(0,0,255));
02061 cv::imshow("initial_vector", disp_img);
02062 }
02063
02064
02065 void displayGoalHeading(cv::Mat& img, PointStamped& centroid, double theta, double goal_theta,
02066 bool force_no_write=false)
02067 {
02068 cv::Mat disp_img;
02069 img.copyTo(disp_img);
02070
02071 cv::Point img_c_idx = pcl_segmenter_->projectPointIntoImage(centroid);
02072 cv::RotatedRect obj_ellipse = obj_tracker_->getMostRecentEllipse();
02073 const float x_maj_rad = (std::cos(theta)*obj_ellipse.size.height*0.5);
02074 const float y_maj_rad = (std::sin(theta)*obj_ellipse.size.height*0.5);
02075 pcl16::PointXYZ table_heading_point(centroid.point.x+x_maj_rad, centroid.point.y+y_maj_rad,
02076 centroid.point.z);
02077 const cv::Point2f img_maj_idx = pcl_segmenter_->projectPointIntoImage(
02078 table_heading_point, centroid.header.frame_id, camera_frame_);
02079 const float goal_x_rad = (std::cos(goal_theta)*obj_ellipse.size.height*0.5);
02080 const float goal_y_rad = (std::sin(goal_theta)*obj_ellipse.size.height*0.5);
02081 pcl16::PointXYZ goal_heading_point(centroid.point.x+goal_x_rad, centroid.point.y+goal_y_rad,
02082 centroid.point.z);
02083 cv::Point2f img_goal_idx = pcl_segmenter_->projectPointIntoImage(
02084 goal_heading_point, centroid.header.frame_id, camera_frame_);
02085
02086 float img_start_angle = RAD2DEG(std::atan2(img_maj_idx.y-img_c_idx.y,
02087 img_maj_idx.x-img_c_idx.x));
02088 float img_end_angle = RAD2DEG(std::atan2(img_goal_idx.y-img_c_idx.y,
02089 img_goal_idx.x-img_c_idx.x));
02090 double img_height = std::sqrt(std::pow(img_maj_idx.x-img_c_idx.x,2) +
02091 std::pow(img_maj_idx.y-img_c_idx.y,2));
02092
02093 cv::Point img_goal_draw_idx(std::cos(DEG2RAD(img_end_angle))*img_height+img_c_idx.x,
02094 std::sin(DEG2RAD(img_end_angle))*img_height+img_c_idx.y);
02095 cv::Size axes(img_height, img_height);
02096
02097 cv::ellipse(disp_img, img_c_idx, axes, img_start_angle, 0,
02098 RAD2DEG(subPIAngle(DEG2RAD(img_end_angle-img_start_angle))), cv::Scalar(0,0,0), 3);
02099 cv::line(disp_img, img_c_idx, img_maj_idx, cv::Scalar(0,0,0), 3);
02100 cv::line(disp_img, img_c_idx, img_goal_draw_idx, cv::Scalar(0,0,0), 3);
02101 cv::ellipse(disp_img, img_c_idx, axes, img_start_angle, 0,
02102 RAD2DEG(subPIAngle(DEG2RAD(img_end_angle-img_start_angle))), cv::Scalar(0,0,255));
02103 cv::line(disp_img, img_c_idx, img_maj_idx, cv::Scalar(0,0,255));
02104 cv::line(disp_img, img_c_idx, img_goal_draw_idx, cv::Scalar(0,0,255));
02105 if (use_displays_)
02106 {
02107 cv::imshow("goal_heading", disp_img);
02108 }
02109 if (write_to_disk_ && !force_no_write)
02110 {
02111
02112 std::stringstream push_out_name;
02113 push_out_name << base_output_path_ << "goal_heading_" << frame_set_count_ << "_"
02114 << goal_heading_count_++ << ".png";
02115 cv::imwrite(push_out_name.str(), disp_img);
02116 }
02117 }
02118
02119 void displayRobotGripperPoses(cv::Mat& img)
02120 {
02121 cv::Mat disp_img;
02122 img.copyTo(disp_img);
02123
02124 PointStamped l_gripper;
02125 l_gripper.header.frame_id = "torso_lift_link";
02126 l_gripper.header.stamp = ros::Time(0);
02127 l_gripper.point = l_arm_pose_.pose.position;
02128 PointStamped r_gripper;
02129 r_gripper.header.frame_id = "torso_lift_link";
02130 r_gripper.header.stamp = ros::Time(0);
02131 r_gripper.point = r_arm_pose_.pose.position;
02132 cv::Point l_gripper_img_point = pcl_segmenter_->projectPointIntoImage(l_gripper);
02133 cv::Point r_gripper_img_point = pcl_segmenter_->projectPointIntoImage(r_gripper);
02134 cv::circle(disp_img, l_gripper_img_point, 4, cv::Scalar(0,0,0),3);
02135 cv::circle(disp_img, l_gripper_img_point, 4, cv::Scalar(0,255,0));
02136 cv::circle(disp_img, r_gripper_img_point, 4, cv::Scalar(0,0,0),3);
02137 cv::circle(disp_img, r_gripper_img_point, 4, cv::Scalar(0,0,255));
02138
02139 if (use_displays_)
02140 {
02141 cv::imshow("grippers", disp_img);
02142 }
02143 }
02144
02145 void displayLearnedPushLocScores(std::vector<double>& push_scores, XYZPointCloud& locs, pcl16::PointXYZ selected,
02146 bool rotate_push)
02147 {
02148 double max_y = 0.2;
02149 double min_y = -0.2;
02150 double max_x = 0.2;
02151 double min_x = -0.2;
02152 int rows = ceil((max_y-min_y)/FOOTPRINT_XY_RES);
02153 int cols = ceil((max_x-min_x)/FOOTPRINT_XY_RES);
02154 cv::Mat footprint(rows, cols, CV_8UC3, cv::Scalar(255,255,255));
02155
02156 for (int i = 0; i < push_scores.size(); ++i)
02157 {
02158 int img_y = rows-objLocToIdx(locs[i].x, min_x, max_x);
02159 int img_x = cols-objLocToIdx(locs[i].y, min_y, max_y);
02160 double score;
02161 if(rotate_push)
02162 {
02163 score = push_scores[i]/M_PI;
02164 }
02165 else
02166 {
02167 score = -log(push_scores[i])/10;
02168 }
02169
02170 cv::Scalar color(0, score*255, (1-score)*255);
02171 cv::circle(footprint, cv::Point(img_x, img_y), 1, color, 3);
02172 cv::circle(footprint, cv::Point(img_x, img_y), 2, color, 3);
02173 cv::circle(footprint, cv::Point(img_x, img_y), 3, color, 3);
02174 }
02175
02176
02177 std::stringstream score_file_name;
02178 score_file_name << base_output_path_ << "score_footprint" << "_" << frame_set_count_ << ".png";
02179 cv::imwrite(score_file_name.str(), footprint);
02180
02181
02182 cv::Point selected_img(cols-objLocToIdx(selected.y, min_y, max_y), rows-objLocToIdx(selected.x, min_x, max_x));
02183 cv::circle(footprint, selected_img, 5, cv::Scalar(0,0,0), 2);
02184 cv::imshow("Push score", footprint);
02185
02186 std::stringstream score_selected_file_name;
02187 score_selected_file_name << base_output_path_ << "score_footprint_selected" << "_" << frame_set_count_ << ".png";
02188 cv::imwrite(score_selected_file_name.str(), footprint);
02189 }
02190
02191 void writePredictedScoreToDisk(XYZPointCloud& hull_cloud, ShapeDescriptors& sds, std::vector<double>& pred_scores)
02192 {
02193
02194 std::stringstream svm_file_name;
02195 svm_file_name << base_output_path_ << "predicted_svm_scores" << "_" << frame_set_count_ << ".txt";
02196
02197 std::ofstream svm_file_stream;
02198 svm_file_stream.open(svm_file_name.str().c_str());
02199 for (int i = 0; i < hull_cloud.size(); ++i)
02200 {
02201 svm_file_stream << hull_cloud[i].x << " " << hull_cloud[i].y << " " << pred_scores[i];
02202 for (int j = 0; j < sds[i].size(); ++j)
02203 {
02204 svm_file_stream << " " << sds[i][j];
02205 }
02206 svm_file_stream << "\n";
02207 }
02208 svm_file_stream.close();
02209 }
02210
02211
02215 void spin()
02216 {
02217 while(n_.ok())
02218 {
02219 ros::spinOnce();
02220 }
02221 }
02222
02223 protected:
02224 ros::NodeHandle n_;
02225 ros::NodeHandle n_private_;
02226 message_filters::Subscriber<sensor_msgs::Image> image_sub_;
02227 message_filters::Subscriber<sensor_msgs::Image> depth_sub_;
02228 message_filters::Subscriber<sensor_msgs::Image> mask_sub_;
02229 message_filters::Subscriber<sensor_msgs::PointCloud2> cloud_sub_;
02230 message_filters::Synchronizer<MySyncPolicy> sync_;
02231 sensor_msgs::CameraInfo cam_info_;
02232 shared_ptr<tf::TransformListener> tf_;
02233 ros::ServiceServer push_pose_server_;
02234 ros::ServiceServer table_location_server_;
02235 ros::Subscriber jinv_l_arm_subscriber_;
02236 ros::Subscriber jinv_r_arm_subscriber_;
02237 ros::Subscriber jtteleop_l_arm_subscriber_;
02238 ros::Subscriber jtteleop_r_arm_subscriber_;
02239 actionlib::SimpleActionServer<PushTrackerAction> as_;
02240 cv::Mat cur_color_frame_;
02241 cv::Mat cur_depth_frame_;
02242 cv::Mat cur_self_mask_;
02243
02244
02245
02246 std_msgs::Header cur_camera_header_;
02247
02248 XYZPointCloud cur_point_cloud_;
02249 XYZPointCloud cur_self_filtered_cloud_;
02250 shared_ptr<PointCloudSegmentation> pcl_segmenter_;
02251 bool have_depth_data_;
02252 int display_wait_ms_;
02253 bool use_displays_;
02254 bool write_input_to_disk_;
02255 bool write_to_disk_;
02256 std::string base_output_path_;
02257 int num_downsamples_;
02258 std::string workspace_frame_;
02259 std::string camera_frame_;
02260 PoseStamped table_centroid_;
02261 bool camera_initialized_;
02262 std::string cam_info_topic_;
02263 bool start_tracking_on_push_call_;
02264 bool recording_input_;
02265 int record_count_;
02266 int learn_callback_count_;
02267 int goal_out_count_;
02268 int goal_heading_count_;
02269 int frame_callback_count_;
02270 shared_ptr<ObjectTracker25D> obj_tracker_;
02271 Pose2D tracker_goal_pose_;
02272 std::string pushing_arm_;
02273 std::string proxy_name_;
02274 std::string controller_name_;
02275 std::string behavior_primitive_;
02276 double tracker_dist_thresh_;
02277 double tracker_angle_thresh_;
02278 bool just_spun_;
02279 double major_axis_spin_pos_scale_;
02280 int frame_set_count_;
02281 PoseStamped l_arm_pose_;
02282 PoseStamped r_arm_pose_;
02283 Twist l_arm_vel_;
02284 Twist r_arm_vel_;
02285 bool use_mps_segmentation_;
02286 double max_object_gripper_dist_;
02287 double object_not_moving_thresh_;
02288 int object_not_moving_count_;
02289 int object_not_moving_count_limit_;
02290 double gripper_not_moving_thresh_;
02291 int gripper_not_moving_count_;
02292 int gripper_not_moving_count_limit_;
02293 int object_not_detected_count_;
02294 int object_not_detected_count_limit_;
02295 int object_too_far_count_;
02296 int object_too_far_count_limit_;
02297 int object_not_between_count_;
02298 int object_not_between_count_limit_;
02299 double object_not_between_epsilon_;
02300 ShapeLocations start_loc_history_;
02301 double start_loc_push_time_;
02302 double start_loc_push_dist_;
02303 double push_start_time_;
02304 bool timing_push_;
02305 bool use_center_pointing_shape_context_;
02306 bool start_loc_use_fixed_goal_;
02307 std::string current_file_id_;
02308 ProtoObject start_loc_obj_;
02309 double start_loc_arc_length_percent_;
02310 int start_loc_push_sample_count_;
02311 bool force_swap_;
02312 int mask_dilate_size_;
02313 double point_cloud_hist_res_;
02314 int num_position_failures_;
02315 double min_goal_x_;
02316 double max_goal_x_;
02317 double min_goal_y_;
02318 double max_goal_y_;
02319 shared_ptr<ArmObjSegmentation> arm_obj_segmenter_;
02320 bool use_graphcut_arm_seg_;
02321 double hull_alpha_;
02322 double gripper_spread_;
02323 int footprint_count_;
02324 #ifdef DEBUG_POSE_ESTIMATION
02325 std::ofstream pose_est_stream_;
02326 #endif
02327
02328 };
02329
02330 int main(int argc, char ** argv)
02331 {
02332 int seed = time(NULL);
02333 srand(seed);
02334 std::cout << "Rand seed is: " << seed << std::endl;
02335 ros::init(argc, argv, "tabletop_pushing_perception_node");
02336 ros::NodeHandle n;
02337 TabletopPushingPerceptionNode perception_node(n);
02338 perception_node.spin();
02339 return 0;
02340 }