From 873fc5cde2bbbeb1dc543db9d41512538935cf4c Mon Sep 17 00:00:00 2001
From: Jennifer Buehler <jennifer.e.buehler@gmail.com>
Date: Sun, 6 Mar 2016 23:28:31 +0100
Subject: [PATCH] added tf object broadcaster and generation of simple grasp
 message

---
 Dockerfile                                    |   1 +
 object_msgs/CMakeLists.txt                    |   2 +-
 object_msgs/msg/Object.msg                    |  49 ++++-
 object_msgs/msg/ObjectPose.msg                |   2 +-
 object_msgs/srv/RegisterObject.srv            |  16 ++
 object_msgs_tools/CMakeLists.txt              | 207 ++++++++++++++++++
 .../config/ObjectTFBroadcaster.yaml           |  26 +++
 .../object_msgs_tools/ObjectFunctions.h       |  57 +++++
 .../object_msgs_tools/ObjectTFBroadcaster.h   | 119 ++++++++++
 .../launch/object_tf_broadcaster.launch       |   5 +
 object_msgs_tools/package.xml                 |  56 +++++
 object_msgs_tools/src/ObjectFunctions.cpp     |  78 +++++++
 object_msgs_tools/src/ObjectTFBroadcaster.cpp | 171 +++++++++++++++
 .../src/object_tf_broadcaster_node.cpp        |  50 +++++
 .../src/register_object_client.cpp            |  32 +++
 15 files changed, 866 insertions(+), 5 deletions(-)
 create mode 100644 object_msgs/srv/RegisterObject.srv
 create mode 100644 object_msgs_tools/CMakeLists.txt
 create mode 100644 object_msgs_tools/config/ObjectTFBroadcaster.yaml
 create mode 100644 object_msgs_tools/include/object_msgs_tools/ObjectFunctions.h
 create mode 100644 object_msgs_tools/include/object_msgs_tools/ObjectTFBroadcaster.h
 create mode 100644 object_msgs_tools/launch/object_tf_broadcaster.launch
 create mode 100644 object_msgs_tools/package.xml
 create mode 100644 object_msgs_tools/src/ObjectFunctions.cpp
 create mode 100644 object_msgs_tools/src/ObjectTFBroadcaster.cpp
 create mode 100644 object_msgs_tools/src/object_tf_broadcaster_node.cpp
 create mode 100644 object_msgs_tools/src/register_object_client.cpp

diff --git a/Dockerfile b/Dockerfile
index 0d4ef3e..a2cc03b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,6 +20,7 @@ RUN apt-get update && apt-get install -y \
     && rm -rf /var/lib/apt/lists/
 
 COPY object_msgs /catkin_ws/src/object_msgs
+COPY object_msgs_tools /catkin_ws/src/object_msgs_tools
 COPY path_navigation_msgs /catkin_ws/src/path_navigation_msgs
 
 # Build
