00001
00002
00003
00004
00005
00006
00007
00008 #include <StateEstimators/ComplementaryFilter/ComplementaryStateEstimator.hpp>
00009 #include <tk_state/StateEstimatorController.hpp>
00010 #include <telekyb_defines/physic_defines.hpp>
00011
00012 #include <telekyb_base/ROS.hpp>
00013 #include <telekyb_msgs/TKState.h>
00014 #include <geometry_msgs/Vector3Stamped.h>
00015
00016 #include <boost/numeric/odeint.hpp>
00017
00018 #include <cmath>
00019
00020 PLUGINLIB_DECLARE_CLASS(tk_state, ComplementaryStateEstimator, telekyb_state::ComplementaryStateEstimator, TELEKYB_NAMESPACE::StateEstimator);
00021
00022 namespace telekyb_state {
00023
00024 ComplementaryStateEstimatorOptions::ComplementaryStateEstimatorOptions()
00025 : OptionContainer("ComplementaryStateEstimator")
00026 {
00027 tViconSeTopicName = addOption<std::string>("tViconSeTopicName","TopicName of Vicon Sensor.","undef",true,true);
00028 tImuSeTopicName = addOption<std::string>("tImuSeTopicName","TopicName of Imu Sensor.","undef",true,true);
00029 tViconRepubTopicName = addOption<std::string>("tViconRepubTopicName","TopicName for Vicon republishing.","undef",true,true);
00030
00031 imuAccBias = addOption<Eigen::Vector3d>("imuAccBias","Imu accelerometer bias", Eigen::Vector3d::Zero(), false, true);
00032 imuGyrBias = addOption<Eigen::Vector3d>("imuGyrBias","Imu gyroscope bias", Eigen::Vector3d::Zero(), false, true);
00033 vicPosition = addOption<Eigen::Vector3d>("vicPosition","Position of the Vicon frame in the IMU frame", Eigen::Vector3d::Zero(), false, true);
00034 vicOrientation = addOption<Eigen::Vector2d>("vicOrientation","Orientation of the Vicon frame w.r.t. the IMU frame (roll and pitch)", Eigen::Vector2d::Zero(), false, true);
00035 worldGravity = addOption<Eigen::Vector3d>("worldGravity","Gravity vector in the world frame", Eigen::Vector3d(0.0,0.0,GRAVITY), false, true);
00036
00037 predStep = addOption<double>("predStep", "Step size for state estimation", 1e-3, false, true);
00038 pubStep = addOption<double>("pubStep", "Step size for state publication", 1e-2, false, true);
00039 normalizationGain = addOption<double>("normalizationGain", "Gain for quaternon normalization", 1, false, true);
00040
00041 positionGain = addOption<Eigen::Matrix3d>("positionGain","Position gain for the estimation", 33*Eigen::Matrix3d::Identity(), false, true);
00042 velocityGain = addOption<Eigen::Matrix3d>("velocityGain","Velocity gain for the estimation", 362*Eigen::Matrix3d::Identity(), false, true);
00043 rotationGain = addOption<Eigen::Matrix3d>("rotationGain","Orientation gain for the estimation", 10*Eigen::Matrix3d::Identity(), false, true);
00044 biasGain = addOption<Eigen::Matrix3d>("biasGain","Bias gain for the estimation", Eigen::Matrix3d::Zero(), false, true);
00045
00046 matrixA = addOption<Eigen::Matrix3d>("matrixA","Matrix A", Eigen::Matrix3d::Identity(), false, true);
00047 vectorB = addOption<Eigen::Vector3d>("vectorB","Vector B", Eigen::Vector3d::Zero(), false, true);
00048
00049 tOmegaFilterFreq = addOption<double>("tOmegaFilterFreq", "Omega filter cutoff frequency in Hz", 30.0, false, true);
00050 tOmegaSampleTime = addOption<double>("tOmegaSampleTime", "Omega filter sample time in s", 1.0/1080.0, false, true);
00051 }
00052
00053
00054 void ComplementaryStateEstimator::initVelocityFilters()
00055 {
00056 IIRLowPass isLowPass;
00057 for(int i=0;i<3;i++){
00058 omegaFilter[i] = new IIRFilter(
00059 isLowPass,
00060 2.0*M_PI*options.tOmegaFilterFreq->getValue(),
00061 1.0,
00062 options.tOmegaSampleTime->getValue());
00063 }
00064 }
00065
00066
00067
00068 ComplementaryStateEstimator::ComplementaryStateEstimator():
00069 spinning(false),
00070 isInitialized(false),
00071 receivedFirstInput(false),
00072 receivedFirstMeasure(false){
00073 }
00074
00075 void ComplementaryStateEstimator::initialize()
00076 {
00077 nodeHandle = ROSModule::Instance().getMainNodeHandle();
00078 system = DynamicSystem(options.positionGain->getValue(), options.velocityGain->getValue(), options.rotationGain->getValue(), options.biasGain->getValue(),
00079 options.worldGravity->getValue(), options.normalizationGain->getValue(), options.matrixA->getValue(), options.vectorB->getValue());
00080
00081 initVelocityFilters();
00082
00083
00084 }
00085
00086 void ComplementaryStateEstimator::initializationDone()
00087 {
00088 boost::mutex::scoped_lock(threadMutex);
00089 if (!isInitialized) {
00090 isInitialized = true;
00091
00092 spinning = true;
00093 ROS_INFO("Initialization Done. Starting State Estimation Thread...");
00094 thread = boost::thread(&ComplementaryStateEstimator::spin, this);
00095 }
00096 }
00097
00098 void ComplementaryStateEstimator::spin()
00099 {
00100 pubTimer.reset();
00101 ros::Rate rate(1/options.predStep->getValue());
00102 while(spinning)
00103 {
00104
00105
00106 prediction(internalState, options.predStep->getValue());
00107
00108
00109
00110
00111
00112 publishState();
00113
00114
00115
00116
00117
00118
00119
00120 rate.sleep();
00121 }
00122 }
00123
00124
00125 void ComplementaryStateEstimator::prediction(State& state, double predStep)
00126 {
00127
00128 ROS_DEBUG_STREAM("Performing prediction with state... " << std::endl
00129 << "Position: [" << state.position.transpose() << "]'" << std::endl
00130 << "Velocity: [" << state.lin_velocity.transpose() << "]'" << std::endl
00131 << "Orientation: [" << state.orientation.w() << " " << state.orientation.vec().transpose() << "]'" << std::endl
00132 << "... input ... " << std::endl
00133 << "Linear acceleration: [" << system.getInput().lin_acc.transpose() << "]'" << std::endl
00134 << "Angular velocity: [" << system.getInput().ang_vel.transpose() << "]'" << std::endl
00135 << "... and measure... " << std::endl
00136 << "Position: [" << system.getMeasure().position.transpose() << "]'" << std::endl
00137 << "Orientation: [" << system.getMeasure().orientation.w() << " " << system.getMeasure().orientation.vec().transpose() << "]'" << std::endl
00138 << "Prediction step: " << predStep << std::endl);
00139
00140 typedef boost::numeric::odeint::runge_kutta4< State, double, State, double, boost::numeric::odeint::vector_space_algebra> stepper;
00141 boost::numeric::odeint::integrate_const( stepper() , system , state , 0.0, predStep, predStep);
00142
00143 ROS_DEBUG_STREAM("Resulting in the state... " << std::endl
00144 << "Position: [" << state.position.transpose() << "]'" << std::endl
00145 << "Velocity: [" << state.lin_velocity.transpose() << "]'" << std::endl
00146 << "Orientation: [" << state.orientation.w() << " " << state.orientation.vec().transpose() << "]'" << std::endl);
00147
00148 return;
00149 }
00150
00151 void ComplementaryStateEstimator::inputCallback(const tk_draft_msgs::TKSmallImuConstPtr& msg)
00152 {
00153 boost::mutex::scoped_lock scopedLock(imuMutex);
00154
00155
00156 double output[3];
00157 std::vector<double> input(1);
00158 input[0]=msg->angular_velocity.x;
00159 omegaFilter[0]->step(input, output[0]);
00160 input[0] = msg->angular_velocity.y;
00161 omegaFilter[1]->step(input, output[1]);
00162 input[0] = msg->angular_velocity.z;
00163 omegaFilter[2]->step(input, output[2]);
00164
00165 angVelFiltered = Eigen::Vector3d(output[0], output[1], output[2]);
00166
00167
00168 Input lastInput;
00169 lastInput.ang_vel = Eigen::Vector3d(msg->angular_velocity.x, msg->angular_velocity.y, msg->angular_velocity.z);
00170
00171 lastInput.lin_acc = Eigen::Vector3d(msg->linear_acceleration.x, msg->linear_acceleration.y, msg->linear_acceleration.z);
00172 system.setInput(lastInput);
00173
00174
00175 ROS_DEBUG_STREAM("Imu received at system time: " << ros::Time::now() << std::endl
00176 << "Linear acceleration: [" << lastInput.ang_vel.transpose() << "]'" << std::endl
00177 << "Angular velocity: [" << lastInput.lin_acc.transpose() << "]'" << std::endl);
00178
00179 if (!isInitialized){
00180 if(!receivedFirstInput) {
00181 ROS_INFO_STREAM("Complementary state estimator received the first input containing: " << std::endl
00182 << "Angular velocity: [" << lastInput.ang_vel.transpose() << "]'" << std::endl
00183 << "Linear acceleration: [" << lastInput.lin_acc.transpose() << "]'");
00184
00185 receivedFirstInput = true;
00186 }
00187 if(receivedFirstMeasure){
00188 internalState.position = system.getMeasure().position;
00189 internalState.lin_velocity = Eigen::Vector3d::Zero();
00190 internalState.orientation = system.getMeasure().orientation;
00191 internalState.bias = options.imuAccBias->getValue();
00192
00193 initializationDone();
00194 ROS_INFO_STREAM("Complementary state estimator is ready to go!" << std::endl
00195 << "Initialized with:" << std::endl
00196 << "Position: [" << internalState.position.transpose() << "]'" << std::endl
00197 << "Velocity: [" << internalState.lin_velocity.transpose() << "]'" << std::endl
00198 << "Orientation: [" << internalState.orientation.w() << " " << internalState.orientation.vec().transpose() << "]'" << std::endl);
00199 }
00200 }
00201
00202 scopedLock.unlock();
00203
00204 }
00205
00206
00207 void ComplementaryStateEstimator::measureCallback(const geometry_msgs::TransformStampedConstPtr& msg)
00208 {
00209 boost::mutex::scoped_lock scopedLock(viconMutex);
00210
00211 Eigen::Quaterniond receivedQuaternion(-msg->transform.rotation.w, -msg->transform.rotation.x, msg->transform.rotation.y, msg->transform.rotation.z);
00212 receivedQuaternion = receivedQuaternion*Eigen::Quaterniond(cos(0.5*options.vicOrientation->getValue()(1)),0.0,-sin(0.5*options.vicOrientation->getValue()(1)),0.0)*
00213 Eigen::Quaterniond(cos(0.5*options.vicOrientation->getValue()(0)),-sin(0.5*options.vicOrientation->getValue()(0)),0.0,0.0);
00214 if (abs(receivedQuaternion.squaredNorm()-1)>1e-5){
00215 ROS_WARN("Received non valid quaternion. Ignoring measure!");
00216 return;
00217 }
00218
00219 Measure lastMeasure;
00220 lastMeasure.position = Eigen::Vector3d(msg->transform.translation.x, -msg->transform.translation.y, -msg->transform.translation.z);
00221 lastMeasure.orientation = receivedQuaternion;
00222 system.setMeasure(lastMeasure);
00223 scopedLock.unlock();
00224
00225 geometry_msgs::TransformStamped repubMsg(*msg);
00226 repubMsg.header.stamp = ros::Time::now();
00227 viconRepublisher.publish(repubMsg);
00228
00229 ROS_DEBUG_STREAM("Measure received at system time: " << ros::Time::now() << std::endl
00230 << "Position: [" << lastMeasure.position.transpose() << "]'" << std::endl
00231 << "Orientation: [" << lastMeasure.orientation.w() << " " << lastMeasure.orientation.vec().transpose() << "]'" << std::endl);
00232
00233 if (!isInitialized){
00234 if(!receivedFirstMeasure){
00235 receivedFirstMeasure = true;
00236 ROS_INFO_STREAM("Complementary state estimator received the first measure containing: " << std::endl
00237 << "Position: [" << lastMeasure.position.transpose() << "]'" << std::endl
00238 << "Orientation: [" << lastMeasure.orientation.w() << " " << lastMeasure.orientation.vec().transpose() << "]'" << std::endl);
00239 }
00240 if(receivedFirstInput){
00241 internalState.position = system.getMeasure().position;
00242 internalState.lin_velocity = Eigen::Vector3d::Zero();
00243 internalState.orientation = system.getMeasure().orientation;
00244 internalState.bias = options.imuAccBias->getValue();
00245
00246 initializationDone();
00247 ROS_INFO_STREAM("Complementary state estimator is ready to go!" << std::endl
00248 << "Initialized with:" << std::endl
00249 << "Position: [" << internalState.position.transpose() << "]'" << std::endl
00250 << "Velocity: [" << internalState.lin_velocity.transpose() << "]'" << std::endl
00251 << "Orientation: [" << internalState.orientation.w() << " " << internalState.orientation.vec().transpose() << "]'" << std::endl);
00252 }
00253 }
00254
00255 }
00256
00257 void ComplementaryStateEstimator::publishState(){
00258
00259 TKState tStateMsg;
00260
00261 tStateMsg.time = ros::Time::now();
00262
00263
00264 tStateMsg.position = internalState.position;
00265 tStateMsg.linVelocity = internalState.lin_velocity;
00266 tStateMsg.orientation = internalState.orientation;
00267
00268 boost::mutex::scoped_lock scopedImuLock(imuMutex);
00269 tStateMsg.angVelocity = angVelFiltered;
00270 scopedImuLock.unlock();
00271
00272 stateEstimatorController.activeStateCallBack(tStateMsg);
00273
00274 geometry_msgs::Vector3Stamped biasMsg;
00275 biasMsg.header.stamp = tStateMsg.time.toRosTime();
00276 biasMsg.vector.x = internalState.bias(0);
00277 biasMsg.vector.y = internalState.bias(1);
00278 biasMsg.vector.z = internalState.bias(2);
00279
00280 biasPub.publish(biasMsg);
00281 }
00282
00283
00284 void ComplementaryStateEstimator::willBecomeActive()
00285 {
00286 viconSub = nodeHandle.subscribe<geometry_msgs::TransformStamped>(
00287 options.tViconSeTopicName->getValue(),1, &ComplementaryStateEstimator::measureCallback, this);
00288
00289
00290 imuSub = nodeHandle.subscribe<tk_draft_msgs::TKSmallImu>(
00291 options.tImuSeTopicName->getValue(),1, &ComplementaryStateEstimator::inputCallback, this);
00292
00293 biasPub = nodeHandle.advertise<geometry_msgs::Vector3Stamped>("AccelerometerBias",1);
00294
00295 viconRepublisher = nodeHandle.advertise<geometry_msgs::TransformStamped>(options.tViconRepubTopicName->getValue(),1);
00296 }
00297
00298 void ComplementaryStateEstimator::willBecomeInActive()
00299 {
00300 viconSub.shutdown();
00301 imuSub.shutdown();
00302 biasPub.shutdown();
00303 }
00304
00305 void ComplementaryStateEstimator::destroy()
00306 {
00307 for(int i=0;i<3;i++){
00308 delete omegaFilter[i];
00309 }
00310
00311 spinning = false;
00312 thread.join();
00313 }
00314
00315 std::string ComplementaryStateEstimator::getName() const
00316 {
00317 return std::string("ComplementaryStateEstimator");
00318 }
00319
00320 }