Skip to content

Commit 1abff89

Browse files
authored
Merge pull request #1055 from pariterre/codex/pinocchio-model-backend
Allowing for pinocchio backend
2 parents d780a4d + 691129a commit 1abff89

22 files changed

Lines changed: 1380 additions & 61 deletions

.vscode/launch.json

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,51 +5,39 @@
55
"version": "0.2.0",
66
"configurations": [
77
{
8-
"name": "Run examples GUI",
8+
"name": "Getting Started",
99
"type": "debugpy",
1010
"request": "launch",
11-
"module": "bioptim.examples",
11+
"program": "${workspaceFolder}/bioptim/examples/getting_started/basic_ocp.py",
12+
"console": "integratedTerminal",
1213
"justMyCode": true,
1314
"env": {
1415
"PYTHONPATH": "${workspaceFolder}"
1516
},
16-
"cwd": "${fileDirname}"
17+
"cwd": "${workspaceFolder}/bioptim/examples/getting_started/"
1718
},
1819
{
19-
"name": "Python : fichier actif",
20+
"name": "Run examples GUI",
2021
"type": "debugpy",
2122
"request": "launch",
22-
"program": "${file}",
23-
"console": "integratedTerminal",
24-
"justMyCode": false,
23+
"module": "bioptim.examples",
24+
"justMyCode": true,
2525
"env": {
2626
"PYTHONPATH": "${workspaceFolder}"
2727
},
2828
"cwd": "${fileDirname}"
2929
},
3030
{
31-
"name": "Pendulum",
31+
"name": "Python : fichier actif",
3232
"type": "debugpy",
3333
"request": "launch",
34-
"program": "${workspaceFolder}/bioptim/examples/getting_started/pendulum.py",
34+
"program": "${file}",
3535
"console": "integratedTerminal",
36-
"justMyCode": true,
36+
"justMyCode": false,
3737
"env": {
3838
"PYTHONPATH": "${workspaceFolder}"
3939
},
40-
"cwd": "${workspaceFolder}/bioptim/examples/getting_started/"
40+
"cwd": "${fileDirname}"
4141
},
42-
{
43-
"name": "Parameter",
44-
"type": "debugpy",
45-
"request": "launch",
46-
"program": "${workspaceFolder}/bioptim/examples/muscle_driven_ocp/custom_parameters.py",
47-
"console": "integratedTerminal",
48-
"justMyCode": true,
49-
"env": {
50-
"PYTHONPATH": "${workspaceFolder}"
51-
},
52-
"cwd": "${workspaceFolder}/bioptim/examples/muscle_driven_ocp/"
53-
}
5442
]
5543
}

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,9 @@ available in the `biorbd` documentation.
20072007
### The [example_optimal_time.py](./bioptim/examples/getting_started/example_optimal_time.py) file
20082008
Examples of time optimization can be found in 'examples/optimal_time_ocp/'.
20092009

2010+
### The [example_pinocchio.py](./bioptim/examples/getting_started/example_pinocchio.py) file
2011+
This example is the exact same as the pendulum example, but with a model defined using the `Pinocchio` backend (instead of the `biorbd` backend). It is designed to show how to use a model defined in Pinocchio instead of biorbd.
2012+
20102013
### The [example_simulation.py](./bioptim/examples/getting_started/example_simulation.py) file
20112014
The first part of this example is a single shooting simulation from initial guesses.
20122015
It is not an optimal control program. It is merely the simulation of values that is applying the dynamics.

bioptim/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
JointAccelerationBiorbdModel,
218218
MultiTorqueBiorbdModel,
219219
)
220+
from .models.pinocchio import PinocchioModel, TorquePinocchioModel
220221
from .models.protocols.biomodel import BioModel
221222
from .models.protocols.holonomic_constraints import HolonomicConstraintsFcn, HolonomicConstraintsList
222223
from .models.protocols.stochastic_biomodel import StochasticBioModel

