protot/3rdparty/rbdl/addons/muscle/Millard2016TorqueMuscle.cc

1446 lines
52 KiB
C++

//==============================================================================
/*
* RBDL - Rigid Body Dynamics Library: Addon : muscle
* Copyright (c) 2016 Matthew Millard <matthew.millard@iwr.uni-heidelberg.de>
*
* Licensed under the zlib license. See LICENSE for more details.
*/
#include "Millard2016TorqueMuscle.h"
#include "TorqueMuscleFunctionFactory.h"
#include "csvtools.h"
#include <limits>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <ostream>
static double EPSILON = std::numeric_limits<double>::epsilon();
static double SQRTEPSILON = sqrt(EPSILON);
using namespace RigidBodyDynamics::Math;
using namespace RigidBodyDynamics::Addons::Muscle;
using namespace RigidBodyDynamics::Addons::Geometry;
using namespace std;
/*************************************************************
Table Access Structure Names
*************************************************************/
const double gravity = 9.81; //Needed for the strength scaling used
//by Anderson et al.
const char* DataSet::names[] = { "Anderson2007",
"Gymnast"};
const char* GenderSet::names[] = {"Male",
"Female"};
const char* AgeGroupSet::names[] = {"Young18To25",
"Middle55To65",
"SeniorOver65"};
const char* JointTorqueSet::names[] = { "HipExtension" ,
"HipFlexion" ,
"KneeExtension" ,
"KneeFlexion" ,
"AnkleExtension" ,
"AnkleFlexion" ,
"ElbowExtension" ,
"ElbowFlexion" ,
"ShoulderExtension" ,
"ShoulderFlexion" ,
"WristExtension" ,
"WristFlexion" ,
"ShoulderHorizontalAdduction",
"ShoulderHorizontalAbduction",
"ShoulderInternalRotation" ,
"ShoulderExternalRotation" ,
"WristUlnarDeviation" ,
"WristRadialDeviation" ,
"WristPronation" ,
"WristSupination" ,
"LumbarExtension" ,
"LumbarFlexion" };
const char* Anderson2007::GenderNames[] = {"Male","Female"};
const char* Anderson2007::AgeGroupNames[] = { "Young18To25",
"Middle55To65",
"SeniorOver65"};
const char* Anderson2007::JointTorqueNames[] = {"HipExtension" ,
"HipFlexion" ,
"KneeExtension" ,
"KneeFlexion" ,
"AnkleExtension",
"AnkleFlexion" };
const char* Gymnast::GenderNames[] = {"Male"};
const char* Gymnast::AgeGroupNames[] = {"Young18To25"};
const char* Gymnast::JointTorqueNames[] =
{ "HipExtension" ,
"HipFlexion" ,
"KneeExtension" ,
"KneeFlexion" ,
"AnkleExtension" ,
"AnkleFlexion" ,
"ElbowExtension" ,
"ElbowFlexion" ,
"ShoulderExtension" ,
"ShoulderFlexion" ,
"WristExtension" ,
"WristFlexion" ,
"ShoulderHorizontalAdduction",
"ShoulderHorizontalAbduction",
"ShoulderInternalRotation" ,
"ShoulderExternalRotation" ,
"WristUlnarDeviation" ,
"WristRadialDeviation" ,
"WristPronation" ,
"WristSupination" ,
"LumbarExtension" ,
"LumbarFlexion"};
/*************************************************************
Coefficient Tables
*************************************************************/
/*
This data is taken from Table 3 of
Anderson, D. E., Madigan, M. L., & Nussbaum, M. A. (2007).
Maximum voluntary joint torque as a function of joint angle
and angular velocity: model development and application to
the lower limb. Journal of biomechanics, 40(14), 3105-3113.
Each row contains the coefficients for the active and
passive torque characteristics for a specific joint,
direction, gender and age group. Each row corresponds
to a single block taken from Table 3, as read from
left to right top to bottom. The first 4 columns have
been added to describe the joint, direction, gender
and age group.
Column labels:
Parameter Set Meta Data
0: joint: hip0_knee1_ankle2,
1: direction: ext0_flex1,
2: gender: male0_female1,
3: age: age18to25_0_55to65_1_g65_2,
Active Torque-Angle and Torque-Velocity Curves
4: c1,
5: c2,
6: c3,
7: c4,
8: c5,
9: c6,
Passive Torque-Angle Curves
10: b1,
11: k1,
12: b2,
13: k2,
*/
double const Millard2016TorqueMuscle::Anderson2007Table3Mean[36][14] = {
{0,0,0,0,0.161,0.958,0.932,1.578,3.19,0.242,-1.21,-6.351,0.476,5.91 },
{0,0,1,0,0.181,0.697,1.242,1.567,3.164,0.164,-1.753,-6.358,0.239,3.872 },
{0,0,0,1,0.171,0.922,1.176,1.601,3.236,0.32,-2.16,-8.073,0.108,4.593 },
{0,0,1,1,0.14,0.83,1.241,1.444,2.919,0.317,-1.361,-7.128,0.013,6.479 },
{0,0,0,2,0.144,0.896,1.125,1.561,3.152,0.477,-2.671,-7.85,0.092,5.192 },
{0,0,1,2,0.138,0.707,1.542,1.613,3.256,0.36,-0.758,-7.545,0.018,6.061 },
{0,1,0,0,0.113,0.738,-0.214,2.095,4.267,0.218,1.21,-6.351,-0.476,5.91 },
{0,1,1,0,0.127,0.65,-0.35,2.136,4.349,0.156,1.753,-6.358,-0.239,3.872 },
{0,1,0,1,0.107,0.712,-0.192,2.038,4.145,0.206,2.16,-8.073,-0.108,4.593 },
{0,1,1,1,0.091,0.812,-0.196,2.145,4.366,0.186,1.361,-7.128,-0.013,6.479 },
{0,1,0,2,0.101,0.762,-0.269,1.875,3.819,0.296,2.671,-7.85,-0.092,5.192 },
{0,1,1,2,0.081,0.625,-0.422,2.084,4.245,0.196,0.758,-7.545,-0.018,6.061 },
{1,0,0,0,0.163,1.258,1.133,1.517,3.952,0.095,0,0,-6.25,-4.521 },
{1,0,1,0,0.159,1.187,1.274,1.393,3.623,0.173,0,0,-8.033,-5.25 },
{1,0,0,1,0.156,1.225,1.173,1.518,3.954,0.266,0,0,-12.83,-5.127 },
{1,0,1,1,0.128,1.286,1.141,1.332,3.469,0.233,0,0,-6.576,-4.466 },
{1,0,0,2,0.137,1.31,1.067,1.141,3.152,0.386,0,0,-10.519,-5.662 },
{1,0,1,2,0.124,1.347,1.14,1.066,2.855,0.464,0,0,-8.8,-6.763 },
{1,1,0,0,0.087,0.869,0.522,2.008,5.233,0.304,0,0,6.25,-4.521 },
{1,1,1,0,0.08,0.873,0.635,1.698,4.412,0.175,0,0,8.033,-5.25 },
{1,1,0,1,0.081,0.986,0.523,1.83,4.777,0.23,0,0,12.83,-5.127 },
{1,1,1,1,0.06,0.967,0.402,1.693,4.41,0.349,0,0,6.576,-4.466 },
{1,1,0,2,0.069,0.838,0.437,1.718,4.476,0.414,0,0,10.519,-5.662 },
{1,1,1,2,0.06,0.897,0.445,1.121,2.922,0.389,0,0,8.8,-6.763 },
{2,0,0,0,0.095,1.391,0.408,0.987,3.558,0.295,-0.0005781,-5.819,0.967,6.09 },
{2,0,1,0,0.104,1.399,0.424,0.862,3.109,0.189,-0.005218,-4.875,0.47,6.425 },
{2,0,0,1,0.114,1.444,0.551,0.593,2.128,0.35,-0.001311,-10.943,0.377,8.916 },
{2,0,1,1,0.093,1.504,0.381,0.86,3.126,0.349,-2.888e-05,-17.189,0.523,7.888 },
{2,0,0,2,0.106,1.465,0.498,0.49,1.767,0.571,-5.693e-05,-21.088,0.488,7.309 },
{2,0,1,2,0.125,1.299,0.58,0.587,1.819,0.348,-2.35e-05,-12.567,0.331,6.629 },
{2,1,0,0,0.033,1.51,-0.187,0.699,1.94,0.828,0.0005781,-5.819,-0.967,6.09 },
{2,1,1,0,0.027,1.079,-0.302,0.864,2.399,0.771,0.005218,-4.875,-0.47,6.425 },
{2,1,0,1,0.028,1.293,-0.284,0.634,1.759,0.999,0.001311,-10.943,-0.377,8.916 },
{2,1,1,1,0.024,1.308,-0.254,0.596,1.654,1.006,2.888e-05,-17.189,-0.523,7.888},
{2,1,0,2,0.029,1.419,-0.174,0.561,1.558,1.198,5.693e-05,-21.088,-0.488,7.309},
{2,1,1,2,0.022,1.096,-0.369,0.458,1.242,1.401,2.35e-05,-12.567,-0.331,6.629 }};
//See the description for the mean data. This table constains the
//parameter standard deviations
double const Millard2016TorqueMuscle::Anderson2007Table3Std[36][14] = {
{0,0,0,0,0.049,0.201,0.358,0.286,0.586,0.272,0.66,0.97,0.547,4.955 },
{0,0,1,0,0.047,0.13,0.418,0.268,0.542,0.175,1.93,2.828,0.292,1.895 },
{0,0,0,1,0.043,0.155,0.195,0.306,0.622,0.189,1.297,2.701,0.091,0.854 },
{0,0,1,1,0.032,0.246,0.365,0.223,0.45,0.14,1.294,2.541,0.02,2.924 },
{0,0,0,2,0.039,0.124,0.077,0.184,0.372,0.368,0.271,3.402,0.111,1.691 },
{0,0,1,2,0.003,0.173,0.279,0.135,0.273,0.237,0.613,0.741,0.031,2.265 },
{0,1,0,0,0.025,0.217,0.245,0.489,0.995,0.225,0.66,0.97,0.547,4.955 },
{0,1,1,0,0.033,0.178,0.232,0.345,0.702,0.179,1.93,2.828,0.292,1.895 },
{0,1,0,1,0.02,0.248,0.274,0.318,0.652,0.088,1.297,2.701,0.091,0.854 },
{0,1,1,1,0.016,0.244,0.209,0.375,0.765,0.262,1.294,2.541,0.02,2.924 },
{0,1,0,2,0.025,0.151,0.234,0.164,0.335,0.102,0.271,3.402,0.111,1.691 },
{0,1,1,2,0.008,0.062,0.214,0.321,0.654,0.28,0.613,0.741,0.031,2.265 },
{1,0,0,0,0.04,0.073,0.073,0.593,1.546,0.171,0,0,2.617,0.553 },
{1,0,1,0,0.028,0.084,0.181,0.38,0.989,0.27,0,0,3.696,1.512 },
{1,0,0,1,0.031,0.063,0.048,0.363,0.947,0.06,0,0,2.541,2.148 },
{1,0,1,1,0.016,0.094,0.077,0.319,0.832,0.133,0,0,1.958,1.63 },
{1,0,0,2,0.017,0.127,0.024,0.046,0.04,0.124,0,0,1.896,1.517 },
{1,0,1,2,0.018,0.044,0.124,0.128,0.221,0.129,0,0,6.141,0.742 },
{1,1,0,0,0.015,0.163,0.317,1.364,3.554,0.598,0,0,2.617,0.553 },
{1,1,1,0,0.015,0.191,0.287,0.825,2.139,0.319,0,0,3.696,1.512 },
{1,1,0,1,0.017,0.138,0.212,0.795,2.067,0.094,0,0,2.541,2.148 },
{1,1,1,1,0.015,0.21,0.273,0.718,1.871,0.143,0,0,1.958,1.63 },
{1,1,0,2,0.022,0.084,0.357,0.716,1.866,0.201,0,0,1.896,1.517 },
{1,1,1,2,0.005,0.145,0.21,0.052,0.135,0.078,0,0,6.141,0.742 },
{2,0,0,0,0.022,0.089,0.083,0.595,2.144,0.214,0.001193,7.384,0.323,1.196 },
{2,0,1,0,0.034,0.19,0.186,0.487,1.76,0.213,0.01135,6.77,0.328,1.177 },
{2,0,0,1,0.029,0.136,0.103,0.165,0.578,0.133,0.003331,10.291,0.403,3.119 },
{2,0,1,1,0.026,0.235,0.143,0.448,1.613,0.27,3.562e-05,7.848,0.394,1.141 },
{2,0,0,2,0.035,0.136,0.132,0.262,0.944,0.313,3.164e-05,1.786,0.258,0.902 },
{2,0,1,2,0.006,0.095,0.115,0.258,0.423,0.158,2.535e-05,10.885,0.247,2.186},
{2,1,0,0,0.005,0.19,0.067,0.108,0.301,0.134,0.001193,7.384,0.323,1.196 },
{2,1,1,0,0.006,0.271,0.171,0.446,1.236,0.206,0.01135,6.77,0.328,1.177 },
{2,1,0,1,0.005,0.479,0.178,0.216,0.601,0.214,0.003331,10.291,0.403,3.119 },
{2,1,1,1,0.002,0.339,0.133,0.148,0.41,0.284,3.562e-05,7.848,0.394,1.141 },
{2,1,0,2,0.002,0.195,0.056,0.188,0.521,0.29,3.164e-05,1.786,0.258,0.902 },
{2,1,1,2,0.003,0.297,0.109,0.089,0.213,0.427,2.535e-05,10.885,0.247,2.186}};
/*
This table contains parameters for the newly made torque muscle curves:
1. maxIsometricTorque Nm
2. maxAngularVelocity rad/s
3. angleAtOneActiveNormTorque rad
4. angularWidthActiveTorque rad
5. tvAtMaxEccentricVelocity Nm/Nm
6. tvAtHalfMaxConcentricVelocity Nm/Nm
7. angleAtZeroPassiveTorque rad
8. angleAtOneNormPassiveTorque rad
*/
double const Millard2016TorqueMuscle::GymnastWholeBody[22][12] =
{ {0,0,0,0,175.746, 9.02335, 1.06465, 1.05941, 1.1, 0.163849, 0.79158, 1.5708 },
{0,1,0,0,157.293, 9.18043, 0.733038, 1.21999, 1.11905, 0.25, -0.0888019,-0.515207 },
{1,0,0,0,285.619, 19.2161, 0.942478, 0.509636, 1.13292, 0.115304, 2.00713, 2.70526 },
{1,1,0,0,98.7579, 16.633, 1.02974, 1.11003, 1.12, 0.19746, 0, -0.174533 },
{2,0,0,0,127.561, 11.7646, 0.408, 0.660752, 1.159, 0.410591, 0.0292126, 0.785398 },
{2,1,0,0,44.3106, 17.2746, -0.187, 0.60868, 1.2656, 0.112303, -0.523599, -1.0472 },
{3,0,0,0,127.401, 16.249, 2.14675, 1.83085, 1.1, 0.250134, 2.8291, 3.55835 },
{3,1,0,0,91.1388, 19.0764, 0.890118, 1.2898, 1.23011, 0.249656, -0.523599, -1.0472 },
{5,0,0,0,15.5653, 36.5472, 1.55334, 1.38928, 1.16875, 0.115356, 1.0472, 1.5708 },
{5,1,0,0,39.2252, 36.3901, 0.663225, 1.71042, 1.14, 0.115456, -0.793739, -1.49714 },
{3,2,0,0,128.496, 18.05, 0.839204, 1.28041, 1.25856, 0.214179, 1.5708, 2.26893 },
{3,3,0,0,94.6839, 18 , -0.277611, 2.37086, 1.23042, 0.224227, -0.523599, -1.0472 },
{3,5,0,0,50.522 , 19.47, -1.18761, 2.80524, 1.27634, 0.285399, 1.39626, 1.91986 },
{3,4,0,0,43.5837, 18, -0.670796, 1.98361, 1.35664, 0.229104, -1.0472, -1.5708 },
{4,1,0,0,101.384, 18.1, 0.33, 3.62155, 1.37223, 0.189909, 0, -0.174533 },
{4,0,0,0,69.8728, 18.45, 1.64319, 1.30795, 1.31709, 0.189676, 2.0944, 2.61799 },
{5,6,0,0,13.5361, 35.45, -0.209204, 1.33735, 1.23945, 0.250544, -0.785398, -1.5708 },
{5,7,0,0,12.976 , 27.88, -0.212389, 0.991803, 1.3877, 0.207506, 0.785398, 1.5708 },
{5,9,0,0,31.4217, 18.02, 0.43, 1.47849, 1.34817, 0.196913, 0, -0.523599},
{5,8,0,0,23.8345, 21.77, -1.14319, 2.56082, 1.31466, 0.2092, 0.349066, 0.872665 },
{6,0,0,0,687.864, 1.0472, 1.5506, 1.14543, 1.1, 0.45, 0.306223, 1.35342 },
{6,1,0,0,211.65 , 0.523599, 0, 6.28319, 1.1, 0.45, 0, -0.785398 }};
/*************************************************************
Map that goes from a single joint-torque-direction index to
the pair of joint and direction indicies used in the tables
*************************************************************/
const static struct JointSet{
enum item { Hip = 0,
Knee,
Ankle,
Shoulder,
Elbow,
Wrist,
Lumbar,
Last};
JointSet(){}
} JointSet;
struct DirectionSet{
enum item {
Extension = 0,
Flexion,
HorizontalAdduction,
HorizontalAbduction,
ExternalRotation,
InternalRotation,
Supination,
Pronation,
RadialDeviation,
UlnarDeviation,
Last
};
DirectionSet(){}
} DirectionSet;
const static int JointTorqueMap[22][3] = {
{(int)JointTorqueSet::HipExtension , (int)JointSet::Hip , (int)DirectionSet::Extension },
{(int)JointTorqueSet::HipFlexion , (int)JointSet::Hip , (int)DirectionSet::Flexion },
{(int)JointTorqueSet::KneeExtension , (int)JointSet::Knee , (int)DirectionSet::Extension },
{(int)JointTorqueSet::KneeFlexion , (int)JointSet::Knee , (int)DirectionSet::Flexion },
{(int)JointTorqueSet::AnkleExtension , (int)JointSet::Ankle , (int)DirectionSet::Extension },
{(int)JointTorqueSet::AnkleFlexion , (int)JointSet::Ankle , (int)DirectionSet::Flexion },
{(int)JointTorqueSet::ElbowExtension , (int)JointSet::Elbow , (int)DirectionSet::Extension },
{(int)JointTorqueSet::ElbowFlexion , (int)JointSet::Elbow , (int)DirectionSet::Flexion },
{(int)JointTorqueSet::ShoulderExtension , (int)JointSet::Shoulder , (int)DirectionSet::Extension },
{(int)JointTorqueSet::ShoulderFlexion , (int)JointSet::Shoulder , (int)DirectionSet::Flexion },
{(int)JointTorqueSet::WristExtension , (int)JointSet::Wrist , (int)DirectionSet::Extension },
{(int)JointTorqueSet::WristFlexion , (int)JointSet::Wrist , (int)DirectionSet::Flexion },
{(int)JointTorqueSet::ShoulderHorizontalAdduction , (int)JointSet::Shoulder , (int)DirectionSet::HorizontalAdduction},
{(int)JointTorqueSet::ShoulderHorizontalAbduction , (int)JointSet::Shoulder , (int)DirectionSet::HorizontalAbduction},
{(int)JointTorqueSet::ShoulderInternalRotation , (int)JointSet::Shoulder , (int)DirectionSet::InternalRotation },
{(int)JointTorqueSet::ShoulderExternalRotation , (int)JointSet::Shoulder , (int)DirectionSet::ExternalRotation },
{(int)JointTorqueSet::WristUlnarDeviation , (int)JointSet::Wrist , (int)DirectionSet::UlnarDeviation },
{(int)JointTorqueSet::WristRadialDeviation , (int)JointSet::Wrist , (int)DirectionSet::RadialDeviation },
{(int)JointTorqueSet::WristPronation , (int)JointSet::Wrist , (int)DirectionSet::Pronation },
{(int)JointTorqueSet::WristSupination , (int)JointSet::Wrist , (int)DirectionSet::Supination },
{(int)JointTorqueSet::LumbarExtension , (int)JointSet::Lumbar , (int)DirectionSet::Extension },
{(int)JointTorqueSet::LumbarFlexion , (int)JointSet::Lumbar , (int)DirectionSet::Flexion }};
/*************************************************************
Constructors
*************************************************************/
Millard2016TorqueMuscle::
Millard2016TorqueMuscle( )
:angleOffset(1.0),
signOfJointAngle(1.0),
signOfJointTorque(1.0),
signOfConcentricAnglularVelocity(1.0),
muscleName("empty")
{
muscleCurvesAreDirty = true;
}
Millard2016TorqueMuscle::Millard2016TorqueMuscle(
DataSet::item dataSet,
const SubjectInformation &subjectInfo,
int jointTorque,
double jointAngleOffsetRelativeToDoxygenFigures,
double signOfJointAngleRelativeToDoxygenFigures,
double signOfJointTorque,
const std::string& name
):angleOffset(jointAngleOffsetRelativeToDoxygenFigures),
signOfJointAngle(signOfJointAngleRelativeToDoxygenFigures),
signOfJointTorque(signOfJointTorque),
signOfConcentricAnglularVelocity(signOfJointTorque),
muscleName(name),
dataSet(dataSet)
{
subjectHeightInMeters = subjectInfo.heightInMeters;
subjectMassInKg = subjectInfo.massInKg;
passiveCurveAngleOffset = 0.;
beta = 0.1;
int gender = (int) subjectInfo.gender;
int ageGroup = (int) subjectInfo.ageGroup;
int joint = -1;
int jointDirection = -1;
for(int i=0; i < JointTorqueSet::Last; ++i){
if(JointTorqueMap[i][0] == jointTorque){
joint = JointTorqueMap[i][1];
jointDirection = JointTorqueMap[i][2];
}
}
if(joint == -1 || jointDirection == -1){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< ": A jointTorqueDirection of " << jointTorque
<< " does not exist.";
assert(0);
abort();
}
if( abs(abs(signOfJointAngle)-1) > EPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< ": signOfJointAngleRelativeToAnderson2007 must be [-1,1] not "
<< signOfJointAngle;
assert(0);
abort();
}
if( abs(abs(signOfConcentricAnglularVelocity)-1) > EPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< ": signOfJointAngularVelocityDuringConcentricContraction "
<< "must be [-1,1] not "
<< signOfConcentricAnglularVelocity;
assert(0);
abort();
}
if( abs(abs(signOfJointTorque)-1) > EPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< ": signOfJointTorque must be [-1,1] not "
<< signOfJointTorque;
assert(0);
abort();
}
if(subjectHeightInMeters <= 0){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< ": subjectHeightInMeters > 0, but it's "
<< subjectHeightInMeters;
assert(0);
abort();
}
if(subjectMassInKg <= 0){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< ": subjectMassInKg > 0, but it's "
<< subjectMassInKg;
assert(0);
abort();
}
int idx = -1;
int jointIdx = 0;
int dirIdx = 1;
int genderIdx = 2;
int ageIdx = 3;
switch(dataSet){
case DataSet::Anderson2007:
{
c1c2c3c4c5c6Anderson2007.resize(6);
b1k1b2k2Anderson2007.resize(4);
for(int i=0; i<36;++i){
if( abs(Anderson2007Table3Mean[i][jointIdx]
-(double)joint) < EPSILON
&& abs(Anderson2007Table3Mean[i][dirIdx]
-(double)jointDirection) < EPSILON
&& abs(Anderson2007Table3Mean[i][genderIdx]
-(double)gender) < EPSILON
&& abs(Anderson2007Table3Mean[i][ageIdx]
-(double)ageGroup) < EPSILON){
idx = i;
}
}
if(idx != -1){
for(int i=0; i<6; ++i){
c1c2c3c4c5c6Anderson2007[i] = Anderson2007Table3Mean[idx][i+4];
}
for(int i=0; i<4; ++i){
b1k1b2k2Anderson2007[i] = Anderson2007Table3Mean[idx][i+10];
}
}
} break;
case DataSet::Gymnast:
{
gymnastParams.resize(8);
for(int i=0; i<JointTorqueSet::Last;++i){
if( abs(GymnastWholeBody[i][jointIdx]
-(double)joint) < EPSILON
&& abs(GymnastWholeBody[i][dirIdx]
-(double)jointDirection) < EPSILON
&& abs(GymnastWholeBody[i][genderIdx]
-(double)gender) < EPSILON
&& abs(GymnastWholeBody[i][ageIdx]
-(double)ageGroup) < EPSILON){
idx = i;
}
}
if(idx != -1){
for(int i=0; i<8; ++i){
gymnastParams[i] = GymnastWholeBody[idx][i+4];
}
}
} break;
default:
{
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< "The requested DataSet does not exist.";
assert(0);
abort();
}
};
if(idx == -1){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< "The combination of data set (" << dataSet << ")"
<< " joint (" << joint << ")"
<< " joint direction (" << jointDirection << ")"
<< " gender, (" << gender << ")"
<< "and age " << ageGroup << ")"
<< "could not be found";
assert(0);
abort();
}
muscleCurvesAreDirty = true;
passiveTorqueScale = 1.0;
updateTorqueMuscleCurves();
}
/*************************************************************
Muscle Model Code
*************************************************************/
double Millard2016TorqueMuscle::
calcJointTorque( double jointAngle,
double jointAngularVelocity,
double activation) const
{
if(muscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
double fiberAngle = calcFiberAngle(jointAngle);
double fiberVelocity = calcFiberAngularVelocity(jointAngularVelocity);
double ta = taCurve.calcValue(fiberAngle);
double tp = tpCurve.calcValue(fiberAngle);
double fiberVelocityNorm = fiberVelocity/omegaMax;
double tv = tvCurve.calcValue(fiberVelocityNorm);
double jointTorque = maxActiveIsometricTorque*(
activation*ta*tv
+ passiveTorqueScale*tp
- beta*fiberVelocityNorm);
if(jointTorque < 0){
jointTorque = 0;
}
return jointTorque*signOfJointTorque;
}
void Millard2016TorqueMuscle::
calcTorqueMuscleInfo(double jointAngle,
double jointAngularVelocity,
double activation,
TorqueMuscleInfo& tmi) const
{
if(muscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
double fiberAngle = calcFiberAngle(jointAngle);
double fiberAngularVelocity = calcFiberAngularVelocity(jointAngularVelocity);
double ta = taCurve.calcValue(fiberAngle);
double tp = passiveTorqueScale*tpCurve.calcValue(fiberAngle);
double omegaNorm = fiberAngularVelocity/omegaMax;
double D_wn_w = 1.0/omegaMax;
double tv = tvCurve.calcValue(omegaNorm);
double betaUpd = beta;
double tb = -betaUpd*omegaNorm;
//The jointTorque is not allowed to go below 0, as this corresponds
//to the muscle pushing. If the joint torque is going negative, we update
//beta so that it will perfectly zero the joint torque.
if(activation*ta*tv + passiveTorqueScale*tp + tb < 0){
betaUpd = (activation*ta*tv + passiveTorqueScale*tp)/omegaNorm;
tb = -betaUpd*omegaNorm;
}
double D_ta_DfiberAngle = taCurve.calcDerivative(fiberAngle,1);
double D_tp_DfiberAngle = passiveTorqueScale*tpCurve.calcDerivative(fiberAngle,1);
double D_tv_DfiberAngularVelocity
= tvCurve.calcDerivative(omegaNorm,1)*D_wn_w;
double D_tb_DfiberAngularVelocity = -betaUpd*D_wn_w;
double D_fiberAngle_D_jointAngle = signOfJointAngle;
double D_tv_DfiberAngularVelocity_D_jointAngularVelocity =
signOfConcentricAnglularVelocity;
tmi.jointAngle = jointAngle;
tmi.jointAngularVelocity = jointAngularVelocity;
tmi.fiberAngle = fiberAngle;
//tmi.tendonAngle = 0.;
tmi.fiberAngularVelocity = fiberAngularVelocity;
//tmi.tendonAngularVelocity = 0.;
tmi.fiberPassiveTorqueAngleMultiplier = tp;
tmi.fiberActiveTorqueAngleMultiplier = ta;
tmi.fiberActiveTorqueAngularVelocityMultiplier = tv;
//tmi.tendonTorqueAngleMultiplier = activation*ta*tv + tp;
tmi.activation = activation;
tmi.fiberActiveTorque = maxActiveIsometricTorque*(activation*ta*tv);
tmi.fiberPassiveTorque = maxActiveIsometricTorque*(tb+tp);
tmi.fiberDampingTorque = maxActiveIsometricTorque*(tb);
tmi.fiberNormDampingTorque = tb;
tmi.fiberTorque = tmi.fiberActiveTorque
+ tmi.fiberPassiveTorque;
//tmi.tendonTorque = tmi.fiberActiveTorque
// + tmi.fiberPassiveTorque;
tmi.jointTorque = signOfJointTorque*tmi.fiberTorque;
tmi.fiberStiffness = maxActiveIsometricTorque*(
activation*D_ta_DfiberAngle*tv
+ D_tp_DfiberAngle);
//tmi.tendonStiffness = std::numeric_limits<double>::infinity();
tmi.jointStiffness = signOfJointTorque
*tmi.fiberStiffness
*D_fiberAngle_D_jointAngle;
tmi.fiberActivePower = tmi.fiberActiveTorque
* tmi.fiberAngularVelocity;
tmi.fiberPassivePower = tmi.fiberPassiveTorque
* tmi.fiberAngularVelocity;
tmi.fiberPower = tmi.fiberActivePower
+ tmi.fiberPassivePower;
//tmi.tendonPower = 0.;
tmi.jointPower = tmi.jointTorque * jointAngularVelocity;
//tau = signTq*tauIso*(a * ta(theta) * tv(thetaDot) + tp(theta) )
// Dtau_Da = tauMaxIso*(ta(theta) * tv(thetaDot) )
tmi.DjointTorqueDactivation =
signOfJointTorque
*maxActiveIsometricTorque
*(ta * tv);
//Dtau_Domega = signTq*tauIso*(a * ta(theta) * Dtv_DthetaDot(thetaDot)* )
tmi.DjointTorqueDjointAngularVelocity =
signOfJointTorque
* maxActiveIsometricTorque
* ( activation
* ta
* D_tv_DfiberAngularVelocity
* D_tv_DfiberAngularVelocity_D_jointAngularVelocity
+ D_tb_DfiberAngularVelocity);
tmi.DjointTorqueDjointAngle =
signOfJointTorque
* maxActiveIsometricTorque
* ( activation
*D_ta_DfiberAngle
* tv
+ D_tp_DfiberAngle
)* D_fiberAngle_D_jointAngle;
}
/*************************************************************
Get / Set Functions
*************************************************************/
double Millard2016TorqueMuscle::
getJointTorqueSign() const
{
return signOfJointTorque;
}
double Millard2016TorqueMuscle::
getJointAngleSign() const
{
return signOfJointAngle;
}
double Millard2016TorqueMuscle::
getJointAngleOffset() const
{
return angleOffset;
}
double Millard2016TorqueMuscle::
getNormalizedDampingCoefficient() const
{
return beta;
}
void Millard2016TorqueMuscle::
setNormalizedDampingCoefficient(double betaUpd)
{
if(betaUpd < 0){
cerr << "Millard2016TorqueMuscle::"
<< "setNormalizedDampingCoefficient:"
<< muscleName
<< "beta is " << betaUpd
<< " but beta must be > 0 "
<< endl;
assert(0);
abort();
}
beta = betaUpd;
}
double Millard2016TorqueMuscle::
getMaximumActiveIsometricTorque() const
{
return maxActiveIsometricTorque;
}
double Millard2016TorqueMuscle::
getMaximumJointAngularVelocity() const
{
return calcFiberAngularVelocity(omegaMax);
}
void Millard2016TorqueMuscle::
setMaximumActiveIsometricTorque(double maxIsoTorque)
{
muscleCurvesAreDirty = true;
maxActiveIsometricTorque = maxIsoTorque;
}
double Millard2016TorqueMuscle::
getJointAngleAtMaximumActiveIsometricTorque() const
{
return calcJointAngle(angleAtOneNormActiveTorque);
}
double Millard2016TorqueMuscle::
getJointAngleAtOneNormalizedPassiveIsometricTorque() const
{
if(muscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return calcJointAngle(angleAtOneNormPassiveTorque);
}
double Millard2016TorqueMuscle::
getPassiveTorqueScale() const
{
return passiveTorqueScale;
}
void Millard2016TorqueMuscle::
setPassiveTorqueScale(double passiveTorqueScaling)
{
muscleCurvesAreDirty = true;
passiveTorqueScale = passiveTorqueScaling;
}
double Millard2016TorqueMuscle::
getPassiveCurveAngleOffset() const
{
return passiveCurveAngleOffset;
}
void Millard2016TorqueMuscle::
setPassiveCurveAngleOffset(double passiveCurveAngleOffsetVal)
{
muscleCurvesAreDirty = true;
passiveCurveAngleOffset = passiveCurveAngleOffsetVal;
}
void Millard2016TorqueMuscle::
fitPassiveCurveAngleOffset(double jointAngleTarget,
double passiveTorqueTarget)
{
muscleCurvesAreDirty = true;
setPassiveCurveAngleOffset(0.0);
if(passiveTorqueTarget < SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "fitPassiveTorqueScale:"
<< muscleName
<< ": passiveTorque " << passiveTorqueTarget
<< " but it should be greater than sqrt(eps)"
<< endl;
assert(0);
abort();
}
//Solve for the fiber angle at which the curve develops
//the desired passiveTorque
double normPassiveTorque = passiveTorqueTarget
/maxActiveIsometricTorque;
//Ge a good initial guess
VectorNd curveDomain = tpCurve.getCurveDomain();
double angleRange = abs( curveDomain[1]-curveDomain[0]);
double fiberAngle = 0.5*(curveDomain[0]+curveDomain[1]);
double jointAngleCurr = calcJointAngle(fiberAngle);
TorqueMuscleInfo tqInfo = TorqueMuscleInfo();
calcTorqueMuscleInfo(jointAngleCurr,0.,0.,tqInfo);
double err = tqInfo.fiberPassiveTorqueAngleMultiplier-normPassiveTorque;
double jointAngleLeft = 0;
double jointAngleRight = 0;
double errLeft = 0;
double errRight = 0;
double h = 0.25*angleRange;
//Get a good initial guess - necessary because these curves
//can be *very* nonlinear.
int iter = 0;
int iterMax = 10;
int tol = sqrt(SQRTEPSILON);
while(iter < iterMax && abs(err) > tol){
jointAngleLeft = jointAngleCurr-h;
jointAngleRight = jointAngleCurr+h;
calcTorqueMuscleInfo(jointAngleLeft,0.,0.,tqInfo);
errLeft = tqInfo.fiberPassiveTorqueAngleMultiplier
- normPassiveTorque;
calcTorqueMuscleInfo(jointAngleRight,0.,0.,tqInfo);
errRight = tqInfo.fiberPassiveTorqueAngleMultiplier
- normPassiveTorque;
if(abs(errLeft)<abs(err) && abs(errLeft)<abs(errRight)){
err = errLeft;
jointAngleCurr = jointAngleLeft;
}
if(abs(errRight)<abs(err) && abs(errRight)<abs(errLeft)){
err = errRight;
jointAngleCurr = jointAngleRight;
}
h = h/2.0;
++iter;
}
//Use Newton's method to polish up this initial guess.
iter = 0;
err = SQRTEPSILON*2;
double derr = 0;
double delta= 0;
while(abs(err) > SQRTEPSILON && iter < iterMax){
calcTorqueMuscleInfo(jointAngleCurr,0.,0.,tqInfo);
err = tqInfo.fiberPassiveTorqueAngleMultiplier
-normPassiveTorque;
derr = signOfJointTorque*tqInfo.DjointTorqueDjointAngle
/ maxActiveIsometricTorque ;
delta = -err/derr;
jointAngleCurr += delta;
++iter;
}
if(abs(err)>SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "fitPassiveCurveAngleOffset:"
<< muscleName
<< ": failed to fit the passive curve offset "
<< " such that the curve develops the desired "
<< " passive torque at the given joint angle. This"
<< " should not be possible - contact the maintainer "
<< " of this addon."
<< endl;
assert(0);
abort();
}
double fiberAngleOffset = calcFiberAngle(jointAngleTarget)
- calcFiberAngle(jointAngleCurr);
setPassiveCurveAngleOffset(fiberAngleOffset);
}
void Millard2016TorqueMuscle::
fitPassiveTorqueScale(double jointAngleTarget,
double passiveTorqueTarget)
{
muscleCurvesAreDirty = true;
setPassiveTorqueScale(1.0);
VectorNd curveDomain = tpCurve.getCurveDomain();
VectorNd curveRange = VectorNd(2);
curveRange[0] = tpCurve.calcValue(curveDomain[0]);
curveRange[1] = tpCurve.calcValue(curveDomain[1]);
double fiberAngle = calcFiberAngle(jointAngleTarget);
if( (fiberAngle < curveDomain[0] && (curveRange[0] < curveRange[1]))
|| (fiberAngle > curveDomain[1] && (curveRange[1] < curveRange[0])) ){
cerr << "Millard2016TorqueMuscle::"
<< "fitPassiveTorqueScale:"
<< muscleName
<< ": joint angle is " << jointAngleTarget
<< " but it should be between "
<< calcJointAngle(curveDomain[0]) << " and "
<< calcJointAngle(curveDomain[1])
<< endl;
assert(0);
abort();
}
if(passiveTorqueTarget < SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "fitPassiveTorqueScale:"
<< muscleName
<< ": passiveTorque " << passiveTorqueTarget
<< " but it should be greater than sqrt(eps)"
<< endl;
assert(0);
abort();
}
double normPassiveTorque = passiveTorqueTarget/maxActiveIsometricTorque;
int iter = 0;
int iterMax = 10;
double err = SQRTEPSILON*2;
double derr = 0;
double delta= 0;
double scale = 1.0;
setPassiveTorqueScale(scale);
TorqueMuscleInfo tqInfo = TorqueMuscleInfo();
while(abs(err) > SQRTEPSILON && iter < iterMax){
setPassiveTorqueScale(scale);
calcTorqueMuscleInfo(jointAngleTarget,0,0,tqInfo);
err = tqInfo.fiberPassiveTorqueAngleMultiplier - normPassiveTorque;
derr= tqInfo.fiberPassiveTorqueAngleMultiplier/scale;
delta = -err/derr;
scale += delta;
if(scale < SQRTEPSILON){
scale = SQRTEPSILON;
}
iter++;
}
if(abs(err) > SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "fitPassiveTorqueScale:"
<< muscleName
<< ": passiveTorqueScale could not be fit to"
<< " the data given. See the maintainer of this"
<< " addon for help."
<< endl;
assert(0);
abort();
}
}
/*
const RigidBodyDynamics::Math::VectorNd&
Millard2016TorqueMuscle::getParametersc1c2c3c4c5c6()
{
return c1c2c3c4c5c6Anderson2007;
}
const RigidBodyDynamics::Math::VectorNd&
Millard2016TorqueMuscle::getParametersb1k1b2k2()
{
return b1k1b2k2Anderson2007;
}
*/
const SmoothSegmentedFunction& Millard2016TorqueMuscle::
getActiveTorqueAngleCurve() const
{
//This must be updated if the parameters have changed
if(muscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return taCurve;
}
const SmoothSegmentedFunction& Millard2016TorqueMuscle::
getPassiveTorqueAngleCurve() const
{
//This must be updated if the parameters have changed
if(muscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return tpCurve;
}
const SmoothSegmentedFunction& Millard2016TorqueMuscle::
getTorqueAngularVelocityCurve() const
{
//This must be updated if the parameters have changed
if(muscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return tvCurve;
}
string Millard2016TorqueMuscle::getName(){
return muscleName;
}
void Millard2016TorqueMuscle::setName(string &name){
muscleCurvesAreDirty = true;
muscleName = name;
}
/*************************************************************
Utilities
*************************************************************/
//fa = signJointAngle*(ja - jaO)
// ja = signJointAngle*fa + ja0
//dja = signJointAngle*dfa
double Millard2016TorqueMuscle::
calcFiberAngle(double jointAngle) const
{
return signOfJointAngle*(jointAngle-angleOffset);
}
double Millard2016TorqueMuscle::
calcJointAngle(double fiberAngle) const
{
return fiberAngle*signOfJointAngle + angleOffset;
}
double Millard2016TorqueMuscle::
calcFiberAngularVelocity(double jointAngularVelocity) const
{
return signOfConcentricAnglularVelocity*jointAngularVelocity;
}
void Millard2016TorqueMuscle::updateTorqueMuscleCurves()
{
std::string tempName = muscleName;
switch(dataSet){
case DataSet::Anderson2007:
{
double c4 = c1c2c3c4c5c6Anderson2007[3];
double c5 = c1c2c3c4c5c6Anderson2007[4];
omegaMax = abs( 2.0*c4*c5/(c5-3.0*c4) );
scaleFactorAnderson2007 = subjectHeightInMeters
*subjectMassInKg
*gravity;
maxActiveIsometricTorque = scaleFactorAnderson2007
*c1c2c3c4c5c6Anderson2007[0];
angleAtOneNormActiveTorque = c1c2c3c4c5c6Anderson2007[2];
TorqueMuscleFunctionFactory::
createAnderson2007ActiveTorqueAngleCurve(
c1c2c3c4c5c6Anderson2007[1],
c1c2c3c4c5c6Anderson2007[2],
tempName.append("_taCurve"),
taCurve);
tempName = muscleName;
TorqueMuscleFunctionFactory::
createAnderson2007ActiveTorqueVelocityCurve(
c1c2c3c4c5c6Anderson2007[3],
c1c2c3c4c5c6Anderson2007[4],
c1c2c3c4c5c6Anderson2007[5],
1.1,
1.4,
tempName.append("_tvCurve"),
tvCurve);
tempName = muscleName;
double normMaxActiveIsometricTorque = maxActiveIsometricTorque
/scaleFactorAnderson2007;
TorqueMuscleFunctionFactory::
createAnderson2007PassiveTorqueAngleCurve(
scaleFactorAnderson2007,
normMaxActiveIsometricTorque,
b1k1b2k2Anderson2007[0],
b1k1b2k2Anderson2007[1],
b1k1b2k2Anderson2007[2],
b1k1b2k2Anderson2007[3],
tempName.append("_tpCurve"),
tpCurve);
tpCurve.shift(passiveCurveAngleOffset,0);
double k = 0;
double b = 0;
if(b1k1b2k2Anderson2007[0] > 0){
b = b1k1b2k2Anderson2007[0];
k = b1k1b2k2Anderson2007[1];
}else if(b1k1b2k2Anderson2007[2] > 0){
b = b1k1b2k2Anderson2007[2];
k = b1k1b2k2Anderson2007[3];
}
if(abs(b) > 0 && passiveTorqueScale > SQRTEPSILON){
angleAtOneNormPassiveTorque =
(1/k)*log(abs(maxActiveIsometricTorque/b))
+ passiveCurveAngleOffset;
}else{
angleAtOneNormPassiveTorque =
std::numeric_limits<double>::signaling_NaN();
}
//angleAtOneNormPassiveTorque
//gymnastParams[Gymnast::PassiveAngleAtOneNormTorque]
} break;
case DataSet::Gymnast:
{
omegaMax = gymnastParams[
Gymnast::OmegaMax];
maxActiveIsometricTorque = gymnastParams[
Gymnast::TauMax];
angleAtOneNormActiveTorque = gymnastParams[
Gymnast::ActiveAngleAtOneNormTorque];
TorqueMuscleFunctionFactory::
createGaussianShapedActiveTorqueAngleCurve(
gymnastParams[Gymnast::ActiveAngleAtOneNormTorque],
gymnastParams[Gymnast::ActiveAngularStandardDeviation],
tempName.append("_taCurve"),
taCurve);
TorqueMuscleFunctionFactory::createPassiveTorqueAngleCurve(
gymnastParams[Gymnast::PassiveAngleAtZeroTorque],
gymnastParams[Gymnast::PassiveAngleAtOneNormTorque],
tempName.append("_tpCurve"),
tpCurve);
tpCurve.shift(passiveCurveAngleOffset,0);
TorqueMuscleFunctionFactory::createTorqueVelocityCurve(
gymnastParams[Gymnast::TvAtMaxEccentricVelocity],
gymnastParams[Gymnast::TvAtHalfMaxConcentricVelocity],
tempName.append("_tvCurve"),
tvCurve);
if(passiveTorqueScale > 0){
angleAtOneNormPassiveTorque =
gymnastParams[Gymnast::PassiveAngleAtOneNormTorque]
+ passiveCurveAngleOffset;
}else{
angleAtOneNormPassiveTorque =
std::numeric_limits<double>::signaling_NaN();
}
} break;
default:
{
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< "dataSet has a value of " << dataSet
<< " which is not a valid choice";
assert(0);
abort();
}
};
//If the passiveScale is < 1 and > 0, then we must iterate to
//find the true value of angleAtOneNormPassiveTorque;
if(isfinite(angleAtOneNormPassiveTorque)
&& (passiveTorqueScale > 0 && passiveTorqueScale != 1.0) ){
int iter = 1;
int iterMax =100;
double err = 10*SQRTEPSILON;
double derr = 0;
double delta = 0;
while(abs(err) > SQRTEPSILON && iter < iterMax){
err = passiveTorqueScale*tpCurve.calcValue(angleAtOneNormPassiveTorque)
- 1.0;
derr = passiveTorqueScale
*tpCurve.calcDerivative(angleAtOneNormPassiveTorque,1);
delta = -err/derr;
angleAtOneNormPassiveTorque += delta;
++iterMax;
}
if(abs(err) > SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< muscleName
<< "Internal Error: failed to solve for "
<<"angleAtOneNormPassiveTorque. Consult the maintainer of this "
<<"addon";
assert(0);
abort();
}
}
muscleCurvesAreDirty = false;
}
void Millard2016TorqueMuscle::printJointTorqueProfileToFile(
const std::string& path,
const std::string& fileNameWithoutExtension,
int numberOfSamplePoints)
{
if(muscleCurvesAreDirty){
updateTorqueMuscleCurves();
}
VectorNd activeDomain = taCurve.getCurveDomain();
VectorNd passiveDomain = tpCurve.getCurveDomain();
VectorNd velocityDomain= tvCurve.getCurveDomain();
double angleMin = activeDomain[0];
double angleMax = activeDomain[1];
if(tpCurve.calcValue(passiveDomain[0]) >= 0.99){
angleMin = passiveDomain[0];
}
if(tpCurve.calcValue(passiveDomain[1]) >= 0.99){
angleMax = passiveDomain[1];
}
double jointMin = signOfJointAngle*angleMin + angleOffset;
double jointMax = signOfJointAngle*angleMax + angleOffset;
if(jointMin > jointMax){
double tmp = jointMin;
jointMin=jointMax;
jointMax=tmp;
}
double range = jointMax-jointMin;
jointMin = jointMin -range*0.1;
jointMax = jointMax +range*0.1;
double jointDelta = (jointMax-jointMin)
/((double)numberOfSamplePoints-1.);
double velMin = omegaMax*signOfConcentricAnglularVelocity*velocityDomain[0];
double velMax = omegaMax*signOfConcentricAnglularVelocity*velocityDomain[1];
if(velMin > velMax){
double tmp = velMin;
velMin = velMax;
velMax = tmp;
}
double velRange = velMax-velMin;
velMin = velMin-0.1*velRange;
velMax = velMax+0.1*velRange;
double velDelta = (velMax-velMin)/((double)numberOfSamplePoints-1.0);
double angleAtMaxIsoTorque = angleAtOneNormActiveTorque;
std::vector< std::vector < double > > matrix;
std::vector < double > row(21);
std::string header("jointAngle,"
"jointVelocity,"
"activation,"
"fiberAngle,"
"fiberAngularVelocity,"
"passiveTorqueAngleMultiplier,"
"activeTorqueAngleMultiplier,"
"torqueVelocityMultiplier,"
"activeTorque,"
"passiveTorque,"
"fiberTorque,"
"jointTorque,"
"fiberStiffness,"
"jointStiffness,"
"fiberActivePower,"
"fiberPassivePower,"
"fiberPower,"
"jointPower,"
"DjointTorqueDactivation,"
"DjointTorqueDjointAngularVelocity,"
"DjointTorqueDjointAngle");
double activation =1.0;
double jointAngle = 0.;
double jointVelocity = 0.;
for(int i=0; i<numberOfSamplePoints; ++i){
jointAngle = jointMin + i*jointDelta;
jointVelocity = 0.;
calcTorqueMuscleInfo(jointAngle,
jointVelocity,
activation,
tmInfo);
row.at(0) = jointAngle;
row.at(1) = jointVelocity;
row.at(2) = activation;
row.at(3) = tmInfo.fiberAngle;
row.at(4) = tmInfo.fiberAngularVelocity;
row.at(5) = tmInfo.fiberPassiveTorqueAngleMultiplier;
row.at(6) = tmInfo.fiberActiveTorqueAngleMultiplier;
row.at(7) = tmInfo.fiberActiveTorqueAngularVelocityMultiplier;
row.at(8) = tmInfo.fiberActiveTorque;
row.at(9) = tmInfo.fiberPassiveTorque;
row.at(10)= tmInfo.fiberTorque;
row.at(11)= tmInfo.jointTorque;
row.at(12)= tmInfo.fiberStiffness;
row.at(13)= tmInfo.jointStiffness;
row.at(14)= tmInfo.fiberActivePower;
row.at(15)= tmInfo.fiberPassivePower;
row.at(16)= tmInfo.fiberPower;
row.at(17)= tmInfo.jointPower;
row.at(18)= tmInfo.DjointTorqueDactivation;
row.at(19)= tmInfo.DjointTorqueDjointAngularVelocity;
row.at(20)= tmInfo.DjointTorqueDjointAngle;
matrix.push_back(row);
}
std::string fullFilePath = path;
if(!path.empty()){
fullFilePath.append("/");
}
fullFilePath.append(fileNameWithoutExtension);
fullFilePath.append("_variableLengthfixedVelocity");
fullFilePath.append(".csv");
printMatrixToFile(matrix,header,fullFilePath);
matrix.clear();
for(int i=0; i<numberOfSamplePoints; ++i){
jointAngle = angleAtMaxIsoTorque;
jointVelocity = velMin + i*velDelta;
calcTorqueMuscleInfo(jointAngle,
jointVelocity,
activation,
tmInfo);
row.at(0) = jointAngle;
row.at(1) = jointVelocity;
row.at(2) = activation;
row.at(3) = tmInfo.fiberAngle;
row.at(4) = tmInfo.fiberAngularVelocity;
row.at(5) = tmInfo.fiberPassiveTorqueAngleMultiplier;
row.at(6) = tmInfo.fiberActiveTorqueAngleMultiplier;
row.at(7) = tmInfo.fiberActiveTorqueAngularVelocityMultiplier;
row.at(8) = tmInfo.fiberActiveTorque;
row.at(9) = tmInfo.fiberPassiveTorque;
row.at(10)= tmInfo.fiberTorque;
row.at(11)= tmInfo.jointTorque;
row.at(12)= tmInfo.fiberStiffness;
row.at(13)= tmInfo.jointStiffness;
row.at(14)= tmInfo.fiberActivePower;
row.at(15)= tmInfo.fiberPassivePower;
row.at(16)= tmInfo.fiberPower;
row.at(17)= tmInfo.jointPower;
row.at(18)= tmInfo.DjointTorqueDactivation;
row.at(19)= tmInfo.DjointTorqueDjointAngularVelocity;
row.at(20)= tmInfo.DjointTorqueDjointAngle;
matrix.push_back(row);
}
fullFilePath = path;
if(!path.empty()){
fullFilePath.append("/");
}
fullFilePath.append(fileNameWithoutExtension);
fullFilePath.append("_fixedLengthVariableVelocity");
fullFilePath.append(".csv");
printMatrixToFile(matrix,header,fullFilePath);
matrix.clear();
}