diff --git a/clever/CMakeLists.txt b/clever/CMakeLists.txt index e0020d00..4cbb44d6 100644 --- a/clever/CMakeLists.txt +++ b/clever/CMakeLists.txt @@ -164,6 +164,8 @@ add_executable(camera_markers src/camera_markers.cpp) add_executable(frames src/frames.cpp) +add_executable(vpe_publisher src/vpe_publisher.cpp) + target_link_libraries(simple_offboard ${catkin_LIBRARIES} ${GeographicLib_LIBRARIES} @@ -175,6 +177,8 @@ target_link_libraries(camera_markers ${catkin_LIBRARIES}) target_link_libraries(frames ${catkin_LIBRARIES}) +target_link_libraries(vpe_publisher ${catkin_LIBRARIES}) + add_dependencies(simple_offboard clever_generate_messages_cpp) ## Rename C++ executable without prefix diff --git a/clever/src/vpe_publisher.cpp b/clever/src/vpe_publisher.cpp new file mode 100644 index 00000000..880ed114 --- /dev/null +++ b/clever/src/vpe_publisher.cpp @@ -0,0 +1,136 @@ +/* + * VPE publisher node + * Copyright (C) 2018 Copter Express Technologies + * + * Author: Oleg Kalachev + * + * Distributed under MIT License (available at https://opensource.org/licenses/MIT). + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include + +using std::string; +using namespace geometry_msgs; + +string local_frame_id, frame_id, child_frame_id, offset_frame_id; +tf2_ros::Buffer tf_buffer; +ros::Publisher vpe_pub; +ros::Subscriber local_position_sub; +ros::Timer zero_timer; +PoseStamped vpe, pose; +ros::Time local_pose_stamp(0); +ros::Duration publish_zero_timout, offset_timeout; +TransformStamped offset, offset_inversed; + +void publishZero(const ros::TimerEvent&) +{ + if ((ros::Time::now() - pose.header.stamp < publish_zero_timout) || + (ros::Time::now() - vpe.header.stamp < publish_zero_timout)) + return; + + ROS_INFO_THROTTLE(10, "vpe_publisher: publish zero"); + static geometry_msgs::PoseStamped zero; + zero.header.frame_id = vpe.header.frame_id; + zero.header.stamp = ros::Time::now(); + zero.pose.orientation.w = 1; + vpe_pub.publish(zero); +} + +void localPositionCallback(const PoseStamped& msg) { pose = msg; } + +inline Pose getPose(const PoseStampedConstPtr& pose) { return pose->pose; } + +inline Pose getPose(const PoseWithCovarianceStampedConstPtr& pose) { return pose->pose.pose; } + +template +void callback(const T& msg) +{ + static tf2_ros::StaticTransformBroadcaster br; + + try { + if (!frame_id.empty()) { + // get VPE transform from TF + auto transform = tf_buffer.lookupTransform(frame_id, child_frame_id, + msg->header.stamp, ros::Duration(0.02)); + vpe.pose.position.x = transform.transform.translation.x; + vpe.pose.position.y = transform.transform.translation.y; + vpe.pose.position.z = transform.transform.translation.z; + vpe.pose.orientation = transform.transform.rotation; + } else { + vpe.pose = getPose(msg); + } + + // offset + if (!offset_frame_id.empty()) { + if (ros::Time::now() - vpe.header.stamp > offset_timeout) { + // calculate the offset + ROS_INFO("vpe_publisher: reset offset"); + offset = tf_buffer.lookupTransform(local_frame_id, frame_id, + msg->header.stamp, ros::Duration(0.02)); + // offset.header.frame_id = vpe.header.frame_id; + offset.child_frame_id = offset_frame_id; + br.sendTransform(offset); + //offset_inversed = inverseTransform(offset); + } + // apply the offset + tf2::doTransform(vpe, vpe, offset); + } + + vpe.header.frame_id = local_frame_id; + vpe.header.stamp = msg->header.stamp; + vpe_pub.publish(vpe); + + } catch (const tf2::TransformException& e) { + ROS_WARN_THROTTLE(10, "vpe_publisher: %s", e.what()); + } +} + +int main(int argc, char **argv) { + ros::init(argc, argv, "vpe_publisher"); + ros::NodeHandle nh, nh_priv("~"); + + tf2_ros::TransformListener tf_listener(tf_buffer); + + nh_priv.param("frame_id", frame_id, ""); + nh_priv.param("child_frame_id", child_frame_id, ""); + nh_priv.param("mavros/local_position/frame_id", local_frame_id, "map"); + nh_priv.param("offset/frame_id", offset_frame_id, ""); + offset_timeout = ros::Duration(nh_priv.param("offset/timeout", 5.0)); + + if (!frame_id.empty()) { + ROS_INFO("vpe_publisher: using data from TF"); + } else { + ROS_INFO("vpe_publisher: using data topic"); + } + + auto pose_sub = nh_priv.subscribe("pose", 1, &callback); + auto pose_cov_sub = nh_priv.subscribe("pose_cov", 1, &callback); + //auto markers_sub = nh_priv.subscribe("markers", 1, &callback); + + vpe_pub = nh_priv.advertise("pose_pub", 1); + //vpe_cov_pub = nh_priv_.advertise("pose_cov_pub", 1); + + vpe.header.stamp = ros::Time(0); + + if (nh_priv.param("publish_zero", false)) { + // publish zero to initialize the local position + zero_timer = nh.createTimer(ros::Duration(0.1), &publishZero); + publish_zero_timout = ros::Duration(nh_priv.param("publish_zero_timout", 5.0)); + local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &localPositionCallback); + } + + ROS_INFO("vpe_publisher: ready"); + ros::spin(); +}