/* * * * Copyright (c) 2016 Matthew Millard * * Licensed under the zlib license. See LICENSE for more details. */ //Some particularly aggressive experimental data unsigned int TorqueMuscleFittingHardTestCaseRows = 100; unsigned int TorqueMuscleFittingHardTestCaseCols = 4; double const TorqueMuscleFittingHardTestCase[100][4] = {{0.,1.9148,-17.618,107.25 }, {0.0044242,1.8318,-18.277,108.39 }, {0.0088485,1.7485,-18.949,110.49 }, {0.013273,1.6636,-19.618,114.57 }, {0.017697,1.5761,-20.243,121.32 }, {0.022121,1.486,-20.758,130.97 }, {0.026545,1.3938,-21.085,143.31 }, {0.03097,1.3004,-21.144,157.68 }, {0.035394,1.2074,-20.866,173.11 }, {0.039818,1.1164,-20.199,188.42 }, {0.044242,1.0292,-19.118,202.36 }, {0.048667,0.94777,-17.626,213.77 }, {0.053091,0.87376,-15.762,221.64 }, {0.057515,0.80872,-13.594,225.25 }, {0.061939,0.75378,-11.22,224.18 }, {0.066364,0.70958,-8.7587,218.42 }, {0.070788,0.67624,-6.3387,208.34 }, {0.075212,0.65329,-4.0821,194.73 }, {0.079636,0.63975,-2.0906,178.7 }, {0.084061,0.63427,-0.43189,161.6 }, {0.088485,0.63528,0.87052,144.75 }, {0.092909,0.6412,1.8387,129.18 }, {0.097333,0.6507,2.5306,115.24 }, {0.10176,0.66277,3.015,102.36 }, {0.10618,0.67685,3.3393,88.889 }, {0.11061,0.69264,3.517,72.481 }, {0.11503,0.71007,3.5756,50.979 }, {0.11945,0.72927,3.6372,23.504 }, {0.12388,0.75048,3.852,-8.9796 }, {0.1283,0.77332,4.1461,-43.995 }, {0.13273,0.79611,4.1835,-78.49 }, {0.13715,0.81635,3.6883,-109.71 }, {0.14158,0.83172,2.6498,-135.63 }, {0.146,0.84066,1.2202,-154.98 }, {0.15042,0.84231,-0.42406,-167.16 }, {0.15485,0.83637,-2.1355,-172.03 }, {0.15927,0.82297,-3.8027,-169.8 }, {0.1637,0.80254,-5.3408,-160.95 }, {0.16812,0.7758,-6.6819,-146.16 }, {0.17255,0.74371,-7.771,-126.31 }, {0.17697,0.70746,-8.5684,-102.56 }, {0.18139,0.66837,-9.0566,-76.369 }, {0.18582,0.62778,-9.2487,-49.623 }, {0.19024,0.58691,-9.1941,-24.563 }, {0.19467,0.54667,-8.9737,-3.6038 }, {0.19909,0.50763,-8.6741,11.165 }, {0.20352,0.47001,-8.3313,18.713 }, {0.20794,0.43418,-7.8528,19.766 }, {0.21236,0.40128,-6.9609,16.835 }, {0.21679,0.37397,-5.266,12.889 }, {0.22121,0.35642,-2.5349,9.4338 }, {0.22564,0.35296,1.0546,6.2364 }, {0.23006,0.36634,4.9935,2.8315 }, {0.23448,0.39689,8.7586,-0.69647 }, {0.23891,0.44304,11.998,-4.1689 }, {0.24333,0.50198,14.522,-7.5873 }, {0.24776,0.57037,16.269,-11.01 }, {0.25218,0.64488,17.306,-14.511 }, {0.25661,0.72269,17.793,-18.219 }, {0.26103,0.8018,17.929,-22.301 }, {0.26545,0.8811,17.902,-26.91 }, {0.26988,0.96017,17.846,-32.128 }, {0.2743,1.0391,17.834,-37.93 }, {0.27873,1.1181,17.879,-44.158 }, {0.28315,1.1973,17.961,-50.527 }, {0.28758,1.277,18.039,-56.666 }, {0.292,1.3569,18.078,-62.174 }, {0.29642,1.4368,18.054,-66.692 }, {0.30085,1.5165,17.955,-69.98 }, {0.30527,1.5956,17.784,-71.983 }, {0.3097,1.6738,17.552,-72.861 }, {0.31412,1.7509,17.272,-72.976 }, {0.31855,1.8266,16.952,-72.834 }, {0.32297,1.9008,16.594,-72.982 }, {0.32739,1.9733,16.191,-73.899 }, {0.33182,2.0439,15.724,-75.903 }, {0.33624,2.1123,15.162,-79.088 }, {0.34067,2.1779,14.47,-83.322 }, {0.34509,2.24,13.609,-88.273 }, {0.34952,2.2979,12.55,-93.472 }, {0.35394,2.3507,11.283,-98.369 }, {0.35836,2.3974,9.8194,-102.39 }, {0.36279,2.4373,8.1988,-105.01 }, {0.36721,2.4698,6.4835,-105.77 }, {0.37164,2.4947,4.7502,-104.37 }, {0.37606,2.512,3.0759,-100.74 }, {0.38048,2.5221,1.5245,-95.063 }, {0.38491,2.5257,0.13371,-87.783 }, {0.38933,2.5235,-1.0913,-79.537 }, {0.39376,2.5163,-2.178,-71.042 }, {0.39818,2.5045,-3.1764,-62.958 }, {0.40261,2.4884,-4.1428,-55.768 }, {0.40703,2.468,-5.122,-49.731 }, {0.41145,2.443,-6.136,-44.898 }, {0.41588,2.4132,-7.1827,-41.179 }, {0.4203,2.3784,-8.2437,-38.413 }, {0.42473,2.3383,-9.2979,-36.422 }, {0.42915,2.2933,-10.332,-35.021 }, {0.43358,2.2443,-11.347,-34.007 }, {0.438,2.193,-12.35,-33.158 }}; //============================================================================== // INCLUDES //============================================================================== #include "../Millard2016TorqueMuscle.h" #ifdef RBDL_BUILD_ADDON_MUSCLE_FITTING #include "../TorqueMuscleFittingToolkit.h" #endif #include "../csvtools.h" #include "../../geometry/tests/numericalTestFunctions.h" #include #include #include #include #include #include #include #include #include #include using namespace RigidBodyDynamics::Addons::Muscle; using namespace RigidBodyDynamics::Addons::Geometry; using namespace std; /* Constructor tests: 1. Coefficients are copied over correctly. 2. Curves are made correctly calcTorqueMuscleInfo test stiffness calculation power calculation */ TEST(ConstructorRegularCallCheck) { //Check that the 3 constructors when called properly //do not abort Millard2016TorqueMuscle test0 = Millard2016TorqueMuscle(); SubjectInformation subjectInfo; subjectInfo.gender = GenderSet::Male; subjectInfo.ageGroup = AgeGroupSet::Young18To25; subjectInfo.heightInMeters = 1.732; subjectInfo.massInKg = 69.0; Millard2016TorqueMuscle test2 = Millard2016TorqueMuscle( DataSet::Anderson2007, subjectInfo, Anderson2007::HipExtension, 0.0, 1.0, 1.0, "test_easyConstructor"); CHECK(fabs( test2.getPassiveTorqueScale()-1.0) < TOL); } TEST(calcJointTorqueCorrectnessTests){ double jointAngleOffset = 0; double signOfJointAngle = 1; double signOfJointTorque = 1; double err = 0.0; std::string name("test"); SubjectInformation subjectInfo; subjectInfo.gender = GenderSet::Male; subjectInfo.ageGroup = AgeGroupSet::Young18To25; subjectInfo.heightInMeters = 1.732; subjectInfo.massInKg = 69.0; Millard2016TorqueMuscle tq = Millard2016TorqueMuscle(DataSet::Anderson2007, subjectInfo, Anderson2007::HipExtension, jointAngleOffset, signOfJointAngle, signOfJointTorque, name); bool flagMakeTestVector = false; if(flagMakeTestVector){ Millard2016TorqueMuscle tqG = Millard2016TorqueMuscle(DataSet::Gymnast, subjectInfo, Gymnast::HipExtension, jointAngleOffset, signOfJointAngle, signOfJointTorque, name); TorqueMuscleInfo tmiG; tqG.calcTorqueMuscleInfo(M_PI/3.0,0.1,0.77,tmiG); printf("%f\n",tmiG.fiberAngle); printf("%f\n",tmiG.fiberAngularVelocity); printf("%f\n",tmiG.activation); printf("%f\n",tmiG.fiberTorque); printf("%f\n",tmiG.fiberStiffness); printf("%f\n",tmiG.fiberPassiveTorqueAngleMultiplier); printf("%f\n",tmiG.fiberActiveTorqueAngleMultiplier); printf("%f\n",tmiG.fiberTorqueAngularVelocityMultiplier); printf("%f\n",tmiG.fiberPassiveTorque); printf("%f\n",tmiG.fiberActiveTorque); printf("%f\n",tmiG.fiberDampingTorque); printf("%f\n",tmiG.fiberNormDampingTorque); printf("%f\n",tmiG.fiberActivePower); printf("%f\n",tmiG.fiberPassivePower); printf("%f\n",tmiG.fiberPower); printf("%f\n",tmiG.DjointTorque_DjointAngle); printf("%f\n",tmiG.DjointTorque_DjointAngularVelocity); printf("%f\n",tmiG.DjointTorque_Dactivation); } //Zero out the passive forces so that calcMuscleTorque reports //just the active force - this allows us to test its correctness. tq.setPassiveTorqueScale(0.0); double tmp = tq.calcJointTorque(0,0,1.0); //Test that the get and set functions work for //maximum isometric torque double tauMaxOld = tq.getMaximumActiveIsometricTorque(); double tauMax = tauMaxOld*10.0; tq.setMaximumActiveIsometricTorque(tauMax); tmp = tq.calcJointTorque(0,0,1.0); //ensures updateTorqueMuscleCurves is called CHECK(fabs( tq.getMaximumActiveIsometricTorque()-tauMax) < TOL ); double omegaMaxOld = tq.getMaximumConcentricJointAngularVelocity(); double omegaMax = 2.0*fabs(omegaMaxOld); tq.setMaximumConcentricJointAngularVelocity(omegaMax); tmp = tq.calcJointTorque(0,0,1.0); //ensures updateTorqueMuscleCurves is called CHECK(fabs( fabs(tq.getMaximumConcentricJointAngularVelocity())-omegaMax) < TOL ); double taAngleScalingOld = tq.getActiveTorqueAngleCurveAngleScaling(); double taAngleScaling = 2.0*taAngleScalingOld; tq.setActiveTorqueAngleCurveAngleScaling(taAngleScaling); CHECK(fabs(taAngleScaling-tq.getActiveTorqueAngleCurveAngleScaling()) = 23); //For joint-torque related constrains that use x = [a,v,p,o] //(see the code for the parts of calcTorqueMuscleInfo that evaluate //the second derivatives for further detail. //Test the first column of the Hessian //0: D^2 tau / D lambdaTa^2 tq.setPassiveTorqueAngleCurveBlendingVariable( tpLambda); tq.setTorqueAngularVelocityCurveBlendingVariable( tvLambda); tq.setActiveTorqueAngleCurveBlendingVariable( taLambda); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setActiveTorqueAngleCurveBlendingVariable( taLambda-h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setActiveTorqueAngleCurveBlendingVariable( taLambda+h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); double D2tau_DtaLambda2 = (tmiR.DjointTorque_DactiveTorqueAngleBlendingVariable - tmiL.DjointTorque_DactiveTorqueAngleBlendingVariable)/(2.0*h); err = fabs(D2tau_DtaLambda2 - tmi.fittingInfo[0]); CHECK(err < SQRTEPSILON*100); //1: Test D^2 tau / D TaLambda D TvLambda double D2tau_DtaLambdaDtvLambda = (tmiR.DjointTorque_DtorqueAngularVelocityBlendingVariable - tmiL.DjointTorque_DtorqueAngularVelocityBlendingVariable)/(2.0*h); err = fabs(D2tau_DtaLambdaDtvLambda -tmi.fittingInfo[1]); CHECK(err < SQRTEPSILON*100); //3: Test D^2 tau/ D TaLambda D TpLambda double D2tau_DtaLambdaDtpLambda = (tmiR.DjointTorque_DpassiveTorqueAngleBlendingVariable -tmiL.DjointTorque_DpassiveTorqueAngleBlendingVariable)/(2.0*h); err = fabs(D2tau_DtaLambdaDtpLambda -tmi.fittingInfo[3]); CHECK(err < SQRTEPSILON*100); //6: Test D^2 tau/ D TaLambda DTpOffset double D2tau_DtaLambdaDtpOffset = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset -tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DtaLambdaDtpOffset - tmi.fittingInfo[6]); //Check the 2nd column of the Hessian //2: Test D^2 tau/ D Tvlambda^2 tq.setActiveTorqueAngleCurveBlendingVariable( taLambda); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setTorqueAngularVelocityCurveBlendingVariable( tvLambda - h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setTorqueAngularVelocityCurveBlendingVariable( tvLambda + h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); double D2tau_DtvLambda2 = (tmiR.DjointTorque_DtorqueAngularVelocityBlendingVariable - tmiL.DjointTorque_DtorqueAngularVelocityBlendingVariable)/(2.0*h); err = fabs(D2tau_DtvLambda2 - tmi.fittingInfo[2]); CHECK(err < SQRTEPSILON*100); //4: Test D^2 tau/ D TvLambda D Tplambda double D2tau_DtvLambdaDtpLambda = (tmiR.DjointTorque_DpassiveTorqueAngleBlendingVariable - tmiL.DjointTorque_DpassiveTorqueAngleBlendingVariable)/(2.0*h); err = fabs(D2tau_DtvLambdaDtpLambda - tmi.fittingInfo[4]); CHECK(err < SQRTEPSILON*100); //7: Test D^2 tau/ D TvLambda D TpOffset double D2tau_DtvLambdaDtpOffset = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset - tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DtvLambdaDtpOffset - tmi.fittingInfo[7]); CHECK(err < SQRTEPSILON*100); //Check the 3rd column of the Hessian. //5: D^2 tau / D TpLambda^2 tq.setTorqueAngularVelocityCurveBlendingVariable(tvLambda); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setPassiveTorqueAngleCurveBlendingVariable( tpLambda - h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setPassiveTorqueAngleCurveBlendingVariable( tpLambda + h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); double D2tau_DtpLambda2 = (tmiR.DjointTorque_DpassiveTorqueAngleBlendingVariable - tmiL.DjointTorque_DpassiveTorqueAngleBlendingVariable)/(2.0*h); err = fabs(D2tau_DtpLambda2 - tmi.fittingInfo[5]); CHECK(err < SQRTEPSILON*100); //8: D^2 tau/ D TpLambda DTpOffset double D2tau_DtpLambdaDtpOffset = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset - tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DtpLambdaDtpOffset - tmi.fittingInfo[8]); CHECK(err < SQRTEPSILON*100); //Check the 4th column of the Hessian. //9: D^2 tau/ D TpOffset^2 tq.setTorqueAngularVelocityCurveBlendingVariable(tvLambda); tq.setPassiveCurveAngleOffset(0.); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setPassiveCurveAngleOffset(-h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setPassiveCurveAngleOffset( h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); double D2tau_DtpOffset2 = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset - tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DtpOffset2 - tmi.fittingInfo[9]); CHECK(err < SQRTEPSILON*1000); // For constraints on the value of the passive element // 10: d2p/dp2 tq.setPassiveTorqueAngleCurveBlendingVariable(tpLambda); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setPassiveTorqueAngleCurveBlendingVariable(tpLambda-h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setPassiveTorqueAngleCurveBlendingVariable(tpLambda+h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); double D2tp_DtpLambda2 = (tmiR.DfiberPassiveTorqueAngleMultiplier_DblendingVariable - tmiL.DfiberPassiveTorqueAngleMultiplier_DblendingVariable)/(2.0*h); err = fabs(D2tp_DtpLambda2 - tmi.fittingInfo[10]); CHECK(err < SQRTEPSILON*1000); // 11: d2p/dpdo double D2tp_DtpLambda_DangleOffset = (tmiR.DfiberPassiveTorqueAngleMultiplier_DangleOffset -tmiL.DfiberPassiveTorqueAngleMultiplier_DangleOffset)/(2.0*h); err = fabs(D2tp_DtpLambda_DangleOffset -tmi.fittingInfo[11]); CHECK(err < SQRTEPSILON*1000); // 12: d2p/do2 tq.setPassiveTorqueAngleCurveBlendingVariable(tpLambda); tq.setPassiveCurveAngleOffset(0.); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setPassiveCurveAngleOffset(-h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setPassiveCurveAngleOffset( h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); double D2tp_DtpOffset2 = (tmiR.DfiberPassiveTorqueAngleMultiplier_DangleOffset - tmiL.DfiberPassiveTorqueAngleMultiplier_DangleOffset)/(2.0*h); err = fabs(D2tp_DtpOffset2 - tmi.fittingInfo[12]); CHECK(err < SQRTEPSILON*1000); // For joint-torque related constraints that use x = [s,m,p,o] // 13: d2t/ds2 tq.setPassiveTorqueAngleCurveBlendingVariable( 0.); tq.setTorqueAngularVelocityCurveBlendingVariable( 0.); tq.setActiveTorqueAngleCurveBlendingVariable( 0.); jointAngleAtOneNormTorque = tq.getJointAngleAtMaximumActiveIsometricTorque(); tq.setActiveTorqueAngleCurveAngleScaling(1.0); tq.calcTorqueMuscleInfo(jointAngleAtOneNormTorque+0.5,omegaMid,1.0,tmi); tq.setActiveTorqueAngleCurveAngleScaling(1.0-h); tq.calcTorqueMuscleInfo(jointAngleAtOneNormTorque+0.5,omegaMid,1.0,tmiL); tq.setActiveTorqueAngleCurveAngleScaling(1.0+h); tq.calcTorqueMuscleInfo(jointAngleAtOneNormTorque+0.5,omegaMid,1.0,tmiR); double D2tau_DtaAngleScaling2 = (tmiR.DjointTorque_DactiveTorqueAngleAngleScaling - tmiL.DjointTorque_DactiveTorqueAngleAngleScaling)/(2.0*h); err = fabs(D2tau_DtaAngleScaling2 - tmi.fittingInfo[13]); CHECK(err < SQRTEPSILON*1000); // 14: d2t/dsdm double D2tau_DtaAngleScaling_DomegaMax = (tmiR.DjointTorque_DmaximumAngularVelocity - tmiL.DjointTorque_DmaximumAngularVelocity)/(2.0*h); err = fabs(D2tau_DtaAngleScaling_DomegaMax - tmi.fittingInfo[14]); CHECK(err < SQRTEPSILON*100); // 16: d2t/dsdp double D2tau_DtaAngleScaling_DtpLambda = (tmiR.DjointTorque_DpassiveTorqueAngleBlendingVariable -tmiL.DjointTorque_DpassiveTorqueAngleBlendingVariable)/(2.0*h); err = fabs(D2tau_DtaAngleScaling_DtpLambda -tmi.fittingInfo[16]); CHECK(err < SQRTEPSILON*100); // 19: d2t/dsdo double D2tau_DtaAngleScaling_DtpOffset = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset -tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DtaAngleScaling_DtpOffset -tmi.fittingInfo[19]); CHECK(err < SQRTEPSILON*100); // 15: d2t/dm2 tq.setActiveTorqueAngleCurveAngleScaling(1.0); omegaMax = tq.getMaximumConcentricJointAngularVelocity(); omegaMid = 0.5*omegaMax; tq.setMaximumConcentricJointAngularVelocity(omegaMax); tq.calcTorqueMuscleInfo(jointAngleAtOneNormTorque,omegaMid,1.0,tmi); tq.setMaximumConcentricJointAngularVelocity(omegaMax-h); tq.calcTorqueMuscleInfo(jointAngleAtOneNormTorque,omegaMid,1.0,tmiL); tq.setMaximumConcentricJointAngularVelocity(omegaMax+h); tq.calcTorqueMuscleInfo(jointAngleAtOneNormTorque,omegaMid,1.0,tmiR); double D2tau_DomegaMax2 = (tmiR.DjointTorque_DmaximumAngularVelocity - tmiL.DjointTorque_DmaximumAngularVelocity)/(2.0*h); err = fabs(D2tau_DomegaMax2 - tmi.fittingInfo[15]); CHECK(err < SQRTEPSILON*100); // 17: d2t/dmdp double D2tau_DomegaMax_DtpLambda = (tmiR.DjointTorque_DpassiveTorqueAngleBlendingVariable -tmiL.DjointTorque_DpassiveTorqueAngleBlendingVariable)/(2.0*h); err = fabs(D2tau_DomegaMax_DtpLambda - tmi.fittingInfo[17]); CHECK(err < SQRTEPSILON*100); // 20: d2t/dmdo double D2tau_DomegaMax_DtpOffset = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset -tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DomegaMax_DtpOffset - tmi.fittingInfo[20]); CHECK(err < SQRTEPSILON*100); // 18: d2t/dp2 tq.setActiveTorqueAngleCurveBlendingVariable(taLambda); tq.setPassiveTorqueAngleCurveBlendingVariable(tpLambda); tq.setTorqueAngularVelocityCurveBlendingVariable(tvLambda); tq.setPassiveCurveAngleOffset(0.0); tq.setActiveTorqueAngleCurveAngleScaling(1.0); tq.setMaximumConcentricJointAngularVelocity(omegaMax); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setPassiveTorqueAngleCurveBlendingVariable( tpLambda - h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setPassiveTorqueAngleCurveBlendingVariable( tpLambda + h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); D2tau_DtpLambda2 = (tmiR.DjointTorque_DpassiveTorqueAngleBlendingVariable - tmiL.DjointTorque_DpassiveTorqueAngleBlendingVariable)/(2.0*h); err = fabs(D2tau_DtpLambda2 - tmi.fittingInfo[18]); CHECK(err < SQRTEPSILON*100); // 21: d2t/dpdo D2tau_DtpLambdaDtpOffset = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset - tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DtpLambdaDtpOffset - tmi.fittingInfo[21]); CHECK(err < SQRTEPSILON*100); // 22: d2t/do2 tq.setPassiveTorqueAngleCurveBlendingVariable(tpLambda); tq.setPassiveCurveAngleOffset(0.); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmi); tq.setPassiveCurveAngleOffset(-h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiL); tq.setPassiveCurveAngleOffset( h); tq.calcTorqueMuscleInfo(jointAngleTpMid,omegaMid,1.0,tmiR); D2tau_DtpOffset2 = (tmiR.DjointTorque_DpassiveTorqueAngleCurveAngleOffset - tmiL.DjointTorque_DpassiveTorqueAngleCurveAngleOffset)/(2.0*h); err = fabs(D2tau_DtpOffset2 - tmi.fittingInfo[22]); CHECK(err < SQRTEPSILON*1000); #endif } #ifdef RBDL_BUILD_ADDON_MUSCLE_FITTING TEST(fittingEasyTest){ double jointAngleOffset = 0; double signOfJointAngle = 1; double signOfJointTorque = 1; double signOfJointVelocity = signOfJointTorque; std::string name("test"); SubjectInformation subjectInfo; subjectInfo.gender = GenderSet::Male; subjectInfo.ageGroup = AgeGroupSet::Young18To25; subjectInfo.heightInMeters = 1.732; subjectInfo.massInKg = 69.0; Millard2016TorqueMuscle tq = Millard2016TorqueMuscle(DataSet::Gymnast, subjectInfo, Gymnast::HipExtension, jointAngleOffset, signOfJointAngle, signOfJointTorque, name); // Testing approach // 1. Generate a data set consistent with a muscle that is more flexible, // is stronger, and has modified curves from the default. // 2. Return the muscle back to its default values of flexibility, maximum // isometric torque and blending variables. // 3. Run the fitting algorithm to see if the parameters from step 1 can be // identified. double tisoOriginal = tq.getMaximumActiveIsometricTorque(); double tisoUpd = 1.2*tisoOriginal; double omegaMaxOrig = tq.getMaximumConcentricJointAngularVelocity(); double omegaMaxUpd = 2.5*omegaMaxOrig; double taAngleScalingOrig = tq.getActiveTorqueAngleCurveAngleScaling(); double taAngleScalingUpd = taAngleScalingOrig*1.35; double taLambda = 0.0; double tpLambda = 0.0; double tvLambda = 0.0; double angleToOneOrig = tq.getJointAngleAtOneNormalizedPassiveIsometricTorque(); tq.setActiveTorqueAngleCurveBlendingVariable(taLambda); tq.setPassiveTorqueAngleCurveBlendingVariable(tpLambda); tq.setTorqueAngularVelocityCurveBlendingVariable(tvLambda); tq.setMaximumActiveIsometricTorque(tisoUpd); tq.setMaximumConcentricJointAngularVelocity(omegaMaxUpd); tq.setActiveTorqueAngleCurveAngleScaling(taAngleScalingUpd); unsigned int npts = 100; RigidBodyDynamics::Math::VectorNd angle, angularVelocity, torque; angle.resize(npts); angularVelocity.resize(npts); torque.resize(npts); //Generate the data set. double activationUpperBound = 0.95; double passiveTorqueAngleMultiplierUpperBound = 0.; TorqueMuscleInfo tmi; TorqueMuscleSummary tms; double angularRange = M_PI/3.0; double angleTpOne = tq.getJointAngleAtOneNormalizedPassiveIsometricTorque(); double angularOffset = angleTpOne-angularRange; double time = 0.; double normTime = 0.; double omega = omegaMaxUpd/angularRange; for(unsigned int i = 0; i passiveTorqueAngleMultiplierUpperBound){ passiveTorqueAngleMultiplierUpperBound = tmi.fiberPassiveTorqueAngleMultiplier; } } //Return the muscle back to its defaults. tq.setActiveTorqueAngleCurveBlendingVariable(0.0); tq.setPassiveTorqueAngleCurveBlendingVariable(0.0); tq.setTorqueAngularVelocityCurveBlendingVariable(0.0); tq.setMaximumActiveIsometricTorque(tisoOriginal); tq.setMaximumConcentricJointAngularVelocity(omegaMaxOrig); tq.setActiveTorqueAngleCurveAngleScaling(taAngleScalingOrig); //Run the fitting routine. passiveTorqueAngleMultiplierUpperBound *= 0.75; TorqueMuscleParameterFittingData tmFittingData; TorqueMuscleFittingToolkit::fitTorqueMuscleParameters( tq, angle,angularVelocity,torque, activationUpperBound, passiveTorqueAngleMultiplierUpperBound, tmFittingData, false); //Now to test the result, update the parameters of the //model and run through all of the test vectors. The muscle //should produce a torque that is >= the desired torque //of the same sign. There should be one value where it //,to numerical precision produces just the required torque //and no more. CHECK(tmFittingData.fittingConverged == true); tq.setFittedParameters(tmFittingData); double minActivation = 1.e20; double maxActivation = -1.e20; double maxPassiveTorqueAngleMultiplier = -1.0e20; for(unsigned int i=0; i 0){ tq.calcActivation(angle[i],angularVelocity[i],torque[i],tms); if(tms.activation < minActivation){ minActivation = tms.activation; } if(tms.activation > maxActivation){ maxActivation = tms.activation; } if(tms.fiberPassiveTorqueAngleMultiplier > maxPassiveTorqueAngleMultiplier){ maxPassiveTorqueAngleMultiplier = tms.fiberPassiveTorqueAngleMultiplier; } } } double err = maxActivation-activationUpperBound; CHECK(err <= 10.0*SQRTEPSILON); err = minActivation; CHECK(err >= -10.0*SQRTEPSILON); err = (maxPassiveTorqueAngleMultiplier - passiveTorqueAngleMultiplierUpperBound); CHECK(err < SQRTEPSILON); } TEST(fittingHardTest) { //std::vector< std::vector< double > > data; //std::string fileName("TorqueMuscleFittingToolkitHardTestCase_TimeQQDotTau.csv"); //data = readMatrixFromFile(fileName, 0); std::string name("hardTest"); SubjectInformation subjectInfo; subjectInfo.gender = GenderSet::Male; subjectInfo.ageGroup = AgeGroupSet::Young18To25; subjectInfo.heightInMeters = 1.732; subjectInfo.massInKg = 69.0; double angleOffset = 0.; double angleSign = 1; double torqueSign =-1; Millard2016TorqueMuscle kneeExt(DataSet::Gymnast, subjectInfo, Gymnast::KneeExtension, angleOffset, angleSign, torqueSign, name); RigidBodyDynamics::Math::VectorNd q, qDot, tau; q.resize( TorqueMuscleFittingHardTestCaseRows); qDot.resize(TorqueMuscleFittingHardTestCaseRows); tau.resize( TorqueMuscleFittingHardTestCaseRows); double tauMax = 0; double omegaMax = 0; for(unsigned int i=0; iomegaMax){ omegaMax = fabs(qDot[i]); } if(fabs(tau[i])>tauMax){ tauMax = fabs(tau[i]); } } double activationUB = 0.9; double tpUB = 0.75; bool verbose = false; TorqueMuscleParameterFittingData fittingData; TorqueMuscleFittingToolkit::fitTorqueMuscleParameters(kneeExt,q,qDot,tau, activationUB,tpUB, fittingData,verbose); CHECK(fittingData.fittingConverged == true); } #endif TEST(exampleUsage){ bool printCurves = false; bool printAllCurves = false; //int dataSet = DataSetAnderson2007; //int gender = 0; //male //int age = 0; //young double jointAngleOffset = 0; double signOfJointAngle = 1; double signOfJointTorque = 1; std::string name("test"); SubjectInformation subjectInfo; subjectInfo.gender = GenderSet::Male; subjectInfo.ageGroup = AgeGroupSet::Young18To25; subjectInfo.heightInMeters = 1.732; subjectInfo.massInKg = 69.0; std::vector< Millard2016TorqueMuscle > muscleVector; bool exception = false; double angleTorqueSigns[][2] = {{-1, 1}, {-1,-1}, { 1,-1}, { 1, 1}, {-1, 1}, {-1,-1}}; Millard2016TorqueMuscle tqMuscle; std::stringstream tqName; int tqIdx; for(int i=0; i < Anderson2007::LastJointTorque; ++i){ tqName.str(""); tqName << DataSet.names[0] <