NaN when calculating the SVD of a matrix with Eigne
Dear Samir, dear Gerald, dear anyone who might know,
in most cases my program works fine, but sometimes it crashes right after starting. I tracked the problem as far as I could. At first I thought it might be the SVD itself, but it appears that the problem starts earlier. I exit the program immediately if any element of data_->Lambda_omega_body_ is a NaN (see code attached).
The previous cout of data shows, that all the values used to calculate data_->Lambda_omega_inv_body_ which is used for the SVD are already NaN (see console output of the program). The first matrix to become NaN is data_->Trans_matrix_.matrix(). It's calculated by TAO.
Any ideas on how to fix the problem?
Thanks a lot, Fabian
CODE:
bool CKUKAOrientationTask::computeModel()
{
#ifdef DEBUG
assert(has_been_init_);
assert(S_NULL!=dynamics_);
#endif
if(data_->has_been_init_)
{
bool flag = true;
const scl::SGcModel* gcm = data_->gc_model_;
// Calculate Body Transformation and Jacobian
dynamics_->calculateTransformationMatrix(data_->link_id_body_,data_->Trans_matrix_);
Eigen::Vector3d pos = data_->Trans_matrix_ * data_->pos_in_parent_;
flag = flag && dynamics_->calculateJacobian(data_->link_id_body_,pos,data_->Jacobian_body_);
// Split into J_v and J_omega
data_->J_omega_body_ = data_->Jacobian_body_.block(3,0,3,data_->robot_->dof_); //angluar velocity Jacobian of Body
// Find #DOF
scl::sUInt dof = data_->robot_->dof_;
std::cout << "\nBEGINNING\n";
std::cout << "\ndata_->Trans_matrix_:\n";
std::cout << data_->Trans_matrix_.matrix() << std::endl;
std::cout << "\ndata_->pos_in_parent_:\n";
std::cout << data_->pos_in_parent_ << std::endl;
std::cout << "\ndata_->link_id_body_:\n";
std::cout << data_->link_id_body_ << std::endl;
std::cout << "\npos:\n";
std::cout << pos << std::endl;
std::cout << "\ndata_->J_omega_body_:\n";
std::cout << data_->J_omega_body_ << std::endl;
std::cout << "\ngcm->Ainv_:\n";
std::cout << gcm->Ainv_ << std::endl;
std::cout << "\ndata_->J_omega_body_.transpose():\n";
std::cout << data_->J_omega_body_.transpose() << std::endl;
//Lambda = (J * Ainv * J')^-1
data_->Lambda_omega_inv_body_ = data_->J_omega_body_ * gcm->Ainv_ * data_->J_omega_body_.transpose(); // Lambda_omega for Body
if(!lambda_inv_singular_)
{
//The general inverse function works very well for op-point controllers.
//3x3 matrix inversion behaves quite well. Even near singularities where
//singular values go down to ~0.001. If the model is coarse, use a n-k rank
//approximation with the SVD for a k rank loss in a singularity.
qr_.compute(data_->Lambda_omega_inv_body_);
qr_.setThreshold(1e-25);
if(qr_.isInvertible())
{ data_->Lambda_omega_body_ = qr_.inverse(); }
else
{
std::cout<<"\nCKUKAOrientationTask::computeModel() : Warning. Lambda_omega_inv_body_ is rank deficient. Using svd. Rank = "<<qr_.rank() << std::endl;
std::cout << std::endl << std::endl << data_->Lambda_omega_inv_body_ << std::endl;
// std::cout << "Threshold= " << qr_.threshold() << std::endl;
lambda_inv_singular_ = true;
}
}
if(lambda_inv_singular_)
{
//Use a Jacobi svd. No preconditioner is required coz lambda inv is square.
//NOTE : This is slower and generally performs worse than the simple inversion
//for small (3x3) matrices that are usually used in op-space controllers.
svd_.compute(data_->Lambda_omega_inv_body_,
Eigen::ComputeFullU | Eigen::ComputeFullV | Eigen::ColPivHouseholderQRPreconditioner);
// svd_.compute(data_->Lambda_omega_inv_body_,
// Eigen::ComputeFullU | Eigen::ComputeFullV | Eigen::FullPivHouseholderQRPreconditioner);
#ifdef DEBUG
std::cout<<"\n Singular values : "<<svd_.singularValues().transpose();
#endif
//NOTE : A threshold of .005 works quite well for most robots.
//Experimentally determined: Take the robot to a singularity
//and observe the response as you allow the min singular values
//to decrease. Stop when the robot starts to go unstable.
//NOTE : This also strongly depends on how good your model is
//and how fast you update it. A bad model will require higher
//thresholds and will result in coarse motions. A better model
//will allow much lower thresholds and will result in smooth
//motions.
if(svd_.singularValues()(0) > 0.005)
{ singular_values_(0,0) = 1.0/svd_.singularValues()(0); }
else { singular_values_(0,0) = 0.0; }
if(svd_.singularValues()(1) > 0.005)
{ singular_values_(1,1) = 1.0/svd_.singularValues()(1); }
else { singular_values_(1,1) = 0.0; }
if(svd_.singularValues()(2) > 0.005)
{ singular_values_(2,2) = 1.0/svd_.singularValues()(2); }
else { singular_values_(2,2) = 0.0; }
data_->Lambda_omega_body_ = svd_.matrixU() * singular_values_ * svd_.matrixV().transpose();
std::cout<<"\nComputing the SVD:" << std::endl;
std::cout << std::endl << std::endl << data_->Lambda_omega_body_ << std::endl << std::endl;
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
if ( isnan(data_->Lambda_omega_body_(i,j)) ) {
data_->Lambda_omega_body_(i,j) = 0.0;
std::cout << "\nNOT A NUMBER!!!\n";
// exit(0);
}
}
}
std::cout<<"\nComputing the SVD:" << std::endl;
std::cout << std::endl << std::endl << data_->Lambda_omega_body_ << std::endl << std::endl;
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
if ( isnan(data_->Lambda_omega_body_(i,j)) ) {
data_->Lambda_omega_body_(i,j) = 0.0;
exit(0);
}
}
}
//Turn off the svd after 20 iterations
//Don't worry, the qr will pop back to svd if it is still singular
static scl::sInt svd_ctr = 0; svd_ctr++;
std::cout << "Counter=" << svd_ctr << "\n";
if(20>=svd_ctr)
{ svd_ctr = 0; lambda_inv_singular_ = false; }
}
//Compute the Jacobian dynamically consistent generalized inverse :
//J_dyn_inv = Ainv * J' * Lambda
data_->jacobian_dyn_inv_ = gcm->Ainv_ * data_->J_omega_body_.transpose() * data_->Lambda_omega_body_;
// Set up the null space
// N = I-J'*J_dyn_inv'
data_->null_space_ = Eigen::MatrixXd::Identity(dof, dof) - data_->J_omega_body_.transpose() * data_->jacobian_dyn_inv_.transpose();
// We do not use the centrifugal/coriolis forces. They can cause instabilities.
data_->mu_.setZero(data_->dof_task_,1);
// J' * J_dyn_inv' * g(q)
//data_->p_ = data_->jacobian_dyn_inv_.transpose() * gcm->g_;
data_->p_.setZero(data_->dof_task_,1);
return flag;
}
else
{ return false; }
}
CONSOLE OUTPUT:
CTaskOpPos::computeModel() : Warning. Lambda_inv is rank deficient. Using svd. Rank = 0
BEGINNING
data_->Trans_matrix_:
nan nan nan nan
nan nan nan nan
nan nan nan nan
0 0 0 1
data_->pos_in_parent_:
0
1
0
data_->link_id_body_:
0xa45a228
pos:
nan
nan
nan
data_->J_omega_body_:
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
gcm->Ainv_:
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
nan nan nan nan nan nan nan
data_->J_omega_body_.transpose():
nan nan nan
nan nan nan
nan nan nan
nan nan nan
nan nan nan
nan nan nan
nan nan nan
CKUKAOrientationTask::computeModel() : Warning. Lambda_omega_inv_body_ is rank deficient. Using svd. Rank = 0
nan nan nan
nan nan nan
nan nan nan
Computing the SVD:
nan nan nan
nan nan nan
nan nan nan
NOT A NUMBER!!!
NOT A NUMBER!!!
NOT A NUMBER!!!
NOT A NUMBER!!!
NOT A NUMBER!!!
NOT A NUMBER!!!
NOT A NUMBER!!!
NOT A NUMBER!!!
NOT A NUMBER!!!
Computing the SVD:
0 0 0
0 0 0
0 0 0
Counter=1
#!
Comments (13)
-
repo owner -
reporter Thanks for the advice, Samir. I'll check that out.
-
Keep me updated on the issue please
-
reporter Ok, I'm initializing all the variables in the init functions. That wasn't it. I'm also calling robot_.computeDynamics(); in CFabianApp::setInitialStateForUIAndDynamics() so that wasn't it either.
Here's my complete code. I would be very grateful for any hints.
CFabianApp.cpp
/* This file is part of scl, a control and simulation library for robots and biomechanical models. scl is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Alternatively, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. scl is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License and a copy of the GNU General Public License along with scl. If not, see <http://www.gnu.org/licenses/>. */ /* \file CFabianApp.cpp * * Created on: Sep 16, 2011 * * Copyright (C) 2011 * * Author: Samir Menon <smenon@stanford.edu> */ #include "CFabianApp.hpp" #include "CKUKAOrientationTask.hpp" #include <scl/DataTypes.hpp> #include <scl/data_structs/SDatabase.hpp> #include <sutil/CSystemClock.hpp> #include <chai3d.h> #include <iostream> #include <stdexcept> #include <Eigen/Dense> #include <math.h> #include <signal.h> // My Shared Memory Class #include "SMIC.hpp" // My Matlab Logging Class #include "MatlabLogger.hpp" #ifndef PI #define PI 3.1415926535897932384626433832795 #endif #ifndef RAD #define RAD(A) ((A) * PI / 180.0 ) #endif #ifndef DEG #define DEG(A) ((A) * 180.0 / PI ) #endif bool ok = true; static void sighandler(int sig) { ok = false; } using namespace chai3d; namespace scl_app { /** Default constructor. Sets stuff to zero. * Uses a task controller*/ CFabianApp::CFabianApp() : // ctrl_(S_NULL), // name_com_task_(""), // task_com_(S_NULL), // task_ds_com_(S_NULL), // ui_pt_com_(-1), // has_been_init_com_task_(false), // chai_com_pos_(S_NULL), // chai_com_pos_des_(S_NULL), haptic_pos_(3), haptic_base_pos_(3), old_goal_pos(3), base_goal_pos(3), has_been_init_haptics_(false) { } scl::sBool CFabianApp::initMyController(const std::vector<std::string>& argv, scl::sUInt args_parsed) { // Set up signal handler signal(SIGINT, sighandler); // RUN SIMULATION (true) OR CONTROL THE REAL ROBOT (false) run_simulation_only=true; use_haptic_interface=true; capture_token_opportunity_time_window.tv_nsec = 80000; // 0.08ms capture_token_opportunity_time_window.tv_sec = 0; TimeInMillisec = 0; if(use_haptic_interface) { //First set up the haptics int tmp_n_haptics_connected = haptics_.connectToDevices(); if(0 >= tmp_n_haptics_connected) { throw(std::runtime_error("Could not connect to haptic devices")); } if (haptics_.getNumDevicesConnected() == 1) { has_been_init_haptics_ = true; scaling_factor = 5.0; clutch_active_previous_cycle = false; } } // // INIT FOR RUNNING THE KUKA HARDWARE IN THE LOOP // if (!run_simulation_only) { // Tell KUKATorqueInterface that the controller is ready to run SharedMemory.StartControllerProcess(); // Initialize all the arrays memset(MeasuredJointPosInRad, 0x0, NUMBER_OF_JOINTS * sizeof(float)); memset(MeasuredJointVelocitiesInRadPerSec, 0x0, NUMBER_OF_JOINTS * sizeof(float)); memset(MeasuredJointTorquesInNm, 0x0, NUMBER_OF_JOINTS * sizeof(float)); memset(CommandedJointTorquesInNm, 0x0, NUMBER_OF_JOINTS * sizeof(float)); memset(CommandedOperationalPoint, 0x0, 3 * sizeof(float)); memset(MeasuredOperationalPoint, 0x0, 3 * sizeof(float)); // Add variables which will be logged MatLog.addFloatArray(MeasuredJointPosInRad, NUMBER_OF_JOINTS, "MeasuredJointPosInRad"); MatLog.addFloatArray(MeasuredJointVelocitiesInRadPerSec, NUMBER_OF_JOINTS, "MeasuredJointVelocitiesInRadPerSec"); MatLog.addFloatArray(MeasuredJointTorquesInNm, NUMBER_OF_JOINTS, "MeasuredJointTorquesInNm"); MatLog.addFloatArray(CommandedJointTorquesInNm, NUMBER_OF_JOINTS, "CommandedJointTorquesInNm"); MatLog.addFloatArray(CommandedOperationalPoint, 3, "CommandedOperationalPoint"); MatLog.addFloatArray(MeasuredOperationalPoint, 3, "MeasuredOperationalPoint"); MatLog.addInteger(&TimeInMillisec, "TimeInMillisec"); } // // INIT FOR RUNNING THE SIMULATION ONLY // else { memset(CommandedOperationalPoint, 0x0, 3 * sizeof(float)); memset(MeasuredOperationalPoint, 0x0, 3 * sizeof(float)); MatLog.addFloatArray(CommandedOperationalPoint, 3, "CommandedOperationalPoint"); MatLog.addFloatArray(MeasuredOperationalPoint, 3, "MeasuredOperationalPoint"); MatLog.addInteger(&TimeInMillisec, "TimeInMillisec"); } // Get the robot out of the singularity to avoid the NaN-problem in the SVD of the orientation task //Convert Arrays to Vectors Eigen::VectorXd JointPosInRadVector(7); Eigen::VectorXd JointVelocitiesInRadPerSecVector(7); for (int j=0; j<NUMBER_OF_JOINTS; j++) { JointPosInRadVector(j) = 0.0; JointVelocitiesInRadPerSecVector(j) = 0.0; } JointPosInRadVector(1) = RAD(22.0); JointPosInRadVector(3) = RAD(44.0); robot_.setGeneralizedCoordinates(JointPosInRadVector); robot_.setGeneralizedVelocities(JointVelocitiesInRadPerSecVector); bool flag; try { //Ctr in array of args_parsed = (args_parsed - 1) //So ctr for un-parsed arg = (args_parsed - 1) + 1 scl::sUInt args_ctr = args_parsed, ui_points_used=0; // Check that we haven't finished parsing everything while(args_ctr < argv.size()) { /* NOTE : ADD MORE COMMAND LINE PARSING OPTIONS IF REQUIRED */ // else if (argv[args_ctr] == "-p") // { } if(true) { std::cout<<"\n Possible example task options: -p (start paused) -l (log file), -com (com control task), -op (op point task)"; args_ctr++; } } return true; } catch(std::exception &e) { std::cout<<"\nCExampleApp::initMyController() : "<<e.what(); } return false; } scl::sBool CFabianApp::registerCustomDynamicTypes() { // return registerFabianTaskType(); return registerKUKAOrientationTaskType(); } void CFabianApp::setInitialStateForUIAndDynamics() { //Compute dynamics and servo once to initialize matrices. robot_.computeDynamics(); robot_.computeNonControlOperations(); robot_.computeServo(); robot_.setGeneralizedCoordinatesToZero(); robot_.setGeneralizedVelocitiesToZero(); robot_.setGeneralizedAccelerationsToZero(); //Update the operational point tasks (if any) std::vector<SUiCtrlPointData>::iterator it,ite; for(it = taskvec_ui_ctrl_point_.begin(), ite = taskvec_ui_ctrl_point_.end(); it!=ite; ++it ) { assert(it->has_been_init_); assert(NULL!=it->chai_pos_des_); assert(NULL!=it->task_); it->task_->getPos(it->pos_); #ifdef DEBUG if( (3 != it->pos_.rows() && 1 != it->pos_.cols()) || (1 != it->pos_.rows() && 3 != it->pos_.cols()) ) { assert(false); } #endif db_->s_gui_.ui_point_[it->ui_pt_] = it->pos_; base_goal_pos(0) = -0.099; base_goal_pos(1) = -0.099; base_goal_pos(2) = 0.20; Eigen::Vector3d& ui_point_ = db_->s_gui_.ui_point_[it->ui_pt_]; for (int i=0; i<3; i++) { //SET THE INITIAL DESIRED OPERATIONAL POINT ui_point_(i) = base_goal_pos(i); old_goal_pos(i) = base_goal_pos(i); if(has_been_init_haptics_) { haptic_pos_(i) = 0.0; haptic_base_pos_(i) = base_goal_pos(i); } } it->chai_pos_des_->setLocalPos(ui_point_(0),ui_point_(1),ui_point_(2)); } } void CFabianApp::stepMySimulation() { // As long as user does not press Control-C ok=truex if(ok) { std::vector<SUiCtrlPointData>::iterator it,ite; // SET LIMITS TO THE OPERATIONAL POINT COORDINATES for(it = taskvec_ui_ctrl_point_.begin(), ite = taskvec_ui_ctrl_point_.end(); it!=ite; ++it ) { if(has_been_init_haptics_) { //Update the position of the first few points using the haptics bool flag = haptics_.getHapticDevicePosition(0, haptic_pos_); assert(flag); // Clutch is active if (db_->s_gui_.ui_flag_[1]) { if(!clutch_active_previous_cycle) { //Reset the clutch origin to the current position of the commanded operational task point bool flag = haptics_.getHapticDevicePosition(0, haptic_base_pos_); assert(flag); //Reset the operational point origin to the current operational point base_goal_pos = db_->s_gui_.ui_point_[it->ui_pt_]; } // Standard mapping for the simulation if (run_simulation_only) { //Overwrite the UI point witht he data from the haptic device db_->s_gui_.ui_point_[it->ui_pt_] = base_goal_pos + 5 * (haptic_pos_ - haptic_base_pos_); } //Invert the perspective of the robot when controlling the real KUKA for more intuitive handling from my workplace perspective else { //Overwrite the UI point witht he data from the haptic device (db_->s_gui_.ui_point_[it->ui_pt_])(0) = base_goal_pos(0) - 5 * (haptic_pos_(0) - haptic_base_pos_(0)); (db_->s_gui_.ui_point_[it->ui_pt_])(1) = base_goal_pos(1) - 5 * (haptic_pos_(1) - haptic_base_pos_(1)); (db_->s_gui_.ui_point_[it->ui_pt_])(2) = base_goal_pos(2) + 5 * (haptic_pos_(2) - haptic_base_pos_(2)); } clutch_active_previous_cycle = true; } else { clutch_active_previous_cycle = false; } } // SET the origin of the transformed coordinate system roughly to the base of the robot // This origin is used for creating a sphere that serves as a limit for the workspace with the goal of avoiding singularities Eigen::Vector3d pos_des_transformed; pos_des_transformed(0) = (db_->s_gui_.ui_point_[it->ui_pt_])(0) + 0.099; pos_des_transformed(1) = (db_->s_gui_.ui_point_[it->ui_pt_])(1) + 0.099; pos_des_transformed(2) = (db_->s_gui_.ui_point_[it->ui_pt_])(2) + 0.55; //Robot must stay in between two bounding spheres with radius 0.4m<r<0.8m double norm = sqrt( pow (pos_des_transformed(0), 2.0) + pow (pos_des_transformed(1), 2.0) + pow (pos_des_transformed(2), 2.0) ); // Out of bounds: Set ui_point_pos back to the last viable position and deactivate clutch if( norm<0.4 || norm>0.8 || pos_des_transformed(2) < 0) { db_->s_gui_.ui_point_[it->ui_pt_] = old_goal_pos; db_->s_gui_.ui_flag_[1]=false; // Deactivate the clutch when leaving workspace } // Within bounds: Always save the last viable ui_point_pos else { old_goal_pos = (db_->s_gui_.ui_point_[it->ui_pt_]); } //Set the goal position for the operational point tasks it->task_->setGoalPos(db_->s_gui_.ui_point_[it->ui_pt_]); // UPDATE THE ARRAYS WHICH ARE BEING TRACKED BY THE LOGGER scl::STaskOpPos* task_data = dynamic_cast<scl::STaskOpPos*> (it->task_->getTaskData()); for (int i=0; i<3; i++) { CommandedOperationalPoint[i] = (db_->s_gui_.ui_point_[it->ui_pt_])(i); MeasuredOperationalPoint[i] = (task_data->x_)(i); } // // Transform commanded OP vector to array for logging // CommandedOperationalPoint[0] = (db_->s_gui_.ui_point_[it->ui_pt_])(0); // CommandedOperationalPoint[1] = (db_->s_gui_.ui_point_[it->ui_pt_])(1); // CommandedOperationalPoint[2] = (db_->s_gui_.ui_point_[it->ui_pt_])(2); // // Transform actual measured OP vector to array for logging // MeasuredOperationalPoint[0] = (task_data->x_)(0); // MeasuredOperationalPoint[1] = (task_data->x_)(1); // MeasuredOperationalPoint[2] = (task_data->x_)(2); } sutil::CSystemClock::tick(db_->sim_dt_);//Tick the clock. // // SIMULATION ONLY // if (run_simulation_only) { // COM TASK DOES NOT DO ANYTHING ASK SAMIR WHAT IT's THERE FOR // if(has_been_init_com_task_) //Update the com task (if any) // { task_com_->setGoal(db_->s_gui_.ui_point_[ui_pt_com_]); } //Update dynamics at a slower rate if(ctrl_ctr_%2 == 0) { robot_.computeDynamics(); robot_.computeNonControlOperations(); } //Update graphics and/or log at a slower rate if(ctrl_ctr_%10 == 0) { //Set the positions of the ui points for(it = taskvec_ui_ctrl_point_.begin(), ite = taskvec_ui_ctrl_point_.end(); it!=ite; ++it ) { Eigen::Vector3d& tmp_ref = db_->s_gui_.ui_point_[it->ui_pt_]; it->chai_pos_des_->setLocalPos(tmp_ref(0),tmp_ref(1),tmp_ref(2)); it->task_->getPos(it->pos_); Eigen::VectorXd& tmp_ref2 = it->pos_; it->chai_pos_->setLocalPos(tmp_ref2(0),tmp_ref2(1),tmp_ref2(2)); } // if(has_been_init_com_task_) // { // chai_com_pos_->setLocalPos(task_ds_com_->x_(0),task_ds_com_->x_(1),task_ds_com_->x_(2)); // Eigen::Vector3d& tmp_ref2 = db_->s_gui_.ui_point_[ui_pt_com_]; // chai_com_pos_des_->setLocalPos(tmp_ref2(0),tmp_ref2(1),tmp_ref2(2)); // } } //Run the servo loop robot_.computeServo(); //Integrate system robot_.integrateDynamics(); TimeInMillisec = 1000 * sutil::CSystemClock::getSimTime(); ctrl_ctr_++;//Increment the counter for dynamics computed. // LOG ONE SET OF DATA //MatLog.logData(); } // // CONTROL THE KUKA LBR (HARDWARE IN THE LOOP) // else { // Process Synchronization // The shared memory token is used to synchronize the kuka_torque_interface with the kuka_scl_controller. // The token is made available from the kuka_torque_interface right after new sensor data was read from the robot and written into the shared memory. // Then the kuka_scl_controller application starts calculating the new torques immediately. SharedMemory.GrabToken(); // COM TASK DOES NOT DO ANYTHING ASK SAMIR WHAT IT's THERE FOR // if(has_been_init_com_task_) //Update the com task (if any) // { task_com_->setGoal(db_->s_gui_.ui_point_[ui_pt_com_]); } // ASK SAMIR WHAT THIS STUFF DOES, TOO robot_.computeDynamics(); robot_.computeNonControlOperations(); //Set the positions of the ui points for(it = taskvec_ui_ctrl_point_.begin(), ite = taskvec_ui_ctrl_point_.end(); it!=ite; ++it ) { Eigen::Vector3d& tmp_ref = db_->s_gui_.ui_point_[it->ui_pt_]; it->chai_pos_des_->setLocalPos(tmp_ref(0),tmp_ref(1),tmp_ref(2)); it->task_->getPos(it->pos_); Eigen::VectorXd& tmp_ref2 = it->pos_; it->chai_pos_->setLocalPos(tmp_ref2(0),tmp_ref2(1),tmp_ref2(2)); } // if(has_been_init_com_task_) // { // chai_com_pos_->setLocalPos(task_ds_com_->x_(0),task_ds_com_->x_(1),task_ds_com_->x_(2)); // Eigen::Vector3d& tmp_ref2 = db_->s_gui_.ui_point_[ui_pt_com_]; // chai_com_pos_des_->setLocalPos(tmp_ref2(0),tmp_ref2(1),tmp_ref2(2)); // } // Get the data from the real robot SharedMemory.GetMeasuredJointPositions(MeasuredJointPosInRad); SharedMemory.GetEstimatedJointVelocities(MeasuredJointVelocitiesInRadPerSec); SharedMemory.GetMeasuredJointTorques(MeasuredJointTorquesInNm); TimeInMillisec = SharedMemory.GetTimeInMillisec(); // Convert Arrays to Vectors Eigen::VectorXd JointPosInRadVector(7); Eigen::VectorXd JointVelocitiesInRadPerSecVector(7); for (int j=0; j<NUMBER_OF_JOINTS; j++) { JointPosInRadVector(j) = (double) MeasuredJointPosInRad[j]; JointVelocitiesInRadPerSecVector(j) = (double) MeasuredJointVelocitiesInRadPerSec[j]; } // Write the sensor data into the data structure of SCL robot_.setGeneralizedCoordinates(JointPosInRadVector); robot_.setGeneralizedVelocities(JointVelocitiesInRadPerSecVector); // Let SCl compute the torques for the robot robot_.computeServo(); // Store the computed torques in an array Eigen::VectorXd GeneralizedForcesCommanded(7); GeneralizedForcesCommanded = robot_.getGeneralizedForcesCommanded(); for (int j=0; j<NUMBER_OF_JOINTS; j++) { CommandedJointTorquesInNm[j] = (float) GeneralizedForcesCommanded[j]; } // Write the commanded torques to shared memory SharedMemory.SetCommandedJointTorques(CommandedJointTorquesInNm); // Tel kuka_torque_interface that kuka_scl_controller is done computing the torques SharedMemory.FreeToken(); //Wait a little to give kuka_torque_interface time to recapture the token nanosleep(&capture_token_opportunity_time_window,NULL); // Log one set of data if ( SharedMemory.LoggerRunning() ) { MatLog.logData(); } } } // Control-C was cought -> tell the TorqueInterface that the controller is no longer running and write the logfile else { SharedMemory.StopControllerProcess(); SharedMemory.StopLogging(); if(run_simulation_only) { MatLog.writeLogFile("log/logfile_sim.mat"); } // Simulation Logfile else { MatLog.writeLogFile("log/logfile_hil.mat"); } // Hardware In the Loop Logfile exit(1); } } }
CFabianApp.hpp
/* This file is part of scl, a control and simulation library for robots and biomechanical models. scl is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Alternatively, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. scl is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License and a copy of the GNU General Public License along with scl. If not, see <http://www.gnu.org/licenses/>. */ /* \file CFabianApp.hpp * * Created on: Sep 16, 2011 * * Copyright (C) 2011 * * Author: Samir Menon <smenon@stanford.edu> */ #ifndef CFABIANAPP_HPP_ #define CFABIANAPP_HPP_ #include <Eigen/Dense> #include <scl/robot/CRobotApp.hpp> #include <scl/control/task/CTaskController.hpp> #include <scl/control/task/tasks/CTaskOpPos.hpp> #include <scl/control/task/tasks/CTaskComPos.hpp> #include <scl/graphics/chai/data_structs/SChaiGraphics.hpp> #include <scl/haptics/chai/CChaiHaptics.hpp> // My Shared Memory Class #include "SMIC.hpp" // My Matlab Logging Class #include "MatlabLogger.hpp" namespace scl_app { class CFabianApp : public scl::CRobotApp { public: // **************************************************** // The main functions // **************************************************** /** Runs the task controller. */ virtual void stepMySimulation(); // **************************************************** // The initialization functions // **************************************************** /** Default constructor. Sets stuff to zero. */ CFabianApp(); /** Default destructor. Does nothing. */ virtual ~CFabianApp(){} /** Sets up the task controller. */ virtual scl::sBool initMyController(const std::vector<std::string>& argv, scl::sUInt args_parsed); /** Register any custom dynamic types that you have. */ virtual scl::sBool registerCustomDynamicTypes(); /** Sets all the ui points to their current position and * run the dynamics once to flush the state. */ void setInitialStateForUIAndDynamics(); //Shared Memory Object SMIC SharedMemory; // Pointer to the logging object MatlabLogger MatLog; // All the variables which should be logged need to be public float MeasuredJointPosInRad[NUMBER_OF_JOINTS]; // Measured float MeasuredJointVelocitiesInRadPerSec[NUMBER_OF_JOINTS]; // Measured float MeasuredJointTorquesInNm[NUMBER_OF_JOINTS]; // Measured float CommandedJointTorquesInNm[NUMBER_OF_JOINTS]; float CommandedOperationalPoint[3]; float MeasuredOperationalPoint[3]; int TimeInMillisec; private: // **************************************************** // The data // **************************************************** // CHOOSE WHETHER TO RUN HARDWARE IN THE LOOP (false) OR SIMULATION (true) bool run_simulation_only; bool use_haptic_interface; // Workspace Limitation - Save the last viable UI Point Eigen::VectorXd old_goal_pos, base_goal_pos; double scaling_factor; bool clutch_active_previous_cycle; struct timespec capture_token_opportunity_time_window; //For controlling op points with haptics //The app will support dual-mode control, with the haptics controlling op points. scl::CChaiHaptics haptics_; scl::sUInt num_haptic_devices_to_use_; //These will directly control ui-points. bool has_been_init_haptics_; Eigen::VectorXd haptic_pos_; Eigen::VectorXd haptic_base_pos_; }; } #endif /* CFABIANAPP_HPP_ */
CKUKAOrientationTask.cpp
/* Copyright (C) 2011 Samir Menon, Stanford University This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * CKUKAOrientationTask.cpp * * Created on: Apr 12, 2011 * Author: samir */ #include "CKUKAOrientationTask.hpp" #include <scl/Singletons.hpp> #include <sutil/CRegisteredDynamicTypes.hpp> #include <stdexcept> #include <math.h> namespace scl_app { CKUKAOrientationTask::CKUKAOrientationTask() : scl::CTaskBase(), data_(S_NULL), link_dynamic_id_(S_NULL), lambda_inv_singular_(false) {} CKUKAOrientationTask::~CKUKAOrientationTask() {} bool CKUKAOrientationTask::computeServo(const scl::SRobotSensorData* arg_sensors) { static int cntr = 0; #ifdef DEBUG assert(has_been_init_); assert(S_NULL!=dynamics_); #endif if(data_->has_been_init_) { // COMPUTE SERVO GENERALIZED FORCES // ***************** BODY ORIENTATION **************** //Step 1: Find current orientation Eigen::Matrix3d R_cur = data_->Trans_matrix_.rotation(); //Step 2: Find desired orientation // Desired orientation, keeps Divebot horizontal, only adjusts z-angle Eigen::Matrix3d R_des; //R_des.setIdentity(3,3); double theta_z = 0.25*3.14; R_des(0,0) = cos(theta_z); R_des(0,1) = -sin(theta_z); R_des(0,2) = 0; R_des(1,0) = sin(theta_z); R_des(1,1) = cos(theta_z); R_des(1,2) = 0; R_des(2,0) = 0; R_des(2,1) = 0; R_des(2,2) = 1; //Step 3: Find angular error //Direction cosines (current): Eigen::Vector3d s1_cur = R_cur.col(0); Eigen::Vector3d s2_cur = R_cur.col(1); Eigen::Vector3d s3_cur = R_cur.col(2); //Direction cosines (desired): Eigen::Vector3d s1_des = R_des.col(0); Eigen::Vector3d s2_des = R_des.col(1); Eigen::Vector3d s3_des = R_des.col(2); Eigen::Vector3d delta_PHI; delta_PHI = -0.5 * ( s1_cur.cross(s1_des) + s2_cur.cross(s2_des) + s3_cur.cross(s3_des) ); //Step 4: Find angular velocity Eigen::Vector3d omega; omega = data_->J_omega_body_ * arg_sensors->dq_; //Step 5: compute unit-mass moment(=acceleration), due to rotation Eigen::Vector3d M_star; M_star = - data_->kp_[0] * delta_PHI - data_->kv_[0] * omega; //TODO: expand kp, kv //Step 6: Compute OP-space moment Eigen::VectorXd M; M = data_->Lambda_omega_body_ * M_star; //Step 7: Compute GC torque Eigen::VectorXd force_gc_orientation; data_->force_gc_ = data_->J_omega_body_.transpose() * M; if(cntr++ % 10000 == 0){ //std::cout << arg_sensors->q_ << std::endl << std::endl; //std::cout << rightOP << std::endl << std::endl; //std::cout << x_body_cur << std::endl << std::endl << std::endl << std::endl; //std::cout << "force_gc_" << std::endl << data_->force_gc_ << std::endl << std::endl << std::endl; // Cross product does not work //std::cout << theta_z << std::endl << std::endl << std::endl; //std::cout << R_cur << std::endl << std::endl << std::endl; //std::cout << force_task << std::endl << std::endl << std::endl; //std::cout << data_->Lambda_v_inv_body_ << std::endl << std::endl << std::endl; //std::cout << data_->p_ << std::endl << std::endl << std::endl; //std::cout << R_des_left << std::endl << std::endl << std::endl; } return true; } else { return false; } } bool CKUKAOrientationTask::computeModel() { #ifdef DEBUG assert(has_been_init_); assert(S_NULL!=dynamics_); #endif if(data_->has_been_init_) { bool flag = true; const scl::SGcModel* gcm = data_->gc_model_; // Calculate Body Transformation and Jacobian dynamics_->calculateTransformationMatrix(data_->link_id_body_,data_->Trans_matrix_); Eigen::Vector3d pos = data_->Trans_matrix_ * data_->pos_in_parent_; flag = flag && dynamics_->calculateJacobian(data_->link_id_body_,pos,data_->Jacobian_body_); // Split into J_v and J_omega data_->J_omega_body_ = data_->Jacobian_body_.block(3,0,3,data_->robot_->dof_); //angluar velocity Jacobian of Body // Find #DOF scl::sUInt dof = data_->robot_->dof_; std::cout << "\nBEGINNING\n"; std::cout << "\ndata_->Trans_matrix_:\n"; std::cout << data_->Trans_matrix_.matrix() << std::endl; std::cout << "\ndata_->pos_in_parent_:\n"; std::cout << data_->pos_in_parent_ << std::endl; std::cout << "\ndata_->link_id_body_:\n"; std::cout << data_->link_id_body_ << std::endl; std::cout << "\npos:\n"; std::cout << pos << std::endl; std::cout << "\ndata_->J_omega_body_:\n"; std::cout << data_->J_omega_body_ << std::endl; std::cout << "\ngcm->Ainv_:\n"; std::cout << gcm->Ainv_ << std::endl; std::cout << "\ndata_->J_omega_body_.transpose():\n"; std::cout << data_->J_omega_body_.transpose() << std::endl; //Lambda = (J * Ainv * J')^-1 data_->Lambda_omega_inv_body_ = data_->J_omega_body_ * gcm->Ainv_ * data_->J_omega_body_.transpose(); // Lambda_omega for Body if(!lambda_inv_singular_) { //The general inverse function works very well for op-point controllers. //3x3 matrix inversion behaves quite well. Even near singularities where //singular values go down to ~0.001. If the model is coarse, use a n-k rank //approximation with the SVD for a k rank loss in a singularity. qr_.compute(data_->Lambda_omega_inv_body_); qr_.setThreshold(1e-25); if(qr_.isInvertible()) { data_->Lambda_omega_body_ = qr_.inverse(); } else { std::cout<<"\nCKUKAOrientationTask::computeModel() : Warning. Lambda_omega_inv_body_ is rank deficient. Using svd. Rank = "<<qr_.rank() << std::endl; std::cout << std::endl << std::endl << data_->Lambda_omega_inv_body_ << std::endl; // std::cout << "Threshold= " << qr_.threshold() << std::endl; lambda_inv_singular_ = true; } } if(lambda_inv_singular_) { //Use a Jacobi svd. No preconditioner is required coz lambda inv is square. //NOTE : This is slower and generally performs worse than the simple inversion //for small (3x3) matrices that are usually used in op-space controllers. svd_.compute(data_->Lambda_omega_inv_body_, Eigen::ComputeFullU | Eigen::ComputeFullV | Eigen::ColPivHouseholderQRPreconditioner); // svd_.compute(data_->Lambda_omega_inv_body_, // Eigen::ComputeFullU | Eigen::ComputeFullV | Eigen::FullPivHouseholderQRPreconditioner); #ifdef DEBUG std::cout<<"\n Singular values : "<<svd_.singularValues().transpose(); #endif //NOTE : A threshold of .005 works quite well for most robots. //Experimentally determined: Take the robot to a singularity //and observe the response as you allow the min singular values //to decrease. Stop when the robot starts to go unstable. //NOTE : This also strongly depends on how good your model is //and how fast you update it. A bad model will require higher //thresholds and will result in coarse motions. A better model //will allow much lower thresholds and will result in smooth //motions. if(svd_.singularValues()(0) > 0.005) { singular_values_(0,0) = 1.0/svd_.singularValues()(0); } else { singular_values_(0,0) = 0.0; } if(svd_.singularValues()(1) > 0.005) { singular_values_(1,1) = 1.0/svd_.singularValues()(1); } else { singular_values_(1,1) = 0.0; } if(svd_.singularValues()(2) > 0.005) { singular_values_(2,2) = 1.0/svd_.singularValues()(2); } else { singular_values_(2,2) = 0.0; } data_->Lambda_omega_body_ = svd_.matrixU() * singular_values_ * svd_.matrixV().transpose(); std::cout<<"\nComputing the SVD:" << std::endl; std::cout << std::endl << std::endl << data_->Lambda_omega_body_ << std::endl << std::endl; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { if ( isnan(data_->Lambda_omega_body_(i,j)) ) { data_->Lambda_omega_body_(i,j) = 0.0; std::cout << "\nNOT A NUMBER!!!\n"; // exit(0); } } } std::cout<<"\nComputing the SVD:" << std::endl; std::cout << std::endl << std::endl << data_->Lambda_omega_body_ << std::endl << std::endl; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) { if ( isnan(data_->Lambda_omega_body_(i,j)) ) { data_->Lambda_omega_body_(i,j) = 0.0; exit(0); } } } //Turn off the svd after 20 iterations //Don't worry, the qr will pop back to svd if it is still singular static scl::sInt svd_ctr = 0; svd_ctr++; std::cout << "Counter=" << svd_ctr << "\n"; if(20>=svd_ctr) { svd_ctr = 0; lambda_inv_singular_ = false; } } //Compute the Jacobian dynamically consistent generalized inverse : //J_dyn_inv = Ainv * J' * Lambda data_->jacobian_dyn_inv_ = gcm->Ainv_ * data_->J_omega_body_.transpose() * data_->Lambda_omega_body_; // Set up the null space // N = I-J'*J_dyn_inv' data_->null_space_ = Eigen::MatrixXd::Identity(dof, dof) - data_->J_omega_body_.transpose() * data_->jacobian_dyn_inv_.transpose(); // We do not use the centrifugal/coriolis forces. They can cause instabilities. data_->mu_.setZero(data_->dof_task_,1); // J' * J_dyn_inv' * g(q) //data_->p_ = data_->jacobian_dyn_inv_.transpose() * gcm->g_; data_->p_.setZero(data_->dof_task_,1); return flag; } else { return false; } } scl::STaskBase* CKUKAOrientationTask::getTaskData() { return data_; } bool CKUKAOrientationTask::init(scl::STaskBase* arg_task_data, scl::CDynamicsBase* arg_dynamics) { try { if(S_NULL == arg_task_data) { throw(std::runtime_error("Passed a null task data structure")); } if(false == arg_task_data->has_been_init_) { throw(std::runtime_error("Passed an uninitialized task data structure")); } if(S_NULL == arg_dynamics) { throw(std::runtime_error("Passed a null dynamics object")); } if(false == arg_dynamics->hasBeenInit()) { throw(std::runtime_error("Passed an uninitialized dynamics object")); } data_ = dynamic_cast<SKUKAOrientationTask*>(arg_task_data); dynamics_ = arg_dynamics; // Gerald: Custom for this task, in order to get the transformation matrix for Body // TODO Change Hardcoding // TODO Parse Cfg.xml file for parent link of the task data_->link_id_body_ = dynamics_->getIdForLink("end-effector"); if(S_NULL == data_->link_id_body_) { throw(std::runtime_error("The link dynamic id for the BODY operational point's parent link is NULL. Probably due to un-initialized dynamics.")); } has_been_init_ = true; } catch(std::exception& e) { std::cerr<<"\nCKUKAOrientationTask::init() :"<<e.what(); has_been_init_ = false; } return has_been_init_; } void CKUKAOrientationTask::reset() { data_ = S_NULL; dynamics_ = S_NULL; has_been_init_ = false; } /******************************************* Dynamic Type : CKUKAOrientationTask NOTE : To enable dynamic typing for tasks, you must define the types for the "CTaskName" computation object AND the "STaskName" data structure. THIS IS NECESSARY. Why? So that you have a quick and easy way to specify custom xml parameters in the *Cfg.xml file. *******************************************/ scl::sBool registerKUKAOrientationTaskType() { bool flag; try { sutil::CDynamicType<std::string,scl_app::CKUKAOrientationTask> typeCKUKAOrientationTask(std::string("CKUKAOrientationTask")); flag = typeCKUKAOrientationTask.registerType(); if(false == flag) {throw(std::runtime_error("Could not register type CKUKAOrientationTask"));} sutil::CDynamicType<std::string,scl_app::SKUKAOrientationTask> typeSKUKAOrientationTask(std::string("SKUKAOrientationTask")); flag = typeSKUKAOrientationTask.registerType(); if(false == flag) {throw(std::runtime_error("Could not register type SKUKAOrientationTask"));} #ifdef DEBUG std::cout<<"\nregisterKUKAOrientationTaskType() : Registered my cool task with the database"; #endif } catch (std::exception& e) { std::cout<<"\nregisterKUKAOrientationTaskType() : Error : "<<e.what(); return false; } return true; } }
CKUKAOrientationTask.hpp
/* Copyright (C) 2011 Samir Menon, Stanford University This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * CKUKAOrientationTask.hpp * * Created on: Apr 12, 2011 * Author: Samir Menon */ #ifndef CKUKAORIENTATIONTASK_HPP_ #define CKUKAORIENTATIONTASK_HPP_ #include <scl/control/task/CTaskBase.hpp> #include "SKUKAOrientationTask.hpp" namespace scl_app { /** Function to register task with the database and enable * dynamic typing. Call this function if you want your * task to be initialized by specifying stuff in a file. */ scl::sBool registerKUKAOrientationTaskType(); /** This is my cool new task. */ class CKUKAOrientationTask : public scl::CTaskBase { public: /******************************************* * CTaskBase API : TODO : IMPLEMENT THESE!! *******************************************/ /** Computes the gc forces that resist gc velocity */ virtual bool computeServo(const scl::SRobotSensorData* arg_sensors); /** Does nothing. GC tasks don't require OSC models */ virtual bool computeModel(); /** Return this task controller's task data structure.*/ virtual scl::STaskBase* getTaskData(); /** Initializes the task object. Required to set output * gc force dofs. */ virtual bool init(scl::STaskBase* arg_task_data, scl::CDynamicsBase* arg_dynamics); /** Resets the task by removing its data. * NOTE : Does not deallocate its data structure */ virtual void reset(); public: CKUKAOrientationTask(); virtual ~CKUKAOrientationTask(); protected: //This will be filled in from the file SKUKAOrientationTask* data_; /** True when the lambda_inv matrix turns singular. */ scl::sBool lambda_inv_singular_; /** For inverting the operational space inertia matrix * near singularities. 3x3 for operational point tasks. */ Eigen::JacobiSVD<Eigen::Matrix3d > svd_; Eigen::Matrix3d singular_values_; /** For inverting the lambda matrix (when it gets singular) */ Eigen::ColPivHouseholderQR<Eigen::Matrix3d> qr_; //TODO : Add what you want. //myCoolDataType data2_; const void * link_dynamic_id_; }; } #endif /* CKUKAOrientationTASK_HPP_ */
SKUKAOrientationTask.hpp
/* Copyright (C) 2011 Samir Menon, Stanford University This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * SKUKAOrientationTask.hpp * * Created on: Sep 1, 2011 * Author: Samir Menon */ #ifndef SKUKAORIENTATIONTASK_HPP_ #define SKUKAORIENTATIONTASK_HPP_ #include <scl/control/task/data_structs/STaskBase.hpp> namespace scl_app { class SKUKAOrientationTask : public scl::STaskBase { public: // Data Eigen::MatrixXd J_omega_body_; //Rotation Jacobian Body Eigen::MatrixXd Jacobian_body_; //Basic Jacobian Body Eigen::Vector3d pos_in_parent_; //Position in the parent link's local frame (x,y,z) std::string link_name_; //The parent link const scl::SRigidBody *link_ds_; //The parent link's parsed data structure scl::sFloat spatial_resolution_; //Meters const void *link_id_body_; //Link ID of body Eigen::Affine3d Trans_matrix_; //Transformation matrix from ground to the body link Eigen::Matrix3d Lambda_omega_inv_body_; Eigen::Matrix3d Lambda_omega_body_; SKUKAOrientationTask(); virtual ~SKUKAOrientationTask(); /** Processes the task's non standard parameters, which the * init() function stores in the task_nonstd_params_. * * NOTE : This is a sample task and hence doesn't have any non * standard params. You may extend it to add yours if you like. */ virtual bool initTaskParams(); }; } /* namespace scl */ #endif /* SKUKAOrientationTASK_HPP_ */
SKUKAOrientationTask.cpp
/* This file is part of scl, a control and simulation library for robots and biomechanical models. scl is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Alternatively, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. scl is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License and a copy of the GNU General Public License along with scl. If not, see <http://www.gnu.org/licenses/>. */ /* \file SPostureBodyOriTask.cpp * * Created on: Jan 1, 2011 * * Copyright (C) 2011 * * Author: Samir Menon <smenon@stanford.edu> */ // file copied from old rotation controller #include "SKUKAOrientationTask.hpp" #include <stdexcept> #include <iostream> namespace scl_app { //0.5cm spatial resolution #define SCL_COPPTTASK_SPATIAL_RESOLUTION 0.005 #define SCL_COPPTTASK_TASK_DOF 3 SKUKAOrientationTask::SKUKAOrientationTask() : STaskBase(), link_name_(""), link_ds_(S_NULL), spatial_resolution_(SCL_COPPTTASK_SPATIAL_RESOLUTION), link_id_body_(S_NULL) { } SKUKAOrientationTask::~SKUKAOrientationTask() { } bool SKUKAOrientationTask::initTaskParams() { try { /** Extract the extra params */ std::string parent_link_name; Eigen::Vector3d pos_in_parent; bool contains_plink = false, contains_posinp = false; std::vector<scl::sString2>::const_iterator it,ite; for(it = task_nonstd_params_.begin(), ite = task_nonstd_params_.end(); it!=ite;++it) { const scl::sString2& param = *it; if(param.data_[0] == std::string("parent_link")) { parent_link_name = param.data_[1]; contains_plink = true; } else if(param.data_[0] == std::string("pos_in_parent")) { std::stringstream ss(param.data_[1]); ss>> pos_in_parent[0]; ss>> pos_in_parent[1]; ss>> pos_in_parent[2]; contains_posinp = true; } } //Error checks /* Gerald: don't need parent link and pos in parent at this point for this task if(false == contains_plink) { throw(std::runtime_error("Task's nonstandard params do not contain a parent link name.")); } if(false == contains_posinp) { throw(std::runtime_error("Task's nonstandard params do not contain a pos in parent.")); } if(0>=parent_link_name.size()) { throw(std::runtime_error("Parent link's name is too short.")); } link_name_ = parent_link_name; link_ds_ = dynamic_cast<const scl::SRobotLink *>(robot_->robot_br_rep_.at_const(link_name_)); if(S_NULL == link_ds_) { throw(std::runtime_error("Could not find the parent link in the parsed robot data structure")); } //Initalize the task data structure. pos_in_parent_ = pos_in_parent; */ //Set variables to zero J_omega_body_.setZero(3,robot_->dof_); Jacobian_body_.setZero(6,robot_->dof_); Trans_matrix_.setIdentity(); Lambda_omega_inv_body_.setZero(3,3); Lambda_omega_body_.setZero(3,3); } catch(std::exception& e) { std::cerr<<"\nSKUKAOrientationTask::init() : "<<e.what(); return false; } return true; } }
-
reporter Finally here's the complete output of the program from one of the times, when it fails. Interesting to notice, that all the variables hold valid numbers for a couple of iterations before NaNs start to appear.
Shared Memory already created! Initialized clock and database. Start Time:0.000118 Running scl task controller for input file: kuka_specs/KukaCfg.xml scl_registry::parseRobot() : Parsed : KukaBot scl_registry::parseGraphics() : Parsed : KukaBotStdView CChaiHaptics::connectToDevices() : Searched and connected to [1] devices Registered haptic device: 0 Connected to haptic device: 0 Possible example task options: -p (start paused) -l (log file), -com (com control task), -op (op point task) Possible example task options: -p (start paused) -l (log file), -com (com control task), -op (op point task) Starting simulation. Integration timestep: 0.0005 *** Robot printables *** scl>> print <printable name> Printable information Printable name Printable: Robot: KukaBotParsed Printable: KukaBot SRigidBody end-effector Printable: KukaBot SRigidBody link_6 Printable: KukaBot SRigidBody link_5 Printable: KukaBot SRigidBody link_4 Printable: KukaBot SRigidBody link_3 Printable: KukaBot SRigidBody link_2 Printable: KukaBot SRigidBody link_1 Printable: KukaBot SRigidBody base Printable: Robot: KukaBot Printable: Task Controller: opc Printable: Task: NullSpaceDampingTask Printable: Task: GcTask Printable: Task: EndEffectorOrientation Printable: Task: EndEffectorPosition Printable: UI-point ui0 Printable: UI-point ui1 Printable: UI-point ui2 Printable: UI-point ui3 Printable: UI-point ui4 Printable: UI-point ui5 Printable: UI-point ui6 Printable: UI-point ui7 Printable: UI-point ui8 Printable: UI-point ui9 Printable: UI-point ui10 Printable: UI-point ui11 BEGINNING data_->Trans_matrix_: 0.927185 0 0.374602 -0.0842674 0 1 0 -0.1 -0.374602 0 0.927185 0.240684 0 0 0 1 data_->pos_in_parent_: 0 1 0 data_->link_id_body_: 0x9129228 pos: -0.0842674 0.9 0.240684 data_->J_omega_body_: 0 0 -0.374605 0 0.374603 0 0.374602 0 -0.999998 0 0.999993 0 -0.999989 0 1 2.20953e-06 0.927184 6.62857e-06 0.927184 1.10476e-05 0.927185 gcm->Ainv_: 9.51511 0.000411905 0.0618413 0.114256 -8.7195 0.42097 -0.147174 0.000411905 1.18352 -0.0389225 2.23625 -0.0792861 1.53848 0.106893 0.0618413 -0.0389225 3.43027 -0.0412501 -2.47944 0.0690744 -0.0454203 0.114256 2.23625 -0.0412501 6.70086 2.93338 12.0791 -3.00967 -8.7195 -0.0792861 -2.47944 2.93338 121.871 11.3207 -112.002 0.42097 1.53848 0.0690744 12.0791 11.3207 124.46 -11.7609 -0.147174 0.106893 -0.0454203 -3.00967 -112.002 -11.7609 3377.48 data_->J_omega_body_.transpose(): 0 0 1 0 -0.999998 2.20953e-06 -0.374605 0 0.927184 0 0.999993 6.62857e-06 0.374603 0 0.927184 0 -0.999989 1.10476e-05 0.374602 0 0.927185 scl>> BEGINNING data_->Trans_matrix_: 1 0 0 -0.1 0 1 0 -0.1 0 0 1 0.301995 0 0 0 1 data_->pos_in_parent_: 0 1 0 data_->link_id_body_: 0x9129228 pos: -0.1 0.9 0.301995 data_->J_omega_body_: 0 0 0 0 0 0 0 0 -0.999998 0 0.999993 0 -0.999989 0 1 2.20953e-06 1 6.62857e-06 1 1.10476e-05 1 gcm->Ainv_: 58.9163 0.620199 -58.8712 1.38802 -0.121656 1.12397 0.0767997 0.620199 2.04174 -0.47263 4.56919 -0.400394 3.69979 0.252805 -58.8712 -0.47263 117.794 -0.303378 -57.8852 1.93882 -1.03738 1.38802 4.56919 -0.303378 12.7088 1.5716 17.4816 -2.65629 -0.121656 -0.400394 -57.8852 1.5716 169.253 8.41725 -111.246 1.12397 3.69979 1.93882 17.4816 8.41725 129.275 -11.4803 0.0767997 0.252805 -1.03738 -2.65629 -111.246 -11.4803 3377.51 data_->J_omega_body_.transpose(): 0 0 1 0 -0.999998 2.20953e-06 0 0 1 0 0.999993 6.62857e-06 0 0 1 0 -0.999989 1.10476e-05 0 0 1 CKUKAOrientationTask::computeModel() : Warning. Lambda_omega_inv_body_ is rank deficient. Using svd. Rank = 2 0 0 0 0 107.321 -0.00111085 0 -0.00111085 3265.31 Computing the SVD: 0 1.49167e-154 5.24706e-161 2.68156e+154 9.43259e+147 3.31799e+141 9.4326e+147 -2.68156e+154 -9.43259e+147 Computing the SVD: 0 1.49167e-154 5.24706e-161 2.68156e+154 9.43259e+147 3.31799e+141 9.4326e+147 -2.68156e+154 -9.43259e+147 Counter=1 BEGINNING data_->Trans_matrix_: 0.734938 0.663146 -0.141764 -0.500279 -0.0730155 -0.130438 -0.988749 0.362399 -0.674187 0.737017 -0.0474396 -0.426256 0 0 0 1 data_->pos_in_parent_: 0 1 0 data_->link_id_body_: 0x9129228 pos: 0.162867 0.23196 0.310761 data_->J_omega_body_: 0 -0.321739 -0.927006 -0.188743 -0.0566774 -0.203358 -0.141764 0 -0.946826 0.315004 0.0770934 0.994388 0.0760046 -0.988749 1 2.20953e-06 0.203531 -0.978982 0.0892419 -0.976128 -0.0474396 gcm->Ainv_: 0.930939 -0.0315523 -0.0934681 1.37798 -0.0721924 0.290922 -0.00913485 -0.0315523 1.81835 -3.13631 -0.0541479 3.12147 -0.0511279 0.675357 -0.0934681 -3.13631 7.95058 -0.140772 -5.30878 0.074136 -0.616924 1.37798 -0.0541479 -0.140772 4.49057 -3.1956 4.47282 -3.02446 -0.0721924 3.12147 -5.30878 -3.1956 113.125 -5.16548 106.712 0.290922 -0.0511279 0.074136 4.47282 -5.16548 95.2348 -4.96783 -0.00913485 0.675357 -0.616924 -3.02446 106.712 -4.96783 3369.03 data_->J_omega_body_.transpose(): 0 0 1 -0.321739 -0.946826 2.20953e-06 -0.927006 0.315004 0.203531 -0.188743 0.0770934 -0.978982 -0.0566774 0.994388 0.0892419 -0.203358 0.0760046 -0.976128 -0.141764 -0.988749 -0.0474396 BEGINNING data_->Trans_matrix_: -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan 0 0 0 1 data_->pos_in_parent_: 0 1 0 data_->link_id_body_: 0x9129228 pos: -nan -nan -nan data_->J_omega_body_: -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan gcm->Ainv_: nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan data_->J_omega_body_.transpose(): -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan -nan CKUKAOrientationTask::computeModel() : Warning. Lambda_omega_inv_body_ is rank deficient. Using svd. Rank = 0 nan nan nan nan nan nan nan nan nan Computing the SVD: nan nan nan nan nan nan nan nan nan NOT A NUMBER!!!
-
I guess the problem is within dynamics_->calculateTransformationMatrix. Can you try to dig deeper into that function and see (cout or debug) what's going on in there when the error occurs?
Have you experienced Eigen reporting rank deficiencies when the Lamba matrix actually is invertible?
-
reporter Here's the implementation of calculateTransformationMatrix:
sBool CTaoDynamics::calculateTransformationMatrix( const void* arg_link_id, Eigen::Affine3d& arg_T) { const taoDNode * link_addr; link_addr = static_cast<const taoDNode *>(arg_link_id); bool flag = model_->getGlobalFrame(link_addr,arg_T); return flag; }
then one level deeper there's the implementation of getGlobalFrame:
bool Model::getGlobalFrame(taoDNode const * node, Eigen::Affine3d & global_transform) const { if ( ! node) { return false; } deFrame const * tao_frame(node->frameGlobal()); deQuaternion const & tao_quat(tao_frame->rotation()); deVector3 const & tao_trans(tao_frame->translation()); #ifndef WIN32 #warning "TO DO: maybe the other way around..." #endif // beware: Eigen::Quaternion(w, x, y, z) asks for w first, (but stores it as xyzw) // whereas // deQuaternion(qx, qy, qz, qw) asks for w last and stores it as xyzw. //DE_MATH_API void deSetQ4S4(deFloat* res, const deFloat x, const deFloat y, const deFloat z, const deFloat w) //{ res[0] = x; res[1] = y; res[2] = z; res[3] = w; } global_transform = Eigen::Translation3d(tao_trans[0], tao_trans[1], tao_trans[2]); global_transform *= Eigen::Quaternion<double>(tao_quat[3], tao_quat[0], tao_quat[1], tao_quat[2]); return true; }
Since I still don't have a debugger under 32 bit ;-) I'll just cout the hell out of stuff :-)
-
reporter OK, I COUTed lots of stuff and tracked the first appearance of a NaN to tao_trans in Model::getGlobalFrame
That one in turn is defined in TaoDeFrame.h:
#ifndef _deFrame_h #define _deFrame_h /*! * \brief Transformation class using quaternion * \ingroup deMath * * This class consists of a quaternion for rotation and a vector for translation. * \sa deVector3, deQuaternion, deTransform */ class deFrame { public: /** Default ctor inits to identity. */ inline deFrame() { identity(); } /** Construction from three floats ends up as pure translation. */ inline deFrame(deFloat tx, deFloat ty, deFloat tz) { set(deQuaternion(), deVector3(tx, ty, tz)); } inline deFrame(deFrame const & orig): _q(orig._q), _v(orig._v) {} //! \return rotation part deQuaternion& rotation() { return _q; } //! \return rotation part const deQuaternion& rotation() const { return _q; } //! \return translation part deVector3& translation() { return _v;; } //! \return translation part const deVector3& translation() const { return _v; } //! this = identity matrix DE_MATH_API void identity(); //! this = f DE_MATH_API void operator=(const deFrame & f); /** Combine two frame transformations: \c f2 gets rotated and translated by \c f1. For example, if you know the COM frame of a link relative to its local frame, and you want to know the global COM position given the global position of the local frame, you do: \code deFrame com_wrt_link, link_wrt_global; deFrame com; com.multiply(link_wrt_global, com_wrt_link); \endcode \note Do NOT invoke this method on one of the parameters, because the operation is not atomic. If you want to replace a frame by the result of this multiplication, use a temporary object. \code // old pseudo-code documentation for this method: this = f1 * f2 = [r1,p1][r2,p2] = [r1*r2, r1*p2 + p1] \endcode \code // BAD example: this will produce garbage deFrame foo, bar; foo.multiply(foo, bar); // foo gets partially modified half way through the operation \endcode \code // GOOD example: use a temporary object if you want to replace a frame deFrame foo, bar; foo.multiply(deFrame(foo), bar); \endcode */ DE_MATH_API void multiply(const deFrame& f1, const deFrame& f2); //! this = f1^-1 * f2 // = ~[r1,p1][r2,p2] = [~r1, -(~r1*p1)][r2,p2] = [~r1*r2, ~r1*p2 - (~r1*p1)] // = [~r1*r2, ~r1*(p2-p1)] DE_MATH_API void inversedMultiply(const deFrame& f1, const deFrame& f2); //! this = f1 * f2^-1 // = [r1,p1]~[r2,p2] = [r1,p1][~r2, -(~r2*p2)] = [r1*~r2, -r1*(~r2*p2) + p1] // = [r1*~r2, -r1*~r2*p2 + p1] DE_MATH_API void multiplyInversed(const deFrame& f1, const deFrame& f2); //! this = f^-1 // = ~[r,p] = [~r, -(~r*p)] DE_MATH_API void inverse(const deFrame& f); //! this = t DE_MATH_API void set(const deTransform& t); //! this = [q, v] DE_MATH_API void set(const deQuaternion& q, const deVector3& v); private: deQuaternion _q; deVector3 _v; }; #endif // _deFrame_h
Any idea where to go on from here?
-
reporter In getGlobalFrame this line is used:
deFrame const * tao_frame(node->frameGlobal());
node is of type taoDNode and in taoDNode.h the method frameGlobal() is commented out: //virtual deFrame* frameGlobal() = 0;
Suspicious?
-
reporter I commented it in and then the compiler complains:
/home/fabian/KUKA_Teleoperation/01_Drivers/scl-manips-group.git/src/scl/dynamics/tao/jspace/Model.cpp: In member function ‘bool jspace::Model::getGlobalFrame(const taoDNode*, Eigen::Affine3d&) const’: /home/fabian/KUKA_Teleoperation/01_Drivers/scl-manips-group.git/src/scl/dynamics/tao/jspace/Model.cpp:284:49: error: passing ‘const taoDNode’ as ‘this’ argument of ‘virtual deFrame* taoDNode::frameGlobal()’ discards qualifiers [-fpermissive] /home/fabian/KUKA_Teleoperation/01_Drivers/scl-manips-group.git/src/scl/dynamics/tao/jspace/Model.cpp: In member function ‘bool jspace::Model::computeJacobian(const taoDNode*, Eigen::MatrixXd&) const’: /home/fabian/KUKA_Teleoperation/01_Drivers/scl-manips-group.git/src/scl/dynamics/tao/jspace/Model.cpp:380:46: error: passing ‘const taoDNode’ as ‘this’ argument of ‘virtual deFrame* taoDNode::frameGlobal()’ discards qualifiers [-fpermissive]
Any smart ideas?
-
repo owner Confirmed that this is a problem with Eigen on 32 bit machines..
-
repo owner This bug's resolution is to stop using tao for Jacobians. Transition to scl dynamics.
-
repo owner - changed status to duplicate
Duplicate of
#110. - Log in to comment
Where is the entire code? Did you set all your variables to zero in the init() function? If not, some of them might have garbage values.
Also, call computeModel() a once in the init function to set up tao's state.