diff --git a/object_msgs/CMakeLists.txt b/object_msgs/CMakeLists.txt
index 2044542..73ffafe 100644
--- a/object_msgs/CMakeLists.txt
+++ b/object_msgs/CMakeLists.txt
@@ -57,7 +57,7 @@ add_message_files(
 ## Generate services in the 'srv' folder
 add_service_files(
   DIRECTORY srv
-  FILES ObjectInfo.srv
+  FILES ObjectInfo.srv RegisterObject.srv
 )
 
 ## Generate actions in the 'action' folder
diff --git a/object_msgs/msg/Object.msg b/object_msgs/msg/Object.msg
index 2794848..3551131 100644
--- a/object_msgs/msg/Object.msg
+++ b/object_msgs/msg/Object.msg
@@ -1,7 +1,10 @@
 # a header, used for interpreting the poses
 Header header
 
-# the name of the object
+# the name of the object. This should usually
+# coincide with the /tf frame of the base/origin
+# of the object, though this is no mandatory
+# requirement.
 string name
 
 # If known: The object type in a database of known objects
@@ -21,12 +24,52 @@ geometry_msgs/Pose[] mesh_poses
 shape_msgs/Plane[] planes
 geometry_msgs/Pose[] plane_poses
 
+# Index into the primitive_poses to the
+# pose which is considered to be the
+# object's root / origin. Can also be
+# set to constants ORIGIN_AVERAGE,
+# ORIGIN_UNDEFINED or ORIGIN_CUSTOM.
+int32 primitive_origin
+
+# Index into the mesh_poses to the
+# pose which is considered to be the
+# object's root / origin. Can also be
+# set to constants ORIGIN_AVERAGE,
+# ORIGIN_UNDEFINED or ORIGIN_CUSTOM.
+int32 mesh_origin
+
+# If 'mesh_origin' or 'primitive_origin'
+# are set to ORIGIN_CUSTOM, this field
+# contains the origin of the overall object.
+geometry_msgs/Pose origin
+
+# Information in the message, either SHAPE or POSE
+byte content
+
+# For field 'content':
 # Message contains the shape(s) of the object including poses
 byte SHAPE=0
 
+# For field 'content':
 # Message contains only the pose of the primitives,
 # i.e. only *_poses will be with values.
 byte POSE=1
 
-# Information in the message, either SHAPE or POSE
-byte content 
+# For fields 'mesh_origin' and 'primitives_origin':
+# the root/origin of the object is the
+# average of all poses.
+# Because the average of several quaternions
+# can only be determined approximately when
+# the quaternions are close together, this
+# setting requires at least the orientation part
+# of the field 'origin' to be set in addition.
+int32 ORIGIN_AVERAGE=-1
+
+# For fields 'mesh_origin' and 'primitives_origin':
+# The origin of the object cannot be defined by this
+# set of poses.
+int32 ORIGIN_UNDEFINED=-2
+
+# For fields 'mesh_origin' and 'primitives_origin':
+# The origin is given in the custom field 'origin' 
+int32 ORIGIN_CUSTOM=-3
diff --git a/object_msgs/msg/ObjectPose.msg b/object_msgs/msg/ObjectPose.msg
index cb20c21..3962ab1 100644
--- a/object_msgs/msg/ObjectPose.msg
+++ b/object_msgs/msg/ObjectPose.msg
@@ -1,6 +1,6 @@
 # the name of the object
 string name
 
-# thep ose of the object
+# the pose of the object
 geometry_msgs/PoseStamped pose
 
diff --git a/object_msgs/srv/RegisterObject.srv b/object_msgs/srv/RegisterObject.srv
new file mode 100644
index 0000000..76fe1d6
--- /dev/null
+++ b/object_msgs/srv/RegisterObject.srv
@@ -0,0 +1,16 @@
+# object name to be registered
+string name
+
+---
+
+# succes flag
+byte success
+
+# successfully registered
+byte SUCCESS=0
+
+# object already registered
+byte EXISTS=1
+
+# could not query required information for this object
+byte ERROR_INFO=2
diff --git a/object_msgs_tools/CMakeLists.txt b/object_msgs_tools/CMakeLists.txt
new file mode 100644
index 0000000..fa5f7b1
--- /dev/null
+++ b/object_msgs_tools/CMakeLists.txt
@@ -0,0 +1,207 @@
+cmake_minimum_required(VERSION 2.8.3)
+project(object_msgs_tools)
+
+## Find catkin macros and libraries
+## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
+## is used, also find other catkin packages
+find_package(catkin REQUIRED COMPONENTS
+  object_msgs
+  roscpp
+  eigen_conversions
+  tf
+)
+
+## System dependencies are found with CMake's conventions
+# find_package(Boost REQUIRED COMPONENTS system)
+
+
+## Uncomment this if the package has a setup.py. This macro ensures
+## modules and global scripts declared therein get installed
+## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
+# catkin_python_setup()
+
+################################################
+## Declare ROS messages, services and actions ##
+################################################
+
+## To declare and build messages, services or actions from within this
+## package, follow these steps:
+## * Let MSG_DEP_SET be the set of packages whose message types you use in
+##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
+## * In the file package.xml:
+##   * add a build_depend tag for "message_generation"
+##   * add a build_depend and a run_depend tag for each package in MSG_DEP_SET
+##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
+##     but can be declared for certainty nonetheless:
+##     * add a run_depend tag for "message_runtime"
+## * In this file (CMakeLists.txt):
+##   * add "message_generation" and every package in MSG_DEP_SET to
+##     find_package(catkin REQUIRED COMPONENTS ...)
+##   * add "message_runtime" and every package in MSG_DEP_SET to
+##     catkin_package(CATKIN_DEPENDS ...)
+##   * uncomment the add_*_files sections below as needed
+##     and list every .msg/.srv/.action file to be processed
+##   * uncomment the generate_messages entry below
+##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
+
+## Generate messages in the 'msg' folder
+# add_message_files(
+#   FILES
+#   Message1.msg
+#   Message2.msg
+# )
+
+## Generate services in the 'srv' folder
+# add_service_files(
+#   FILES
+#   Service1.srv
+#   Service2.srv
+# )
+
+## Generate actions in the 'action' folder
+# add_action_files(
+#   FILES
+#   Action1.action
+#   Action2.action
+# )
+
+## Generate added messages and services with any dependencies listed here
+# generate_messages(
+#   DEPENDENCIES
+#   object_msgs
+# )
+
+################################################
+## Declare ROS dynamic reconfigure parameters ##
+################################################
+
+## To declare and build dynamic reconfigure parameters within this
+## package, follow these steps:
+## * In the file package.xml:
+##   * add a build_depend and a run_depend tag for "dynamic_reconfigure"
+## * In this file (CMakeLists.txt):
+##   * add "dynamic_reconfigure" to
+##     find_package(catkin REQUIRED COMPONENTS ...)
+##   * uncomment the "generate_dynamic_reconfigure_options" section below
+##     and list every .cfg file to be processed
+
+## Generate dynamic reconfigure parameters in the 'cfg' folder
+# generate_dynamic_reconfigure_options(
+#   cfg/DynReconf1.cfg
+#   cfg/DynReconf2.cfg
+# )
+
+###################################
+## catkin specific configuration ##
+###################################
+## The catkin_package macro generates cmake config files for your package
+## Declare things to be passed to dependent projects
+## INCLUDE_DIRS: uncomment this if you package contains header files
+## LIBRARIES: libraries you create in this project that dependent projects also need
+## CATKIN_DEPENDS: catkin_packages dependent projects also need
+## DEPENDS: system dependencies of this project that dependent projects also need
+catkin_package(
+  INCLUDE_DIRS include
+  LIBRARIES object_msgs_tools
+  CATKIN_DEPENDS object_msgs roscpp eigen_conversions tf
+#  DEPENDS system_lib
+)
+
+###########
+## Build ##
+###########
+
+## Specify additional locations of header files
+## Your package locations should be listed before other locations
+include_directories(
+  include
+  ${catkin_INCLUDE_DIRS}
+)
+
+## Declare a C++ library
+add_library(object_msgs_tools
+  src/ObjectTFBroadcaster.cpp
+  src/ObjectFunctions.cpp
+)
+
+## Add cmake target dependencies of the library
+## as an example, code may need to be generated before libraries
+## either from message generation or dynamic reconfigure
+add_dependencies(object_msgs_tools ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+
+## Declare a C++ executable
+add_executable(object_tf_broadcaster_node src/object_tf_broadcaster_node.cpp)
+add_executable(register_object_client src/register_object_client.cpp)
+
+## Add cmake target dependencies of the executable
+## same as for the library above
+add_dependencies(object_tf_broadcaster_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+add_dependencies(register_object_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+
+## Specify libraries to link a library or executable target against
+target_link_libraries(object_msgs_tools
+  ${catkin_LIBRARIES}
+)
+target_link_libraries(object_tf_broadcaster_node
+  object_msgs_tools
+  ${catkin_LIBRARIES}
+)
+target_link_libraries(register_object_client
+  ${catkin_LIBRARIES}
+)
+
+#############
+## Install ##
+#############
+
+# all install targets should use catkin DESTINATION variables
+# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
+
+## Mark executable scripts (Python etc.) for installation
+## in contrast to setup.py, you can choose the destination
+# install(PROGRAMS
+#   scripts/my_python_script
+#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+# )
+
+## Mark executables and/or libraries for installation
+install(TARGETS object_msgs_tools object_tf_broadcaster_node register_object_client
+  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
+  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
+  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+)
+
+## Mark cpp header files for installation
+install(DIRECTORY include/${PROJECT_NAME}/
+  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
+  FILES_MATCHING PATTERN "*.h"
+  PATTERN ".git" EXCLUDE
+)
+
+install(DIRECTORY launch/ 
+  DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
+  FILES_MATCHING PATTERN "*.launch"
+  PATTERN ".git" EXCLUDE
+)
+
+
+
+## Mark other files for installation (e.g. launch and bag files, etc.)
+# install(FILES
+#   # myfile1
+#   # myfile2
+#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
+# )
+
+#############
+## Testing ##
+#############
+
+## Add gtest based cpp test target and link libraries
+# catkin_add_gtest(${PROJECT_NAME}-test test/test_object_msgs_tools.cpp)
+# if(TARGET ${PROJECT_NAME}-test)
+#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
+# endif()
+
+## Add folders to be run by python nosetests
+# catkin_add_nosetests(test)
diff --git a/object_msgs_tools/config/ObjectTFBroadcaster.yaml b/object_msgs_tools/config/ObjectTFBroadcaster.yaml
new file mode 100644
index 0000000..b161def
--- /dev/null
+++ b/object_msgs_tools/config/ObjectTFBroadcaster.yaml
@@ -0,0 +1,26 @@
+# the topic at which object_msgs/Object information is being
+# published. Can be left empty to instead rely on object_service
+object_topic: ""
+
+# the name under which a service of type object_msgs/RegisterObject
+# is going to be provided which can be used to add objects to be published wiht /tf.
+register_object_service: "/object_tf_broadcaster/register_object"
+
+# the object_msgs/ObjectInfo service where information
+# about objects can be queried. Can be left empty to rely on messages being
+# published on object_topic. The service is queried at query_object_info_rate.
+object_service: "/gazebo_world/object_info"
+
+# rate at which to publish information to /tf. This may differ
+# from the query_object_info_rate, i.e. it may be set higer, in order
+# to keep refreshing the timestamps of the /tf tree. Because it is expected
+# that objects don't change their state very often, query_object_info_rate
+# (or the rate at which object messages arrive at object_topic) can be lower.
+publish_tf_rate: 10
+
+# when using the object_msgs/ObjectInfo service
+# to obtain object information, this is the rate at which this is done.
+# If no service is to be used, and instead it should be relied on
+# messages published on object_topic only, then object_service
+# should be left empty, and query_object_info_rate will be ignored.
+query_object_info_rate: 1
diff --git a/object_msgs_tools/include/object_msgs_tools/ObjectFunctions.h b/object_msgs_tools/include/object_msgs_tools/ObjectFunctions.h
new file mode 100644
index 0000000..af76dff
--- /dev/null
+++ b/object_msgs_tools/include/object_msgs_tools/ObjectFunctions.h
@@ -0,0 +1,57 @@
+#ifndef OBJECT_MSGS_TOOLS_OBJECTFUNCTIONS_H
+#define OBJECT_MSGS_TOOLS_OBJECTFUNCTIONS_H
+
+#include <object_msgs/Object.h>
+#include <geometry_msgs/PoseStamped.h>
+
+namespace object_msgs_tools {
+
+/**
+ * Provides a collection of convenience function for
+ * object_msgs/Object.
+ *
+ * \author Jennifer Buehler
+ * \date March 2016
+ */
+class ObjectFunctions
+{
+public:
+
+     ObjectFunctions(){}
+
+    /**
+     * Get the object pose out of the information in \e object according to what's specified
+     * in the message (fields object.primitive_origin and/or object.mesh_origin).
+     * If both mesh and primitive origins are enabled, the primitives are used to determine
+     * the object pose.
+     * \return false if insufficient information given to determine object pose, true if
+     *      pose is returned in \e pose.
+     */
+    static bool getObjectPose(const object_msgs::Object& object, geometry_msgs::PoseStamped& pose); 
+
+private:
+
+    /**
+     * \param idx the value of either Object::primitive_origin or Object::mesh_origin
+     * \param object_header the header of the Object message.
+     * \param poses the poses, either Object::primitive_poses or Object::mesh_poses
+     * \param origin the field Object::origin, in case it is required.
+     */
+    static bool getPoseFromFields(const std_msgs::Header& object_header, int idx,
+        const std::vector<geometry_msgs::Pose>& poses,
+        const geometry_msgs::Pose & origin,
+        geometry_msgs::PoseStamped& pose);
+
+    /**
+     *  Computes the average pose of several positions.
+     *  Because the average of several quaternions
+     *  can only be determined approximately when
+     *  the quaternions are close together, the orientation
+     *  parts are ignored, and only the position is returned.
+     */
+    static geometry_msgs::Point getAveragePointFrom(const std::vector<geometry_msgs::Pose>& poses); 
+};
+
+}
+
+#endif  // OBJECT_MSGS_TOOLS_OBJECTFUNCTIONS_H
diff --git a/object_msgs_tools/include/object_msgs_tools/ObjectTFBroadcaster.h b/object_msgs_tools/include/object_msgs_tools/ObjectTFBroadcaster.h
new file mode 100644
index 0000000..50c9bce
--- /dev/null
+++ b/object_msgs_tools/include/object_msgs_tools/ObjectTFBroadcaster.h
@@ -0,0 +1,119 @@
+#ifndef OBJECT_MSGS_TOOLS_OBJECTTFBROADCASTER_H
+#define OBJECT_MSGS_TOOLS_OBJECTTFBROADCASTER_H
+
+#include <ros/ros.h>
+#include <object_msgs/Object.h>
+#include <object_msgs/RegisterObject.h>
+#include <tf/transform_broadcaster.h>
+#include <boost/thread.hpp>
+
+namespace object_msgs_tools {
+
+/**
+ * Subscribes to object_msgs/Object topic or uses the object_msgs/ObjectInfo service
+ * to query information about objects, and then broadcasts the information as /tf transform.
+ * Only objects which have been explicitly registered with registerObject() are processed.
+ * Only objects which arrive or are queried with the service and have a Object.header.frame_id
+ * which is **not empty** are considered valid and are processed.
+ *
+ * Refer to the constructor for more information about how it works.
+ *
+ * \author Jennifer Buehler
+ * \date March 2016
+ */
+class ObjectTFBroadcaster
+{
+public:
+
+    /**
+     * Constructor. Parameters describe how the ObjectTFBRoadcaster works.
+     * \param register_object_service the name under which a service of type object_msgs/RegisterObject
+     *      is going to be provided which can be used to add objects to be published wiht /tf.
+     * \param query_object_info_rate when using the object_msgs/ObjectInfo service
+     *      to obtain object information, this is the rate at which this is done.
+     *      If no service is to be used, and instead it should be relied on
+     *      messages published on \e object_topic only, then \e object_service
+     *      should be left empty, and \e query_object_info_rate will be ignored.
+     * \param publish_tf_rate rate at which to publish information to /tf. This may differ
+     *      from the \e query_object_info_rate, i.e. it may be set higer, in order
+     *      to keep refreshing the timestamps of the /tf tree. Because it is expected
+     *      that objects don't change their state very often, \e query_object_info_rate
+     *      (or the rate at which object messages arrive at \e object_topic) can be lower.
+     * \param object_topic the topic at which object_msgs/Object information is being
+     *      published. Can be left empty to instead rely on \e object_service
+     * \param object_service the object_msgs/ObjectInfo service where information
+     *      about objects can be queried. Can be left empty to rely on messages being
+     *      published on \e object_topic. The service is queried at \e query_object_info_rate.
+     */
+    ObjectTFBroadcaster(
+        ros::NodeHandle& n,
+        const std::string& register_object_service,
+        unsigned int publish_tf_rate,
+        unsigned int query_object_info_rate,
+        const std::string& object_topic,
+        const std::string& object_service);
+
+    bool isRegistered(const std::string& name) const; 
+
+    /**
+     * return code as in object_msgs::RegisterObject
+     */
+    int registerObject(const std::string& name, bool printMsgs = true); 
+
+private:
+
+    /**
+     * service callback
+     */
+    bool registerObjectService(object_msgs::RegisterObject::Request &req,
+                               object_msgs::RegisterObject::Response &res);
+
+    /**
+     * Uses the service to request object information (only pose)
+     */     
+    bool queryObjectPose(const std::string& name, object_msgs::Object& obj, bool printErrors = true);
+
+    /**
+     * This transform (pose) holds for the target frame frame_name
+     */
+    void sendTF(const geometry_msgs::PoseStamped& pose, const std::string& frame_name);
+
+    /**
+     * Updates information of this object in the map.
+     */
+    bool updateObject(const object_msgs::Object& obj);
+   
+    /**
+     * Callback for subscriber to object_msgs/Object
+     */ 
+    void objectCallback(const object_msgs::Object& obj);
+
+    /**
+     * Event loop for publishing tf
+     */
+    void publishTFEvent(const ros::TimerEvent& e);
+
+    /**
+     * Event loop for querying the newest object information
+     */
+    void queryObjectPoseEvent(const ros::TimerEvent& e);
+
+    // all object poses which have been registered
+    // and will be continuously re-published on /tf. 
+    std::map<std::string, geometry_msgs::PoseStamped> obj_poses;
+    mutable boost::recursive_mutex obj_poses_mutex;
+
+    ros::ServiceServer register_object_srv;
+    ros::ServiceClient object_info_srv;
+    ros::Subscriber object_info_sub;
+
+    ros::NodeHandle node;
+            
+    tf::TransformBroadcaster tf_broadcaster;
+    ros::Timer publish_tf_timer;
+    ros::Timer query_object_info_timer;
+};
+
+}
+
+#endif  // OBJECT_MSGS_TOOLS_OBJECTTFBROADCASTER_H
diff --git a/object_msgs_tools/launch/object_tf_broadcaster.launch b/object_msgs_tools/launch/object_tf_broadcaster.launch
new file mode 100644
index 0000000..ed61d24
--- /dev/null
+++ b/object_msgs_tools/launch/object_tf_broadcaster.launch
@@ -0,0 +1,5 @@
+<launch>
+    <arg name="config_file" default="$(find object_msgs_tools)/config/ObjectTFBroadcaster.yaml"/>
+    <rosparam ns="object_tf_broadcaster" file="$(arg config_file)"/>
+    <node name="object_tf_broadcaster" pkg="object_msgs_tools" type="object_tf_broadcaster_node" args="" output="screen" respawn="true"/>
+</launch>
diff --git a/object_msgs_tools/package.xml b/object_msgs_tools/package.xml
new file mode 100644
index 0000000..038e84a
--- /dev/null
+++ b/object_msgs_tools/package.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<package>
+  <name>object_msgs_tools</name>
+  <version>0.0.0</version>
+  <description>The object_msgs_tools package</description>
+
+  <!-- One maintainer tag required, multiple allowed, one person per tag --> 
+  <maintainer email="jennifer.e.buehler@gmail.com">Jennifer Buehler</maintainer>
+
+
+  <!-- One license tag required, multiple allowed, one license per tag -->
+  <!-- Commonly used license strings: -->
+  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
+  <license>GPLv3</license>
+
+
+  <!-- Url tags are optional, but mutiple are allowed, one per tag -->
+  <!-- Optional attribute type can be: website, bugtracker, or repository -->
+  <!-- Example: -->
+  <!-- <url type="website">http://wiki.ros.org/object_msgs_tools</url> -->
+
+
+  <!-- Author tags are optional, mutiple are allowed, one per tag -->
+  <!-- Authors do not have to be maintianers, but could be -->
+  <!-- Example: -->
+  <!-- <author email="jane.doe@example.com">Jane Doe</author> -->
+
+
+  <!-- The *_depend tags are used to specify dependencies -->
+  <!-- Dependencies can be catkin packages or system dependencies -->
+  <!-- Examples: -->
+  <!-- Use build_depend for packages you need at compile time: -->
+  <!--   <build_depend>message_generation</build_depend> -->
+  <!-- Use buildtool_depend for build tool packages: -->
+  <!--   <buildtool_depend>catkin</buildtool_depend> -->
+  <!-- Use run_depend for packages you need at runtime: -->
+  <!--   <run_depend>message_runtime</run_depend> -->
+  <!-- Use test_depend for packages you need only for testing: -->
+  <!--   <test_depend>gtest</test_depend> -->
+  <buildtool_depend>catkin</buildtool_depend>
+  <build_depend>object_msgs</build_depend>
+  <build_depend>roscpp</build_depend>
+  <build_depend>eigen_conversions</build_depend>
+  <build_depend>tf</build_depend>
+  <run_depend>object_msgs</run_depend>
+  <run_depend>roscpp</run_depend>
+  <run_depend>eigen_conversions</run_depend>
+  <run_depend>tf</run_depend>
+
+
+  <!-- The export tag contains other, unspecified, tags -->
+  <export>
+    <!-- Other tools can request additional information be placed here -->
+
+  </export>
+</package>
diff --git a/object_msgs_tools/src/ObjectFunctions.cpp b/object_msgs_tools/src/ObjectFunctions.cpp
new file mode 100644
index 0000000..5650657
--- /dev/null
+++ b/object_msgs_tools/src/ObjectFunctions.cpp
@@ -0,0 +1,78 @@
+#include <ros/ros.h>
+#include <object_msgs_tools/ObjectFunctions.h>
+#include <eigen_conversions/eigen_msg.h>
+
+using object_msgs_tools::ObjectFunctions;
+
+bool ObjectFunctions::getObjectPose(const object_msgs::Object& object, geometry_msgs::PoseStamped& pose)
+{
+    if ((object.primitive_origin == object_msgs::Object::ORIGIN_UNDEFINED) &&
+        (object.mesh_origin == object_msgs::Object::ORIGIN_UNDEFINED))
+    {
+        return false;
+    }
+    
+    bool success = false;
+    if (object.primitive_origin != object_msgs::Object::ORIGIN_UNDEFINED)
+    {
+        success = getPoseFromFields(object.header,object.primitive_origin,
+                                          object.primitive_poses, object.origin, pose);
+    }
+    else  // the mesh origin must be defined because of check above
+    {
+        success = getPoseFromFields(object.header,object.mesh_origin,
+                                          object.mesh_poses, object.origin, pose);
+    }
+    return success;
+}
+
+bool ObjectFunctions::getPoseFromFields(const std_msgs::Header& object_header, int idx,
+    const std::vector<geometry_msgs::Pose>& poses,
+    const geometry_msgs::Pose & origin,
+    geometry_msgs::PoseStamped& pose)
+{
+    if (idx == object_msgs::Object::ORIGIN_UNDEFINED)
+        return false;
+
+    if (idx == object_msgs::Object::ORIGIN_AVERAGE)
+    {   
+        pose.pose.position = getAveragePointFrom(poses);
+        pose.pose.orientation = origin.orientation;
+    }
+    else if (idx == object_msgs::Object::ORIGIN_CUSTOM)
+    {
+        pose.pose = origin;
+    }
+    else if (idx > 0)
+    {
+        if (poses.size() <= idx)
+        {
+            ROS_ERROR_STREAM("ObjectFunctions: Inconsistent object, "<<
+                "has less primitive poses than required (" <<
+                poses.size()<<", required "<<idx <<")");
+            return false;
+        }
+        pose.pose = poses[idx];
+    }
+    else
+    {
+        ROS_ERROR_STREAM("Unknown mode of Object::primitive_origin or Object::mesh_origin: "<<idx);
+        return false;
+    }
+    pose.header = object_header;
+    return true;
+}
+geometry_msgs::Point ObjectFunctions::getAveragePointFrom(const std::vector<geometry_msgs::Pose>& poses)
+{
+    Eigen::Vector3d p;
+    for (int i=0; i < poses.size(); ++i)
+    {
+        Eigen::Vector3d _p;
+        const geometry_msgs::Pose& pi = poses[i];
+        tf::pointMsgToEigen(pi.position,_p);
+        p+=_p;
+    }
+    geometry_msgs::Point ret;
+    tf::pointEigenToMsg(p,ret);
+    return ret;
+}
diff --git a/object_msgs_tools/src/ObjectTFBroadcaster.cpp b/object_msgs_tools/src/ObjectTFBroadcaster.cpp
new file mode 100644
index 0000000..fc0a227
--- /dev/null
+++ b/object_msgs_tools/src/ObjectTFBroadcaster.cpp
@@ -0,0 +1,171 @@
+#include <object_msgs_tools/ObjectTFBroadcaster.h>
+#include <object_msgs_tools/ObjectFunctions.h>
+#include <object_msgs/ObjectInfo.h>
+
+using object_msgs_tools::ObjectTFBroadcaster;
+
+ObjectTFBroadcaster::ObjectTFBroadcaster(
+    ros::NodeHandle& n,
+    const std::string& register_object_service,
+    unsigned int publish_tf_rate,
+    unsigned int query_object_info_rate,
+    const std::string& object_topic,
+    const std::string& object_service): 
+    node(n)
+{
+    if (object_service.empty() && object_topic.empty())
+    {
+        ROS_ERROR("Can't use ObjetTFBroadcaster if neither service nor object info topic is set");
+        throw std::runtime_error("Can't use ObjetTFBroadcaster if neither service nor object info topic is set");
+    }
+    
+    ros::Rate rate(publish_tf_rate);
+    publish_tf_timer = node.createTimer(rate, &ObjectTFBroadcaster::publishTFEvent, this);
+    if (!register_object_service.empty())
+    {
+        ROS_INFO_STREAM("Advertising service "<<register_object_service);
+        register_object_srv = node.advertiseService(register_object_service, &ObjectTFBroadcaster::registerObjectService,this);
+    }
+    
+    if (!object_service.empty())
+    {
+        ros::Rate qRate(query_object_info_rate);
+        query_object_info_timer=node.createTimer(qRate,&ObjectTFBroadcaster::queryObjectPoseEvent, this);
+        query_object_info_timer.start();
+        object_info_srv = node.serviceClient<object_msgs::ObjectInfo>(object_service);
+    }
+    if (!object_topic.empty())
+        object_info_sub = node.subscribe(object_topic,1000,&ObjectTFBroadcaster::objectCallback, this);
+}
+
+bool ObjectTFBroadcaster::isRegistered(const std::string& name) const
+{
+    obj_poses_mutex.lock();
+    bool exists= obj_poses.find(name) != obj_poses.end();
+    obj_poses_mutex.unlock();
+    return exists;
+}
+
+bool ObjectTFBroadcaster::registerObjectService(object_msgs::RegisterObject::Request &req,
+                                                object_msgs::RegisterObject::Response &res)
+{
+    ROS_INFO_STREAM("Calling ObjectTFBroadcaster service with "<<req.name);
+    res.success = registerObject(req.name, true);
+    // always return success because none of the errors
+    // are fatal, the object will probably still be updated
+    // even if the information was not available at the time.
+    // It was still added (or already exists).
+    return true;
+}
+
+
+int ObjectTFBroadcaster::registerObject(const std::string& name, bool printMsgs)
+{
+    object_msgs::Object obj;
+    // Get initial information of the object.
+    // If this fails it doesn't matter, the first pose inserted will be invalid
+    queryObjectPose(name,obj, false);
+    geometry_msgs::PoseStamped pose;
+    if (!ObjectFunctions::getObjectPose(obj, pose)){
+        if (printMsgs) ROS_WARN_STREAM("ObjectTFBroadcaster: Could not get pose for object '"<<name<<"'");
+        return object_msgs::RegisterObject::Response::ERROR_INFO;
+    }
+
+    obj_poses_mutex.lock();
+    bool success = obj_poses.insert(std::make_pair(name,pose)).second;
+    obj_poses_mutex.unlock();
+    if (!success)
+    {
+        if (printMsgs)
+            ROS_WARN_STREAM("Object " << name << 
+                " could not be added in ObjectTFBroadcaster because it was already registered.");
+        return object_msgs::RegisterObject::Response::EXISTS;
+    }
+    return object_msgs::RegisterObject::Response::SUCCESS;
+}
+
+ 
+bool ObjectTFBroadcaster::queryObjectPose(const std::string& name, object_msgs::Object& obj, bool printErrors)
+{
+    if (object_info_srv.getService().empty())
+    {
+        if (printErrors) ROS_ERROR("ObjectTFBroadcaster: Service to request object_msgs/Object is not running.");
+        return false;
+    }
+    object_msgs::ObjectInfo srv;
+    srv.request.name = name;
+    srv.request.get_geometry = false;
+    if (object_info_srv.call(srv)){
+        //ROS_INFO("Result:");
+        //std::cout<<srv.response<<std::endl;
+    }else{
+        if (printErrors) ROS_ERROR("ObjectTFBroadcaster: Failed to call service to obtain object info");
+        return false;
+    }
+    obj=srv.response.object;
+    return true;
+}
+
+void ObjectTFBroadcaster::sendTF(const geometry_msgs::PoseStamped& pose, const std::string& frame_name){
+    tf::Transform transform;
+    transform.setOrigin( tf::Vector3(pose.pose.position.x, pose.pose.position.y, pose.pose.position.z) );
+    tf::Quaternion q(pose.pose.orientation.x,pose.pose.orientation.y,pose.pose.orientation.z,pose.pose.orientation.w);
+    transform.setRotation(q);
+    // ROS_INFO_STREAM("Send tf in "<<frame_name<<". "); //<<std::endl<<pose);
+    tf_broadcaster.sendTransform(tf::StampedTransform(transform, pose.header.stamp, pose.header.frame_id, frame_name));
+    // ROS_INFO("TF sent.");
+}
+
+
+bool ObjectTFBroadcaster::updateObject(const object_msgs::Object& obj)
+{
+    // ROS_INFO_STREAM("ObjectTFBroadcaster: Got object "<<obj);
+    geometry_msgs::PoseStamped pose;
+    if (!ObjectFunctions::getObjectPose(obj, pose)){
+        ROS_ERROR("ObjectTFBroadcaster: Could not get pose");
+        return false;
+    }
+    std::map<std::string, geometry_msgs::PoseStamped>::iterator it;
+    obj_poses_mutex.lock();
+    it = obj_poses.find(obj.name);
+    bool success = (it != obj_poses.end());
+    if (success) it->second=pose;
+    obj_poses_mutex.unlock();
+    if (!success)
+    {
+        ROS_ERROR_STREAM("ObjectTFBroadcaster: Could not update object "
+            <<obj.name<<" because it was not registered. Call registerObject() to add it.");
+    }
+    return success;
+}
+
+void ObjectTFBroadcaster::objectCallback(const object_msgs::Object& obj){
+    updateObject(obj);
+}
+
+void ObjectTFBroadcaster::publishTFEvent(const ros::TimerEvent& e){
+    // ROS_INFO("TF");
+    obj_poses_mutex.lock();
+    std::map<std::string, geometry_msgs::PoseStamped>::iterator it;
+    for (it = obj_poses.begin(); it!=obj_poses.end(); ++it)
+    {
+        it->second.header.stamp=ros::Time::now();
+        // ROS_INFO_STREAM("Sending TF for "<<it->first);//<<": "<<it->second);
+        sendTF(it->second,it->first);    
+    }
+    obj_poses_mutex.unlock();
+}
+
+void ObjectTFBroadcaster::queryObjectPoseEvent(const ros::TimerEvent& e)
+{
+    // ROS_INFO("Query");
+    obj_poses_mutex.lock();
+    std::map<std::string, geometry_msgs::PoseStamped>::iterator it;
+    for (it = obj_poses.begin(); it!=obj_poses.end(); ++it)
+    {
+        object_msgs::Object obj;
+        if (!queryObjectPose(it->first,obj,true)) continue;
+        updateObject(obj);
+    }
+    obj_poses_mutex.unlock();
+}
diff --git a/object_msgs_tools/src/object_tf_broadcaster_node.cpp b/object_msgs_tools/src/object_tf_broadcaster_node.cpp
new file mode 100644
index 0000000..07b5cf5
--- /dev/null
+++ b/object_msgs_tools/src/object_tf_broadcaster_node.cpp
@@ -0,0 +1,50 @@
+#include <object_msgs_tools/ObjectTFBroadcaster.h>
+
+/***
+ * Launches a node which runs the ObjectTFBroadcaster.
+ *
+ * \author Jennifer Buehler
+ * \date March 2016
+ */
+int main(int argc, char**argv){
+	ros::init(argc, argv, "object_tf_broadcaster");
+
+	ros::NodeHandle priv("~");
+	ros::NodeHandle pub("");
+
+    // use same namespace which is used by launch file to load ROS params
+    ros::NodeHandle paramNH("object_tf_broadcaster");	
+
+    std::string REGISTER_OBJECT_SERVICE="/register_object";
+	paramNH.param<std::string>("register_object_service", REGISTER_OBJECT_SERVICE, REGISTER_OBJECT_SERVICE);
+
+	std::string OBJECT_TOPIC="/gazebo_world/object";
+	paramNH.param<std::string>("object_topic", OBJECT_TOPIC, OBJECT_TOPIC);
+	
+    std::string OBJECT_SERVICE="/gazebo_world/object_info";
+	paramNH.param<std::string>("object_service", OBJECT_SERVICE, OBJECT_SERVICE);
+	
+	int PUBLISH_TF_RATE=10;
+	paramNH.param<int>("publish_tf_rate", PUBLISH_TF_RATE, PUBLISH_TF_RATE);
+	
+    int QUERY_OBJECT_INFO_RATE=1;
+	paramNH.param<int>("query_object_info_rate", QUERY_OBJECT_INFO_RATE, QUERY_OBJECT_INFO_RATE);
+
+    ROS_INFO_STREAM("Starting ObjectTFBroadcaster with topics: register="<<REGISTER_OBJECT_SERVICE 
+        <<", object="<<OBJECT_TOPIC<<", object service="<<OBJECT_SERVICE<<" tf rate ="
+        <<PUBLISH_TF_RATE<<", query info rate: "<<QUERY_OBJECT_INFO_RATE);
+
+    object_msgs_tools::ObjectTFBroadcaster broadcaster(
+        pub,
+        REGISTER_OBJECT_SERVICE,
+        PUBLISH_TF_RATE,
+        QUERY_OBJECT_INFO_RATE,
+        OBJECT_TOPIC,
+        OBJECT_SERVICE);
+
+    // ros::MultiThreadedSpinner spinner(4); // Use 4 threads
+    // spinner.spin(); // spin() will not return until the node has been shutdown
+	ros::spin();
+    ROS_INFO("Exit ObjectTFBroadcater");
+    return 0;
+}
diff --git a/object_msgs_tools/src/register_object_client.cpp b/object_msgs_tools/src/register_object_client.cpp
new file mode 100644
index 0000000..5b7f126
--- /dev/null
+++ b/object_msgs_tools/src/register_object_client.cpp
@@ -0,0 +1,32 @@
+#include <ros/ros.h>
+#include <object_msgs/RegisterObject.h>
+
+int main(int argc, char** argv)
+{
+    ros::init(argc, argv, "register_object_client");
+    ros::NodeHandle priv("~");
+    ros::NodeHandle pub("");
+   
+    if (!pub.hasParam("/object_tf_broadcaster/register_object_service"))
+    {
+        ROS_ERROR("Missing parameter register_object_service");
+        return 0;
+    }
+    std::string REGISTER_OBJECT_SERVICE; 
+	priv.param<std::string>("/object_tf_broadcaster/register_object_service", REGISTER_OBJECT_SERVICE, REGISTER_OBJECT_SERVICE);
+	ROS_INFO("Using register object service name: <%s>", REGISTER_OBJECT_SERVICE.c_str());
+
+	ros::ServiceClient client = pub.serviceClient<object_msgs::RegisterObject>(REGISTER_OBJECT_SERVICE);
+    object_msgs::RegisterObject srv;
+	srv.request.name = argv[1];
+	if (client.call(srv))
+	{
+		ROS_INFO("Result:");
+		std::cout<<srv.response<<std::endl;
+	}
+	else
+	{
+		ROS_ERROR("Failed to call service %s",REGISTER_OBJECT_SERVICE.c_str());
+	}
+    return 0;
+}
-- 
GitLab