bioptim/examples/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
("Example simulation", "example_simulation.py"),
5252
("Example cyclic movement", "example_cyclic_movement.py"),
5353
("Example constraint weight", "custom_constraint_weights.py"),
54+
("Example Pinocchio", "example_pinocchio.py"),
5455
("How to plot", "how_to_plot.py"),
5556
]
5657
),
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""
2+
A very simple yet meaningful optimal control program consisting in a pendulum starting downward and ending upward
3+
while requiring the minimum of generalized forces. The solver is only allowed to move the pendulum sideways.
4+
5+
This simple example is a good place to start investigating bioptim as it describes the most common dynamics out there
6+
(the joint torque driven), it defines an objective function and some boundaries and initial guesses
7+
8+
During the optimization process, the graphs are updated real-time (even though it is a bit too fast and short to really
9+
appreciate it). Finally, once it finished optimizing, it animates the model using the optimal solution
10+
"""
11+
12+
from bioptim import (
13+
OptimalControlProgram,
14+
DynamicsOptions,
15+
BoundsList,
16+
InitialGuessList,
17+
ObjectiveFcn,
18+
Objective,
19+
OdeSolver,
20+
OdeSolverBase,
21+
Solver,
22+
TorquePinocchioModel,
23+
PhaseDynamics,
24+
OnlineOptim,
25+
OrderingStrategy,
26+
CostType,
27+
)
28+
29+
from bioptim.examples.utils import ExampleUtils
30+
31+
32+
def prepare_ocp(
33+
model_path: str,
34+
final_time: float,
35+
n_shooting: int,
36+
ode_solver: OdeSolverBase = OdeSolver.RK4(),
37+
n_threads: int = 1,
38+
phase_dynamics: PhaseDynamics = PhaseDynamics.SHARED_DURING_THE_PHASE,
39+
expand_dynamics: bool = True,
40+
ordering_strategy: OrderingStrategy = OrderingStrategy.VARIABLE_MAJOR,
41+
) -> OptimalControlProgram:
42+
"""
43+
The initialization of an ocp
44+
45+
Parameters
46+
----------
47+
model_path: str
48+
The path to the Pinocchio model
49+
final_time: float
50+
The time in second required to perform the task
51+
n_shooting: int
52+
The number of shooting points to define int the direct multiple shooting program
53+
ode_solver: OdeSolverBase = OdeSolver.RK4()
54+
Which type of OdeSolver to use
55+
n_threads: int
56+
The number of threads to use in the paralleling (1 = no parallel computing)
57+
phase_dynamics: PhaseDynamics
58+
If the dynamics equation within a phase is unique or changes at each node.
59+
PhaseDynamics.SHARED_DURING_THE_PHASE is much faster, but lacks the capability to have changing dynamics within
60+
a phase. PhaseDynamics.ONE_PER_NODE should also be used when multi-node penalties with more than 3 nodes or with COLLOCATION (cx_intermediate_list) are added to the OCP.
61+
expand_dynamics: bool
62+
If the dynamics function should be expanded. Please note, this will solve the problem faster, but will slow down
63+
the declaration of the OCP, so it is a trade-off. Also depending on the solver, it may or may not work
64+
(for instance IRK is not compatible with expanded dynamics)
65+
66+
Returns
67+
-------
68+
The OptimalControlProgram ready to be solved
69+
"""
70+
71+
bio_model = TorquePinocchioModel(model_path)
72+
73+
# Add objective functions
74+
objective_functions = Objective(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="tau")
75+
76+
# DynamicsOptions
77+
dynamics = DynamicsOptions(ode_solver=ode_solver, expand_dynamics=expand_dynamics, phase_dynamics=phase_dynamics)
78+
79+
# Path bounds
80+
x_bounds = BoundsList()
81+
x_bounds["q"] = bio_model.bounds_from_ranges("q")
82+
x_bounds["q"][:, [0, -1]] = 0 # Start and end at 0...
83+
x_bounds["q"][1, -1] = 3.14 # ...but end with pendulum 180 degrees rotated
84+
x_bounds["qdot"] = bio_model.bounds_from_ranges("qdot")
85+
x_bounds["qdot"][:, [0, -1]] = 0 # Start and end without any velocity
86+
87+
# Initial guess (optional since it is 0, we show how to initialize anyway)
88+
x_init = InitialGuessList()
89+
x_init["q"] = [0] * bio_model.nb_q
90+
x_init["qdot"] = [0] * bio_model.nb_qdot
91+
92+
# Define control path bounds
93+
n_tau = bio_model.nb_tau
94+
u_bounds = BoundsList()
95+
u_bounds["tau"] = [-100] * n_tau, [100] * n_tau # Limit the strength of the pendulum to (-100 to 100)...
96+
u_bounds["tau"][1, :] = 0 # ...but remove the capability to actively rotate
97+
98+
# Initial guess (optional since it is 0, we show how to initialize anyway)
99+
u_init = InitialGuessList()
100+
u_init["tau"] = [0] * n_tau
101+
102+
return OptimalControlProgram(
103+
bio_model,
104+
n_shooting,
105+
final_time,
106+
dynamics=dynamics,
107+
x_init=x_init,
108+
u_init=u_init,
109+
x_bounds=x_bounds,
110+
u_bounds=u_bounds,
111+
objective_functions=objective_functions,
112+
use_sx=True,
113+
n_threads=n_threads,
114+
ordering_strategy=ordering_strategy,
115+
)
116+
117+
118+
def main():
119+
"""
120+
If pendulum is run as a script, it will perform the optimization and animates it
121+
"""
122+
123+
# --- Prepare the ocp --- #
124+
model_path = ExampleUtils.folder + "/models/pendulum.urdf"
125+
ocp = prepare_ocp(model_path=model_path, final_time=1, n_shooting=400, n_threads=2)
126+
127+
# --- Solve the ocp --- #
128+
ocp.add_plot_penalty(CostType.ALL) # This will display the objectives and constraints at the current iteration
129+
solver = Solver.IPOPT(online_optim=OnlineOptim.DEFAULT)
130+
sol = ocp.solve(solver)
131+
132+
# --- Show the results graph --- #
133+
sol.print_cost()
134+
# sol.graphs(show_bounds=True, save_name="results.png")
135+
136+
137+
if __name__ == "__main__":
138+
main()
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?xml version="1.0"?>
2+
<robot name="Pendulum">
3+
4+
<!-- World link -->
5+
<link name="world"/>
6+
7+
<!-- Intermediate link for Y translation -->
8+
<link name="Seg1_translation_link"/>
9+
10+
<!-- Prismatic joint: translation along y -->
11+
<joint name="Seg1_translation_y" type="prismatic">
12+
<parent link="world"/>
13+
<child link="Seg1_translation_link"/>
14+
15+
<origin xyz="0 0 0" rpy="0 0 0"/>
16+
<axis xyz="0 1 0"/>
17+
18+
<limit lower="-1" upper="5" effort="1000" velocity="100"/>
19+
</joint>
20+
21+
<!-- Main segment link -->
22+
<link name="Seg1">
23+
<inertial>
24+
<!-- Center of mass -->
25+
<origin xyz="-0.0005 0.0688 -0.9542" rpy="0 0 0"/>
26+
27+
<mass value="1"/>
28+
29+
<inertia
30+
ixx="0.0391"
31+
ixy="0.0000"
32+
ixz="0.0000"
33+
iyy="0.0335"
34+
iyz="-0.0032"
35+
izz="0.0090"/>
36+
</inertial>
37+
38+
<visual>
39+
<origin xyz="0 0 0" rpy="0 0 0"/>
40+
<geometry>
41+
<mesh filename="mesh/pendulum.STL"/>
42+
</geometry>
43+
</visual>
44+
45+
<collision>
46+
<origin xyz="0 0 0" rpy="0 0 0"/>
47+
<geometry>
48+
<mesh filename="mesh/pendulum.STL"/>
49+
</geometry>
50+
</collision>
51+
</link>
52+
53+
<!-- Revolute joint: rotation about x -->
54+
<joint name="Seg1_rotation_x" type="revolute">
55+
<parent link="Seg1_translation_link"/>
56+
<child link="Seg1"/>
57+
58+
<origin xyz="0 0 0" rpy="0 0 0"/>
59+
<axis xyz="1 0 0"/>
60+
61+
<limit lower="-6.28318530718"
62+
upper="6.28318530718"
63+
effort="1000"
64+
velocity="100"/>
65+
</joint>
66+
67+
<!-- Marker 1 -->
68+
<link name="marker_1"/>
69+
70+
<joint name="marker_1_fixed" type="fixed">
71+
<parent link="Seg1"/>
72+
<child link="marker_1"/>
73+
<origin xyz="0 0 0" rpy="0 0 0"/>
74+
</joint>
75+
76+
<!-- Marker 2 -->
77+
<link name="marker_2"/>
78+
79+
<joint name="marker_2_fixed" type="fixed">
80+
<parent link="Seg1"/>
81+
<child link="marker_2"/>
82+
<origin xyz="-0.0005 0.0688 -0.9542" rpy="0 0 0"/>
83+
</joint>
84+
85+
</robot>

bioptim/gui/online_callback_multiprocess.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,7 @@
88
from .plot import PlotOcp, OcpSerializable
99
from ..optimization.optimization_vector import OptimizationVectorHelper
1010
from .online_callback_abstract import OnlineCallbackAbstract
11-
from ..misc.parameters_types import (
12-
Bool,
13-
Float,
14-
AnyIterable,
15-
AnyDictOptional,
16-
IntListOptional,
17-
)
11+
from ..misc.parameters_types import Bool, Float, AnyIterable, AnyDictOptional, IntListOptional
1812

1913

2014
class OnlineCallbackMultiprocess(OnlineCallbackAbstract):

bioptim/models/biorbd/biorbd_model.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ def set_gravity(self, new_gravity: Parameter | MX | np.ndarray) -> None:
198198
self.model.setGravity(new_gravity.mx)
199199
else:
200200
self.model.setGravity(new_gravity)
201-
return
202201

203202
@property
204203
def nb_tau(self) -> int:
@@ -537,7 +536,7 @@ def forward_dynamics_free_floating_base(self) -> Function:
537536
return casadi_fun
538537

539538
@staticmethod
540-
def reorder_qddot_root_joints(qddot_root, qddot_joints) -> MX | SX:
539+
def reorder_qddot_root_joints(qddot_root: CX, qddot_joints: CX) -> CX:
541540
return vertcat(qddot_root, qddot_joints)
542541

543542
def _dispatch_forces(self) -> biorbd.ExternalForceSet:

bioptim/models/biorbd/model_dynamics.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
from typing import Callable
22
import biorbd_casadi as biorbd
3-
import numpy as np
43

5-
from .external_forces import (
6-
ExternalForceSetTimeSeries,
7-
ExternalForceSetVariables,
8-
)
4+
from .external_forces import ExternalForceSetTimeSeries, ExternalForceSetVariables
95
from ...optimization.parameters import ParameterList
106
from .biorbd_model import BiorbdModel
117
from .multi_biorbd_model import MultiBiorbdModel
@@ -26,13 +22,7 @@
2622
MusclesDynamicsWithExcitations,
2723
)
2824
from ..protocols.holonomic_constraints import HolonomicConstraintsList
29-
from ...misc.parameters_types import (
30-
Str,
31-
Int,
32-
Bool,
33-
NpArray,
34-
DM,
35-
)
25+
from ...misc.parameters_types import Str, Int, Bool, NpArray, DM
3626
from ...misc.mapping import BiMappingList
3727
from ...misc.enums import ContactType, QuadratureRule, ControlType
3828
from ...optimization.problem_type import SocpType
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .pinocchio_model import PinocchioModel
2+
from .model_dynamics import TorquePinocchioModel

0 commit comments

Comments
 (0)