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 <message_filters/subscriber.h>
00037 #include <message_filters/time_synchronizer.h>
00038 #include <tf/transform_broadcaster.h>
00039 #include <tf/transform_datatypes.h>
00040 #include <tf/transform_listener.h>
00041 #include <tf/message_filter.h>
00042 #include <nav_msgs/Odometry.h>
00043 #include <geometry_msgs/Transform.h>
00044 #include <std_srvs/Empty.h>
00045 #include <sensor_msgs/JointState.h>
00046 #include <nao_msgs/TorsoOdometry.h>
00047 #include <nao_msgs/TorsoIMU.h>
00048 #include <nao_remote/SetTransform.h>
00049 #include <cmath>
00050
00051 class OdometryRemap
00052 {
00053 public:
00054 OdometryRemap();
00055 ~OdometryRemap();
00056 bool pauseOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res);
00057 bool resumeOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res);
00058 bool odomOffsetCallback(nao_remote::SetTransform::Request& req, nao_remote::SetTransform::Response& res);
00059 bool setOdomPoseCallback(nao_remote::SetTransform::Request& req, nao_remote::SetTransform::Response& res);
00060
00061
00062 private:
00063 void jsCallback(const sensor_msgs::JointState::ConstPtr & msg);
00064 void torsoOdomCallback(const nao_msgs::TorsoOdometryConstPtr& odom, const nao_msgs::TorsoIMUConstPtr& imu);
00065
00066 ros::Publisher m_odomPub;
00067 tf::TransformBroadcaster m_transformBroadcaster;
00068 message_filters::Subscriber<nao_msgs::TorsoOdometry>* m_torsoOdomSub;
00069 message_filters::Subscriber<nao_msgs::TorsoIMU>* m_torsoIMUSub;
00070 message_filters::TimeSynchronizer<nao_msgs::TorsoOdometry, nao_msgs::TorsoIMU>* m_synchronizer;
00071 ros::ServiceServer m_pauseOdomSrv;
00072 ros::ServiceServer m_resumeOdomSrv;
00073 ros::ServiceServer m_odomOffsetSrv;
00074 ros::ServiceServer m_setOdomPoseSrv;
00075 ros::NodeHandle m_nh;
00076 ros::NodeHandle m_privateNh;
00077
00078
00079 message_filters::Subscriber<sensor_msgs::JointState> * m_jsSub;
00080 tf::MessageFilter<sensor_msgs::JointState> * m_jsFilter;
00081 tf::TransformBroadcaster m_brBaseFootPrint;
00082 tf::TransformListener m_listener;
00083
00084 geometry_msgs::TransformStamped m_odomTransformMsg;
00085 tf::Pose m_odomPose;
00086 tf::Transform m_odomOffset;
00087 nav_msgs::Odometry m_odom;
00088
00089 std::string m_odomFrameId;
00090 std::string m_baseFrameId;
00091 std::string m_lfootFrameId;
00092 std::string m_rfootFrameId;
00093 std::string m_imuTopic;
00094 std::string m_baseFootPrintID;
00095 bool m_useIMUAngles;
00096 bool m_paused;
00097 double m_lastOdomTime;
00098
00099 double m_lastWx;
00100 double m_lastWy;
00101 double m_lastWz;
00102
00103 tf::Pose m_targetPose;
00104 bool m_mustUpdateOffset;
00105 bool m_initializeFromIMU;
00106 bool m_initializeFromOdometry;
00107 bool m_isInitialized;
00108 };
00109
00110 OdometryRemap::OdometryRemap()
00111 : m_privateNh("~"), m_odomFrameId("odom"), m_baseFrameId("base_link"),
00112 m_lfootFrameId("l_sole"), m_rfootFrameId("r_sole"),
00113 m_imuTopic("torso_imu"), m_baseFootPrintID("base_footprint"),
00114 m_useIMUAngles(false), m_paused(false),
00115 m_lastOdomTime(0.0), m_lastWx(0.0), m_lastWy(0.0), m_lastWz(0.0),
00116 m_mustUpdateOffset(false), m_initializeFromIMU(false),
00117 m_initializeFromOdometry(false), m_isInitialized(false)
00118 {
00119
00120 m_privateNh.param("odom_frame_id", m_odomFrameId, m_odomFrameId);
00121 m_privateNh.param("base_frame_id", m_baseFrameId, m_baseFrameId);
00122 m_privateNh.param("use_imu_angles", m_useIMUAngles, m_useIMUAngles);
00123 m_privateNh.param("init_from_imu", m_initializeFromIMU, m_initializeFromIMU);
00124 m_privateNh.param("init_from_odometry", m_initializeFromOdometry, m_initializeFromOdometry);
00125 m_privateNh.param("imu_topic", m_imuTopic, m_imuTopic);
00126
00127
00128 m_odomFrameId = m_listener.resolve(m_odomFrameId);
00129 m_baseFrameId = m_listener.resolve(m_baseFrameId);
00130 m_baseFootPrintID = m_listener.resolve(m_baseFootPrintID);
00131 m_lfootFrameId = m_listener.resolve(m_lfootFrameId);
00132 m_rfootFrameId = m_listener.resolve(m_rfootFrameId);
00133
00134 m_odomPub = m_nh.advertise<nav_msgs::Odometry>("odom", 50);
00135
00136 m_pauseOdomSrv = m_nh.advertiseService("pause_odometry", &OdometryRemap::pauseOdomCallback, this);
00137 m_resumeOdomSrv = m_nh.advertiseService("resume_odometry", &OdometryRemap::resumeOdomCallback, this);
00138 m_odomOffsetSrv = m_nh.advertiseService("odometry_offset", &OdometryRemap::odomOffsetCallback, this);
00139 m_setOdomPoseSrv = m_nh.advertiseService("set_odometry_pose", &OdometryRemap::setOdomPoseCallback, this);
00140
00141 m_torsoOdomSub = new message_filters::Subscriber<nao_msgs::TorsoOdometry>(m_nh, "torso_odometry", 10);
00142 m_torsoIMUSub = new message_filters::Subscriber<nao_msgs::TorsoIMU>(m_nh, m_imuTopic, 10);
00143 m_synchronizer = new message_filters::TimeSynchronizer<nao_msgs::TorsoOdometry, nao_msgs::TorsoIMU>(*m_torsoOdomSub, *m_torsoIMUSub, 10);
00144 m_synchronizer->registerCallback(boost::bind(&OdometryRemap::torsoOdomCallback, this, _1, _2));
00145
00146 m_jsSub = new message_filters::Subscriber<sensor_msgs::JointState>(m_nh, "joint_states", 50);
00147 m_jsFilter = new tf::MessageFilter<sensor_msgs::JointState>(*m_jsSub, m_listener, m_rfootFrameId, 50);
00148 std::vector<std::string> frames;
00149 frames.push_back(m_rfootFrameId);
00150 frames.push_back(m_odomFrameId);
00151 m_jsFilter->setTargetFrames(frames);
00152 m_jsFilter->registerCallback(boost::bind(&OdometryRemap::jsCallback, this, _1));
00153
00154 if (m_useIMUAngles)
00155 ROS_INFO("Using IMU for odometry roll & pitch");
00156
00157
00158 m_odomTransformMsg.header.frame_id = m_odomFrameId;
00159 m_odomTransformMsg.child_frame_id = m_baseFrameId;
00160
00161 m_odomOffset = tf::Transform(tf::createIdentityQuaternion());
00162 m_odomPose = tf::Transform(tf::createIdentityQuaternion());
00163
00164 if(m_initializeFromIMU || m_initializeFromOdometry) {
00165 m_mustUpdateOffset = true;
00166 } else {
00167 m_mustUpdateOffset = false;
00168 m_isInitialized = true;
00169 }
00170 }
00171
00172 OdometryRemap::~OdometryRemap(){
00173 delete m_synchronizer;
00174 delete m_torsoOdomSub;
00175 delete m_torsoIMUSub;
00176 delete m_jsFilter;
00177 delete m_jsSub;
00178 }
00179
00180 void OdometryRemap::torsoOdomCallback(const nao_msgs::TorsoOdometryConstPtr& msgOdom, const nao_msgs::TorsoIMUConstPtr& msgIMU){
00181 if (m_paused){
00182 ROS_DEBUG("Skipping odometry callback, paused");
00183 return;
00184 }
00185
00186 double odomTime = (msgOdom->header.stamp).toSec();
00187 ROS_DEBUG("Received [%f %f %f %f (%f/%f) (%f/%f) %f]", odomTime, msgOdom->x, msgOdom->y, msgOdom->z, msgOdom->wx, msgIMU->angleX, msgOdom->wy, msgIMU->angleY, msgOdom->wz);
00188
00189 double dt = (odomTime - m_lastOdomTime);
00190
00191
00192 tf::Quaternion q;
00193
00194 if (m_useIMUAngles)
00195 q.setRPY(msgIMU->angleX, msgIMU->angleY, msgOdom->wz);
00196 else
00197 q.setRPY(msgOdom->wx, msgOdom->wy, msgOdom->wz);
00198
00199
00200 m_odomPose.setOrigin(tf::Vector3(msgOdom->x, msgOdom->y, msgOdom->z));
00201 m_odomPose.setRotation(q);
00202
00203
00204 tf::Pose transformedPose;
00205
00206 if(m_mustUpdateOffset) {
00207 if(!m_isInitialized) {
00208 if(m_initializeFromIMU) {
00209
00210 m_targetPose.setOrigin(m_odomPose.getOrigin());
00211 m_targetPose.setRotation(tf::createQuaternionFromRPY(msgIMU->angleX, msgIMU->angleY, msgOdom->wz));
00212 } else if(m_initializeFromOdometry) {
00213 m_targetPose.setOrigin(m_odomPose.getOrigin());
00214 m_targetPose.setRotation(tf::createQuaternionFromRPY(msgOdom->wx, msgOdom->wy, msgOdom->wz));
00215 }
00216 m_isInitialized = true;
00217 } else {
00218
00219 const double target_yaw = tf::getYaw(m_targetPose.getRotation());
00220 if(m_initializeFromIMU) {
00221 m_targetPose.setRotation(tf::createQuaternionFromRPY(msgIMU->angleX, msgIMU->angleY, target_yaw));
00222 } else if(m_initializeFromOdometry){
00223 m_targetPose.setRotation(tf::createQuaternionFromRPY(msgOdom->wx, msgOdom->wy, target_yaw));
00224 }
00225 }
00226 m_odomOffset = m_targetPose * m_odomPose.inverse();
00227 transformedPose = m_targetPose;
00228 m_mustUpdateOffset = false;
00229 } else {
00230 transformedPose = m_odomOffset * m_odomPose;
00231 }
00232
00233
00234 m_odomTransformMsg.header.stamp = msgOdom->header.stamp;
00235 tf::transformTFToMsg(transformedPose, m_odomTransformMsg.transform);
00236 m_transformBroadcaster.sendTransform(m_odomTransformMsg);
00237
00238
00239 m_odom.header.stamp = msgOdom->header.stamp;
00240 m_odom.header.frame_id = m_odomFrameId;
00241
00242
00243
00244 m_odom.twist.twist.linear.x = (msgOdom->x - m_odom.pose.pose.position.x) / dt;
00245 m_odom.twist.twist.linear.y = (msgOdom->y - m_odom.pose.pose.position.y) / dt;
00246 m_odom.twist.twist.linear.z = (msgOdom->z - m_odom.pose.pose.position.z) / dt;
00247
00248
00249
00250
00251 m_odom.pose.pose.orientation = m_odomTransformMsg.transform.rotation;
00252 m_odom.pose.pose.position.x = m_odomTransformMsg.transform.translation.x;
00253 m_odom.pose.pose.position.y = m_odomTransformMsg.transform.translation.y;
00254 m_odom.pose.pose.position.z = m_odomTransformMsg.transform.translation.z;
00255
00256
00257 m_odomPub.publish(m_odom);
00258
00259 m_lastOdomTime = odomTime;
00260
00261
00262
00263
00264
00265 }
00266
00267 bool OdometryRemap::pauseOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res){
00268 if (m_paused){
00269 ROS_WARN("Odometry pause requested, but is already paused");
00270 return false;
00271 } else{
00272 ROS_INFO("Odometry paused");
00273 m_paused = true;
00274 m_targetPose = m_odomOffset * m_odomPose;
00275 m_mustUpdateOffset = true;
00276 return true;
00277 }
00278 }
00279
00280 bool OdometryRemap::resumeOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res){
00281 if (m_paused){
00282 ROS_INFO("Odometry resumed");
00283 m_paused = false;
00284 return true;
00285 } else{
00286 ROS_WARN("Odometry resume requested, but is not paused");
00287 return false;
00288 }
00289 }
00290
00291 bool OdometryRemap::odomOffsetCallback(nao_remote::SetTransform::Request& req, nao_remote::SetTransform::Response& res){
00292 ROS_INFO("New odometry offset received");
00293 tf::Transform newOffset;
00294 tf::transformMsgToTF(req.offset, newOffset);
00295
00296
00297 if(!m_mustUpdateOffset) {
00298 m_mustUpdateOffset = true;
00299 m_targetPose = m_odomOffset * m_odomPose * newOffset;
00300 } else {
00301 m_targetPose = m_targetPose * newOffset;
00302 }
00303
00304 return true;
00305 }
00306
00307 bool OdometryRemap::setOdomPoseCallback(nao_remote::SetTransform::Request& req, nao_remote::SetTransform::Response& res){
00308 ROS_INFO("New target for current odometry pose received");
00309 tf::Transform targetPose;
00310 tf::transformMsgToTF(req.offset, targetPose);
00311
00312 m_odomOffset = targetPose * m_odomPose.inverse();
00313
00314 return true;
00315 }
00316 void OdometryRemap::jsCallback(const sensor_msgs::JointState::ConstPtr & ptr)
00317 {
00318 ros::Time time = ptr->header.stamp;
00319 tf::StampedTransform tf_odom_to_base, tf_odom_to_left_foot, tf_odom_to_right_foot;
00320
00321 ROS_DEBUG("JointState callback function, computing frame %s", m_baseFootPrintID.c_str());
00322 try {
00323 m_listener.lookupTransform(m_odomFrameId, m_lfootFrameId, time, tf_odom_to_left_foot);
00324 m_listener.lookupTransform(m_odomFrameId, m_rfootFrameId, time, tf_odom_to_right_foot);
00325 m_listener.lookupTransform(m_odomFrameId, m_baseFrameId, time, tf_odom_to_base);
00326 } catch (const tf::TransformException& ex){
00327 ROS_ERROR("%s",ex.what());
00328 return ;
00329 }
00330
00331 tf::Vector3 new_origin = (tf_odom_to_right_foot.getOrigin() + tf_odom_to_left_foot.getOrigin())/2.0;
00332 double height = std::min(tf_odom_to_left_foot.getOrigin().getZ(), tf_odom_to_right_foot.getOrigin().getZ());
00333 new_origin.setZ(height);
00334
00335
00336 double roll, pitch, yaw;
00337 tf_odom_to_base.getBasis().getRPY(roll, pitch, yaw);
00338
00339 tf::Transform tf_odom_to_footprint(tf::createQuaternionFromYaw(yaw), new_origin);
00340 tf::Transform tf_base_to_footprint = tf_odom_to_base.inverse() * tf_odom_to_footprint;
00341
00342
00343
00344 m_brBaseFootPrint.sendTransform(tf::StampedTransform(tf_base_to_footprint, time, m_baseFrameId, m_baseFootPrintID));
00345 ROS_DEBUG("Published Transform %s --> %s", m_baseFrameId.c_str(), m_baseFootPrintID.c_str());
00346
00347 return;
00348 }
00349
00350 int main(int argc, char** argv){
00351 ros::init(argc, argv, "remap_odometry");
00352 OdometryRemap odomRemap;
00353 ros::spin();
00354
00355 return 0;
00356 }
00357
00358