j96w/DenseFusion

orientation error using 6D pose estimate to display results in 3D

Closed this issue · 2 comments

@j96w in #88, you mentioned that the canonical frame is the object mesh. If I load the points.xyz file into RVIZ, using PCL, the frame appears like this

frame

for the mustard bottle. So, when the 6D estimate is all zeros, then the canonical frame is equal to the camera frame. But, when the cannonical frame needs to be shifted, I am seeing errors when I use the 6D pose estimate to show the result in 3D

where the RGB pose visualization estimate looks good
rgb

Then I publish the 6D pose of the object and use it to transform the points.xyz file from the camera frame to the 6D pose for visualization in RVIZ, then the pose estimate looks bad
pc

As you can see, it is flipped and twisted, so the orientation is off

FYI: I have tried it several ways, including publishing the tf from the camera to the object then publishing the cloud in the object frame, and they have all given the same result.

First, I was thinking that I need to determine the initial pose of the object, by finding the centeriod of the PLY file and shifting the cloud using that result, similar to #88 yuxng/YCB_Video_toolbox#11 and yuxng/PoseCNN#103 , but I switched to directly using the points.xyz file,
without shifting and there is no difference.

Might this be due to differences in coordinate systems in RVIZ and the points.xyz file? For instance if I flip the z when I import the points.xyz file I get

rot

In NVlabs/Deep_Object_Pose#101 @mintar mentions that the pose topic can be used for visualization, which looks like
pose

But I would like to see the point clouds line up as this pose arrow does not help me evaluate accuracy of the orientation

@hoangcuongbk80 @j96w or @jontremblay or @yuxng or @MrLuer or @mintar any help with this question would be appreciated

Instead of using the .xyz file, I used the .obj meshfile, but the result is still poor.
obj

where the script that I use to produce this is

#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <pcl_ros/point_cloud.h>
#include <pcl_ros/transforms.h>
#include <pcl/conversions.h>
#include <pcl/io/ply_io.h>
#include <pcl/io/obj_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/time.h>   // TicToc
#include <pcl/filters/voxel_grid.h>
#include <geometry_msgs/PoseStamped.h>
#include <tf2_eigen/tf2_eigen.h>
#include <pcl/point_types.h>
#include <tf_conversions/tf_eigen.h>
#include <Eigen/Geometry>

using namespace pcl;

typedef PointXYZRGBNormal PointT;
typedef PointCloud<PointT> PointCloudT;

ros::Subscriber pose_sub;
ros::Publisher pub_pc;

// parameters
std::string camera_frame_id;
std::string pose_topic, pose_pc_topic;

Eigen::Affine3d transform = Eigen::Affine3d::Identity();

void callback(const geometry_msgs::PoseStamped msg) {
    ROS_INFO_STREAM("Received pose: " << msg);

    tf::Pose pose;
    tf::poseMsgToTF(msg.pose, pose);
    const tf::Pose p2 = pose;
    tf::poseTFToEigen(p2, transform);

    return;
}

int main (int argc, char* argv[]){
  ros::init (argc, argv, "publish_pose_pc");   // initialize ROS
  ros::NodeHandle nh;

  nh.getParam("/frames/camera_frame_id", camera_frame_id);
  nh.getParam("/topics/pose_topic", pose_topic);
  nh.getParam("/topics/pose_pc_topic", pose_pc_topic);

  // read polygon textured mesh
  PointCloudT::Ptr cloud(new PointCloudT);
  TextureMesh::Ptr mesh(new TextureMesh);
  OBJReader obj;
  std::string mesh_filename = "../models/006_mustard_bottle/textured.obj";
  if (obj.read(mesh_filename, *mesh) == -1) {
    cout << "Could not read " << mesh_filename;
    return -1;
  }

  // convert polygon mesh to point cloud
  fromPCLPointCloud2(mesh->cloud, *cloud);

  // cloud shifted by pose estimate
  PointCloudT::Ptr transformed_cloud (new PointCloudT);

  pub_pc = nh.advertise<sensor_msgs::PointCloud2>(pose_pc_topic, 100);

  // subscribe to pose topic in camera_frame_id
  pose_sub = nh.subscribe(pose_topic, 1, callback);

  console::TicToc time;
  ros::Rate rate(10.0);
  while(ros::ok()){

    // transform the cloud
    transformPointCloud(*cloud, *transformed_cloud, transform);

    // convert to a ros message
    sensor_msgs::PointCloud2 pc_tr;
    toROSMsg(*transformed_cloud, pc_tr);

    // publish the point cloud for visualization in the camera_frame_id
    pc_tr.header.frame_id = camera_frame_id;
    pub_pc.publish(pc_tr);

    ros::spinOnce();
    rate.sleep();
  }
}

And pose is published from the NN as

# publish the pose of the object in the camera_frame
pose = PoseStamped()
pose.header.stamp = rospy.Time.now()
pose.header.frame_id = self.camera_frame_id
pose.pose.position = Point(my_t[0], my_t[1], my_t[2])  # 'my_t': translation
pose.pose.orientation = Quaternion(my_r[0], my_r[1], my_r[2], my_r[3]) #'my_r': quaternion
self.pub_pose.publish(pose)

I fixed the issue by changing the callback to

void callback(const geometry_msgs::PoseStamped msg) {
    ROS_INFO_STREAM("Received pose: " << msg);

    Eigen::Matrix4f T(Eigen::Matrix4f::Identity());
    Eigen::Vector3f trans;
    float rot_quaternion[4];
    trans(0) = msg.pose.position.x; trans(1) = msg.pose.position.y; trans(2) = msg.pose.position.z; //translaton
    rot_quaternion[0] = msg.pose.orientation.x; rot_quaternion[1] = msg.pose.orientation.y; //rotation
    rot_quaternion[2] = msg.pose.orientation.z; rot_quaternion[3] = msg.pose.orientation.w; //rotation

    Eigen::Quaternionf q(rot_quaternion[0], rot_quaternion[1], rot_quaternion[2], rot_quaternion[3]); //w x y z

    T.block(0, 3, 3, 1) = trans;
    T.block(0, 0, 3, 3) = q.normalized().toRotationMatrix();

    transform = T;
    return;
}

the code here helped. Thanks @hoangcuongbk80