diff --git a/Bindings/Python/examples/Moco/example2DWalking/2D_gait.osim b/Bindings/Python/examples/Moco/example2DWalking/2D_gait.osim
new file mode 100644
index 0000000000..a784971254
--- /dev/null
+++ b/Bindings/Python/examples/Moco/example2DWalking/2D_gait.osim
@@ -0,0 +1,2720 @@
+
+
+
+
+
+
+
+ /contactgeometryset/heel_r
+
+ /contactgeometryset/floor
+
+ 3067776
+
+ 2
+
+ 0.80000000000000004
+
+ 0.80000000000000004
+
+ 0.5
+
+ 0.20000000000000001
+
+ 300
+
+ 50
+
+
+
+ /contactgeometryset/heel_l
+
+ /contactgeometryset/floor
+
+ 3067776
+
+ 2
+
+ 0.80000000000000004
+
+ 0.80000000000000004
+
+ 0.5
+
+ 0.20000000000000001
+
+ 300
+
+ 50
+
+
+
+ /contactgeometryset/front_r
+
+ /contactgeometryset/floor
+
+ 3067776
+
+ 2
+
+ 0.80000000000000004
+
+ 0.80000000000000004
+
+ 0.5
+
+ 0.20000000000000001
+
+ 300
+
+ 50
+
+
+
+ /contactgeometryset/front_l
+
+ /contactgeometryset/floor
+
+ 3067776
+
+ 2
+
+ 0.80000000000000004
+
+ 0.80000000000000004
+
+ 0.5
+
+ 0.20000000000000001
+
+ 300
+
+ 50
+
+
+
+ -150
+
+ 150
+
+ lumbar
+
+ 1
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.121645 -0.099055900000000002 0.068467600000000003
+
+
+
+ /bodyset/tibia_r
+
+ -0.029098599999999999 -0.034802399999999997 0.028450900000000001
+
+
+
+ /bodyset/tibia_r
+
+ -0.022621499999999999 -0.054427000000000003 0.033158899999999998
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 2700
+
+ 0.10905928770854099
+
+ 0.32617731920169202
+
+ 0
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/femur_r
+
+ 0.0050137300000000001 -0.21167900000000001 0.023464200000000001
+
+
+
+ /bodyset/tibia_r
+
+ -0.029098599999999999 -0.034802399999999997 0.028450900000000001
+
+
+
+ /bodyset/tibia_r
+
+ -0.022621499999999999 -0.054427000000000003 0.033158899999999998
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 804
+
+ 0.171872304562285
+
+ 0.088419856104296701
+
+ 0.40142572999999998
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.127188 0.0084019400000000001 0.045553000000000003
+
+
+
+ /bodyset/pelvis
+
+ -0.12979499999999999 -0.058813600000000001 0.080161499999999997
+
+
+
+ /bodyset/femur_r
+
+ -0.045123499999999997 -0.058560300000000003 0.025269199999999999
+
+
+
+ /bodyset/femur_r
+
+ -0.015642799999999998 -0.101879 0.042014999999999997
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 1944
+
+ 0.15477839388148101
+
+ 0.072308835382489306
+
+ 0
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.062483400000000001 0.085661100000000004 0.028495300000000001
+
+
+
+ /bodyset/pelvis
+
+ -0.022984600000000001 -0.055047199999999998 0.074837100000000004
+
+
+
+ /bodyset/pelvis
+
+ -0.027813299999999999 -0.077742000000000006 0.080457299999999995
+
+ /jointset/hip_r/hip_flexion_r
+
+ -1.5708 0.78539800000000004
+
+
+
+ /bodyset/femur_r
+
+ 0.00160439 -0.050839200000000001 0.0038104300000000001
+
+
+
+ /bodyset/femur_r
+
+ -0.0188516 -0.059863899999999998 0.0104285
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 2342
+
+ 0.097499585002471204
+
+ 0.15599933600395399
+
+ 0.13962633999999999
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.028489299999999999 -0.030034499999999999 0.095444399999999999
+
+
+
+ /bodyset/femur_r
+
+ 0.033491699999999999 -0.40410600000000002 0.00190522
+
+ /jointset/knee_r/knee_angle_r
+
+ -2.6179899999999998 -1.45997
+
+
+
+ /bodyset/tibia_r
+
+ /jointset/knee_r/knee_angle_r
+
+ /jointset/knee_r/knee_angle_r
+
+ /jointset/knee_r/knee_angle_r
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.0155805 0.0179938 0.0275081 0.0296564 0.0307615 0.0365695 0.0422074 0.0450902 0.048391 0.0534299 0.0617576 0.0617669 0.0617762 0.0633083 0.066994 0.0733035 0.0573481
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.0234116 0.0237613 0.0251141 0.0252795 0.0253146 0.0249184 0.0242373 0.0238447 0.0234197 0.0227644 0.020984 0.0209814 0.0209788 0.0205225 0.0191754 0.0159554 -0.0673774
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 0.1745
+ 0.0014 0.0014
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 1169
+
+ 0.114154396713697
+
+ 0.31041985071268602
+
+ 0.087266460000000004
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/femur_r
+
+ 0.029079600000000001 -0.19292799999999999 0.031085100000000001
+
+
+
+ /bodyset/femur_r
+
+ 0.033591999999999997 -0.20897199999999999 0.028578200000000002
+
+
+
+ /bodyset/femur_r
+
+ 0.0343942 -0.40410600000000002 0.0055151000000000002
+
+ /jointset/knee_r/knee_angle_r
+
+ -2.6179899999999998 -1.4199999999999999
+
+
+
+ /bodyset/tibia_r
+
+ /jointset/knee_r/knee_angle_r
+
+ /jointset/knee_r/knee_angle_r
+
+ /jointset/knee_r/knee_angle_r
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.0082733 0.0106866 0.0202042 0.022353 0.0234583 0.0292715 0.0349465 0.037871 0.0412569 0.0465287 0.0554632 0.0554735 0.0554837 0.0571717 0.061272 0.0684368 0.0648818
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.025599 0.0259487 0.0273124 0.0274796 0.0275151 0.0271363 0.0265737 0.0263073 0.0261187 0.0260129 0.0252923 0.0252911 0.0252898 0.0250526 0.0242191 0.0218288 -0.0685706
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 2.0944
+ 0.0018 0.0018
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 5000
+
+ 0.107708763662917
+
+ 0.116768379298115
+
+ 0.052359879999999998
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/femur_r
+
+ -0.019052199999999998 -0.39397900000000002 -0.023564499999999999
+
+
+
+ /bodyset/femur_r
+
+ -0.030082399999999999 -0.403304 -0.025870799999999999
+
+ /jointset/knee_r/knee_angle_r
+
+ -0.78539800000000004 0.17453299999999999
+
+
+
+ /bodyset/calcn_r
+
+ 0 0.028331700000000001 -0.0048437999999999997
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 2500
+
+ 0.086926079311056698
+
+ 0.34770431724422701
+
+ 0.29670596999999999
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/tibia_r
+
+ -0.0023201599999999999 -0.1482 0.0068637999999999998
+
+
+
+ /bodyset/calcn_r
+
+ 0 0.028331700000000001 -0.0048437999999999997
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 5137
+
+ 0.048170551124502803
+
+ 0.24085275562251399
+
+ 0.43633231
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/tibia_r
+
+ 0.0173045 -0.156997 0.0111174
+
+
+
+ /bodyset/tibia_r
+
+ 0.0318055 -0.38195600000000002 -0.0171112
+
+
+
+ /bodyset/calcn_r
+
+ 0.10656400000000001 0.016267899999999998 -0.027874699999999999
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 3000
+
+ 0.093789916893817799
+
+ 0.213419912931851
+
+ 0.087266460000000004
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.121645 -0.099055900000000002 -0.068467600000000003
+
+
+
+ /bodyset/tibia_l
+
+ -0.029098599999999999 -0.034802399999999997 -0.028450900000000001
+
+
+
+ /bodyset/tibia_l
+
+ -0.022621499999999999 -0.054427000000000003 -0.033158899999999998
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 2700
+
+ 0.10905928770854099
+
+ 0.32617731920169202
+
+ 0
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/femur_l
+
+ 0.0050137300000000001 -0.21167900000000001 -0.023464200000000001
+
+
+
+ /bodyset/tibia_l
+
+ -0.029098599999999999 -0.034802399999999997 -0.028450900000000001
+
+
+
+ /bodyset/tibia_l
+
+ -0.022621499999999999 -0.054427000000000003 -0.033158899999999998
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 804
+
+ 0.171872304562285
+
+ 0.088419856104296701
+
+ 0.40142572999999998
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.127188 0.0084019400000000001 -0.045553000000000003
+
+
+
+ /bodyset/pelvis
+
+ -0.12979499999999999 -0.058813600000000001 -0.080161499999999997
+
+
+
+ /bodyset/femur_l
+
+ -0.045123499999999997 -0.058560300000000003 -0.025269199999999999
+
+
+
+ /bodyset/femur_l
+
+ -0.015642799999999998 -0.101879 -0.042014999999999997
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 1944
+
+ 0.15477839388148101
+
+ 0.072308835382489306
+
+ 0
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.062483400000000001 0.085661100000000004 -0.028495300000000001
+
+
+
+ /bodyset/pelvis
+
+ -0.022984600000000001 -0.055047199999999998 -0.074837100000000004
+
+
+
+ /bodyset/pelvis
+
+ -0.027813299999999999 -0.077742000000000006 -0.080457299999999995
+
+ /jointset/hip_l/hip_flexion_l
+
+ -1.5708 0.78539800000000004
+
+
+
+ /bodyset/femur_l
+
+ 0.00160439 -0.050839200000000001 -0.0038104300000000001
+
+
+
+ /bodyset/femur_l
+
+ -0.0188516 -0.059863899999999998 -0.0104285
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 2342
+
+ 0.097499585002471204
+
+ 0.15599933600395399
+
+ 0.13962633999999999
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/pelvis
+
+ -0.028489299999999999 -0.030034499999999999 -0.095444399999999999
+
+
+
+ /bodyset/femur_l
+
+ 0.033491699999999999 -0.40410600000000002 -0.00190522
+
+ /jointset/knee_l/knee_angle_l
+
+ -2.6179899999999998 -1.45997
+
+
+
+ /bodyset/tibia_l
+
+ /jointset/knee_l/knee_angle_l
+
+ /jointset/knee_l/knee_angle_l
+
+ /jointset/knee_l/knee_angle_l
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.0155805 0.0179938 0.0275081 0.0296564 0.0307615 0.0365695 0.0422074 0.0450902 0.048391 0.0534299 0.0617576 0.0617669 0.0617762 0.0633083 0.066994 0.0733035 0.0573481
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.0234116 0.0237613 0.0251141 0.0252795 0.0253146 0.0249184 0.0242373 0.0238447 0.0234197 0.0227644 0.020984 0.0209814 0.0209788 0.0205225 0.0191754 0.0159554 -0.0673774
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 0.1745
+ -0.0014 -0.0014
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 1169
+
+ 0.114154396713697
+
+ 0.31041985071268602
+
+ 0.087266460000000004
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/femur_l
+
+ 0.029079600000000001 -0.19292799999999999 -0.031085100000000001
+
+
+
+ /bodyset/femur_l
+
+ 0.033591999999999997 -0.20897199999999999 -0.028578200000000002
+
+
+
+ /bodyset/femur_l
+
+ 0.0343942 -0.40410600000000002 -0.0055151000000000002
+
+ /jointset/knee_l/knee_angle_l
+
+ -2.6179899999999998 -1.4199999999999999
+
+
+
+ /bodyset/tibia_l
+
+ /jointset/knee_l/knee_angle_l
+
+ /jointset/knee_l/knee_angle_l
+
+ /jointset/knee_l/knee_angle_l
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.0082733 0.0106866 0.0202042 0.022353 0.0234583 0.0292715 0.0349465 0.037871 0.0412569 0.0465287 0.0554632 0.0554735 0.0554837 0.0571717 0.061272 0.0684368 0.0648818
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 -1.99997 -1.5708 -1.45752 -1.39626 -1.0472 -0.698132 -0.526391 -0.349066 -0.174533 0 0.00017453 0.00034907 0.0279253 0.0872665 0.174533 2.0944
+ 0.025599 0.0259487 0.0273124 0.0274796 0.0275151 0.0271363 0.0265737 0.0263073 0.0261187 0.0260129 0.0252923 0.0252911 0.0252898 0.0250526 0.0242191 0.0218288 -0.0685706
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+ -2.0944 2.0944
+ -0.0018 -0.0018
+
+
+ 0.96673215203466201
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 5000
+
+ 0.107708763662917
+
+ 0.116768379298115
+
+ 0.052359879999999998
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/femur_l
+
+ -0.019052199999999998 -0.39397900000000002 0.023564499999999999
+
+
+
+ /bodyset/femur_l
+
+ -0.030082399999999999 -0.403304 0.025870799999999999
+
+ /jointset/knee_l/knee_angle_l
+
+ -0.78539800000000004 0.17453299999999999
+
+
+
+ /bodyset/calcn_l
+
+ 0 0.028331700000000001 0.0048437999999999997
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 2500
+
+ 0.086926079311056698
+
+ 0.34770431724422701
+
+ 0.29670596999999999
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/tibia_l
+
+ -0.0023201599999999999 -0.1482 -0.0068637999999999998
+
+
+
+ /bodyset/calcn_l
+
+ 0 0.028331700000000001 0.0048437999999999997
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 5137
+
+ 0.048170551124502803
+
+ 0.24085275562251399
+
+ 0.43633231
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+ 0.01
+
+ 1
+
+
+
+
+
+
+
+ /bodyset/tibia_l
+
+ 0.0173045 -0.156997 -0.0111174
+
+
+
+ /bodyset/tibia_l
+
+ 0.0318055 -0.38195600000000002 0.0171112
+
+
+
+ /bodyset/calcn_l
+
+ 0.10656400000000001 0.016267899999999998 0.027874699999999999
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.80000000000000004 0.10000000000000001 0.10000000000000001
+
+
+
+ 3000
+
+ 0.093789916893817799
+
+ 0.213419912931851
+
+ 0.087266460000000004
+
+ 10
+
+ true
+
+ false
+
+ 0.014999999999999999
+
+ 0.059999999999999998
+
+ 0.01
+
+ 1
+
+ 0.01
+
+ 0.049000000000000002
+
+ false
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.96574000000000004 0.96574000000000004 0.98599599999999998
+
+
+
+ 1
+
+ 1 1 1
+
+
+ r_pelvis.vtp
+
+
+
+ ..
+
+ 0.96574000000000004 0.96574000000000004 0.98599599999999998
+
+
+
+ 1
+
+ 1 1 1
+
+
+ l_pelvis.vtp
+
+
+
+ ..
+
+ 0.96574000000000004 0.96574000000000004 0.98599599999999998
+
+
+
+ 1
+
+ 1 1 1
+
+
+ sacrum.vtp
+
+
+
+ 9.7143336091723995
+
+ -0.0682778 0 0
+
+ 0.081492884605030597 0.081492884605030597 0.044542759153066699 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 1.00275 1.00275 1.00275
+
+
+
+ 1
+
+ 1 1 1
+
+
+ femur_l.vtp
+
+
+
+ 7.6723191502382804
+
+ 0 -0.17046700000000001 0
+
+ 0.11105547289013901 0.029111628815861601 0.117110028170931 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 1.00275 1.00275 1.00275
+
+
+
+ 1
+
+ 1 1 1
+
+
+ femur_r.vtp
+
+
+
+ 7.6723191502382804
+
+ 0 -0.17046700000000001 0
+
+ 0.11105547289013901 0.029111628815861601 0.117110028170931 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.96673200000000004 0.96673200000000004 0.96673200000000004
+
+
+
+ 1
+
+ 1 1 1
+
+
+ tibia_l.vtp
+
+
+
+ ..
+
+ 0.96673200000000004 0.96673200000000004 0.96673200000000004
+
+
+
+ 1
+
+ 1 1 1
+
+
+ l_fibula.vtp
+
+
+
+ 3.0581550357482099
+
+ 0 -0.18048900000000001 0
+
+ 0.038852699659735403 0.0039315231798541803 0.039392320488342902 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.96673200000000004 0.96673200000000004 0.96673200000000004
+
+
+
+ 1
+
+ 1 1 1
+
+
+ tibia_r.vtp
+
+
+
+ ..
+
+ 0.96673200000000004 0.96673200000000004 0.96673200000000004
+
+
+
+ 1
+
+ 1 1 1
+
+
+ r_fibula.vtp
+
+
+
+ 3.0581550357482099
+
+ 0 -0.18048900000000001 0
+
+ 0.038852699659735403 0.0039315231798541803 0.039392320488342902 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.91392399999999996 0.91392399999999996 0.91392399999999996
+
+
+
+ 1
+
+ 1 1 1
+
+
+ l_talus.vtp
+
+
+
+ 0.082485638186061
+
+ 0 0 0
+
+ 0.00068896770091018196 0.00068896770091018196 0.00068896770091018196 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.91392399999999996 0.91392399999999996 0.91392399999999996
+
+
+
+ 1
+
+ 1 1 1
+
+
+ r_talus.vtp
+
+
+
+ 0.082485638186061
+
+ 0 0 0
+
+ 0.00068896770091018196 0.00068896770091018196 0.00068896770091018196 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.91392399999999996 0.91392399999999996 0.91392399999999996
+
+
+
+ 1
+
+ 1 1 1
+
+
+ l_foot.vtp
+
+
+
+ 1.03107047732576
+
+ 0.091392399999999999 0.0274177 0
+
+ 0.00096455478127425401 0.0026869740335497098 0.0028247675737317502 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.91392399999999996 0.91392399999999996 0.91392399999999996
+
+
+
+ 1
+
+ 1 1 1
+
+
+ r_foot.vtp
+
+
+
+ 1.03107047732576
+
+ 0.091392399999999999 0.0274177 0
+
+ 0.00096455478127425401 0.0026869740335497098 0.0028247675737317502 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.91392399999999996 0.91392399999999996 0.91392399999999996
+
+
+
+ 1
+
+ 1 1 1
+
+
+ l_bofoot.vtp
+
+
+
+ 0.17866389231100799
+
+ 0.031621799999999999 0.0054835500000000002 0.0159937
+
+ 6.8896770091018202e-05 0.000137793540182036 6.8896770091018202e-05 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.91392399999999996 0.91392399999999996 0.91392399999999996
+
+
+
+ 1
+
+ 1 1 1
+
+
+ r_bofoot.vtp
+
+
+
+ 0.17866389231100799
+
+ 0.031621799999999999 0.0054835500000000002 -0.0159937
+
+ 6.8896770091018202e-05 0.000137793540182036 6.8896770091018202e-05 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+
+
+
+ ..
+
+ 0.96574000000000004 0.96574000000000004 0.98599599999999998
+
+
+
+ 1
+
+ 1 1 1
+
+
+ hat_spine.vtp
+
+
+
+ ..
+
+ 0.96574000000000004 0.96574000000000004 0.98599599999999998
+
+
+
+ 1
+
+ 1 1 1
+
+
+ hat_jaw.vtp
+
+
+
+ ..
+
+ 0.96574000000000004 0.96574000000000004 0.98599599999999998
+
+
+
+ 1
+
+ 1 1 1
+
+
+ hat_skull.vtp
+
+
+
+ ..
+
+ 0.96574000000000004 0.96574000000000004 0.98599599999999998
+
+
+
+ 1
+
+ 1 1 1
+
+
+ hat_ribs_scap.vtp
+
+
+
+ 28.240278003208999
+
+ -0.0289722 0.30903700000000001 0
+
+ 1.14043571182129 0.593400919285897 1.14043571182129 0 0 0
+
+
+
+
+
+
+
+
+
+ ground_offset
+
+ pelvis_offset
+
+
+
+
+ -1.570796326794897 1.570796326794897
+
+
+
+ -5 5
+
+
+
+ -3 3
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /ground
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/pelvis
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ pelvis_offset
+
+ femur_l_offset
+
+
+
+
+ -1.0471975511965981 2.0943951023931948
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/pelvis
+
+ -0.068277800171117897 -0.063835397331130098 -0.082330694005868801
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/femur_l
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ pelvis_offset
+
+ femur_r_offset
+
+
+
+
+ -1.0471975511965981 2.0943951023931948
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/pelvis
+
+ -0.068277800171117897 -0.063835397331130098 0.082330694005868801
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/femur_r
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ femur_l_offset
+
+ tibia_l_offset
+
+
+
+
+ -2.0943951023931948 0.174532925199433
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/femur_l
+
+ -0.0045122123214679797 -0.39690724592144699 0
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/tibia_l
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ femur_r_offset
+
+ tibia_r_offset
+
+
+
+
+ -2.0943951023931948 0.174532925199433
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/femur_r
+
+ -0.0045122123214679797 -0.39690724592144699 0
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/tibia_r
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ tibia_l_offset
+
+ talus_l_offset
+
+
+
+
+ -0.69813170079773201 0.52359877559829904
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/tibia_l
+
+ 0 -0.41569482537490499 0
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/talus_l
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ tibia_r_offset
+
+ talus_r_offset
+
+
+
+
+ -0.69813170079773201 0.52359877559829904
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/tibia_r
+
+ 0 -0.41569482537490499 0
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/talus_r
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ talus_l_offset
+
+ calcn_l_offset
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/talus_l
+
+ -0.044572091911732101 -0.038339127654237401 -0.0072382810732195598
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/calcn_l
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ talus_r_offset
+
+ calcn_r_offset
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/talus_r
+
+ -0.044572091911732101 -0.038339127654237401 0.0072382810732195598
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/calcn_r
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ calcn_l_offset
+
+ toes_l_offset
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/calcn_l
+
+ 0.16340967877419901 -0.00182784875586352 -0.00098703832816630292
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/toes_l
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ calcn_r_offset
+
+ toes_r_offset
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/calcn_r
+
+ 0.16340967877419901 -0.00182784875586352 0.00098703832816630292
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/toes_r
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+ pelvis_offset
+
+ torso_offset
+
+
+
+
+ -1.570796326794897 1.570796326794897
+
+
+
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/pelvis
+
+ -0.0972499926058214 0.078707789447611198 0
+
+ 0 0 0
+
+
+
+
+
+ ..
+
+ 0.20000000000000001 0.20000000000000001 0.20000000000000001
+
+
+ /bodyset/torso
+
+ 0 0 0
+
+ 0 0 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /bodyset/calcn_r
+
+ 0.031307527581931796 0.010435842527310599 0
+
+ 0.035000000000000003
+
+
+
+ /bodyset/calcn_l
+
+ 0.031307527581931796 0.010435842527310599 0
+
+ 0.035000000000000003
+
+
+
+ /bodyset/calcn_r
+
+ 0.17740932296428019 -0.015653763790965898 0.0052179212636552993
+
+ 0.014999999999999999
+
+
+
+ /bodyset/calcn_l
+
+ 0.17740932296428019 -0.015653763790965898 -0.0052179212636552993
+
+ 0.014999999999999999
+
+
+
+ /ground
+
+ 0 0 -1.5707963267948966
+
+
+
+
+
diff --git a/Bindings/Python/examples/Moco/example2DWalking/example2DWalking.py b/Bindings/Python/examples/Moco/example2DWalking/example2DWalking.py
new file mode 100644
index 0000000000..4a788cd379
--- /dev/null
+++ b/Bindings/Python/examples/Moco/example2DWalking/example2DWalking.py
@@ -0,0 +1,416 @@
+# -*- coding: utf-8 -*-
+"""
+MOCO: WALKING 2D EXAMPLE - TRACKING & PREDICTION
+
+@author: Prasanna Sritharan, June 2022
+
+This is a Python implementation of an example optimal control
+problem (2D walking) orginally created in C++ by Antoine Falisse
+(see: example2DWalking.cpp) and adapted for Matlab by Brian Umberger
+(see: example2DWalking.m).
+"""
+
+from math import pi
+import opensim as osim
+
+
+
+
+"""
+gait_tracking():
+Set up a coordinate tracking problem where the goal is to minimize the
+difference between provided and simulated coordinate values and speeds (and
+ground reaction forces), as well as to minimize an effort cost (squared
+controls). The provided data represents one step. Endpoint constraints
+enforce periodicity of the coordinate values (except for pelvis tx) and speeds,
+coordinate actuator controls, and muscle activations.
+"""
+def gait_tracking():
+
+
+
+ # **********************************
+ # DEFINE THE OPTIMAL CONTROL PROBLEM
+
+ # Create tracking problem
+ track = osim.MocoTrack()
+ track.setName("gait_tracking")
+
+ # Construct a ModelProcessor and set it on the tool
+ modelProcessor = osim.ModelProcessor("2D_gait.osim")
+ track.setModel(modelProcessor)
+
+
+ # Get the coordinates into a TableProcessor
+ tableProcessor = osim.TableProcessor("referenceCoordinates.sto")
+ tableProcessor.append(osim.TabOpLowPassFilter(6.0))
+
+ # Set the coordinates as the reference states
+ track.setStatesReference(tableProcessor)
+ track.set_allow_unused_references(True)
+
+ # Only coordinates are provided so derive to get speeds and track these too
+ track.set_track_reference_position_derivatives(True)
+
+ # use coordinates and speeds for initial guess
+ track.set_apply_tracked_states_to_guess(True)
+
+ # Define the global state tracking weight
+ track.set_states_global_tracking_weight(1.0)
+
+
+ # Set initial time, final time and mesh interval
+ track.set_initial_time(0.0)
+ track.set_final_time(0.47008941)
+
+ # Call initialize() to receive a pre-configured MocoStudy object based on
+ # the settings above. Use this to customize the problem beyond the
+ # Mocotrack interface.
+ study = track.initialize()
+
+ # Get a writable reference to the MocoProblem from the MocoStudy to perform
+ # more customisation
+ problem = study.updProblem()
+
+
+
+ # **********************************
+ # SET GOALS
+
+ # Symmetry:
+ # This goal allows us to simulate only one step with left-right symmetry
+ # that we can then double to create two steps, one on each leg
+ # (IFO>IFS>CFO>CFS). Note that this is not actually a full gait cycle
+ # (IFO>IFS>CFO>CFS>IFO).
+ symmetryGoal = osim.MocoPeriodicityGoal("symmetry_goal")
+ problem.addGoal(symmetryGoal)
+
+ # Enforce periodic symmetry
+ model = modelProcessor.process()
+ model.initSystem()
+ state_names = [model.getStateVariableNames().getitem(sn)
+ for sn in range(model.getNumStateVariables())]
+ for sn in state_names:
+
+ # Symmetric coordinate values and speeds (except for pelvis_tx value):
+ # Here we constrain final coordinate values of one leg to match the
+ # initial value of the other leg. Manually add pelvis_tx speed later.
+ if "jointset" in sn:
+ if "_r" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_r", "_l")))
+ elif "_l" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_l", "_r")))
+ elif "pelvis_tx" not in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn))
+
+ # Symmetric muscle activations:
+ # Here, we constrain final muscle activation values of one leg to match
+ # the initial activation values of the other leg.
+ elif "activation" in sn:
+ if "_r" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_r", "_l")))
+ elif "_l" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_l", "_r")))
+
+
+ # The pelvis_tx speed has periodic symmetry
+ symmetryGoal.addStatePair(
+ osim.MocoPeriodicityGoalPair("/jointset/groundPelvis/pelvis_tx/speed"))
+
+ # Lumbar control has periodic symmetry. The other controls don't need
+ # symmetry enforced as they are all muscle excitations. Their behaviour
+ # will be contstrained by the periodicity imposed on their respective
+ # activations.
+ symmetryGoal.addControlPair(osim.MocoPeriodicityGoalPair('/lumbarAct'))
+
+
+ # Effort:
+ # Get a reference to the MocoControlGoal that is added to a MocoTrack
+ # problem by default and adjust the weight
+ effort = osim.MocoControlGoal.safeDownCast(
+ problem.updGoal("control_effort"))
+ effort.setWeight(10.0)
+
+
+ # Ground contact:
+ # Track the left and right vertical and fore-aft GRFs
+ contact_r = ["contactHeel_r", "contactFront_r"]
+ contact_l = ["contactHeel_l", "contactFront_l"]
+ grf_tracking_weight = 1
+ if grf_tracking_weight != 0:
+
+ # Create a contact tracking goal
+ contactTracking = osim.MocoContactTrackingGoal("contact",
+ grf_tracking_weight)
+ contactTracking.setExternalLoadsFile("referenceGRF.xml")
+
+ # Add contact groups. The sum of all the contact forces in each group
+ # should track the force data from a single ExternalForce
+ contactTracking.addContactGroup(contact_r, "Right_GRF")
+ contactTracking.addContactGroup(contact_l, "Left_GRF")
+
+ # 2D walking problem so consider force errors in the sagittal plane
+ contactTracking.setProjection("plane")
+ contactTracking.setProjectionVector(osim.Vec3(0, 0, 1))
+
+ # add the goal to the MocoProblem
+ problem.addGoal(contactTracking)
+
+
+
+ # **********************************
+ # SET BOUNDS
+
+ # Coordinate bounds as dict
+ coord_bounds = {}
+ coord_bounds["/jointset/groundPelvis/pelvis_tilt/value"] = [-20*pi/180,
+ -10*pi/180]
+ coord_bounds["/jointset/groundPelvis/pelvis_tx/value"] = [0.0, 1.0]
+ coord_bounds["/jointset/groundPelvis/pelvis_ty/value"] = [0.75, 1.25]
+ coord_bounds["/jointset/hip_r/hip_flexion_r/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/hip_l/hip_flexion_l/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/knee_r/knee_angle_r/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/knee_l/knee_angle_l/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/ankle_r/ankle_angle_r/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/ankle_l/ankle_angle_l/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/lumbar/lumbar/value"] = [0, 20*pi/180]
+
+ # Set coordinate bounds
+ for bnd in coord_bounds:
+ problem.setStateInfo(bnd, coord_bounds[bnd]);
+
+
+
+ # **********************************
+ # SOLVE
+
+ # The Solver is pre-configured, however, uncomment below to configure
+ # further if desired. This gets a writable reference to the Solver, for
+ # custom configuration.
+ # solver = study.updSolver()
+ # solver.set_num_mesh_intervals(50);
+ # solver.set_verbosity(2);
+ # solver.set_optim_solver("ipopt");
+ # solver.set_optim_convergence_tolerance(1e-4);
+ # solver.set_optim_constraint_tolerance(1e-4);
+ # solver.set_optim_max_iterations(1000);
+
+
+ # Solve the problem for a single step
+ solution = study.solve()
+
+
+ # Create two mirrored steps from the single step solution, see API
+ # documentation for use of this utility function
+ two_steps_solution = osim.createPeriodicTrajectory(solution)
+ two_steps_solution.write("walk_2D_two_steps_tracking_solution.sto")
+
+ # Also extract the predicted ground forces, see API documentation for use
+ # of this utility function
+ contact_forces_table = osim.createExternalLoadsTableForGait(model,
+ two_steps_solution, contact_r, contact_l)
+ osim.STOFileAdapter().write(contact_forces_table,
+ "walk_2D_two_steps_tracking_ground_forces.sto")
+
+
+ return study, solution, two_steps_solution
+
+
+
+
+"""
+gait_prediction(tracking_solution):
+Set up a gait prediction problem where the goal is to minimize effort
+(squared controls) divided by distance traveled while enforcing symmetry of
+the walking cycle and a prescribed average gait speed through endpoint
+constraints. The solution of the coordinate tracking problem is used as an
+initial guess for the prediction.
+"""
+def gait_prediction(tracking_solution):
+
+
+
+ # **********************************
+ # DEFINE THE OPTIMAL CONTROL PROBLEM
+
+ # Create predcition problem
+ study = osim.MocoStudy()
+ study.setName("gait_prediciton")
+
+ # Get a writable reference to the MocoProblem from the MocoStudy to
+ # customise the problem settings
+ problem = study.updProblem()
+
+ # Construct a ModelProcessor and set it on the Problem
+ modelProcessor = osim.ModelProcessor("2D_gait.osim")
+ problem.setModelProcessor(modelProcessor)
+
+
+
+ # **********************************
+ # SET GOALS
+
+ # Symmetry: This goal allows us to simulate only one step with left-right
+ # symmetry that we can then double to create two steps, one on each leg
+ # (IFO>IFS>CFO>CFS). Note that this is not actually a full gait cycle
+ # (IFO>IFS>CFO>CFS>IFO)
+ symmetryGoal = osim.MocoPeriodicityGoal("symmetry_goal")
+ problem.addGoal(symmetryGoal)
+
+
+ # Enforce periodic symmetry
+ model = modelProcessor.process()
+ model.initSystem()
+ state_names = [model.getStateVariableNames().getitem(sn)
+ for sn in range(model.getNumStateVariables())]
+ for sn in state_names:
+
+ # Symmetric coordinate values and speeds (except for pelvis_tx value):
+ # Here we constrain final coordinate values of one leg to match the
+ # initial value of the other leg. Manually add pelvis_tx speed later.
+ if "jointset" in sn:
+ if "_r" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_r", "_l")))
+ elif "_l" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_l", "_r")))
+ elif "pelvis_tx" not in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn))
+
+ # Symmetric muscle activations:
+ # Here, we constrain final muscle activation values of one leg to match
+ # the initial activation values of the other leg.
+ elif "activation" in sn:
+ if "_r" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_r", "_l")))
+ elif "_l" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_l", "_r")))
+
+ # The pelvis_tx speed has periodic symmetry
+ symmetryGoal.addStatePair(
+ osim.MocoPeriodicityGoalPair("/jointset/groundPelvis/pelvis_tx/speed"))
+
+ # Lumbar control has periodic symmetry. The other controls don't need
+ # symmetry enforces as they are all muscle excitations. Their behaviour
+ # will be modulated by the periodicity imposed on their activations.
+ symmetryGoal.addControlPair(osim.MocoPeriodicityGoalPair('/lumbarAct'))
+
+ # Specify the desired average walk speed, add as a goal
+ speedGoal = osim.MocoAverageSpeedGoal('avg_speed')
+ speedGoal.set_desired_average_speed(1.2)
+ problem.addGoal(speedGoal)
+
+ # Minimise total "effort" over the distance, i.e. minimise sum of the
+ # absolute values of all controls raised to a given exponent, integrated
+ # over the phase. In a MocoTrack problem, this is automatically added,
+ # however, in this predictive problem, we have created a MocoStudy from
+ # scratch, so we need to add this goal manually to the Problem. The second
+ # input argument to the constructor is the weight applied to this goal.
+ # We also normalise the effort by the distance travelled.
+ effortGoal = osim.MocoControlGoal('effort', 10)
+ effortGoal.setExponent(3)
+ effortGoal.setDivideByDisplacement(True)
+ problem.addGoal(effortGoal)
+
+
+
+ # **********************************
+ # SET BOUNDS
+
+ # set time bounds
+ problem.setTimeBounds(0, [0.4, 0.6])
+
+
+ # Coordinate bounds as dict
+ coord_bounds = {}
+ coord_bounds["/jointset/groundPelvis/pelvis_tilt/value"] = [-20*pi/180,
+ -10*pi/180]
+ coord_bounds["/jointset/groundPelvis/pelvis_tx/value"] = [0.0, 1.0]
+ coord_bounds["/jointset/groundPelvis/pelvis_ty/value"] = [0.75, 1.25]
+ coord_bounds["/jointset/hip_r/hip_flexion_r/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/hip_l/hip_flexion_l/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/knee_r/knee_angle_r/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/knee_l/knee_angle_l/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/ankle_r/ankle_angle_r/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/ankle_l/ankle_angle_l/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/lumbar/lumbar/value"] = [0, 20*pi/180]
+
+ # Set coordinate bounds
+ for bnd in coord_bounds:
+ problem.setStateInfo(bnd, coord_bounds[bnd])
+
+
+
+ # **********************************
+ # SOLVE
+
+ # Configure the solver. In the tracking problem, the solver was
+ # pre-configured, using MocoTrack, however, as we have created a MocoStudy
+ # from scratch, we need to initialise the MocoSolver and configure it.
+ solver = study.initCasADiSolver()
+ solver.set_num_mesh_intervals(50)
+ solver.set_verbosity(2)
+ solver.set_optim_solver("ipopt")
+ solver.set_optim_convergence_tolerance(1e-4)
+ solver.set_optim_constraint_tolerance(1e-4)
+ solver.set_optim_max_iterations(1000)
+
+ # Set the tracking solution for one step as the initial guess
+ solver.setGuess(tracking_solution)
+
+
+ # Solve the problem for a single step
+ solution = study.solve()
+
+
+ # Create two mirrored steps from the single step solution, see API
+ # documentation for use of this utility function
+ two_steps_solution = osim.createPeriodicTrajectory(solution)
+ two_steps_solution.write("walk_2D_two_steps_prediction_solution.sto")
+
+ # Also extract the predicted ground forces, see API documentation for use
+ # of this utility function
+ contact_r = ["contactHeel_r", "contactFront_r"]
+ contact_l = ["contactHeel_l", "contactFront_l"]
+ contact_forces_table = osim.createExternalLoadsTableForGait(model,
+ two_steps_solution, contact_r, contact_l)
+ osim.STOFileAdapter().write(contact_forces_table,
+ "walk_2D_two_steps_prediction_ground_forces.sto")
+
+
+ return study, solution, two_steps_solution
+
+
+
+# %% TRACKING PROBLEM
+
+# Solve the problem and visualise
+tracking_study, tracking_solution, tracking_two_steps_solution = \
+ gait_tracking()
+tracking_study.visualize(tracking_two_steps_solution)
+
+
+
+# %% PREDICTION PROBLEM
+
+# Solve the problem and visualise (uses tracking_solution as initial guess)
+prediction_study, prediction_solution, prediction_two_steps_solution = \
+ gait_prediction(tracking_solution)
+prediction_study.visualize(prediction_two_steps_solution)
+
+
diff --git a/Bindings/Python/examples/Moco/example2DWalking/example2DWalkingMetabolics.py b/Bindings/Python/examples/Moco/example2DWalking/example2DWalkingMetabolics.py
new file mode 100644
index 0000000000..88b36600cf
--- /dev/null
+++ b/Bindings/Python/examples/Moco/example2DWalking/example2DWalkingMetabolics.py
@@ -0,0 +1,219 @@
+# -*- coding: utf-8 -*-
+"""
+MOCO: WALKING 2D EXAMPLE - COORDINATE TRACKING, MINIMISE METABOLIC COST
+
+@author: Prasanna Sritharan, June 2022
+
+This is a Python implementation of an example optimal control
+problem (2D walking) orginally created in C++ by Antoine Falisse
+(see: example2DWalkingMetabolics.cpp) and adapted for Matlab by Brian Umberger
+(see: example2DWalkingMetabolics.m).
+
+This example features a tracking simulation of walking that includes
+minimisation of the metabolic cost of transport computed using a smooth
+approximation of the metabolic energy model of Bhargava et al (2004).
+
+Set a coordinate tracking problem where the goal is to minimize the
+difference between provided and simulated coordinate values and speeds
+as well as to minimize an effort cost (squared controls) and a metabolic
+cost (metabolic energy normalized by distance traveled and body mass;
+the metabolics model is based on a smooth approximation of the
+phenomenological model described by Bhargava et al. (2004)). The provided
+data represents half a gait cycle. Endpoint constraints enforce periodicity
+of the coordinate values (except for pelvis tx) and speeds, coordinate
+actuator controls, and muscle activations.
+"""
+
+
+from math import pi
+import opensim as osim
+
+
+
+# %% SET THE MODEL AND METABOLICS ANALYSIS
+
+# Get the OpenSim model
+model = osim.Model("2D_gait.osim")
+
+# Add a component to calculate metabolics
+metabolics = osim.Bhargava2004SmoothedMuscleMetabolics()
+metabolics.setName("metabolics")
+metabolics.set_use_smoothing(True)
+
+# Add muscles to metabolics component
+leg = ["r", "l"]
+musclist = ["hamstrings", "bifemsh", "glut_max", "iliopsoas", "rect_fem",
+ "vasti", "gastroc", "soleus", "tib_ant"]
+for mc in [m + "_" + l for m in musclist for l in leg]:
+ metabolics.addMuscle(mc, osim.Muscle.safeDownCast(model.getComponent(mc)))
+
+# Add metabolics component to model
+model.addComponent(metabolics)
+model.finalizeConnections()
+
+
+
+# %% DEFINE THE OPTIMAL CONTROL PROBLEM
+
+# Create the tracking problem
+track = osim.MocoTrack()
+track.setName("gait_tracking_met_cost")
+
+# Pass the model to MocoTrack
+modelProcessor = osim.ModelProcessor(model)
+track.setModel(modelProcessor)
+
+# Reference data for coordinate tracking
+tableProcessor = osim.TableProcessor("referenceCoordinates.sto")
+tableProcessor.append(osim.TabOpLowPassFilter(6.0))
+track.setStatesReference(tableProcessor)
+
+# Remaining MocoTrack settings
+track.set_states_global_tracking_weight(30)
+track.set_allow_unused_references(True)
+track.set_track_reference_position_derivatives(True)
+track.set_apply_tracked_states_to_guess(True)
+track.set_initial_time(0.0)
+track.set_final_time(0.47008941)
+
+# Call initialize() to get the internal MocoStudy. The MocoStudy is comprised
+# of a MocoProblem and a MocoSolver. Get the MocoProblem to configure further.
+study = track.initialize()
+problem = study.updProblem()
+
+
+
+# %% SET GOALS
+
+# Symmetry:
+# This goal allows us to simulate only one step with left-right symmetry that
+# we can then double to create two steps, one on each leg (IFO>IFS>CFO>CFS).
+# Note that this is not actually a full gait cycle (IFO>IFS>CFO>CFS>IFO).
+symmetryGoal = osim.MocoPeriodicityGoal("symmetry_goal")
+problem.addGoal(symmetryGoal)
+
+# Enforce periodic symmetry
+model = modelProcessor.process()
+model.initSystem()
+state_names = [model.getStateVariableNames().getitem(sn)
+ for sn in range(model.getNumStateVariables())]
+for sn in state_names:
+
+ # Symmetric coordinate values and speeds (except for pelvis_tx value):
+ # Here we constrain final coordinate values of one leg to match the initial
+ # value of the other leg. Manually add pelvis_tx speed later.
+ if "jointset" in sn:
+ if "_r" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_r", "_l")))
+ elif "_l" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_l", "_r")))
+ elif "pelvis_tx" not in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn))
+
+ # Symmetric muscle activations:
+ # Here, we constrain final muscle activation values of one leg to match the
+ # initial activation values of the other leg.
+ elif "activation" in sn:
+ if "_r" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_r", "_l")))
+ elif "_l" in sn:
+ symmetryGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn,
+ sn.replace("_l", "_r")))
+
+
+# The pelvis_tx speed has periodic symmetry
+symmetryGoal.addStatePair(
+ osim.MocoPeriodicityGoalPair("/jointset/groundPelvis/pelvis_tx/speed"))
+
+# Lumbar control has periodic symmetry. The other controls don't need symmetry
+# enforced as they are all muscle excitations. Their behaviour will be
+# modulated by the periodicity imposed on their respective activations.
+symmetryGoal.addControlPair(osim.MocoPeriodicityGoalPair('/lumbarAct'))
+
+
+# Effort:
+# Get a reference to the MocoControlGoal that is added to a MocoTrack
+# problem by default and adjust the weight
+effort = osim.MocoControlGoal().safeDownCast(problem.updGoal("control_effort"))
+effort.setWeight(0.1)
+
+
+# Metabolic cost:
+# Total metabolic rate includes activation heat rate, maintenance heat rate,
+# shortening heat rate, mechanical work rate, and basal metabolic rate.
+met_weight = 0.1
+metabolicsGoal = osim.MocoOutputGoal("metabolics", met_weight)
+metabolicsGoal.setOutputPath("/metabolics|total_metabolic_rate")
+metabolicsGoal.setDivideByDisplacement(True)
+metabolicsGoal.setDivideByMass(True)
+problem.addGoal(metabolicsGoal)
+
+
+
+# %% SET BOUNDS
+
+# Coordinate bounds as dict
+coord_bounds = {}
+coord_bounds["/jointset/groundPelvis/pelvis_tilt/value"] = [-20*pi/180,
+ -10*pi/180]
+coord_bounds["/jointset/groundPelvis/pelvis_tx/value"] = [0.0, 1.0]
+coord_bounds["/jointset/groundPelvis/pelvis_ty/value"] = [0.75, 1.25]
+coord_bounds["/jointset/hip_r/hip_flexion_r/value"] = [-10*pi/180,
+ 60*pi/180]
+coord_bounds["/jointset/hip_l/hip_flexion_l/value"] = [-10*pi/180,
+ 60*pi/180]
+coord_bounds["/jointset/knee_r/knee_angle_r/value"] = [-50*pi/180, 0]
+coord_bounds["/jointset/knee_l/knee_angle_l/value"] = [-50*pi/180, 0]
+coord_bounds["/jointset/ankle_r/ankle_angle_r/value"] = [-15*pi/180,
+ 25*pi/180]
+coord_bounds["/jointset/ankle_l/ankle_angle_l/value"] = [-15*pi/180,
+ 25*pi/180]
+coord_bounds["/jointset/lumbar/lumbar/value"] = [0, 20*pi/180]
+
+# Set coordinate bounds
+for bnd in coord_bounds:
+ problem.setStateInfo(bnd, coord_bounds[bnd]);
+
+
+
+# %% SOLVE
+
+# Configure the solver
+solver = study.initCasADiSolver()
+solver.set_num_mesh_intervals(50)
+solver.set_verbosity(2)
+solver.set_optim_solver("ipopt")
+solver.set_optim_convergence_tolerance(1e-4)
+solver.set_optim_constraint_tolerance(1e-4)
+solver.set_optim_max_iterations(10000)
+
+# Solve the problem for a single step
+solution = study.solve()
+
+
+# Create two mirrored steps from the single step solution, see API
+# documentation for use of this utility function
+two_steps_solution = osim.createPeriodicTrajectory(solution)
+two_steps_solution.write("walk_2D_metabolics_two_steps_tracking_solution.sto")
+
+# Also extract the predicted ground forces, see API documentation for use of
+# this utility function
+contact_r = ["contactHeel_r", "contactFront_r"]
+contact_l = ["contactHeel_l", "contactFront_l"]
+contact_forces_table = osim.createExternalLoadsTableForGait(model,
+ two_steps_solution, contact_r, contact_l)
+osim.STOFileAdapter().write(contact_forces_table,
+ "walk_2D_metabolics_two_steps_tracking_ground_forces.sto")
+
+
+# Determine the cost of transport from the metabolics cost term. Need to divide
+# by the weight applied earlier.
+COT = solution.getObjectiveTerm("metabolics") / met_weight
+print("\nThe metabolic cost of transport is: %f" % COT)
+
+
+# visualise
+study.visualize(two_steps_solution)
\ No newline at end of file
diff --git a/Bindings/Python/examples/Moco/example2DWalking/example2DWalkingStepAsymmetry.py b/Bindings/Python/examples/Moco/example2DWalking/example2DWalkingStepAsymmetry.py
new file mode 100644
index 0000000000..fd8fdfdefe
--- /dev/null
+++ b/Bindings/Python/examples/Moco/example2DWalking/example2DWalkingStepAsymmetry.py
@@ -0,0 +1,537 @@
+# -*- coding: utf-8 -*-
+"""
+MOCO: WALKING 2D EXAMPLE - STEP ASYMMETRY
+
+@author: Prasanna Sritharan, June 2022
+
+
+This is a Python implementation of an example optimal control problem
+originally written by Russell T. Johnson for Matlab
+(see example2DWalkingStepAsymmetry.m).
+
+Simulate asymmetric gait using MocoStepTimeAsymmetryGoal and
+MocoStepLengthAsymmetryGoal. This is an extension of the example2DWalking
+MATLAB example (see example2DWalking.m for details about model and data used).
+"""
+
+
+from math import pi
+import opensim as osim
+import os
+import numpy as np
+
+
+
+"""
+stepTimeAsymmetry():
+
+Set up a predictive optimization problem where the goal is to minimize an
+effort cost (cubed controls) and hit a target step time asymmetry. Unlike
+example2DWalking, this problem requires simulating a full gait cycle.
+Additionally, endpoint constraints enforce periodicity of the coordinate values
+(except for pelvis tx) and speeds, coordinate actuator controls, and muscle
+activations.
+
+Step time is defined as the time between consecutive foot strikes. Step Time
+Asymmetry (STA) is a ratio and is calculated as follows:
+ - Right Step Time (RST) = Time from left foot-strike to right foot-strike
+ - Left Step Time (LST) = Time from right foot-strike to left foot-strike
+ - STA = (RST - LST) / (RST + LST)
+
+The step time goal works by "counting" the number of nodes that each foot is in
+contact with the ground (with respect to a specified contact force threshold).
+Since, in walking, there are double support phases where both feet are on the
+ground, the goal also detects which foot is in front and assigns the step time
+to the leading foot. Altogether, it estimates the time between consecutive
+heel strikes in order to infer the left and right step times.
+
+The contact elements for each foot must specified via 'setLeftContactGroup()'
+and 'setRightContactGroup()'. The force element and force threshold used to
+determine when a foot is in contact is set via 'setContactForceDirection()' and
+'setContactForceThreshold()'.
+
+Users must provide the target asymmetry value via 'setTargetAsymmetry()'.
+Asymmetry values ranges from -1.0 to 1.0. For example, 0.20 is 20positive
+step time asymmetry with greater right step times than left step times. A
+symmetric step times solution can be achieved by setting this property to zero.
+This goal can be used only in 'cost' mode, where the error between the target
+asymmetry and model asymmetry is squared. To make this goal suitable for
+gradient-based optimization, step time values are assigned via smoothing
+functions which can be controlled via 'setAsymmetrySmoothing()' and
+'setContactDetectionSmoothing()'.
+
+Since this goal doesn't directly compute the step time asymmetry from heel
+strikes, users should confirm that the step time asymmetry from the solution
+matches closely to the target. To do this, we provide the helper function
+computeStepAsymmetryValues() below.
+"""
+def stepTimeAsymmetry():
+
+
+ # **********************************
+ # DEFINE THE OPTIMAL CONTROL PROBLEM
+
+ # Create a MocoStudy
+ study = osim.MocoStudy()
+ study.setName("step_time_asymmetry")
+
+ # Get the model
+ modelProcessor = osim.ModelProcessor("2D_gait.osim")
+ modelProcessor.append(
+ osim.ModOpTendonComplianceDynamicsModeDGF("implicit"))
+
+ # Get the MocoProblem from the MocoStudy and set the model on it
+ problem = study.updProblem()
+ problem.setModelProcessor(modelProcessor)
+
+
+
+ # **********************************
+ # SET GOALS
+
+ # Periodicity:
+ # All states are periodic except pelvis_tx value, lumbar actuator control
+ # is periodic.
+ periodicityGoal = osim.MocoPeriodicityGoal("periodicity")
+ model = modelProcessor.process()
+ model.initSystem()
+ state_names = [model.getStateVariableNames().getitem(sn)
+ for sn in range(model.getNumStateVariables())]
+ for sn in state_names:
+ if "pelvis_tx/value" not in sn:
+ periodicityGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn))
+ periodicityGoal.addControlPair(osim.MocoPeriodicityGoalPair("/lumbarAct"))
+ problem.addGoal(periodicityGoal)
+
+ # Average gait speed
+ speedGoal = osim.MocoAverageSpeedGoal("avg_speed")
+ speedGoal.set_desired_average_speed(1.0)
+ problem.addGoal(speedGoal)
+
+ # Effort
+ effortGoal = osim.MocoControlGoal("effort", 10.0)
+ effortGoal.setExponent(3)
+ effortGoal.setDivideByDisplacement(True)
+ problem.addGoal(effortGoal)
+
+ # Step time asymmetry:
+ # Create the goal, and configure it
+ stepTimeAsymmetryGoal = \
+ osim.MocoStepTimeAsymmetryGoal("step_time_asymmetry")
+ # Value for smoothing term used to compute when foot contact is made
+ # (default = 0.25). Users may need to adjust this based on convergence and
+ # matching the target asymmetry.
+ stepTimeAsymmetryGoal.setContactDetectionSmoothing(0.4)
+ # Contact threshold based on vertical GRF (default = 25 N)
+ stepTimeAsymmetryGoal.setContactForceThreshold(25.0)
+ # Value for smoothing term used to compute asymmetry (default = 10). Users
+ # may need to adjust this based on convergence and matching the target
+ # asymmetry.
+ stepTimeAsymmetryGoal.setAsymmetrySmoothing(3.0)
+ # Target step length asymmetry. Positive values mean greater right step
+ # length than left.
+ stepTimeAsymmetryGoal.setTargetAsymmetry(0.10)
+ # Set goal weight
+ stepTimeAsymmetryGoal.setWeight(5.0)
+ # Need to define the names of the left and right heel spheres: this is
+ # used to detect which foot is in front during double support phase.
+ contact_r = ["contactHeel_r", "contactFront_r"]
+ contact_l = ["contactHeel_l", "contactFront_l"]
+ stepTimeAsymmetryGoal.setRightContactGroup(contact_r, "contactHeel_r")
+ stepTimeAsymmetryGoal.setLeftContactGroup(contact_l, "contactHeel_l")
+ # Add the goal to the problem
+ problem.addGoal(stepTimeAsymmetryGoal)
+
+
+
+ # **********************************
+ # SET BOUNDS
+
+ # Set time bounds
+ problem.setTimeBounds(0.0, 0.94)
+
+ # Coordinate bounds as dict
+ coord_bounds = {}
+ coord_bounds["/jointset/groundPelvis/pelvis_tilt/value"] = [-20*pi/180,
+ 20*pi/180]
+ coord_bounds["/jointset/groundPelvis/pelvis_tx/value"] = [0, 2]
+ coord_bounds["/jointset/groundPelvis/pelvis_ty/value"] = [0.75, 1.25]
+ coord_bounds["/jointset/hip_r/hip_flexion_r/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/hip_l/hip_flexion_l/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/knee_r/knee_angle_r/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/knee_l/knee_angle_l/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/ankle_r/ankle_angle_r/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/ankle_l/ankle_angle_l/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/lumbar/lumbar/value"] = [0, 20*pi/180]
+
+ # Set coordinate bounds
+ for bnd in coord_bounds:
+ problem.setStateInfo(bnd, coord_bounds[bnd])
+
+
+
+ # **********************************
+ # SOLVE
+
+ # Configure the solver
+ solver = study.initCasADiSolver()
+ solver.set_num_mesh_intervals(100)
+ solver.set_verbosity(2)
+ solver.set_optim_convergence_tolerance(1e-4)
+ solver.set_optim_constraint_tolerance(1e-4)
+ solver.set_optim_max_iterations(2000)
+
+ # Set the initial guess to be the symmetric two-steps tracking solution
+ # from example2DWalking.py. Run this first, or proceed without a guess.
+ guess_file = "walk_2D_two_steps_tracking_solution.sto"
+ if os.path.isfile(guess_file):
+ solver.setGuessFile(guess_file)
+
+ # Print the Study to file
+ study.printToXML("example2DWalkingStepTimeAsymmetry.omoco")
+
+
+ # Solve
+ solution = study.solve()
+ solution.write("walk_2D_step_time_asym_solution.sto")
+
+
+ # Write predicted GRF to file
+ contact_forces_table = osim.createExternalLoadsTableForGait(model,
+ solution, contact_r, contact_l)
+ osim.STOFileAdapter().write(contact_forces_table,
+ "walk_2D_step_time_asym_ground_forces.sto")
+
+ # Calculate step time asymmetry
+ step_time_asym, _ = computeStepAsymmetry(model, 25,
+ "walk_2D_step_time_asym_solution.sto",
+ "walk_2D_step_time_asym_ground_forces.sto")
+ print("\nStep time asymmetry: %f\n" % step_time_asym)
+
+
+ return study, solution
+
+
+
+"""
+stepLengthAsymmetry():
+
+This goal works by limiting the distance between feet, or "foot frames",
+throughout the gait cycle. The goal calculates the distance between the left
+foot and right foot, then limits the distance between feet to not pass beyond
+minimum (negative) or maximum (positive) bounds. There are two limits used:
+one that limits the distance between feet when the right foot is in front, and
+one that limits the distance between feet when the left foot is in front.
+
+Step Length Asymmetry (SLA) is a ratio and is calculated as follows:
+The Right Step Length (RSL) is the distance between feet at right foot strike
+The Left Step Length (LSL) is the distance between feet at left foot strike
+Step Length Asymmetry = (RSL - LSL)/ (RSL + LSL)
+
+Users must provide the target asymmetry value via 'setTargetAsymmetry()'.
+Asymmetry values ranges from -1.0 to 1.0. For example, 0.20 is 20positive
+step length asymmetry with greater right step length than left step length. A
+symmetric step length solution can be achieved by setting this property to zero.
+This goal can be used only in 'cost' mode, where the error between the target
+asymmetry and model asymmetry is squared. To make this goal suitable for
+gradient-based optimization, step length values are assigned via a smoothing
+function which can be controlled via 'setAsymmetrySmoothing()'.
+
+Users must also prescribed the stride length via 'setStrideLength()'. The goal
+then calculates the minimum and maximum bounds on the distance between right
+and left foot. Users must ensure that this stride length is met via problem
+bounds or other goals; the value provided to MocoStepLengthAsymmetryGoal is
+only used to compute the model's asymmetry in the cost function.
+
+Because this goal doesn't directly compute the step length asymmetry from
+heel strike data, users should confirm that the step length asymmetry
+from the solution matches closely to their target. To do this, we
+provide the helper function computeStepAsymmetryValues() below. Users may
+also want to confirm that the stride length from the optimization
+matches with setStrideLength(), or set additional constraints for stride length
+within the optimization. Additionally, in some cases users may want to set
+target asymmetries above or below the desired value, in the event there is
+some offset.
+"""
+def stepLengthAsymmetry():
+
+
+ # **********************************
+ # DEFINE THE OPTIMAL CONTROL PROBLEM
+
+ # Create a MocoStudy
+ study = osim.MocoStudy()
+ study.setName("step_length_asymmetry")
+
+ # Get the model
+ modelProcessor = osim.ModelProcessor("2D_gait.osim")
+ modelProcessor.append(
+ osim.ModOpTendonComplianceDynamicsModeDGF("implicit"))
+
+ # Get the MocoProblem from the MocoStudy and set the model on it
+ problem = study.updProblem()
+ problem.setModelProcessor(modelProcessor)
+
+
+
+ # **********************************
+ # SET GOALS
+
+ # Periodicity:
+ # All states are periodic except pelvis_tx value, lumbar actuator control
+ # is periodic.
+ periodicityGoal = osim.MocoPeriodicityGoal("periodicity")
+ model = modelProcessor.process()
+ model.initSystem()
+ state_names = [model.getStateVariableNames().getitem(sn)
+ for sn in range(model.getNumStateVariables())]
+ for sn in state_names:
+ if "pelvis_tx/value" not in sn:
+ periodicityGoal.addStatePair(osim.MocoPeriodicityGoalPair(sn))
+ periodicityGoal.addControlPair(osim.MocoPeriodicityGoalPair("/lumbarAct"))
+ problem.addGoal(periodicityGoal)
+
+ # Average gait speed
+ speedGoal = osim.MocoAverageSpeedGoal("avg_speed")
+ speedGoal.set_desired_average_speed(1.0)
+ problem.addGoal(speedGoal)
+
+ # Effort
+ effortGoal = osim.MocoControlGoal("effort", 10.0)
+ effortGoal.setExponent(3)
+ effortGoal.setDivideByDisplacement(True)
+ problem.addGoal(effortGoal)
+
+ # Step length asymmetry:
+ # Create the goal, and configure it
+ stepLengthAsymmetryGoal = osim.MocoStepLengthAsymmetryGoal()
+ # Set body names for left and right foot
+ stepLengthAsymmetryGoal.setRightFootFrame('/bodyset/calcn_r')
+ stepLengthAsymmetryGoal.setLeftFootFrame('/bodyset/calcn_l')
+ # Provide the stride length for the simulation (m)
+ stepLengthAsymmetryGoal.setStrideLength(0.904)
+ # Value for smoothing term used to compute asymmetry (default = 5). Users
+ # may need to adjust this based on convergence and matching the target
+ # asymmetry.
+ stepLengthAsymmetryGoal.setAsymmetrySmoothing(5)
+ # Target step length asymmetry. Positive values mean greater right step
+ # length than left.
+ stepLengthAsymmetryGoal.setTargetAsymmetry(-0.10)
+ # Set goal weight
+ stepLengthAsymmetryGoal.setWeight(5)
+ # Add the goal to the problem
+ problem.addGoal(stepLengthAsymmetryGoal)
+
+
+
+ # **********************************
+ # SET BOUNDS
+
+ # Set time bounds
+ problem.setTimeBounds(0.0, 0.94)
+
+ # Coordinate bounds as dict
+ coord_bounds = {}
+ coord_bounds["/jointset/groundPelvis/pelvis_tilt/value"] = [-20*pi/180,
+ 20*pi/180]
+ coord_bounds["/jointset/groundPelvis/pelvis_tx/value"] = [0, 2]
+ coord_bounds["/jointset/groundPelvis/pelvis_ty/value"] = [0.75, 1.25]
+ coord_bounds["/jointset/hip_r/hip_flexion_r/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/hip_l/hip_flexion_l/value"] = [-10*pi/180,
+ 60*pi/180]
+ coord_bounds["/jointset/knee_r/knee_angle_r/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/knee_l/knee_angle_l/value"] = [-50*pi/180, 0]
+ coord_bounds["/jointset/ankle_r/ankle_angle_r/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/ankle_l/ankle_angle_l/value"] = [-15*pi/180,
+ 25*pi/180]
+ coord_bounds["/jointset/lumbar/lumbar/value"] = [0, 20*pi/180]
+
+ # Set coordinate bounds
+ for bnd in coord_bounds:
+ problem.setStateInfo(bnd, coord_bounds[bnd])
+
+
+
+ # **********************************
+ # SOLVE
+
+ # Configure the solver
+ solver = study.initCasADiSolver()
+ solver.set_num_mesh_intervals(100)
+ solver.set_verbosity(2)
+ solver.set_optim_convergence_tolerance(1e-4)
+ solver.set_optim_constraint_tolerance(1e-4)
+ solver.set_optim_max_iterations(2000)
+
+ # Set the initial guess to be the symmetric two-steps tracking solution
+ # from example2DWalking.py. Run this first, or proceed without a guess.
+ guess_file = "walk_2D_two_steps_tracking_solution.sto"
+ if os.path.isfile(guess_file):
+ solver.setGuessFile(guess_file)
+
+ # Print the Study to file
+ study.printToXML("example2DWalkingStepLengthAsymmetry.omoco")
+
+
+ # Solve
+ solution = study.solve()
+ solution.write("walk_2D_step_length_asym_solution.sto")
+
+ # Write predicted GRF to file
+ contact_r = ["contactHeel_r", "contactFront_r"]
+ contact_l = ["contactHeel_l", "contactFront_l"]
+ contact_forces_table = osim.createExternalLoadsTableForGait(model,
+ solution, contact_r, contact_l)
+ osim.STOFileAdapter().write(contact_forces_table,
+ "walk_2D_step_length_asym_ground_forces.sto")
+
+ # Calculate step time asymmetry
+ _, step_length_asym = computeStepAsymmetry(model, 25,
+ "walk_2D_step_length_asym_solution.sto",
+ "walk_2D_step_length_asym_ground_forces.sto")
+ print("\nStep length asymmetry: %f\n" % step_length_asym)
+
+
+ return study, solution
+
+
+
+"""
+computeStepAsymmetry():
+
+Calculate the values of the step length and step time asymmetry from the
+results of the simulation.
+"""
+def computeStepAsymmetry(model_file, threshold, solution_file, grf_file):
+
+ # Load model
+ model = osim.Model(model_file)
+
+ # Load predicted GRF
+ grf = np.genfromtxt(grf_file, skip_header=5, delimiter="\t")
+
+ # GRF time vector and vertical components
+ tvec = grf[:, 0]
+
+
+ # **********************************
+ # STEP TIME ASYMMETRY
+
+ # Find index of heel strike on each leg
+ hs_idxR = findHeelStrikeIndex(grf[:, 2], threshold)
+ hs_idxL = findHeelStrikeIndex(grf[:, 8], threshold)
+
+ # Compute step time on each leg
+ hs_timeR = tvec[hs_idxR]
+ hs_timeL = tvec[hs_idxL]
+ if hs_timeR < hs_timeL:
+ step_timeL = hs_timeL - hs_timeR
+ step_timeR = tvec[-1] - step_timeL
+ else:
+ step_timeR = hs_timeR - hs_timeL
+ step_timeL = tvec[-1] - step_timeR
+
+ # Calculate step time asymmetry (%)
+ step_time_asym = 100 * (step_timeR - step_timeL) \
+ / (step_timeR + step_timeL)
+
+
+ # **********************************
+ # STEP LENGTH ASYMMETRY
+
+ # Get the states for each limb at the instant of heel strike on that limb
+ states_traj = osim.StatesTrajectory().createFromStatesTable(model,
+ osim.TimeSeriesTable(solution_file), False, True, True)
+ statesR = states_traj.get(hs_idxR)
+ statesL = states_traj.get(hs_idxL)
+
+ # Calculate the step length
+ step_lengthR = calculateStepLength(model, statesR)
+ step_lengthL = calculateStepLength(model, statesL)
+
+ # Calculate step length asymmetry (%)
+ step_length_asym = 100 * (step_lengthR - step_lengthL) \
+ / (step_lengthR + step_lengthL)
+
+
+ return step_time_asym, step_length_asym
+
+
+
+"""
+findHeelStrikeIndex():
+
+Find heel strike index by determining foot contact on-off instances. If no
+heel strike is found, then assume first index is heel strike. This
+implementation differs from that of Russell T. Johnson's Matlab version, but
+follows the same prinicples.
+"""
+def findHeelStrikeIndex(grfy, threshold):
+
+ # Find windows representing ground contact
+ is_contact = (grfy > threshold).astype(int)
+
+ # Calculate transition points, i.e. heel strike (1) and foot off (-1)
+ contact_diff = np.diff(np.insert(is_contact, 0, 1))
+
+ # Extract heel strike and foot off indices. If no heel strike found,
+ # assume first index is heel strike.
+ idxs = np.where(contact_diff == 1)[0]
+ if idxs.size == 0:
+ idx = 0
+ else:
+ idx = idxs[0]
+
+ return int(idx)
+
+
+
+"""
+calculateStepLength():
+
+Find step length by configuring the model at heel strike, then compute distance
+between contact spheres along the fore-aft coordinate.
+"""
+def calculateStepLength(model, state):
+
+ # Configure the model at heel strike
+ model.initSystem()
+ model.realizePosition(state)
+
+ # Get the heel contact spheres
+ contact_r = model.getContactGeometrySet().get("heel_r")
+ contact_l = model.getContactGeometrySet().get("heel_l")
+
+ # Find the positions of the contact spheres in the global frame
+ pos_r = contact_r.getFrame().getPositionInGround(state)
+ pos_l = contact_l.getFrame().getPositionInGround(state)
+
+ # Step length is the difference between global position of the left and
+ # right along the fore-aft coordinate (x)
+ step_length = abs(pos_r.get(0) - pos_l.get(0))
+
+ return step_length
+
+
+
+
+# %% STEP TIME ASYMMETRY
+
+# Solve and visualise
+step_time_study, step_time_solution = stepTimeAsymmetry()
+step_time_study.visualize(step_time_solution)
+
+
+
+# %% STEP LENGTH ASYMMETRY
+
+# Solve and visualise
+step_length_study, step_length_solution = stepLengthAsymmetry()
+step_length_study.visualize(step_length_solution)
+
diff --git a/Bindings/Python/examples/Moco/example2DWalking/referenceCoordinates.sto b/Bindings/Python/examples/Moco/example2DWalking/referenceCoordinates.sto
new file mode 100644
index 0000000000..49c1856b97
--- /dev/null
+++ b/Bindings/Python/examples/Moco/example2DWalking/referenceCoordinates.sto
@@ -0,0 +1,56 @@
+version=1
+nRows=50
+nColumns=11
+inDegrees=no
+endheader
+time /jointset/groundPelvis/pelvis_tilt/value /jointset/groundPelvis/pelvis_tx/value /jointset/groundPelvis/pelvis_ty/value /jointset/hip_l/hip_flexion_l/value /jointset/hip_r/hip_flexion_r/value /jointset/knee_l/knee_angle_l/value /jointset/knee_r/knee_angle_r/value /jointset/ankle_l/ankle_angle_l/value /jointset/ankle_r/ankle_angle_r/value /jointset/lumbar/lumbar/value
+0 -0.263901172 0 0.88365423 0.016469227 0.664226832 -0.283287738 -0.184426289 0.100897108 0.241505399 0.167890235
+0.00959366 -0.263237135 0.01477811 0.88286864 0.014834571 0.655766195 -0.309360488 -0.173860159 0.079944899 0.23368018 0.169581415
+0.01918732 -0.262380255 0.02981966 0.88203224 0.014574244 0.645644188 -0.338356542 -0.168285571 0.057179922 0.224945653 0.17150017
+0.02878098 -0.261375157 0.04510758 0.88116841 0.015814559 0.632672942 -0.370221258 -0.169806187 0.03225246 0.215265722 0.173715833
+0.03837465 -0.260365147 0.06045601 0.88040404 0.018636448 0.617605696 -0.404630306 -0.17528638 0.006652231 0.2053268 0.176063851
+0.04796831 -0.259541769 0.07552426 0.87996855 0.023508629 0.602627326 -0.439970753 -0.180172488 -0.015752649 0.195721202 0.178138544
+0.05756197 -0.259054514 0.0900652 0.88012353 0.030924383 0.588387845 -0.473795415 -0.184675993 -0.033251158 0.186446945 0.179697019
+0.06715563 -0.258944308 0.10401582 0.88102469 0.040812304 0.574930106 -0.505010842 -0.188707257 -0.04635638 0.177759897 0.180719341
+0.07674929 -0.259129324 0.11745937 0.88263129 0.052879604 0.561930784 -0.533978387 -0.192057021 -0.055232943 0.169944453 0.181312727
+0.08634295 -0.259494498 0.13052005 0.88473361 0.066767753 0.549021373 -0.561206172 -0.1945697 -0.060103311 0.163201244 0.181599832
+0.09593662 -0.259946181 0.14330536 0.88709913 0.082205144 0.535908621 -0.587001137 -0.196126516 -0.061239178 0.15763983 0.181673436
+0.10553028 -0.260424344 0.15588956 0.88954517 0.098981772 0.522427673 -0.61151102 -0.196694092 -0.058902809 0.153288535 0.181589336
+0.11512394 -0.260895925 0.16831964 0.89194813 0.116940376 0.508499514 -0.634758933 -0.196280109 -0.053378743 0.150129505 0.181378224
+0.1247176 -0.261344146 0.18062565 0.89422878 0.135951798 0.494097143 -0.656690572 -0.194919508 -0.044959219 0.148116785 0.18105773
+0.13431126 -0.261760474 0.19282791 0.89633673 0.155908169 0.47922707 -0.677220781 -0.19267299 -0.033938675 0.147185144 0.180638595
+0.14390492 -0.262140251 0.20494149 0.89823955 0.176712591 0.463918059 -0.696243373 -0.189625148 -0.020612934 0.147255397 0.180128698
+0.15349858 -0.262480488 0.2169788 0.89991627 0.19827165 0.448215384 -0.713640048 -0.185881233 -0.005281646 0.14823791 0.179535128
+0.16309225 -0.262779029 0.22895074 0.90135424 0.220490754 0.432176747 -0.729285592 -0.181563646 0.011751635 0.150035399 0.178865135
+0.17268591 -0.26303437 0.24086683 0.90254857 0.243275884 0.415871218 -0.743047478 -0.176811974 0.030180266 0.152546468 0.178125628
+0.18227957 -0.26324622 0.25273466 0.90350253 0.266539966 0.39937649 -0.754783066 -0.171763051 0.049695868 0.15566883 0.177322116
+0.19187323 -0.263416705 0.26456153 0.90422669 0.290178829 0.382760737 -0.764361195 -0.166540245 0.06998758 0.159289606 0.176462865
+0.20146689 -0.263548689 0.27635373 0.90473797 0.314090464 0.366096416 -0.771648066 -0.16127501 0.09074392 0.16327494 0.175555437
+0.21106055 -0.263644631 0.28811681 0.90505705 0.338178841 0.34946335 -0.776507563 -0.156104717 0.111653878 0.167483997 0.174605815
+0.22065421 -0.263707062 0.29985674 0.90520533 0.362331265 0.332945267 -0.778818108 -0.151175098 0.132406904 0.171777842 0.173620572
+0.23024788 -0.263739786 0.31158062 0.90520302 0.386426416 0.31659922 -0.778481885 -0.146585483 0.152694553 0.176027951 0.172609028
+0.23984154 -0.2637483 0.3232968 0.90506819 0.410344037 0.300439585 -0.775428078 -0.142358927 0.172213726 0.180117981 0.171584122
+0.2494352 -0.263737527 0.33501406 0.90481637 0.43394679 0.284490683 -0.769621757 -0.138543468 0.190672344 0.18392919 0.170558684
+0.25902886 -0.263711718 0.34674065 0.90446067 0.457095181 0.268790075 -0.761047023 -0.135202579 0.207800883 0.187354915 0.1695436
+0.26862252 -0.263675066 0.3584856 0.90401181 0.479636001 0.253365432 -0.74975634 -0.13238925 0.223365577 0.190313693 0.168551632
+0.27821618 -0.263632059 0.37025742 0.90347895 0.501429082 0.238233036 -0.735829337 -0.130139148 0.237187888 0.192753458 0.167594908
+0.28780985 -0.263587296 0.38206273 0.90287023 0.522366351 0.223407778 -0.719342179 -0.128470889 0.249161679 0.194654099 0.166681952
+0.29740351 -0.263544928 0.39390784 0.90219222 0.542348415 0.208902072 -0.700411802 -0.127413669 0.259259119 0.196027907 0.165820444
+0.30699717 -0.263508812 0.40579709 0.90145035 0.561323972 0.194721571 -0.679134063 -0.126983291 0.26752961 0.196923886 0.16501368
+0.31659083 -0.263482642 0.41773411 0.90064844 0.579268997 0.18086429 -0.655596358 -0.127186852 0.274082766 0.19741978 0.164262844
+0.32618449 -0.263470241 0.42972222 0.89978852 0.59617356 0.167325862 -0.629892637 -0.128024382 0.279064674 0.197608562 0.163567518
+0.33577815 -0.263476545 0.44176595 0.89887075 0.612011344 0.154095665 -0.602153429 -0.129486079 0.282634911 0.197582259 0.162929015
+0.34537181 -0.263508411 0.45387176 0.89789403 0.626721369 0.141155521 -0.572512693 -0.131553545 0.284952621 0.197403826 0.162352733
+0.35496548 -0.263571485 0.46604776 0.89685722 0.640223615 0.128499339 -0.541170932 -0.13423171 0.286171047 0.197087781 0.161845392
+0.36455914 -0.263667009 0.47830591 0.89576296 0.65240092 0.116142625 -0.508586093 -0.137584219 0.286432272 0.196606361 0.161417274
+0.3741528 -0.263795524 0.49066444 0.89462361 0.663026895 0.104120714 -0.475355592 -0.141719617 0.285863414 0.195854526 0.161091135
+0.38374646 -0.26395744 0.50314483 0.89346085 0.67182581 0.092488606 -0.442041485 -0.146762118 0.284576033 0.19461586 0.160897007
+0.39334012 -0.264147207 0.51576584 0.89229721 0.678633505 0.081323148 -0.409129284 -0.152859379 0.282664158 0.192606219 0.160855576
+0.40293378 -0.264350906 0.52854205 0.89115067 0.683448148 0.070716099 -0.377000866 -0.16018322 0.280199755 0.189537207 0.160973895
+0.41252745 -0.264547355 0.54148631 0.89003554 0.686389878 0.060759752 -0.346033277 -0.168913938 0.277228166 0.185159684 0.161250717
+0.42212111 -0.264715253 0.5546125 0.888964 0.687584644 0.05153945 -0.316484219 -0.179220643 0.273770226 0.17924927 0.161687636
+0.43171477 -0.264834637 0.56793507 0.88794522 0.68715166 0.043132573 -0.288559446 -0.191252879 0.269824084 0.171593841 0.162288809
+0.44130843 -0.26488361 0.58146821 0.88698448 0.685223599 0.03561966 -0.262556525 -0.205167641 0.265367335 0.162003974 0.163056655
+0.45090209 -0.264840126 0.59522634 0.88608284 0.68188844 0.029097011 -0.238729181 -0.221144437 0.260364772 0.150288055 0.163996272
+0.46049575 -0.264679681 0.60922304 0.88523602 0.677229457 0.02366775 -0.217423937 -0.239373949 0.254766118 0.136263805 0.165112063
+0.47008941 -0.264375087 0.62347006 0.88443285 0.671340242 0.019430621 -0.199143374 -0.260034354 0.248505043 0.119800092 0.166406632
diff --git a/Bindings/Python/examples/Moco/example2DWalking/referenceGRF.sto b/Bindings/Python/examples/Moco/example2DWalking/referenceGRF.sto
new file mode 100644
index 0000000000..ea7372e1bc
--- /dev/null
+++ b/Bindings/Python/examples/Moco/example2DWalking/referenceGRF.sto
@@ -0,0 +1,56 @@
+version=1
+nRows=50
+nColumns=19
+inDegrees=no
+endheader
+time ground_force_r_vx ground_force_r_vy ground_force_r_vz ground_force_l_vx ground_force_l_vy ground_force_l_vz ground_force_r_px ground_force_r_py ground_force_r_pz ground_torque_r_x ground_torque_r_y ground_torque_r_z ground_force_l_px ground_force_l_py ground_force_l_pz ground_torque_l_x ground_torque_l_y ground_torque_l_z
+0 -44.72876818 29.04783005 0 116.9150482 518.9919061 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.00959366 -80.70993994 58.17685056 0 112.5300264 447.22788 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.01918732 -194.1883161 163.4317854 0 96.94474098 369.4335826 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.02878098 -101.1143896 473.9459685 0 71.19198143 269.1178404 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.03837465 -123.1892148 832.2568261 0 20.52447173 107.1657532 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.04796831 -162.6262962 1082.657734 0 -0.351146207 0.399165328 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.05756197 -162.3223543 1137.739521 0 0.735020556 -0.697238179 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.06715563 -133.0618758 1020.885259 0 0.063943811 -0.053070133 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.07674929 -94.31653198 849.0253536 0 0.008493774 -0.006365101 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.08634295 -63.94628978 703.0735561 0 0.005012414 -0.003452482 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.09593662 -43.36830109 602.3660585 0 0.017215667 -0.011031164 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.10553028 -29.98021554 538.4096336 0 0.003994266 -0.002401198 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.11512394 -20.86032418 498.9031722 0 -0.103988334 0.059018415 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.1247176 -14.15437725 474.5977654 0 -0.07149884 0.038510149 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.13431126 -8.682453945 459.6932278 0 -0.043882155 0.022527374 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.14390492 -3.767695979 450.99912 0 -0.030029999 0.014749761 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.15349858 0.5711294 447.3545664 0 -0.024522938 0.011563929 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.16309225 4.627454508 448.9596287 0 -0.024375213 0.011071356 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.17268591 8.52901387 455.9589672 0 -0.029440536 0.012916646 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.18227957 9.836865735 467.6678075 0 -0.042527454 0.018066634 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.19187323 13.2070312 482.7432469 0 -0.070878936 0.029253636 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.20146689 15.14708192 498.9268941 0 -0.132125005 0.053077912 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.21106055 17.81920746 514.8192008 0 -0.263673162 0.103364878 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.22065421 18.78544929 529.1738275 0 -0.539813205 0.206966148 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.23024788 20.75158299 541.8117521 0 -1.082144951 0.406899831 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.23984154 21.26780054 552.4245249 0 -2.041623387 0.754504993 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.2494352 24.11255405 561.0728088 0 -3.480735071 1.268525498 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.25902886 23.01025613 568.1294535 0 -5.231812724 1.884036085 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.26862252 25.24384531 574.1443889 0 -6.73844898 2.408260617 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.27821618 24.24124078 579.2951272 0 -7.385977339 2.623760693 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.28780985 23.34982403 583.8783841 0 -6.773099693 2.39836048 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.29740351 22.31168622 587.8150344 0 -5.081857993 1.796612744 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.30699717 21.16301834 590.8194335 0 -2.986235199 1.05487801 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.31659083 20.79062274 592.5106855 0 -1.279853528 0.451987769 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.32618449 19.3131051 592.7073041 0 -0.400529529 0.14155292 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.33577815 18.83286351 591.3300124 0 -0.503920494 0.178513154 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.34537181 19.3077819 589.3487921 0 -2.660445586 0.943528286 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.35496548 21.04548114 588.8376909 0 -9.676101293 3.448528052 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.36455914 25.66171516 591.3196451 0 -22.07264702 7.967110626 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.3741528 32.92462926 596.1476995 0 -33.61999039 12.41666689 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.38374646 40.98291968 600.4637207 0 -38.79529227 14.80407071 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.39334012 50.03052978 604.7580299 0 -38.45857743 15.26104242 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.40293378 59.06582568 609.712179 0 -35.30109901 14.61504568 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.41252745 68.31852304 613.8200683 0 -31.25816585 13.5369722 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.42212111 77.82744972 616.1591504 0 -27.65935088 12.54038274 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.43171477 87.50658842 615.5098877 0 -25.00832044 11.89319162 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.44130843 97.27797645 610.7621406 0 -23.65794881 11.83094037 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.45090209 106.6857723 601.3876317 0 -23.96903654 12.64370381 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.46049575 115.2949437 585.3046159 0 -26.4721059 14.81458097 0 0 0 0 0 0 0 0 0 0 0 0 0
+0.47008941 119.1204798 559.9827889 0 -32.28560944 19.3260534 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/Bindings/Python/examples/Moco/example2DWalking/referenceGRF.xml b/Bindings/Python/examples/Moco/example2DWalking/referenceGRF.xml
new file mode 100644
index 0000000000..f16e4519a9
--- /dev/null
+++ b/Bindings/Python/examples/Moco/example2DWalking/referenceGRF.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+ calcn_r
+
+ ground
+
+ ground
+
+ ground_force_r_v
+
+ ground_force_r_p
+
+ ground_torque_r_
+
+
+ calcn_l
+ ground
+ ground
+ ground_force_l_v
+ ground_force_l_p
+ ground_torque_l_
+
+
+
+
+ referenceGRF.sto
+
+