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

2478 lines
90 KiB
C++
Raw Permalink Normal View History

2020-10-03 22:55:14 +02:00
//==============================================================================
/*
* 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;
const double Millard2016TorqueMuscle::mTaLambdaMax = 1.0;
const double Millard2016TorqueMuscle::mTpLambdaMax = 0.0;
//static double mTvLambdaMax = 1.0;
/*************************************************************
Table Access Structure Names
*************************************************************/
const double Gravity = 9.81; //Needed for the strength scaling used
//by Anderson et al. And this value has to
//be equal to the gravity parameter used by
//Anderson et al.: it should not be changed if
//the force of gravity differs in the model!
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. mAngleAtOneNormPassiveTorque 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, 7.98695, 1.5506, 1.14543, 1.1, 0.150907, 0.306223, 1.35342 },
{6,1,0,0,211.65 , 19.2310, 0, 6.28319, 1.1, 0.150907, 0, -0.785398 }};
/*
Original lumbar parameters
{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( )
:mAngleOffset(1.0),
mSignOfJointAngle(1.0),
mSignOfJointTorque(1.0),
mSignOfConcentricAnglularVelocity(1.0),
mMuscleName("empty")
{
mMuscleCurvesAreDirty = true;
mUseTabularMaxActiveIsometricTorque = true;
mUseTabularOmegaMax = true;
mPassiveTorqueScale = 1.0;
mTaLambda = 0.0;
mTpLambda = 0.0;
mTvLambda = 0.0;
mTvLambdaMax = 1.0;
mTaAngleScaling = 1.0;
}
Millard2016TorqueMuscle::Millard2016TorqueMuscle(
DataSet::item dataSet,
const SubjectInformation &subjectInfo,
int jointTorque,
double jointAngleOffsetRelativeToDoxygenFigures,
double signOfJointAngleRelativeToDoxygenFigures,
double mSignOfJointTorque,
const std::string& name
):mAngleOffset(jointAngleOffsetRelativeToDoxygenFigures),
mSignOfJointAngle(signOfJointAngleRelativeToDoxygenFigures),
mSignOfJointTorque(mSignOfJointTorque),
mSignOfConcentricAnglularVelocity(mSignOfJointTorque),
mMuscleName(name),
mDataSet(dataSet)
{
mSubjectHeightInMeters = subjectInfo.heightInMeters;
mSubjectMassInKg = subjectInfo.massInKg;
mPassiveCurveAngleOffset = 0.;
mPassiveTorqueScale = 1.0;
mBetaMax = 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:"
<< mMuscleName
<< ": A jointTorqueDirection of " << jointTorque
<< " does not exist.";
assert(0);
abort();
}
if( abs(abs(mSignOfJointAngle)-1) > EPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< ": signOfJointAngleRelativeToAnderson2007 must be [-1,1] not "
<< mSignOfJointAngle;
assert(0);
abort();
}
if( abs(abs(mSignOfConcentricAnglularVelocity)-1) > EPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< ": signOfJointAngularVelocityDuringConcentricContraction "
<< "must be [-1,1] not "
<< mSignOfConcentricAnglularVelocity;
assert(0);
abort();
}
if( abs(abs(mSignOfJointTorque)-1) > EPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< ": mSignOfJointTorque must be [-1,1] not "
<< mSignOfJointTorque;
assert(0);
abort();
}
if(mSubjectHeightInMeters <= 0){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< ": mSubjectHeightInMeters > 0, but it's "
<< mSubjectHeightInMeters;
assert(0);
abort();
}
if(mSubjectMassInKg <= 0){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< ": mSubjectMassInKg > 0, but it's "
<< mSubjectMassInKg;
assert(0);
abort();
}
int idx = -1;
int jointIdx = 0;
int dirIdx = 1;
int genderIdx = 2;
int ageIdx = 3;
switch(mDataSet){
case DataSet::Anderson2007:
{
mAnderson2007c1c2c3c4c5c6.resize(6);
mAnderson2007b1k1b2k2.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){
mAnderson2007c1c2c3c4c5c6[i] = Anderson2007Table3Mean[idx][i+4];
}
for(int i=0; i<4; ++i){
mAnderson2007b1k1b2k2[i] = Anderson2007Table3Mean[idx][i+10];
}
}
} break;
case DataSet::Gymnast:
{
mGymnastParams.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){
mGymnastParams[i] = GymnastWholeBody[idx][i+4];
}
}
} break;
default:
{
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< "The requested DataSet does not exist.";
assert(0);
abort();
}
};
if(idx == -1){
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< "The combination of data set (" << mDataSet << ")"
<< " joint (" << joint << ")"
<< " joint direction (" << jointDirection << ")"
<< " gender, (" << gender << ")"
<< "and age " << ageGroup << ")"
<< "could not be found";
assert(0);
abort();
}
mMuscleCurvesAreDirty = true;
mUseTabularMaxActiveIsometricTorque = true;
mUseTabularOmegaMax = true;
mUseTabularTorqueVelocityMultiplierAtHalfOmegaMax = true;
mTaLambda = 0.0;
mTpLambda = 0.0;
mTvLambda = 0.0;
mTaAngleScaling = 1.0;
updateTorqueMuscleCurves();
#ifdef RBDL_BUILD_ADDON_MUSCLE_FITTING
mTmInfo.fittingInfo.resize(3);
/*
[0]: D^2 jointTorque / D lambdaA ^2
[1]: D^2 jointTorque / D lambdaV ^2
[2]: D^2 jointTorque / D lambdaA D lambdaV
*/
#endif
}
/*************************************************************
Muscle Model Code
*************************************************************/
/*
To explain the const casting:
1. calcJointTorque does not modify any member variables which affect
the output fo the model, and thus it should be const for the user.
2. To make the code maintainable the model is evaluated only in 2
places updTorqueMuscleSummary & updTorqueMuscleInfo.
3. To save on memory, these upd functions modify a struct, which is a member
variable, but is only being used as cache.
To achieve a const calcJointTorque function, maintainable code (e.g. model
evaluated in only 2 spots), and with minimal use of memory/copying I am
using const_casting.
There is perhaps a more elegant way to do this. I dislike
const_casting, but for now I do not see a better solution.
*/
double Millard2016TorqueMuscle::
calcJointTorque( double jointAngle,
double jointAngularVelocity,
double activation) const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
updTorqueMuscleSummary(activation,
jointAngle,jointAngularVelocity,
mTaLambda,mTpLambda,mTvLambda,
mTaAngleScaling, mAngleAtOneNormActiveTorque,
mPassiveCurveAngleOffset,
mOmegaMax,
mMaxActiveIsometricTorque,
mutableThis->mTmSummary);
return mTmSummary.jointTorque;
}
void Millard2016TorqueMuscle::
calcActivation(double jointAngle,
double jointAngularVelocity,
double jointTorque,
TorqueMuscleSummary &tms) const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
updInvertTorqueMuscleSummary(jointTorque,jointAngle,jointAngularVelocity,
mTaLambda,mTpLambda,mTvLambda,
mTaAngleScaling,
mAngleAtOneNormActiveTorque,
mPassiveCurveAngleOffset,
mOmegaMax,
mMaxActiveIsometricTorque,
tms);
}
double Millard2016TorqueMuscle::
calcMaximumActiveIsometricTorqueScalingFactor(
double jointAngle,
double jointAngularVelocity,
double activation,
double jointTorque) const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
double scaleFactor = 0.;
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
updTorqueMuscleSummary(activation,
jointAngle,jointAngularVelocity,
mTaLambda,mTpLambda,mTvLambda,
mTaAngleScaling,
mAngleAtOneNormActiveTorque,
mPassiveCurveAngleOffset,
mOmegaMax,
mMaxActiveIsometricTorque,
mutableThis->mTmSummary);
scaleFactor = jointTorque/mTmSummary.jointTorque;
return scaleFactor;
}
void Millard2016TorqueMuscle::
calcTorqueMuscleInfo(double jointAngle,
double jointAngularVelocity,
double activation,
TorqueMuscleInfo& tmi) const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
updTorqueMuscleInfo(activation, jointAngle, jointAngularVelocity,
mTaLambda,mTpLambda,mTvLambda,
mTaAngleScaling, mAngleAtOneNormActiveTorque,
mPassiveCurveAngleOffset,
mOmegaMax,
mMaxActiveIsometricTorque,
tmi);
}
/*************************************************************
Get / Set Functions
*************************************************************/
double Millard2016TorqueMuscle::
getJointTorqueSign() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mSignOfJointTorque;
}
double Millard2016TorqueMuscle::
getJointAngleSign() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mSignOfJointAngle;
}
double Millard2016TorqueMuscle::
getJointAngleOffset() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mAngleOffset;
}
double Millard2016TorqueMuscle::
getNormalizedDampingCoefficient() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mBetaMax;
}
void Millard2016TorqueMuscle::
setNormalizedDampingCoefficient(double betaUpd)
{
if(betaUpd < 0){
cerr << "Millard2016TorqueMuscle::"
<< "setNormalizedDampingCoefficient:"
<< mMuscleName
<< "mBetaMax is " << betaUpd
<< " but mBetaMax must be > 0 "
<< endl;
assert(0);
abort();
}
mBetaMax = betaUpd;
}
double Millard2016TorqueMuscle::
getMaximumActiveIsometricTorque() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
/*
if(mUseTabularMaxActiveIsometricTorque){
return mMaxActiveIsometricTorque;
}else{
return mMaxActiveIsometricTorque*mMaxActiveIsometricMultiplerProduct;
}
*/
return mMaxActiveIsometricTorque;
}
double Millard2016TorqueMuscle::
getMaximumConcentricJointAngularVelocity() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return calcJointAngularVelocity( mOmegaMax );
}
double Millard2016TorqueMuscle::
getTorqueVelocityMultiplierAtHalfOmegaMax() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mTorqueVelocityMultiplierAtHalfOmegaMax;
}
void Millard2016TorqueMuscle::
setTorqueVelocityMultiplierAtHalfOmegaMax(double tvAtHalfOmegaMax)
{
if(mDataSet == DataSet::Anderson2007){
cerr << "Millard2016TorqueMuscle::"
<< "setTorqueVelocityMultiplierAtHalfOmegaMax:"
<< mMuscleName
<< " This function is only compatible with the Gymnast dataset"
<< " but this muscle is from the Anderson2007 dataset. Switch"
<< " data sets or stop using this function.";
assert(0);
abort();
}
mMuscleCurvesAreDirty = true;
mUseTabularTorqueVelocityMultiplierAtHalfOmegaMax = false;
mTorqueVelocityMultiplierAtHalfOmegaMax = tvAtHalfOmegaMax;
}
void Millard2016TorqueMuscle::
setMaximumActiveIsometricTorque(double maxIsoTorque)
{
mMuscleCurvesAreDirty = true;
mUseTabularMaxActiveIsometricTorque = false;
//mMaxActiveIsometricTorqueUserDefined = maxIsoTorque;
mMaxActiveIsometricTorque = maxIsoTorque;
}
void Millard2016TorqueMuscle::
setMaximumConcentricJointAngularVelocity(double maxAngularVelocity){
if(fabs(maxAngularVelocity) < SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "setMaximumJointAngularVelocity:"
<< mMuscleName
<< " The value of maxJointAngularVelocity needs to be greater "
" than sqrt(epsilon), but it is "
<< maxAngularVelocity;
assert(0);
abort();
}
mMuscleCurvesAreDirty = true;
mUseTabularOmegaMax = false;
mOmegaMax = fabs(maxAngularVelocity);
}
double Millard2016TorqueMuscle::
getJointAngleAtMaximumActiveIsometricTorque() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return calcJointAngle(mAngleAtOneNormActiveTorque);
}
double Millard2016TorqueMuscle::
getActiveTorqueAngleCurveWidth() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
VectorNd domain = mTaCurve.getCurveDomain();
double activeTorqueAngleAngleScaling
= getActiveTorqueAngleCurveAngleScaling();
double width = fabs(domain[1]-domain[0])/activeTorqueAngleAngleScaling;
return width;
}
double Millard2016TorqueMuscle::
getJointAngleAtOneNormalizedPassiveIsometricTorque() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return calcJointAngle(mAngleAtOneNormPassiveTorque);
}
double Millard2016TorqueMuscle::
getJointAngleAtSmallestNormalizedPassiveIsometricTorque() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return calcJointAngle(mAngleAtSmallestNormPassiveTorque);
}
double Millard2016TorqueMuscle::
getPassiveTorqueScale() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mPassiveTorqueScale;
}
void Millard2016TorqueMuscle::
setPassiveTorqueScale(double passiveTorqueScaling)
{
mMuscleCurvesAreDirty = true;
mPassiveTorqueScale = passiveTorqueScaling;
}
double Millard2016TorqueMuscle::
getPassiveCurveAngleOffset() const
{
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mPassiveCurveAngleOffset;
}
void Millard2016TorqueMuscle::
setPassiveCurveAngleOffset(double passiveCurveAngleOffsetVal)
{
mMuscleCurvesAreDirty = true;
mPassiveCurveAngleOffset = passiveCurveAngleOffsetVal;
}
void Millard2016TorqueMuscle::
fitPassiveCurveAngleOffset(double jointAngleTarget,
double passiveFiberTorqueTarget)
{
mMuscleCurvesAreDirty = true;
setPassiveCurveAngleOffset(0.0);
updateTorqueMuscleCurves();
if(passiveFiberTorqueTarget < SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "fitPassiveTorqueScale:"
<< mMuscleName
<< ": passiveTorque " << passiveFiberTorqueTarget
<< " 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 normPassiveFiberTorque = passiveFiberTorqueTarget
/mMaxActiveIsometricTorque;
double currentFiberAngle = calcFiberAngleGivenNormalizedPassiveTorque(
normPassiveFiberTorque,
mTpLambda,
mPassiveCurveAngleOffset); //0.);
//double fiberAngleOffset = mPassiveCurveAngleOffset
// +calcFiberAngle(jointAngleTarget)
// -currentFiberAngle;
double fiberAngleOffset = calcFiberAngle(jointAngleTarget)
-currentFiberAngle;
setPassiveCurveAngleOffset(fiberAngleOffset);
updateTorqueMuscleCurves();
}
void Millard2016TorqueMuscle::
fitPassiveTorqueScale(double jointAngleTarget,
double passiveFiberTorqueTarget)
{
mMuscleCurvesAreDirty = true;
setPassiveTorqueScale(1.0);
updateTorqueMuscleCurves();
double normPassiveFiberTorqueTarget = passiveFiberTorqueTarget
/mMaxActiveIsometricTorque;
double fiberAngle = calcFiberAngle(jointAngleTarget);
double normPassiveFiberTorqueCurrent=
calcBlendedCurveDerivative(fiberAngle-mPassiveCurveAngleOffset,
mTpLambda, mTpLambdaMax,
0,0,
mTpCurve);
double passiveTorqueScale = normPassiveFiberTorqueTarget
/normPassiveFiberTorqueCurrent;
setPassiveTorqueScale(passiveTorqueScale);
updateTorqueMuscleCurves();
}
void Millard2016TorqueMuscle::calcTorqueMuscleDataFeatures(
RigidBodyDynamics::Math::VectorNd const &jointTorque,
RigidBodyDynamics::Math::VectorNd const &jointAngle,
RigidBodyDynamics::Math::VectorNd const &jointAngularVelocity,
double activeTorqueAngleBlendingVariable,
double passiveTorqueAngleBlendingVariable,
double torqueVelocityBlendingVariable,
double activeTorqueAngleAngleScaling,
double activeTorqueAngleAtOneNormTorque,
double passiveTorqueAngleCurveOffset,
double maxAngularVelocity,
double maxActiveIsometricTorque,
TorqueMuscleDataFeatures &tmf) const
{
TorqueMuscleSummary tmsCache;
double activation;
double tp;
double minActivation = 1.0;
double maxActivation = 0.;
double maxTp = 0.;
tmf.isInactive = true;
for(unsigned int i=0; i<jointAngle.rows();++i){
updInvertTorqueMuscleSummary(jointTorque[i],
jointAngle[i],
jointAngularVelocity[i],
activeTorqueAngleBlendingVariable,
passiveTorqueAngleBlendingVariable,
torqueVelocityBlendingVariable,
activeTorqueAngleAngleScaling,
activeTorqueAngleAtOneNormTorque,
passiveTorqueAngleCurveOffset,
maxAngularVelocity,
maxActiveIsometricTorque,
tmsCache);
activation = tmsCache.activation;
tp = tmsCache.fiberPassiveTorqueAngleMultiplier;
if(mSignOfJointTorque*jointTorque[i] > 0){
tmf.isInactive=false;
if(activation <= minActivation){
minActivation = activation;
tmf.indexOfMinActivation = i;
tmf.summaryAtMinActivation = tmsCache;
}
if(activation >= maxActivation){
tmf.indexOfMaxActivation = i;
tmf.summaryAtMaxActivation = tmsCache;
maxActivation = activation;
}
}
if(tmsCache.fiberPassiveTorqueAngleMultiplier >= maxTp){
maxTp = tmsCache.fiberPassiveTorqueAngleMultiplier;
tmf.summaryAtMaxPassiveTorqueAngleMultiplier = tmsCache;
tmf.indexOfMaxPassiveTorqueAngleMultiplier = i;
}
}
}
const SmoothSegmentedFunction& Millard2016TorqueMuscle::
getActiveTorqueAngleCurve() const
{
//This must be updated if the parameters have changed
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mTaCurve;
}
const SmoothSegmentedFunction& Millard2016TorqueMuscle::
getPassiveTorqueAngleCurve() const
{
//This must be updated if the parameters have changed
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mTpCurve;
}
const SmoothSegmentedFunction& Millard2016TorqueMuscle::
getTorqueAngularVelocityCurve() const
{
//This must be updated if the parameters have changed
if(mMuscleCurvesAreDirty){
Millard2016TorqueMuscle* mutableThis =
const_cast<Millard2016TorqueMuscle* >(this);
mutableThis->updateTorqueMuscleCurves();
}
return mTvCurve;
}
string Millard2016TorqueMuscle::getName(){
return mMuscleName;
}
void Millard2016TorqueMuscle::setName(string &name){
mMuscleCurvesAreDirty = true;
mMuscleName = name;
}
double Millard2016TorqueMuscle::
getActiveTorqueAngleCurveBlendingVariable() const
{
return mTaLambda;
}
double Millard2016TorqueMuscle::
getPassiveTorqueAngleCurveBlendingVariable() const
{
return mTpLambda;
}
double Millard2016TorqueMuscle::
getTorqueAngularVelocityCurveBlendingVariable() const
{
return mTvLambda;
}
double Millard2016TorqueMuscle::getActiveTorqueAngleCurveAngleScaling() const
{
return mTaAngleScaling;
}
void Millard2016TorqueMuscle::
setActiveTorqueAngleCurveAngleScaling(double angleScaling)
{
if(angleScaling < SQRTEPSILON){
cerr << "Millard2016TorqueMuscle::"
<< "setActiveTorqueAngleCurveAngleScaling:"
<< mMuscleName
<< ": angleScaling must be > sqrt(eps), this "
<< angleScaling
<<" is outside the acceptable range."
<< endl;
assert(0);
abort();
}
mMuscleCurvesAreDirty = true;
mTaAngleScaling = angleScaling;
}
void Millard2016TorqueMuscle::
setActiveTorqueAngleCurveBlendingVariable(double blendingVariable)
{
if(blendingVariable < 0. || blendingVariable > 1.0){
cerr << "Millard2016TorqueMuscle::"
<< "setActiveTorqueAngleCurveBlendingVariable:"
<< mMuscleName
<< ": blending variable must be [0,1] and this "
<< blendingVariable
<< " is outside the acceptable range."
<< endl;
assert(0);
abort();
}
mMuscleCurvesAreDirty = true;
mTaLambda = blendingVariable;
}
void Millard2016TorqueMuscle::
setPassiveTorqueAngleCurveBlendingVariable(double blendingVariable)
{
if(blendingVariable < 0. || blendingVariable > 1.0){
cerr << "Millard2016TorqueMuscle::"
<< "setPassiveTorqueAngleCurveBlendingVariable:"
<< mMuscleName
<< ": blending variable must be [0,1] and this "
<< blendingVariable
<< " is outside the acceptable range."
<< endl;
assert(0);
abort();
}
mMuscleCurvesAreDirty = true;
mTpLambda = blendingVariable;
}
void Millard2016TorqueMuscle::
setTorqueAngularVelocityCurveBlendingVariable(double blendingVariable)
{
if(blendingVariable < 0. || blendingVariable > 1.0){
cerr << "Millard2016TorqueMuscle::"
<< "setTorqueAngularVelocityCurveBlendingVariable:"
<< mMuscleName
<< ": blending variable must be [0,1] and this "
<< blendingVariable
<< " is outside the acceptable range."
<< endl;
assert(0);
abort();
}
mMuscleCurvesAreDirty = true;
mTvLambda = blendingVariable;
}
void Millard2016TorqueMuscle::setFittedParameters(
const TorqueMuscleParameterFittingData &fittedParameters)
{
if(!fittedParameters.fittingConverged){
cerr << "Millard2016TorqueMuscle::"
<< "setTorqueMuscleParameters:"
<< mMuscleName
<< ": The fittingConverged field of fittedParameters is false! "
<< endl;
assert(0);
abort();
}
setPassiveTorqueAngleCurveBlendingVariable(
fittedParameters.passiveTorqueAngleBlendingVariable);
setActiveTorqueAngleCurveBlendingVariable(
fittedParameters.activeTorqueAngleBlendingVariable);
setTorqueAngularVelocityCurveBlendingVariable(
fittedParameters.torqueVelocityBlendingVariable);
setPassiveCurveAngleOffset(
fittedParameters.passiveTorqueAngleCurveOffset);
setMaximumActiveIsometricTorque(
fittedParameters.maximumActiveIsometricTorque);
setMaximumConcentricJointAngularVelocity(
fittedParameters.maximumAngularVelocity);
setActiveTorqueAngleCurveAngleScaling(
fittedParameters.activeTorqueAngleAngleScaling);
}
/*************************************************************
Utilities
*************************************************************/
//fa = signJointAngle*(ja - jaO)
// ja = signJointAngle*fa + ja0
//dja = signJointAngle*dfa
double Millard2016TorqueMuscle::
calcFiberAngle(double jointAngle) const
{
return mSignOfJointAngle*(jointAngle-mAngleOffset);
}
double Millard2016TorqueMuscle::
calcJointAngle(double fiberAngle) const
{
return fiberAngle*mSignOfJointAngle + mAngleOffset;
}
double Millard2016TorqueMuscle::
calcFiberAngularVelocity(double jointAngularVelocity) const
{
return mSignOfConcentricAnglularVelocity*jointAngularVelocity;
}
double Millard2016TorqueMuscle::
calcJointAngularVelocity(double fiberAngularVelocity) const
{
return mSignOfConcentricAnglularVelocity*fiberAngularVelocity;
}
void Millard2016TorqueMuscle::updateTorqueMuscleCurves()
{
std::string tempName = mMuscleName;
switch(mDataSet){
case DataSet::Anderson2007:
{
double c4 = mAnderson2007c1c2c3c4c5c6[3];
double c5 = mAnderson2007c1c2c3c4c5c6[4];
if(mUseTabularOmegaMax){
mOmegaMax = abs( 2.0*c4*c5/(c5-3.0*c4) );
}
mScaleFactorAnderson2007 = mSubjectHeightInMeters
*mSubjectMassInKg
*Gravity;
if(mUseTabularMaxActiveIsometricTorque){
mMaxActiveIsometricTorque = mScaleFactorAnderson2007
*mAnderson2007c1c2c3c4c5c6[0];
}
mAngleAtOneNormActiveTorque = mAnderson2007c1c2c3c4c5c6[2];
TorqueMuscleFunctionFactory::
createAnderson2007ActiveTorqueAngleCurve(
mAnderson2007c1c2c3c4c5c6[1],
mAnderson2007c1c2c3c4c5c6[2],
tempName.append("_taCurve"),
mTaCurve);
tempName = mMuscleName;
TorqueMuscleFunctionFactory::
createAnderson2007ActiveTorqueVelocityCurve(
mAnderson2007c1c2c3c4c5c6[3],
mAnderson2007c1c2c3c4c5c6[4],
mAnderson2007c1c2c3c4c5c6[5],
1.1,
1.4,
tempName.append("_tvCurve"),
mTvCurve);
mTorqueVelocityMultiplierAtHalfOmegaMax = mTvCurve.calcValue(0.5);
tempName = mMuscleName;
double normMaxActiveIsometricTorque = mMaxActiveIsometricTorque
/mScaleFactorAnderson2007;
TorqueMuscleFunctionFactory::
createAnderson2007PassiveTorqueAngleCurve(
mScaleFactorAnderson2007,
normMaxActiveIsometricTorque,
mAnderson2007b1k1b2k2[0],
mAnderson2007b1k1b2k2[1],
mAnderson2007b1k1b2k2[2],
mAnderson2007b1k1b2k2[3],
tempName.append("_tpCurve"),
mTpCurve);
//mTpCurve.shift(mPassiveCurveAngleOffset,0);
mTpCurve.scale(1.0,mPassiveTorqueScale);
double k = 0;
double b = 0;
if(mAnderson2007b1k1b2k2[0] > 0){
b = mAnderson2007b1k1b2k2[0];
k = mAnderson2007b1k1b2k2[1];
}else if(mAnderson2007b1k1b2k2[2] > 0){
b = mAnderson2007b1k1b2k2[2];
k = mAnderson2007b1k1b2k2[3];
}
VectorNd xDomain = mTpCurve.getCurveDomain();
double tpAtX0 = mTpCurve.calcValue(xDomain[0]);
double tpAtX1 = mTpCurve.calcValue(xDomain[1]);
if(fabs(tpAtX0) < SQRTEPSILON){
mAngleAtSmallestNormPassiveTorque = xDomain[0];
}else if(fabs(tpAtX1) < SQRTEPSILON){
mAngleAtSmallestNormPassiveTorque = xDomain[1];
}else{
mAngleAtSmallestNormPassiveTorque =
std::numeric_limits<double>::signaling_NaN();
}
if( fabs(tpAtX0) > SQRTEPSILON
|| fabs(tpAtX1) > SQRTEPSILON){
double argGuess = (1/k)*log(abs(mMaxActiveIsometricTorque/b));
// + mPassiveCurveAngleOffset;
mAngleAtOneNormPassiveTorque = calcInverseBlendedCurveValue(1.0,
argGuess,
mTpLambda,
mTpLambdaMax,
mTpCurve);
mAngleAtOneNormPassiveTorque += mPassiveCurveAngleOffset;
}else{
mAngleAtOneNormPassiveTorque =
std::numeric_limits<double>::signaling_NaN();
}
//mAngleAtOneNormPassiveTorque
//mGymnastParams[Gymnast::PassiveAngleAtOneNormTorque]
} break;
case DataSet::Gymnast:
{
if(mUseTabularOmegaMax){
mOmegaMax = mGymnastParams[
Gymnast::OmegaMax];
}
if(mUseTabularMaxActiveIsometricTorque){
mMaxActiveIsometricTorque = mGymnastParams[
Gymnast::TauMax];
}
mAngleAtOneNormActiveTorque = mGymnastParams[
Gymnast::ActiveAngleAtOneNormTorque];
TorqueMuscleFunctionFactory::
createGaussianShapedActiveTorqueAngleCurve(
mGymnastParams[Gymnast::ActiveAngleAtOneNormTorque],
mGymnastParams[Gymnast::ActiveAngularStandardDeviation],
tempName.append("_taCurve"),
mTaCurve);
TorqueMuscleFunctionFactory::createPassiveTorqueAngleCurve(
mGymnastParams[Gymnast::PassiveAngleAtZeroTorque],
mGymnastParams[Gymnast::PassiveAngleAtOneNormTorque],
tempName.append("_tpCurve"),
mTpCurve);
//mTpCurve.shift(mPassiveCurveAngleOffset,0);
mTpCurve.scale(1.0,mPassiveTorqueScale);
if(mUseTabularTorqueVelocityMultiplierAtHalfOmegaMax){
mTorqueVelocityMultiplierAtHalfOmegaMax
= mGymnastParams[Gymnast::TvAtHalfMaxConcentricVelocity];
}
TorqueMuscleFunctionFactory::createTorqueVelocityCurve(
mGymnastParams[Gymnast::TvAtMaxEccentricVelocity],
mTorqueVelocityMultiplierAtHalfOmegaMax,
tempName.append("_tvCurve"),
mTvCurve);
//If the passive curve is non-zero, recalculate the angle at which
//it hits one normalized force.
mAngleAtSmallestNormPassiveTorque =
mGymnastParams[Gymnast::PassiveAngleAtZeroTorque]
+mPassiveCurveAngleOffset;
//Verify that the curve hits zero at this angle
if(fabs(mTpCurve.calcValue(mAngleAtSmallestNormPassiveTorque))
>SQRTEPSILON){
mAngleAtSmallestNormPassiveTorque =
numeric_limits<double>::signaling_NaN();
}
VectorNd xDomain = mTpCurve.getCurveDomain();
double tpAtX0 = mTpCurve.calcValue(xDomain[0]);
double tpAtX1 = mTpCurve.calcValue(xDomain[1]);
if( fabs(tpAtX0) > SQRTEPSILON
|| fabs(tpAtX1) > SQRTEPSILON){
double argGuess =mGymnastParams[Gymnast::PassiveAngleAtOneNormTorque];
//+ mPassiveCurveAngleOffset;
mAngleAtOneNormPassiveTorque =
calcInverseBlendedCurveValue(1.0,argGuess,mTpLambda,mTpLambdaMax,
mTpCurve);
mAngleAtOneNormPassiveTorque += mPassiveCurveAngleOffset;
}else{
mAngleAtOneNormPassiveTorque =
std::numeric_limits<double>::signaling_NaN();
}
} break;
default:
{
cerr << "Millard2016TorqueMuscle::"
<< "Millard2016TorqueMuscle:"
<< mMuscleName
<< "mDataSet has a value of " << mDataSet
<< " which is not a valid choice";
assert(0);
abort();
}
};
VectorNd tvDomain = mTvCurve.getCurveDomain();
mTvLambdaMax = mTvCurve.calcValue(tvDomain[0]);
assert(mTvLambdaMax >= 1.0); //If this fails you've gotten the incorrect
//domain end.
/*
double taIsoMax = calcBlendedCurveDerivative(mAngleAtOneNormActiveTorque,
mTaLambda, mTaLambdaMax,
0,0, mTaCurve);
double tvIsoMax = calcBlendedCurveDerivative(0,
mTvLambda, mTvLambdaMax,
0,0, mTvCurve);
mMaxActiveIsometricMultiplerProduct = taIsoMax*tvIsoMax;
if(mUseTabularMaxActiveIsometricTorque == false){
mMaxActiveIsometricTorque = mMaxActiveIsometricTorqueUserDefined
/mMaxActiveIsometricMultiplerProduct;
}
*/
mMuscleCurvesAreDirty = false;
}
void Millard2016TorqueMuscle::printJointTorqueProfileToFile(
const std::string& path,
const std::string& fileNameWithoutExtension,
int numberOfSamplePoints)
{
if(mMuscleCurvesAreDirty){
updateTorqueMuscleCurves();
}
VectorNd activeDomain = mTaCurve.getCurveDomain();
VectorNd passiveDomain = mTpCurve.getCurveDomain();
VectorNd velocityDomain= mTvCurve.getCurveDomain();
double jointMin = calcJointAngle( activeDomain[0] );
double jointMax = calcJointAngle( activeDomain[1] );
if(mTpCurve.calcValue( activeDomain[0] ) >= 0.99){
jointMin = calcJointAngle( passiveDomain[0] );
}
if(mTpCurve.calcValue(activeDomain[1]) >= 0.99){
jointMax = calcJointAngle( passiveDomain[1] );
}
if(jointMin > jointMax){
double tmp = jointMin;
jointMin=jointMax;
jointMax=tmp;
}
double range = jointMax-jointMin;
jointMin = jointMin -range*0.5;
jointMax = jointMax +range*0.5;
double jointDelta = (jointMax-jointMin)
/((double)numberOfSamplePoints-1.);
double velMin = calcJointAngularVelocity( -mOmegaMax );
double velMax = calcJointAngularVelocity( mOmegaMax );
if(velMin > velMax){
double tmp = velMin;
velMin = velMax;
velMax = tmp;
}
double velRange = velMax-velMin;
velMin = velMin-0.5*velRange;
velMax = velMax+0.5*velRange;
double velDelta = (velMax-velMin)/((double)numberOfSamplePoints-1.0);
double angleAtMaxIsoTorque = mAngleAtOneNormActiveTorque;
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,
mTmInfo);
row.at(0) = jointAngle;
row.at(1) = jointVelocity;
row.at(2) = activation;
row.at(3) = mTmInfo.fiberAngle;
row.at(4) = mTmInfo.fiberAngularVelocity;
row.at(5) = mTmInfo.fiberPassiveTorqueAngleMultiplier;
row.at(6) = mTmInfo.fiberActiveTorqueAngleMultiplier;
row.at(7) = mTmInfo.fiberTorqueAngularVelocityMultiplier;
row.at(8) = mTmInfo.fiberActiveTorque;
row.at(9) = mTmInfo.fiberPassiveTorque;
row.at(10)= mTmInfo.fiberTorque;
row.at(11)= mTmInfo.jointTorque;
row.at(12)= mTmInfo.fiberStiffness;
row.at(13)= mTmInfo.jointStiffness;
row.at(14)= mTmInfo.fiberActivePower;
row.at(15)= mTmInfo.fiberPassivePower;
row.at(16)= mTmInfo.fiberPower;
row.at(17)= mTmInfo.jointPower;
row.at(18)= mTmInfo.DjointTorque_Dactivation;
row.at(19)= mTmInfo.DjointTorque_DjointAngularVelocity;
row.at(20)= mTmInfo.DjointTorque_DjointAngle;
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 = calcJointAngle( angleAtMaxIsoTorque );
jointVelocity = calcJointAngularVelocity( velMin + i*velDelta );
calcTorqueMuscleInfo(jointAngle,
jointVelocity,
activation,
mTmInfo);
row.at(0) = jointAngle;
row.at(1) = jointVelocity;
row.at(2) = activation;
row.at(3) = mTmInfo.fiberAngle;
row.at(4) = mTmInfo.fiberAngularVelocity;
row.at(5) = mTmInfo.fiberPassiveTorqueAngleMultiplier;
row.at(6) = mTmInfo.fiberActiveTorqueAngleMultiplier;
row.at(7) = mTmInfo.fiberTorqueAngularVelocityMultiplier;
row.at(8) = mTmInfo.fiberActiveTorque;
row.at(9) = mTmInfo.fiberPassiveTorque;
row.at(10)= mTmInfo.fiberTorque;
row.at(11)= mTmInfo.jointTorque;
row.at(12)= mTmInfo.fiberStiffness;
row.at(13)= mTmInfo.jointStiffness;
row.at(14)= mTmInfo.fiberActivePower;
row.at(15)= mTmInfo.fiberPassivePower;
row.at(16)= mTmInfo.fiberPower;
row.at(17)= mTmInfo.jointPower;
row.at(18)= mTmInfo.DjointTorque_Dactivation;
row.at(19)= mTmInfo.DjointTorque_DjointAngularVelocity;
row.at(20)= mTmInfo.DjointTorque_DjointAngle;
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();
}
//=============================================================================
//=============================================================================
double Millard2016TorqueMuscle::
calcInverseBlendedCurveValue(
double blendedCurveValue,
double argGuess,
double blendingVariable,
double maximumBlendingValue,
RigidBodyDynamics::Addons::Geometry::SmoothSegmentedFunction
const &curve) const
{
double arg = 0;
double curveValue =
(blendedCurveValue-(blendingVariable*maximumBlendingValue))
/(1-blendingVariable);
arg = curve.calcInverseValue(curveValue,argGuess);
return arg;
}
double Millard2016TorqueMuscle::
calcBlendedCurveDerivative(
double curveArgument,
double blendingVariable,
double maximumBlendingValue,
unsigned int derivativeOrderArgument,
unsigned int derivativeOrderBlendingVariable,
RigidBodyDynamics::Addons::Geometry::SmoothSegmentedFunction
const &curve) const
{
double output = 0;
switch(derivativeOrderArgument) {
case 0: {
switch(derivativeOrderBlendingVariable) {
case 0: {
output = (1.-blendingVariable)*curve.calcValue(curveArgument)
+ blendingVariable*maximumBlendingValue;
} break;
case 1: {
output = maximumBlendingValue - curve.calcValue(curveArgument);
} break;
case 2: {
output = 0.;
} break;
default: {
output =0.; //Higher order derivatives are zero.
}
}
} break;
case 1: {
switch(derivativeOrderBlendingVariable) {
case 0: {
output = (1.-blendingVariable)*curve.calcDerivative(curveArgument,1);
} break;
case 1: {
output = -1.0*curve.calcDerivative(curveArgument,1);
} break;
case 2: {
output = 0.;
} break;
default: {
output = 0.; //Higher order derivatives are zero.
}
}
} break;
case 2: {
switch(derivativeOrderBlendingVariable) {
case 0: {
output = (1.-blendingVariable)*curve.calcDerivative(curveArgument,2);
} break;
case 1: {
output = -1.0*curve.calcDerivative(curveArgument,2);
} break;
case 2: {
output = 0.;
} break;
default: {
output = 0.; //Higher order derivatives are zero.
}
}
} break;
default: {
cerr << "3rd order derivatives not yet implemented";
assert(0);
abort();
}
};
return output;
}
//=============================================================================
//=============================================================================
double Millard2016TorqueMuscle::
calcFiberAngleGivenNormalizedPassiveTorque(
double normPassiveFiberTorque,
double blendingVariable,
double passiveTorqueAngleCurveOffset) const
{
double angle = numeric_limits<double>::signaling_NaN();
VectorNd domain = mTpCurve.getCurveDomain();
double y0 = mTpCurve.calcValue(domain[0]);
double y1 = mTpCurve.calcValue(domain[1]);
if( (blendingVariable < 1) && (y0 > SQRTEPSILON || y1 > SQRTEPSILON) ){
double tp = normPassiveFiberTorque/(1-blendingVariable);
angle = mTpCurve.calcInverseValue(tp,0.)-passiveTorqueAngleCurveOffset;
}
return angle;
}
//=============================================================================
//=============================================================================
void Millard2016TorqueMuscle::
updTorqueMuscleSummaryCurveValues(double fiberAngle,
double normFiberAngularVelocity,
double activeTorqueAngleBlendingVariable,
double passiveTorqueAngleBlendingVariable,
double torqueAngularVelocityBlendingVariable,
double activeTorqueAngleAngleScaling,
double activeTorqueAngleAtOneNormTorque,
double passiveTorqueAngleCurveOffset,
TorqueMuscleSummary &updTms) const
{
double taAngle = (fiberAngle-activeTorqueAngleAtOneNormTorque)
/activeTorqueAngleAngleScaling
+ activeTorqueAngleAtOneNormTorque;
updTms.fiberActiveTorqueAngleMultiplier =
calcBlendedCurveDerivative(taAngle,
activeTorqueAngleBlendingVariable,
mTaLambdaMax,
0,0,
mTaCurve);
updTms.fiberPassiveTorqueAngleMultiplier =
calcBlendedCurveDerivative(fiberAngle-passiveTorqueAngleCurveOffset,
passiveTorqueAngleBlendingVariable,
mTpLambdaMax,
0,0,
mTpCurve);
updTms.fiberTorqueAngularVelocityMultiplier =
calcBlendedCurveDerivative(
normFiberAngularVelocity,
torqueAngularVelocityBlendingVariable,
mTvLambdaMax,
0,0,
mTvCurve);
updTms.fiberNormalizedDampingTorque = updTms.fiberPassiveTorqueAngleMultiplier
* (-normFiberAngularVelocity*mBetaMax);
}
void Millard2016TorqueMuscle::
updTorqueMuscleInfo(
double activation,
double jointAngle,
double jointAngularVelocity,
double activeTorqueAngleBlendingVariable,
double passiveTorqueAngleBlendingVariable,
double torqueAngularVelocityBlendingVariable,
double activeTorqueAngleAngleScaling,
double activeTorqueAngleAtOneNormTorque,
double passiveTorqueAngleCurveOffset,
double maxAngularVelocity,
double maxActIsoTorque,
TorqueMuscleInfo &updTmi) const
{
//Update state quantities
updTmi.activation = activation;
updTmi.jointAngle = jointAngle;
updTmi.jointAngularVelocity = jointAngularVelocity;
double fiberAngle = calcFiberAngle(jointAngle);
double fiberAngularVelocity = calcFiberAngularVelocity(jointAngularVelocity);
double omegaNorm = fiberAngularVelocity/maxAngularVelocity;
double D_wn_D_w = 1.0/maxAngularVelocity;
double D_wn_D_wmax = -fiberAngularVelocity
/(maxAngularVelocity*maxAngularVelocity);
double D2_wn_D_wmax2 = -2.0*D_wn_D_wmax/maxAngularVelocity;
double taAngle = (fiberAngle-activeTorqueAngleAtOneNormTorque)
/activeTorqueAngleAngleScaling
+ activeTorqueAngleAtOneNormTorque;
double D_taAngle_D_fiberAngle = 1.0/activeTorqueAngleAngleScaling;
double D_taAngle_D_angleScaling =
-(fiberAngle-activeTorqueAngleAtOneNormTorque)
/(activeTorqueAngleAngleScaling*activeTorqueAngleAngleScaling);
double D2_taAngle_D_angleScaling2 = -2.0*D_taAngle_D_angleScaling
/activeTorqueAngleAngleScaling;
//Update force component values
double ta =
calcBlendedCurveDerivative(taAngle,
activeTorqueAngleBlendingVariable,
mTaLambdaMax,
0,0,
mTaCurve);
double tp =
calcBlendedCurveDerivative(fiberAngle-passiveTorqueAngleCurveOffset,
passiveTorqueAngleBlendingVariable,
mTpLambdaMax,
0,0,
mTpCurve);
double tv = calcBlendedCurveDerivative(
omegaNorm,
torqueAngularVelocityBlendingVariable,
mTvLambdaMax,
0,0,
mTvCurve);
double tb = tp * (-omegaNorm*mBetaMax);
double D_tb_D_omegaMax = tp*(-D_wn_D_wmax*mBetaMax);
//Update force component derivative values;
//1st derivatives w.r.t fiber angle/velocity
double D_ta_D_taAngle = calcBlendedCurveDerivative(
taAngle,
activeTorqueAngleBlendingVariable,
mTaLambdaMax,
1,0,
mTaCurve);
double D_ta_D_angleScaling = D_ta_D_taAngle*D_taAngle_D_angleScaling;
double D_ta_D_fiberAngle = D_ta_D_taAngle*D_taAngle_D_fiberAngle;
double D_tp_D_fiberAngle = calcBlendedCurveDerivative(
fiberAngle-passiveTorqueAngleCurveOffset,
passiveTorqueAngleBlendingVariable,
mTpLambdaMax,
1,0,
mTpCurve);
double D_tp_D_fiberAngleOffset= -D_tp_D_fiberAngle;
double D_tv_D_wn = calcBlendedCurveDerivative(
omegaNorm,
torqueAngularVelocityBlendingVariable,
mTvLambdaMax,
1,0,
mTvCurve);
double D_tv_D_fiberAngularVelocity = D_tv_D_wn*D_wn_D_w;
double D_tv_D_omegaMax = D_tv_D_wn*D_wn_D_wmax;
//1st derivatives w.r.t fitting-related variables
double D_ta_D_taLambda = calcBlendedCurveDerivative(
taAngle,
activeTorqueAngleBlendingVariable,
mTaLambdaMax,
0,1,
mTaCurve);
double D_tp_D_tpLambda = calcBlendedCurveDerivative(
fiberAngle-passiveTorqueAngleCurveOffset,
passiveTorqueAngleBlendingVariable,
mTpLambdaMax,
0,1,
mTpCurve);
double D_tv_D_tvLambda = calcBlendedCurveDerivative(
omegaNorm,
torqueAngularVelocityBlendingVariable,
mTvLambdaMax,
0,1,
mTvCurve);
//2nd derivatives w.r.t fitting-related variables.
double D2_ta_D_taLambda2 = calcBlendedCurveDerivative(
taAngle,
activeTorqueAngleBlendingVariable,
mTaLambdaMax,
0,2,
mTaCurve);
double D2_tv_D_tvLambda2 = calcBlendedCurveDerivative(
omegaNorm,
torqueAngularVelocityBlendingVariable,
mTvLambdaMax,
0,2,
mTvCurve);
double D2_tp_D_tpLambda2 = calcBlendedCurveDerivative(
fiberAngle-passiveTorqueAngleCurveOffset,
passiveTorqueAngleBlendingVariable,
mTpLambdaMax,
0,2,
mTpCurve);
double D2_tp_D_fiberAngle2 =calcBlendedCurveDerivative(
fiberAngle-passiveTorqueAngleCurveOffset,
passiveTorqueAngleBlendingVariable,
mTpLambdaMax,
2,0,
mTpCurve);
double D2_ta_D_taAngle2 = calcBlendedCurveDerivative(
taAngle,
activeTorqueAngleBlendingVariable,
mTaLambdaMax,
2,0,
mTaCurve);
double D2_ta_D_angleScaling2 =
D2_ta_D_taAngle2*D_taAngle_D_angleScaling*D_taAngle_D_angleScaling
+ D_ta_D_taAngle*D2_taAngle_D_angleScaling2;
double D2_tv_D_wn2 = calcBlendedCurveDerivative(
omegaNorm,
torqueAngularVelocityBlendingVariable,
mTvLambdaMax,
2,0,
mTvCurve);
double D2_tb_D_omegaMax2 = tp*(-D2_wn_D_wmax2*mBetaMax);
double D2_tb_D_omegaMax_D_tpLambda = D_tp_D_tpLambda*(-D_wn_D_wmax*mBetaMax);
double D2_tb_D_omegaMax_D_tpOffset = D_tp_D_fiberAngleOffset
*(-D_wn_D_wmax*mBetaMax);
double D2_tv_D_omegaMax2 = D2_tv_D_wn2*D_wn_D_wmax*D_wn_D_wmax
+ D_tv_D_wn*D2_wn_D_wmax2;
//Note that d/d_tpOffsetAngle kicks out another -1, so this 2nd derivative has
//a positive sign.
double D2_tp_D_fiberAngleOffset2= D2_tp_D_fiberAngle2;
double D2_tp_D_tpLambda_D_fiberAngle =
calcBlendedCurveDerivative( fiberAngle-passiveTorqueAngleCurveOffset,
passiveTorqueAngleBlendingVariable,
mTpLambdaMax,
1,1,
mTpCurve);
double D2_tp_D_tpLambda_D_fiberAngleOffset = -D2_tp_D_tpLambda_D_fiberAngle;
//Damping derivatives
double D_tb_D_wn = tp * (-1.0*mBetaMax);
double D_tb_D_tpLambda = -D_tp_D_tpLambda*mBetaMax*omegaNorm;
double D_tb_D_fiberAngularVelocity = D_tb_D_wn*D_wn_D_w;
double D_tb_D_fiberAngle = -D_tp_D_fiberAngle*mBetaMax*omegaNorm;
double D_tb_D_fiberAngleOffset= -D_tp_D_fiberAngleOffset*mBetaMax*omegaNorm;
//Damping second derivatives
double D2_tb_D_tpLambda_D_fiberAngleOffset =
-D2_tp_D_tpLambda_D_fiberAngleOffset*mBetaMax*omegaNorm;
double D2_tb_D_tpLambda2 =
-D2_tp_D_tpLambda2*mBetaMax*omegaNorm;
double D2_tb_D_fiberAngleOffset2 =
-D2_tp_D_fiberAngleOffset2*mBetaMax*omegaNorm;
//Sign conventions
double D_fiberAngle_D_jointAngle = mSignOfJointAngle;
double D_fiberAngularVelocity_D_jointAngularVelocity =
mSignOfConcentricAnglularVelocity;
updTmi.jointAngle = jointAngle;
updTmi.jointAngularVelocity = jointAngularVelocity;
updTmi.fiberAngle = fiberAngle;
updTmi.fiberAngularVelocity = fiberAngularVelocity;
updTmi.fiberPassiveTorqueAngleMultiplier = tp;
updTmi.fiberActiveTorqueAngleMultiplier = ta;
updTmi.fiberTorqueAngularVelocityMultiplier = tv;
updTmi.activation = activation;
updTmi.fiberActiveTorque = maxActIsoTorque*(activation*ta*tv);
updTmi.fiberPassiveTorque = maxActIsoTorque*(tp+tb);
updTmi.fiberPassiveElasticTorque = maxActIsoTorque*tp;
updTmi.fiberDampingTorque = maxActIsoTorque*tb;
updTmi.fiberNormDampingTorque = tb;
updTmi.fiberTorque = updTmi.fiberActiveTorque + updTmi.fiberPassiveTorque;
updTmi.jointTorque = mSignOfJointTorque*updTmi.fiberTorque;
updTmi.fiberStiffness = maxActIsoTorque*(
activation*D_ta_D_fiberAngle*tv
+ D_tp_D_fiberAngle
+ D_tb_D_fiberAngle);
updTmi.jointStiffness = mSignOfJointTorque
*updTmi.fiberStiffness
*D_fiberAngle_D_jointAngle;
updTmi.fiberActivePower = updTmi.fiberActiveTorque
* updTmi.fiberAngularVelocity;
updTmi.fiberPassivePower = updTmi.fiberPassiveTorque
* updTmi.fiberAngularVelocity;
updTmi.fiberPower = updTmi.fiberActivePower
+ updTmi.fiberPassivePower;
updTmi.jointPower = updTmi.jointTorque * jointAngularVelocity;
updTmi.DfiberPassiveTorqueAngleMultiplier_DblendingVariable
= D_tp_D_tpLambda;
updTmi.DfiberActiveTorqueAngleMultiplier_DblendingVariable
= D_ta_D_taLambda;
updTmi.DfiberTorqueAngularVelocityMultiplier_DblendingVariable
= D_tv_D_tvLambda;
updTmi.DfiberPassiveTorqueAngleMultiplier_DangleOffset
= D_tp_D_fiberAngleOffset;
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)*(1-beta*omegaNorm)
// Dtau_Da = tauMaxIso*(ta(theta) * tv(thetaDot) )
updTmi.DjointTorque_Dactivation =
mSignOfJointTorque
*maxActIsoTorque
*(ta * tv);
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)*(1-beta*omegaNorm)
//Dtau_Dtheta = signTq*tauIso*(a*Dta_Dtheta(theta)*tv(thetaDot)
// + Dtp_Dtheta(theta)*(1-beta*omegaNorm)
updTmi.DjointTorque_DjointAngle =
mSignOfJointTorque
* maxActIsoTorque
* ( activation
*D_ta_D_fiberAngle
* tv
+ ( D_tp_D_fiberAngle
+ D_tb_D_fiberAngle)
)* D_fiberAngle_D_jointAngle;
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)*(1-beta*omegaNorm)
//Dtau_Domega = signTq*tauIso*(a * ta(theta) * Dtv_DthetaDot(thetaDot)
// - tp(theta)*beta*DomegaNorm_thetaDot
updTmi.DjointTorque_DjointAngularVelocity =
mSignOfJointTorque
* maxActIsoTorque
* ( activation
* ta
* ( D_tv_D_fiberAngularVelocity
*D_fiberAngularVelocity_D_jointAngularVelocity)
+ ( D_tb_D_fiberAngularVelocity
*D_fiberAngularVelocity_D_jointAngularVelocity)
);
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)*(1-beta*omegaNorm)
//Dtau_DtaLambda = signTq*tauIso*(a * Dta(theta)_Dlambda * tv(thetaDot)
updTmi.DjointTorque_DactiveTorqueAngleBlendingVariable =
mSignOfJointTorque
* maxActIsoTorque
*(activation*D_ta_D_taLambda*tv);
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)*(1-beta*omegaNorm)
//Dtau_DtpLambda = signTq*tauIso*( D_tp(theta)_D_lambda(1-beta*omegaNorm)
updTmi.DjointTorque_DpassiveTorqueAngleBlendingVariable =
mSignOfJointTorque
* maxActIsoTorque
*(D_tp_D_tpLambda + D_tb_D_tpLambda);
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)*(1-beta*omegaNorm)
//Dtau_tvLambda = signTq*tauIso*(a*ta(theta)*Dtv_Dlambda(thetaDot)
updTmi.DjointTorque_DtorqueAngularVelocityBlendingVariable =
mSignOfJointTorque
* maxActIsoTorque
*(activation*ta*D_tv_D_tvLambda);
// mSignOfJointTorque*( (maxActIsoTorque*(activation*ta*tv)
// + maxActIsoTorque*(tp+tb)))
updTmi.DjointTorque_DmaximumIsometricTorque =
mSignOfJointTorque
*((activation*ta*tv) + (tp+tb));
updTmi.DjointTorque_DpassiveTorqueAngleCurveAngleOffset =
mSignOfJointTorque
*maxActIsoTorque
*(D_tp_D_fiberAngleOffset + D_tb_D_fiberAngleOffset);
//* D_fiberAngle_D_jointAngle;
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)+tb(theta,omega)
//Dtau_DomegaMax = signTq*tauIso*(a*ta(theta)*Dtv_DomegaMax(thetaDot)
// + tp(theta)*(-beta*D_omegaNorm_DomegaMax)
updTmi.DjointTorque_DmaximumAngularVelocity =
mSignOfJointTorque
*maxActIsoTorque
*(activation*ta*D_tv_D_omegaMax + D_tb_D_omegaMax);
//tau = signTq*tauIso*(a*ta(theta)*tv(thetaDot) + tp(theta)+tb(theta,omega)
//Dtau_DtaAngleScaling = signTq*tauIso*(a*Dta(theta)_DangleScaling*tv(thetaDot)
updTmi.DjointTorque_DactiveTorqueAngleAngleScaling =
mSignOfJointTorque
*maxActIsoTorque
*(activation*D_ta_D_angleScaling*tv);
/*
Second derivatives of the exposed variables, x, for fitting:
Option 1:
x = [taLambda,tvLambda,tpLambda,tpOffset]
For brevity, the short forms will be used just in the comments below
x = [a, v, p, o]
For reference:
t(a,v,p,o) = mSignOfJointTorque*(
(maxActIsoTorque*(
activation*ta(a)*tv(v)) + (tp(p,o)+tb(p,o))))
Where 't' is the shortform used to represent joint-torque.
The lower right triangle for the Hessian of tau(a,v,p,o) w.r.t. x is
stored, row-wise, in the vector fittingInfo. Thus we have
a v p o
H(tau(a,v,p,o),x) = a [ 0: d2t/da2
v [ 1: d2t/dadv 2: d2t/dv2
p [ 3: d2t/dadp 4: d2t/dvdp 5: d2t/dp2
o [ 6: d2t/dado 7: d2t/dvdo 8: d2t/dpdo 9: d2t/do2
The numbers indicate the index of the quantity in fittingInfo.
Option 2:
x = [taAngleScaling, tvOmegaMax, tpLambda,tpOffset]
for brevity
x = [s, m, p, o]
For reference:
t(s,m,p,o) = mSignOfJointTorque*(
(maxActIsoTorque*(
activation*ta(s)*tv(m)) + (tp(p,o)+tb(p,o))))
Where 't' is the shortform used to represent joint-torque.
The lower right triangle for the Hessian of tau(s,m,p,o) w.r.t. x is
stored, row-wise, in the vector fittingInfo. Thus we have
s m p o
H(tau(s,m,p,o),x) = s [ 0: d2t/ds2
m [ 1: d2t/dsdm 2: d2t/dm2
p [ 3: d2t/dsdp 4: d2t/dmdp 5: d2t/dp2
o [ 6: d2t/dsdo 7: d2t/dmdo 8: d2t/dpdo 9: d2t/do2
The numbers indicate the index of the quantity in fittingInfo.
So that both fitting options are possible, the extra entries for the
above Hessian will just be concatentated to the existing vector. Three
of the entries are duplicates, but for now they are being included
just to make this slightly easier to use for now. Thus the vector of
2nd derivatives will look like:
For joint-torque related constraints that use x = [a,v,p,o]
0: d2t/da2
1: d2t/dadv
2: d2t/dv2
3: d2t/dadp
4: d2t/dvdp
5: d2t/dp2
6: d2t/dado
7: d2t/dvdo
8: d2t/dpdo
9: d2t/do2
For constraints on the value of the passive element
10: d2p/dp2
11: d2p/dpdo
12: d2p/do2
For joint-torque related constraints that use x = [s,m,p,o]
13: d2t/ds2
14: d2t/dsdm
15: d2t/dm2
16: d2t/dsdp
17: d2t/dmdp
18: d2t/dp2
19: d2t/dsdo
20: d2t/dmdo
21: d2t/dpdo
22: d2t/do2
*/
#ifdef RBDL_BUILD_ADDON_MUSCLE_FITTING
updTmi.fittingInfo.conservativeResize(23);
//0: d2t/da2
updTmi.fittingInfo[0] = mSignOfJointTorque*(maxActIsoTorque*(
activation*D2_ta_D_taLambda2*tv));
//1: d2t/dadv
updTmi.fittingInfo[1] = mSignOfJointTorque*(maxActIsoTorque*
(activation*D_ta_D_taLambda*D_tv_D_tvLambda));
//2: d2t/dv2
updTmi.fittingInfo[2] = mSignOfJointTorque*(maxActIsoTorque*
(activation*ta*D2_tv_D_tvLambda2));
//3: d2t/dadp
updTmi.fittingInfo[3] = 0.;
//4: d2t/dvdp
updTmi.fittingInfo[4] = 0.;
//5: d2t/dp2
updTmi.fittingInfo[5] = mSignOfJointTorque*(maxActIsoTorque*(
D2_tp_D_tpLambda2+D2_tb_D_tpLambda2));
//6: d2t/dado
updTmi.fittingInfo[6] = 0.;
//7: d2t/dvdo
updTmi.fittingInfo[7] = 0.;
//8: d2t/dpdo
updTmi.fittingInfo[8] = mSignOfJointTorque*(maxActIsoTorque*(
D2_tp_D_tpLambda_D_fiberAngleOffset
+D2_tb_D_tpLambda_D_fiberAngleOffset
));//*D_fiberAngle_D_jointAngle);
//9: d2t/do2
updTmi.fittingInfo[9] = mSignOfJointTorque*(maxActIsoTorque*(
D2_tp_D_fiberAngleOffset2
+D2_tb_D_fiberAngleOffset2
));//*D_fiberAngle_D_jointAngle);
//10: d2tp/dp2
updTmi.fittingInfo[10] = D2_tp_D_tpLambda2;
//11: d2tp/dpdo
updTmi.fittingInfo[11] = D2_tp_D_tpLambda_D_fiberAngleOffset;
//12: d2tp/do2
updTmi.fittingInfo[12] = D2_tp_D_fiberAngleOffset2;
//13: d2t/ds2
updTmi.fittingInfo[13] = mSignOfJointTorque
*maxActIsoTorque
*(activation*D2_ta_D_angleScaling2*tv);
//14: d2t/dsdm
updTmi.fittingInfo[14] = mSignOfJointTorque
*maxActIsoTorque
*(activation*D_ta_D_angleScaling*D_tv_D_omegaMax);
//15: d2t/dm2
updTmi.fittingInfo[15] = mSignOfJointTorque
*maxActIsoTorque
*(activation*ta*D2_tv_D_omegaMax2 + D2_tb_D_omegaMax2);
//16: d2t/dsdp
updTmi.fittingInfo[16] = 0.;
//17: d2t/dmdp
updTmi.fittingInfo[17] = mSignOfJointTorque
*maxActIsoTorque
*(D2_tb_D_omegaMax_D_tpLambda);
//18: d2t/dp2
updTmi.fittingInfo[18] = mSignOfJointTorque*(
maxActIsoTorque*(
D2_tp_D_tpLambda2+D2_tb_D_tpLambda2));
//19: d2t/dsdo
updTmi.fittingInfo[19] = 0.;
//20: d2t/dmdo
updTmi.fittingInfo[20] = mSignOfJointTorque
*maxActIsoTorque
*(D2_tb_D_omegaMax_D_tpOffset);
//21: d2t/dpdo
updTmi.fittingInfo[21] = mSignOfJointTorque
*(maxActIsoTorque
*( D2_tp_D_tpLambda_D_fiberAngleOffset
+D2_tb_D_tpLambda_D_fiberAngleOffset));
//22: d2t/do2
updTmi.fittingInfo[22] = mSignOfJointTorque
*(maxActIsoTorque
*( D2_tp_D_fiberAngleOffset2
+D2_tb_D_fiberAngleOffset2));
#endif
}
//=============================================================================
void Millard2016TorqueMuscle::
updTorqueMuscleSummary( double activation,
double jointAngle,
double jointAngularVelocity,
double activeTorqueAngleBlendingVariable,
double passiveTorqueAngleBlendingVariable,
double torqueAngularVelocityBlendingVariable,
double activeTorqueAngleAngleScaling,
double activeTorqueAngleAtOneNormTorque,
double passiveTorqueAngleCurveOffset,
double maxAngularVelocity,
double maxActIsoTorque,
TorqueMuscleSummary &updTms) const
{
double fiberAngle = calcFiberAngle(jointAngle);
double fiberVelocity = calcFiberAngularVelocity(jointAngularVelocity);
double fiberVelocityNorm = fiberVelocity/maxAngularVelocity;
updTorqueMuscleSummaryCurveValues(fiberAngle,
fiberVelocityNorm,
activeTorqueAngleBlendingVariable,
passiveTorqueAngleBlendingVariable,
torqueAngularVelocityBlendingVariable,
activeTorqueAngleAngleScaling,
activeTorqueAngleAtOneNormTorque,
passiveTorqueAngleCurveOffset,
updTms);
updTms.fiberAngle = fiberAngle;
updTms.fiberAngularVelocity = fiberVelocity;
updTms.activation = activation;
updTms.fiberTorque =
maxActIsoTorque
*( activation*( updTms.fiberActiveTorqueAngleMultiplier
*updTms.fiberTorqueAngularVelocityMultiplier
)
+ ( updTms.fiberPassiveTorqueAngleMultiplier
+ updTms.fiberNormalizedDampingTorque
)
);
updTms.jointTorque = updTms.fiberTorque*mSignOfJointTorque;
}
void Millard2016TorqueMuscle::
updInvertTorqueMuscleSummary(double jointTorque,
double jointAngle,
double jointAngularVelocity,
double activeTorqueAngleBlendingVariable,
double passiveTorqueAngleBlendingVariable,
double torqueAngularVelocityBlendingVariable,
double activeTorqueAngleAngleScaling,
double activeTorqueAngleAtOneNormTorque,
double passiveTorqueAngleCurveOffset,
double maxAngularVelocity,
double maxActIsoTorque,
TorqueMuscleSummary &updTms) const
{
double fiberAngle = calcFiberAngle(jointAngle);
double fiberVelocity = calcFiberAngularVelocity(jointAngularVelocity);
double fiberVelocityNorm = fiberVelocity/maxAngularVelocity;
updTorqueMuscleSummaryCurveValues(fiberAngle,
fiberVelocityNorm,
activeTorqueAngleBlendingVariable,
passiveTorqueAngleBlendingVariable,
torqueAngularVelocityBlendingVariable,
activeTorqueAngleAngleScaling,
activeTorqueAngleAtOneNormTorque,
passiveTorqueAngleCurveOffset,
updTms);
updTms.fiberAngle = fiberAngle;
updTms.fiberAngularVelocity = fiberVelocity;
updTms.jointTorque = jointTorque;
updTms.fiberTorque = jointTorque*mSignOfJointTorque;
updTms.activation =
( (updTms.fiberTorque/maxActIsoTorque)
- (updTms.fiberPassiveTorqueAngleMultiplier
+ updTms.fiberNormalizedDampingTorque)
)/( updTms.fiberActiveTorqueAngleMultiplier
*updTms.fiberTorqueAngularVelocityMultiplier);
}
//==============================================================================