naoqi_joint_states.cpp
Go to the documentation of this file.
00001 /*
00002 # ROS node to read Nao's sensors and torso odometry through the Aldebaran API.
00003 # This code is currently compatible to NaoQI version 1.6
00004 #
00005 # Copyright 2011 Daniel Maier, University of Freiburg
00006 # http://www.ros.org/wiki/nao
00007 #
00008 # Redistribution and use in source and binary forms, with or without
00009 # modification, are permitted provided that the following conditions are met:
00010 #
00011 #    # Redistributions of source code must retain the above copyright
00012 #       notice, this list of conditions and the following disclaimer.
00013 #    # Redistributions in binary form must reproduce the above copyright
00014 #       notice, this list of conditions and the following disclaimer in the
00015 #       documentation and/or other materials provided with the distribution.
00016 #    # Neither the name of the University of Freiburg nor the names of its
00017 #       contributors may be used to endorse or promote products derived from
00018 #       this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 # POSSIBILITY OF SUCH DAMAGE.
00031 */
00032 
00033 #include <ros/ros.h>
00034 #include <sensor_msgs/JointState.h>
00035 #include <geometry_msgs/Transform.h>
00036 #include <nav_msgs/Odometry.h>
00037 #include <sensor_msgs/Imu.h>
00038 #include <iostream>
00039 
00040 #include <std_srvs/Empty.h>
00041 #include <naoqi_msgs/SetTransform.h>
00042 
00043 #include <tf/transform_broadcaster.h>
00044 #include <tf/transform_datatypes.h>
00045 
00046 // Aldebaran includes
00047 #include <alproxies/almemoryproxy.h>
00048 #include <alproxies/almotionproxy.h>
00049 #include <alvision/alvisiondefinitions.h>
00050 #include <alerror/alerror.h>
00051 #include <alvalue/alvalue.h>
00052 #include <alcommon/altoolsmain.h>
00053 #include <alproxies/alvideodeviceproxy.h>
00054 #include <alcommon/albroker.h>
00055 #include <alcommon/albrokermanager.h>
00056 #include <alcommon/alproxy.h>
00057 #include <alproxies/alproxies.h>
00058 #include <alproxies/almotionproxy.h>
00059 #include <alproxies/almemoryproxy.h>
00060 #include <alcommon/almodule.h>
00061 #include <alcommon/albroker.h>
00062 #include <alcommon/albrokermanager.h>
00063 
00064 #include <naoqi_driver/naoqi_node.h>
00065 
00066 // Other includes
00067 #include <boost/program_options.hpp>
00068 
00069 using namespace std;
00070 
00071 class NaoqiJointStates : public NaoqiNode
00072 {
00073 public:
00074 
00075     NaoqiJointStates(int argc, char ** argv);
00076     ~NaoqiJointStates();
00077 
00078     bool connectProxy();
00079     void run();
00080 
00081     bool pauseOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res);
00082     bool resumeOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res);
00083     bool odomOffsetCallback(naoqi_msgs::SetTransform::Request& req, naoqi_msgs::SetTransform::Response& res);
00084     bool setOdomPoseCallback(naoqi_msgs::SetTransform::Request& req, naoqi_msgs::SetTransform::Response& res);
00085 
00086 
00087 protected:
00088 
00089     double m_rate;
00090 
00091     // NAOqi
00092     boost::shared_ptr<AL::ALMotionProxy> m_motionProxy;
00093     boost::shared_ptr<AL::ALMemoryProxy> m_memoryProxy;
00094     AL::ALValue m_dataNamesList;
00095 
00096     // ROS
00097     ros::NodeHandle m_nh;
00098     ros::NodeHandle m_privateNh;
00099 
00100     // Services
00101     ros::ServiceServer m_pauseOdomSrv;
00102     ros::ServiceServer m_resumeOdomSrv;
00103     ros::ServiceServer m_odomOffsetSrv;
00104     ros::ServiceServer m_setOdomPoseSrv;
00105 
00106     std::string m_odomFrameId;
00107     std::string m_baseFrameId;
00108 
00109     nav_msgs::Odometry m_odom;
00110     sensor_msgs::Imu m_torsoIMU;
00111     sensor_msgs::JointState m_jointState;
00112 
00113     ros::Publisher m_odomPub;
00114     tf::TransformBroadcaster m_transformBroadcaster;
00115     ros::Publisher m_torsoIMUPub;
00116     ros::Publisher m_jointStatePub;
00117 
00118     // Odometry-specific members
00119     geometry_msgs::TransformStamped m_odomTransformMsg;
00120     tf::Pose m_odomPose; // current "real" odometry pose in original (Nao) odom frame
00121     tf::Transform m_odomOffset; // offset on odometry origin
00122 
00123     bool m_useIMUAngles;
00124 
00125     bool m_paused;
00126     double m_lastOdomTime;
00127 
00128     tf::Pose m_targetPose;
00129     bool m_mustUpdateOffset;
00130     bool m_initializeFromIMU;
00131     bool m_initializeFromOdometry;
00132     bool m_isInitialized;
00133 };
00134 
00135 bool NaoqiJointStates::connectProxy()
00136 {
00137    if (!m_broker)
00138    {
00139       ROS_ERROR("Broker is not ready. Have you called connectNaoQi()?");
00140       return false;
00141    }
00142    try
00143    {
00144       //m_motionProxy = boost::shared_ptr<AL::ALProxy>(m_broker->getProxy("ALMotion"));
00145       m_motionProxy = boost::shared_ptr<AL::ALMotionProxy>(new AL::ALMotionProxy(m_broker));
00146    }
00147    catch (const AL::ALError& e)
00148    {
00149       ROS_ERROR("Could not create ALMotionProxy.");
00150       return false;
00151    }
00152    try
00153    {
00154       //m_memoryProxy = boost::shared_ptr<AL::ALMemoryProxy>(m_broker->getProxy("ALMemory"));
00155       m_memoryProxy = boost::shared_ptr<AL::ALMemoryProxy>(new AL::ALMemoryProxy(m_broker));
00156    }
00157    catch (const AL::ALError& e)
00158    {
00159       ROS_ERROR("Could not create ALMemoryProxy.");
00160       return false;
00161    }
00162    ROS_INFO("Proxies to ALMotion and ALMemory ready.");
00163    return true;
00164 }
00165 
00166 NaoqiJointStates::NaoqiJointStates(int argc, char ** argv)
00167  : NaoqiNode(argc, argv),
00168    m_rate(25.0), m_privateNh("~"),
00169    m_odomFrameId("odom"),
00170    m_baseFrameId("base_link"),
00171    m_useIMUAngles(false),
00172    m_paused(false),
00173    m_lastOdomTime(0.0),
00174    m_mustUpdateOffset(false), m_initializeFromIMU(false),
00175    m_initializeFromOdometry(false), m_isInitialized(false)
00176 {
00177     parse_command_line(argc,argv);
00178     if (   ! connectNaoQi() || !  connectProxy() )
00179     {
00180       ROS_ERROR("Gosh! Throwsing exception");
00181       throw std::exception();
00182     }
00183     m_dataNamesList = AL::ALValue::array("DCM/Time",
00184          "Device/SubDeviceList/InertialSensor/AngleX/Sensor/Value","Device/SubDeviceList/InertialSensor/AngleY/Sensor/Value",
00185          "Device/SubDeviceList/InertialSensor/AngleZ/Sensor/Value",
00186          "Device/SubDeviceList/InertialSensor/GyroscopeX/Sensor/Value", "Device/SubDeviceList/InertialSensor/GyroscopeY/Sensor/Value", 
00187          "Device/SubDeviceList/InertialSensor/GyroscopeZ/Sensor/Value",
00188          "Device/SubDeviceList/InertialSensor/AccelerometerX/Sensor/Value", "Device/SubDeviceList/InertialSensor/AccelerometerY/Sensor/Value",
00189          "Device/SubDeviceList/InertialSensor/AccelerometerZ/Sensor/Value");
00190 
00191 
00192     // get update frequency. default sensor rate: 25 Hz (50 is max, stresses Nao's CPU)
00193     m_privateNh.param("sensor_rate", m_rate,m_rate);
00194 
00195     // get base_frame_id (and fix prefix if necessary)
00196     m_privateNh.param("base_frame_id", m_baseFrameId, m_baseFrameId);
00197     m_privateNh.param("odom_frame_id", m_odomFrameId, m_odomFrameId);
00198     m_privateNh.param("use_imu_angles", m_useIMUAngles, m_useIMUAngles);
00199 
00200     m_pauseOdomSrv = m_nh.advertiseService("pause_odometry", &NaoqiJointStates::pauseOdomCallback, this);
00201     m_resumeOdomSrv = m_nh.advertiseService("resume_odometry", &NaoqiJointStates::resumeOdomCallback, this);
00202     m_odomOffsetSrv = m_nh.advertiseService("odometry_offset", &NaoqiJointStates::odomOffsetCallback, this);
00203     m_setOdomPoseSrv = m_nh.advertiseService("set_odometry_pose", &NaoqiJointStates::setOdomPoseCallback, this);
00204 
00205 
00206     m_odom.header.frame_id = m_odomFrameId;
00207     m_torsoIMU.header.frame_id = m_baseFrameId;
00208     m_jointState.name = m_motionProxy->getJointNames("Body");
00209 
00210     // simluated model misses some joints, we need to fill:
00211     if (m_jointState.name.size() == 22)
00212     {
00213       // TODO: Check this!
00214       m_jointState.name.insert(m_jointState.name.begin()+6,"LWristYaw");
00215       m_jointState.name.insert(m_jointState.name.begin()+7,"LHand");
00216       m_jointState.name.push_back("RWristYaw");
00217       m_jointState.name.push_back("RHand");
00218     }
00219 
00220     std::stringstream ss;
00221     std::copy(m_jointState.name.begin(), m_jointState.name.end()-1, std::ostream_iterator<std::string>(ss,","));
00222     std::copy(m_jointState.name.end()-1, m_jointState.name.end(), std::ostream_iterator<std::string>(ss));
00223     ROS_INFO("Nao joints found: %s",ss.str().c_str());
00224 
00225     if (m_useIMUAngles)
00226         ROS_INFO("Using IMU for odometry roll & pitch");
00227 
00228 
00229     // default values of transforms:
00230     m_odomTransformMsg.header.frame_id = m_odomFrameId;
00231     m_odomTransformMsg.child_frame_id = m_baseFrameId;
00232 
00233     m_odomOffset = tf::Transform(tf::createIdentityQuaternion());
00234     m_odomPose = tf::Transform(tf::createIdentityQuaternion());
00235 
00236     m_odomPub = m_nh.advertise<nav_msgs::Odometry>("odom",5);
00237     m_torsoIMUPub = m_nh.advertise<sensor_msgs::Imu>("imu",5);
00238     m_jointStatePub = m_nh.advertise<sensor_msgs::JointState>("joint_states",5);
00239 
00240     ROS_INFO("naoqi_joint_states initialized");
00241 
00242 }
00243 
00244 NaoqiJointStates::~NaoqiJointStates()
00245 {
00246 }
00247 
00248 void NaoqiJointStates::run()
00249 {
00250    ros::Rate r(m_rate);
00251    ros::Time stamp1;
00252    ros::Time stamp2;
00253    ros::Time stamp;
00254 
00255    std::vector<float> odomData;
00256    float odomX, odomY, odomZ, odomWX, odomWY, odomWZ;
00257 
00258    std::vector<float> memData;
00259 
00260    std::vector<float> positionData;
00261 
00262    ROS_INFO("Staring main loop. ros::ok() is %d nh.ok() is %d",ros::ok(),m_nh.ok());
00263    while(ros::ok() )
00264    {
00265       r.sleep();
00266       ros::spinOnce();
00267       stamp1 = ros::Time::now();
00268       try
00269       {
00270          memData = m_memoryProxy->getListData(m_dataNamesList);
00271          // {SPACE_TORSO = 0, SPACE_WORLD = 1, SPACE_NAO = 2}. (second argument)
00272          odomData = m_motionProxy->getPosition("Torso", 1, true);
00273          positionData = m_motionProxy->getAngles("Body", true);
00274       }
00275       catch (const AL::ALError & e)
00276       {
00277          ROS_ERROR( "Error accessing ALMemory, exiting...");
00278          ROS_ERROR( "%s", e.what() );
00279          //ros.signal_shutdown("No NaoQI available anymore");
00280       }
00281       stamp2 = ros::Time::now();
00282       //ROS_DEBUG("dt is %f",(stamp2-stamp1).toSec()); % dt is typically around 1/1000 sec
00283       // TODO: Something smarter than this..
00284       stamp = stamp1 + ros::Duration((stamp2-stamp1).toSec()/2.0);
00285 
00286       /******************************************************************
00287        *                              IMU
00288        *****************************************************************/
00289       if (memData.size() != m_dataNamesList.getSize())
00290       {
00291          ROS_ERROR("memData length %zu does not match expected length %u",memData.size(),m_dataNamesList.getSize() );
00292          continue;
00293       }
00294       // IMU data:
00295       m_torsoIMU.header.stamp = stamp;
00296 
00297       float angleX = memData[1];
00298       float angleY = memData[2];
00299       float angleZ = memData[3];
00300       float gyroX = memData[4];
00301       float gyroY = memData[5];
00302       float gyroZ = memData[6];
00303       float accX = memData[7];
00304       float accY = memData[8];
00305       float accZ = memData[9];
00306 
00307       m_torsoIMU.orientation = tf::createQuaternionMsgFromRollPitchYaw(
00308                                 angleX,
00309                                 angleY,
00310                                 angleZ); // yaw currently always 0
00311 
00312       m_torsoIMU.angular_velocity.x = gyroX;
00313       m_torsoIMU.angular_velocity.y = gyroY;
00314       m_torsoIMU.angular_velocity.z = gyroZ; // currently always 0
00315 
00316       m_torsoIMU.linear_acceleration.x = accX;
00317       m_torsoIMU.linear_acceleration.y = accY;
00318       m_torsoIMU.linear_acceleration.z = accZ;
00319 
00320       // covariances unknown
00321       // cf http://www.ros.org/doc/api/sensor_msgs/html/msg/Imu.html
00322       m_torsoIMU.orientation_covariance[0] = -1;
00323       m_torsoIMU.angular_velocity_covariance[0] = -1;
00324       m_torsoIMU.linear_acceleration_covariance[0] = -1;
00325 
00326       m_torsoIMUPub.publish(m_torsoIMU);
00327 
00328 
00329       /******************************************************************
00330        *                            Joint state
00331        *****************************************************************/
00332       m_jointState.header.stamp = stamp;
00333       m_jointState.header.frame_id = m_baseFrameId;
00334       m_jointState.position.resize(positionData.size());
00335       for(unsigned i = 0; i<positionData.size(); ++i)
00336       {
00337          m_jointState.position[i] = positionData[i];
00338       }
00339 
00340       // simulated model misses some joints, we need to fill:
00341       if (m_jointState.position.size() == 22)
00342       {
00343          m_jointState.position.insert(m_jointState.position.begin()+6,0.0);
00344          m_jointState.position.insert(m_jointState.position.begin()+7,0.0);
00345          m_jointState.position.push_back(0.0);
00346          m_jointState.position.push_back(0.0);
00347       }
00348 
00349       m_jointStatePub.publish(m_jointState);
00350 
00351 
00352         /******************************************************************
00353         *                            Odometry
00354         *****************************************************************/
00355         if (!m_paused) {
00356 
00357         // apply offset transformation:
00358         tf::Pose transformedPose;
00359 
00360 
00361         if (odomData.size()!=6)
00362         {
00363             ROS_ERROR( "Error getting odom data. length is %zu",odomData.size() );
00364             continue;
00365         }
00366 
00367         double dt = (stamp.toSec() - m_lastOdomTime);
00368 
00369         odomX = odomData[0];
00370         odomY = odomData[1];
00371         odomZ = odomData[2];
00372         odomWX = odomData[3];
00373         odomWY = odomData[4];
00374         odomWZ = odomData[5];
00375 
00376         tf::Quaternion q;
00377         // roll and pitch from IMU, yaw from odometry:
00378         if (m_useIMUAngles)
00379             q.setRPY(angleX, angleY, odomWZ);
00380         else
00381             q.setRPY(odomWX, odomWY, odomWZ);
00382 
00383         m_odomPose.setOrigin(tf::Vector3(odomX, odomY, odomZ));
00384         m_odomPose.setRotation(q);
00385 
00386         if(m_mustUpdateOffset) {
00387             if(!m_isInitialized) {
00388                 if(m_initializeFromIMU) {
00389                     // Initialization from IMU: Take x, y, z, yaw from odometry, roll and pitch from IMU
00390                     m_targetPose.setOrigin(m_odomPose.getOrigin());
00391                     m_targetPose.setRotation(tf::createQuaternionFromRPY(angleX, angleY, odomWZ));
00392                 } else if(m_initializeFromOdometry) {
00393                     m_targetPose.setOrigin(m_odomPose.getOrigin());
00394                     m_targetPose.setRotation(tf::createQuaternionFromRPY(odomWX, odomWY, odomWZ));
00395                 }
00396                 m_isInitialized = true;
00397             } else {
00398                 // Overwrite target pitch and roll angles with IMU data
00399                 const double target_yaw = tf::getYaw(m_targetPose.getRotation());
00400                 if(m_initializeFromIMU) {
00401                     m_targetPose.setRotation(tf::createQuaternionFromRPY(angleX, angleY, target_yaw));
00402                 } else if(m_initializeFromOdometry){
00403                     m_targetPose.setRotation(tf::createQuaternionFromRPY(odomWX, odomWY, target_yaw));
00404                 }
00405             }
00406             m_odomOffset = m_targetPose * m_odomPose.inverse();
00407             transformedPose = m_targetPose;
00408             m_mustUpdateOffset = false;
00409         } else {
00410             transformedPose = m_odomOffset * m_odomPose;
00411         }
00412 
00413         //
00414         // publish the transform over tf first
00415         //
00416         m_odomTransformMsg.header.stamp = stamp;
00417         tf::transformTFToMsg(transformedPose, m_odomTransformMsg.transform);
00418         m_transformBroadcaster.sendTransform(m_odomTransformMsg);
00419 
00420 
00421         //
00422         // Fill the Odometry msg
00423         //
00424         m_odom.header.stamp = stamp;
00425         //set the velocity first (old values still valid)
00426         m_odom.twist.twist.linear.x = (odomX - m_odom.pose.pose.position.x) / dt;
00427         m_odom.twist.twist.linear.y = (odomY - m_odom.pose.pose.position.y) / dt;
00428         m_odom.twist.twist.linear.z = (odomZ - m_odom.pose.pose.position.z) / dt;
00429 
00430         // TODO: calc angular velocity!
00431         //      m_odom.twist.twist.angular.z = vth; ??
00432 
00433         //set the position from the above calculated transform
00434         m_odom.pose.pose.orientation = m_odomTransformMsg.transform.rotation;
00435         m_odom.pose.pose.position.x = m_odomTransformMsg.transform.translation.x;
00436         m_odom.pose.pose.position.y = m_odomTransformMsg.transform.translation.y;
00437         m_odom.pose.pose.position.z = m_odomTransformMsg.transform.translation.z;
00438 
00439 
00440         m_odomPub.publish(m_odom);
00441 
00442 
00443         m_lastOdomTime = stamp.toSec();
00444 
00445         }
00446     }
00447     ROS_INFO("naoqi_sensors stopped.");
00448 
00449 }
00450 
00451 bool NaoqiJointStates::pauseOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res){
00452     if (m_paused){
00453         ROS_WARN("Odometry pause requested, but is already paused");
00454         return false;
00455     } else{
00456         ROS_INFO("Odometry paused");
00457         m_paused = true;
00458         m_targetPose = m_odomOffset * m_odomPose;
00459         m_mustUpdateOffset = true;
00460         return true;
00461     }
00462 }
00463 
00464 bool NaoqiJointStates::resumeOdomCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res){
00465     if (m_paused){
00466         ROS_INFO("Odometry resumed");
00467         m_paused = false;
00468         return true;
00469     } else{
00470         ROS_WARN("Odometry resume requested, but is not paused");
00471         return false;
00472     }
00473 }
00474 
00475 bool NaoqiJointStates::odomOffsetCallback(naoqi_msgs::SetTransform::Request& req,
00476                                           naoqi_msgs::SetTransform::Response& res){
00477     ROS_INFO("New odometry offset received");
00478     tf::Transform newOffset;
00479     tf::transformMsgToTF(req.offset, newOffset);
00480 
00481     // add new offset to current (transformed) odom pose:
00482     if(!m_mustUpdateOffset) {
00483         m_mustUpdateOffset = true;
00484         m_targetPose = m_odomOffset * m_odomPose * newOffset;
00485     } else {
00486         m_targetPose = m_targetPose * newOffset;
00487     }
00488 
00489     return true;
00490 }
00491 
00492 bool NaoqiJointStates::setOdomPoseCallback(naoqi_msgs::SetTransform::Request& req,
00493                                            naoqi_msgs::SetTransform::Response& res){
00494     ROS_INFO("New target for current odometry pose received");
00495     tf::Transform targetPose;
00496     tf::transformMsgToTF(req.offset, targetPose);
00497 
00498     m_odomOffset = targetPose * m_odomPose.inverse();
00499 
00500     return true;
00501 }
00502 
00503 int main(int argc, char ** argv)
00504 {
00505    ros::init(argc, argv, "naoqi_joint_states");
00506    NaoqiJointStates * sensors;
00507    try{
00508       sensors = new NaoqiJointStates(argc,argv);
00509       //NaoqiJointStates sensors(argc,argv);
00510    }
00511    catch (const std::exception & e)
00512       //catch (...)
00513    {
00514       // TODO: why does this not work?
00515       //ROS_ERROR("Creating NaoqiJointStates object failed with error %s",e.what());
00516       ROS_ERROR("Creating NaoqiJointStates object failed with error ");
00517       return -1;
00518    }
00519 
00520    sensors->run();
00521 
00522    delete sensors;
00523 
00524    ROS_INFO("naoqi_joint_states stopped");
00525    return 0;
00526 }


naoqi_driver
Author(s): Armin Hornung, Armin Hornung, Stefan Osswald, Daniel Maier, Miguel Sarabia, Severin Lemaignan
autogenerated on Fri Jul 3 2015 12:51:45