From 5c3d989cf0d6dc22c3ecb4f276d66574318d66fc Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Thu, 1 Dec 2022 17:11:00 +0100
Subject: [PATCH] initial version of table position detection

---
 CMakeLists.txt                                |   6 +
 config/make_tag/tags-80mm-100-103.pdf         | Bin 0 -> 22431 bytes
 config/tags.yaml                              |  10 ++
 .../table_detection.launch                    |  35 +++++
 src/table_locator.cpp                         | 134 ++++++++++++++++++
 5 files changed, 185 insertions(+)
 create mode 100644 config/make_tag/tags-80mm-100-103.pdf
 create mode 100644 launch/object-localization/table_detection.launch
 create mode 100644 src/table_locator.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f5c9aee..8e90bd2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -141,6 +141,7 @@ add_executable(${PROJECT_NAME}_main_controller src/main_controller.cpp ${PROTO_S
 add_executable(${PROJECT_NAME}_virtual_scene_provider src/virtual_scene_provider.cpp ${PROTO_SRCS} ${PROTO_HDRS})
 add_executable(${PROJECT_NAME}_moveit_sorting_controller src/moveit_sorting_controller.cpp ${PROTO_SRCS} ${PROTO_HDRS})
 add_executable(${PROJECT_NAME}_object_locator src/object_locator.cpp ${PROTO_SRCS} ${PROTO_HDRS})
+add_executable(${PROJECT_NAME}_table_locator src/table_locator.cpp ${PROTO_SRCS} ${PROTO_HDRS})
 
 
 ## Rename C++ executable without prefix
@@ -154,6 +155,7 @@ set_target_properties(${PROJECT_NAME}_main_controller PROPERTIES OUTPUT_NAME mai
 set_target_properties(${PROJECT_NAME}_virtual_scene_provider PROPERTIES OUTPUT_NAME virtual_scene_provider PREFIX "")
 set_target_properties(${PROJECT_NAME}_moveit_sorting_controller PROPERTIES OUTPUT_NAME moveit_sorting_controller PREFIX "")
 set_target_properties(${PROJECT_NAME}_object_locator PROPERTIES OUTPUT_NAME object_locator PREFIX "")
+set_target_properties(${PROJECT_NAME}_table_locator PROPERTIES OUTPUT_NAME table_locator PREFIX "")
 
 ## Add cmake target dependencies of the executable
 ## same as for the library above
@@ -164,6 +166,7 @@ add_dependencies(${PROJECT_NAME}_main_controller ${${PROJECT_NAME}_EXPORTED_TARG
 add_dependencies(${PROJECT_NAME}_virtual_scene_provider ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
 add_dependencies(${PROJECT_NAME}_moveit_sorting_controller ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
 add_dependencies(${PROJECT_NAME}_object_locator ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+add_dependencies(${PROJECT_NAME}_table_locator ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
 
 ## Specify libraries to link a library or executable target against
 target_link_libraries(${PROJECT_NAME}_dummy_sorting_controller
@@ -188,6 +191,9 @@ target_link_libraries(${PROJECT_NAME}_dummy_pickplace_provider
 target_link_libraries(${PROJECT_NAME}_object_locator
         ${catkin_LIBRARIES}
         )
+target_link_libraries(${PROJECT_NAME}_table_locator
+        ${catkin_LIBRARIES}
+        )
 
 #############
 ## Install ##
diff --git a/config/make_tag/tags-80mm-100-103.pdf b/config/make_tag/tags-80mm-100-103.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..a3d7a8ffb879e346ab401099056107ad2b718eaf
GIT binary patch
literal 22431
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%0A+8tFEbt`botf
zYHzvfC41h%#%rU_CWEDkP0^JXdV{Rn*1ydD-M;F=`-vZKy_ve1Mb>%R8vb1$KGmGP
z*|qu5l!sTA`v@DE9XR9>z-IXDjsE?^u^U-c8J_=t`usopk9y^XnX)HXxSDUwcF1L%
z7cfDXhw1NM^LT~_^$bU9Vo!q>C!?)i1}#_xt>9HqP%t#GFfsrs0r3rC=cR#`_WGoP
z&muGgE%$}2y2ZA-75Om2*udR+hYfh{d=-_stoT8@Q0jm|z>@BEyMXQT3p_%y7EEKk
zDf#ES>QMz&s|cHk<w~_@{O%S%Z)e@~-D0MkctP`a!RIffcn?YHH;21&mKmIWJI&Ad
z)dC}-@2~efxKU}oa#`b>=C02|;Wun<zU<-WV%W4j!;tSE)9m%Z`YZoQ9pdkMA$}tC
zc9X07`Mobbta5MB>D)ed>8}lIq9^?|w7cFAu&i29dFIJSGFuL=_{$nP`MIvixvgA#
z|AuS#DOp;7Ub{GJ)ua`bevYQ?$6GW7?s47|T-V*ADPYH8XRutz&vl0hXRyM#KnwS0
z84okgy;t3@9uxU8ThjY}*Y9Z>?-iaGpM1V%YMkl!Rj$#Sw@v$*J#T+mlWcZs8duJt
zxA`~u?%lqdUferT$M3Z7y-ll|kN^I6SR`WVzIf)Rv;Km@A1#VNp$>`?1qB6TLsO6f
z5YG^Fpd5$+iXhM$YX$I8bcjXLT%gs)pmo!b1?B<1VUZpl+RnZ~hL9EBZa7w$n;1Y>
zm>U>aDA0X{xgls(HS#iR?A`=>E*Reub4>r@Tw`tsJzfqJScvdb0EaLnxx?0!lex&;
z$Q<Mg2x)3ypa8>&#poar7|$H68pfl92AOJVYN}vj0?OtP5+r7z0C5Y*Yzt#hh(km{
zd}B)!1!D^f1rXan!PE%ka*$5U1@WK~0y(BY8%9tQB<L)|VsKP>XQqHw;TwU@{DW>;
zaLz9U9c{>E2i~Uw(r5^>6~q7~>42jAl+xtXA_a})#LS|61w%bUGd%+Zjf|3#f?_Lu
zeXwYHQDQ+xW^%D!eo?xnCfFwDqSVBa%=|nT&?$}@E>=bcMn;B)My7_w#>R#Q+6IQ|
z1_qkQmRce$<A>xU=fsl4ocwgKabPb%4`4*Nw4x|Ajmtp6$bicL4iwBxO^r<z(i9+K
zMuz4FmI`25g*>R3fuVtsDY}?}g{3)$m;tE#1!+d;HLx(p&}(U8V1cIB$k52b7){K;
z(8v_S4hsuIGjw$p7RDHEwlp-wwAaua-ChGrGcyA;^NbA4Fv7sdz|7bLU7ewU5xRLs
zh6bjF80yT;G4)zlBB?7WO3chjEdn2*8=P5{3W_68K^T;uU!q_Lifc%Y_RLGmR{$qC
aa1t&qNh~S>$EuN`rHKibs;aBM8y5gOxI@zb

literal 0
HcmV?d00001

diff --git a/config/tags.yaml b/config/tags.yaml
index ea70499..4b0172d 100644
--- a/config/tags.yaml
+++ b/config/tags.yaml
@@ -58,6 +58,16 @@ tag_bundles:
           { id: 3, size: .08, x:  0.3, y:  0.3, z: 0.890, qw: 0.707, qx: 0, qy: 0, qz: 0.707 }
         ]
     },
+    {
+      name: 'CETI_TABLE_TWO',
+      layout:
+        [
+          { id: 100, size: .08, x: -0.3, y: -0.3, z: 0.890, qw: 0.707, qx: 0, qy: 0, qz: 0.707 },
+          { id: 101, size: .08, x: -0.3, y:  0.3, z: 0.890, qw: 0.707, qx: 0, qy: 0, qz: 0.707 },
+          { id: 102, size: .08, x:  0.3, y: -0.3, z: 0.890, qw: 0.707, qx: 0, qy: 0, qz: 0.707 },
+          { id: 103, size: .08, x:  0.3, y:  0.3, z: 0.890, qw: 0.707, qx: 0, qy: 0, qz: 0.707 }
+        ]
+    },
     { name: 'smallRed65',     layout: [ { id: 65, size: .024, x: 0.016, y: 0, z: 0.0350, qw: 0.5, qx: 0.5, qy: 0.5, qz: 0.5 } ] },
     { name: 'smallRed70',     layout: [ { id: 70, size: .024, x: 0.016, y: 0, z: 0.0350, qw: 0.5, qx: 0.5, qy: 0.5, qz: 0.5 } ] },
     { name: 'bigRed7',        layout: [ { id:  7, size: .052, x: 0.016, y: 0, z: 0.0525, qw: 0.5, qx: 0.5, qy: 0.5, qz: 0.5 } ] },
diff --git a/launch/object-localization/table_detection.launch b/launch/object-localization/table_detection.launch
new file mode 100644
index 0000000..01f4305
--- /dev/null
+++ b/launch/object-localization/table_detection.launch
@@ -0,0 +1,35 @@
+<launch>
+    <arg name="launch_prefix" default=""/> <!-- set to value="gdbserver localhost:10000" for remote debugging -->
+    <arg name="node_namespace" default="apriltag_ros_continuous_node"/>
+    <arg name="camera_name" default="/usb_cam"/>
+    <arg name="image_topic" default="image_rect"/>
+    <arg name="device" default="/dev/video0"/>
+
+    <!-- Set parameters -->
+    <rosparam command="load" file="$(find ccf_immersive_sorting)/config/settings.yaml" ns="$(arg node_namespace)"/>
+    <rosparam command="load" file="$(find ccf_immersive_sorting)/config/tags.yaml" ns="$(arg node_namespace)"/>
+
+    <node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="log" respawn="true" respawn_delay="3">
+        <param name="video_device" value="$(arg device)"/>
+        <param name="image_width" value="2560"/>
+        <param name="image_height" value="1440"/>
+        <param name="autofocus" value="1"/>
+        <param name="pixel_format" value="mjpeg"/>
+        <param name="framerate" value="1"/>
+        <param name="autofocus" value="true"/>
+    </node>
+
+
+    <node name="image_proc" pkg="image_proc" type="image_proc" ns="usb_cam" respawn="true"/>
+
+    <node pkg="apriltag_ros" type="apriltag_ros_continuous_node" name="$(arg node_namespace)" clear_params="true"
+          output="screen" launch-prefix="$(arg launch_prefix)" respawn="true">
+        <!-- Remap topics from those used in code to those on the ROS network -->
+        <remap from="image_rect" to="$(arg camera_name)/$(arg image_topic)"/>
+        <remap from="camera_info" to="$(arg camera_name)/camera_info"/>
+
+        <param name="publish_tag_detections_image" type="bool" value="true"/>      <!-- default: false -->
+    </node>
+
+    <node pkg="ccf_immersive_sorting" type="table_locator" name="table_locator" respawn="true"/>
+</launch>
diff --git a/src/table_locator.cpp b/src/table_locator.cpp
new file mode 100644
index 0000000..ed4867b
--- /dev/null
+++ b/src/table_locator.cpp
@@ -0,0 +1,134 @@
+/*! \file object_locator.cpp
+
+    A ROS node that detects objects
+
+    \author Johannes Mey
+    \date 15.06.22
+*/
+
+#include <ros/ros.h>
+#include <ros/package.h>
+#include <apriltag_ros/AprilTagDetectionArray.h>
+
+#include "std_msgs/String.h"
+
+#include "tf2/LinearMath/Transform.h"
+
+#include "ccf/util/NodeUtil.h"
+
+#include "connector.pb.h"
+#include "google/protobuf/text_format.h"
+
+#include <google/protobuf/util/json_util.h>
+#include <google/protobuf/message.h>
+#include <ccf/controller/DummyRobotArmController.h>
+#include <ccf/connection/MqttConnection.h>
+
+using CetiRosToolbox::getParameter;
+using CetiRosToolbox::getPrivateParameter;
+
+const std::string CELL_BUNDLE = "CETI_TABLE_ONE";
+const double TABLE_HEIGHT = 0.89;
+
+int main(int argc, char** argv)
+{
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  ros::init(argc, argv, "table_locator");
+
+  ros::NodeHandle n("ccf");
+
+  ros::Rate loop_rate(200);
+  ros::Rate pause_rate(ros::Duration(2));  // seconds
+
+  pause_rate.sleep();
+
+  DummyRobotArmController controller(n, "object_locator", "nobot");
+
+  auto mqttServer = getPrivateParameter<std::string>("mqtt_server", "localhost:1883");
+  std::unique_ptr<MqttConnection> mqtt_connection =
+      std::make_unique<MqttConnection>(mqttServer, ros::this_node::getName());
+
+  controller.initScene({}, false);
+
+  std::map<std::string, geometry_msgs::Pose> located_poses;
+
+  // read the bundles and extract the base pose
+  std::map<int, std::string> tagBundleTags;
+  auto tagBundles = CetiRosToolbox::getParameter<XmlRpc::XmlRpcValue>(n, "/apriltag_ros_continuous_node/tag_bundles");
+  for (int bundle_index = 0; bundle_index < tagBundles.size(); bundle_index++)
+  {
+    auto& bundle = tagBundles[bundle_index];
+    for (int tag_index = 0; tag_index < bundle["layout"].size(); tag_index++)
+    {
+      auto& tag = bundle["layout"][tag_index];
+      tagBundleTags[tag["id"]] = std::string(bundle["name"]);
+    }
+  }
+
+  ros::Subscriber sub_tages = n.subscribe<apriltag_ros::AprilTagDetectionArray>(
+      getPrivateParameter<std::string>("tag_topic", "/tag_detections"), 1000, [&](auto& m) {
+        tf2::Transform base_pose;  // pose of the origin relative to the camera
+        std::map<std::string, geometry_msgs::Pose> allObjectPoses;
+
+        // check if the base frame was detected
+        bool base_tag_found = false;
+        for (const auto& detection : m->detections)
+        {
+          if (tagBundleTags[detection.id[0]] == CELL_BUNDLE)
+          {  // a base tag was found if an id of the cell bundle was found
+            base_tag_found = true;
+            base_pose.setOrigin({ detection.pose.pose.pose.position.x, detection.pose.pose.pose.position.y,
+                                  detection.pose.pose.pose.position.z });
+            base_pose.setRotation({ detection.pose.pose.pose.orientation.x, detection.pose.pose.pose.orientation.y,
+                                    detection.pose.pose.pose.orientation.z, detection.pose.pose.pose.orientation.w });
+          }
+          else
+          {
+            allObjectPoses[tagBundleTags[detection.id[0]]] = detection.pose.pose.pose;
+          }
+        }
+        if (!base_tag_found)
+        {
+          ROS_WARN_STREAM("Skipping tag detection because no base tag was found");
+          return;
+        }
+
+        ROS_INFO_STREAM("base tag and " << allObjectPoses.size() << " object poses found!");
+
+        // make pose relative to origin instead of camera
+        for (auto& [name, pose] : allObjectPoses)
+        {
+          ROS_INFO_STREAM("make pose " << name << "relative");
+          tf2::Transform object({ pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w },
+                                { pose.position.x, pose.position.y, pose.position.z });
+          tf2::Transform result = base_pose.inverseTimes(object);
+
+          double roll, pitch, yaw;
+          tf2::Matrix3x3 resultMatrix(result.getRotation());
+          resultMatrix.getRPY(roll, pitch, yaw);
+
+          pose.position.x = result.getOrigin().x();
+          pose.position.y = result.getOrigin().y();
+          pose.position.z = result.getOrigin().z();
+          pose.orientation.x = result.getRotation().x();
+          pose.orientation.y = result.getRotation().y();
+          pose.orientation.z = result.getRotation().z();
+          pose.orientation.w = result.getRotation().w();
+
+          tf2::Quaternion q{ pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w };
+          std::cout.precision(3);
+          ROS_WARN_STREAM("object " << name << " has pose " << pose << "and RPY " << roll << ", " << pitch << ", "
+                                    << yaw);
+        }
+      });
+
+  while (ros::ok())
+  {
+    ros::spinOnce();
+
+    loop_rate.sleep();
+  }
+
+  return 0;
+}
-- 
GitLab