@@ -42,9 +42,9 @@ def create_rnd_commands(shape: tuple[int, ...] = (), dim: int = 4) -> Array:
4242
4343def skip_models_without_features (model : Callable , features : list [str ]):
4444 """Skip the model if it does not have the required features."""
45- for feature in model_features ( model ) :
46- if feature not in features :
47- pytest .skip (f"Model { model .__name__ } does not have the required features ." )
45+ for feature in features :
46+ if not model_features ( model )[ feature ] :
47+ pytest .skip (f"Model { model .__name__ } does not have the feature ' { feature } ' ." )
4848
4949
5050@pytest .mark .unit
@@ -54,25 +54,76 @@ def test_model_features(model_name: str, model: Callable):
5454 assert hasattr (model , "__drone_model_features__" ), (
5555 f"Model function { model_name } does not have __drone_model_features__ attribute"
5656 )
57- features = getattr (model , "__drone_model_features__" )
57+ features = model_features (model )
5858 assert isinstance (features , dict ), (
59- f"__drone_model_features__ should be a dict, got { type (features )} for { model_name } "
59+ f"model features should be a dict, got { type (features )} for { model_name } "
6060 )
6161 assert "rotor_dynamics" in features , (
62- f"__drone_model_features__ should contain 'rotor_dynamics' key for { model_name } "
62+ f"model features should contain 'rotor_dynamics' key for { model_name } "
6363 )
6464
6565
6666@pytest .mark .unit
6767@pytest .mark .parametrize ("model_name, model" , available_models .items ())
6868@pytest .mark .parametrize ("drone_name" , Constants .available_configs )
69- def test_model_single_input_no_rotor_dynamics (model_name : str , model : Callable , drone_name : str ):
69+ def test_model_single_no_rotor_dynamics (model_name : str , model : Callable , drone_name : str ):
7070 pos , quat , vel , ang_vel , _ , _ , _ = create_rnd_states ()
71- commands = create_rnd_commands (dim = 4 ) # TODO make dependent on model
71+ cmd = create_rnd_commands (dim = 4 ) # TODO make dependent on model
72+ if model_features (model )["rotor_dynamics" ]:
73+ with pytest .warns (UserWarning , match = "Rotor velocity is not provided" ):
74+ dpos , dquat , dvel , dang_vel , drotor_vel = model (
75+ pos , quat , vel , ang_vel , cmd , Constants .from_config (drone_name , xp ), rotor_vel = None
76+ )
77+ else :
78+ dpos , dquat , dvel , dang_vel , drotor_vel = model (
79+ pos , quat , vel , ang_vel , cmd , Constants .from_config (drone_name , xp ), rotor_vel = None
80+ )
81+ assert drotor_vel is None , "Model should not return rotor velocities without rotor_vel input"
82+ # Check if the output is on the correct device, has the correct type and shape
83+ for dx , x in zip ([dpos , dquat , dvel , dang_vel ], [pos , quat , vel , ang_vel ], strict = True ):
84+ assert isinstance (dx , type (x ))
85+ assert xp_device (dx ) == xp_device (x )
86+ assert dx .shape == x .shape
87+
88+
89+ @pytest .mark .unit
90+ @pytest .mark .parametrize ("model_name, model" , available_models .items ())
91+ @pytest .mark .parametrize ("drone_name" , Constants .available_configs )
92+ def test_model_single_rotor_dynamics (model_name : str , model : Callable , drone_name : str ):
93+ skip_models_without_features (model , ["rotor_dynamics" ])
94+
95+ pos , quat , vel , ang_vel , rotor_vel , _ , _ = create_rnd_states ()
96+ cmd = create_rnd_commands (dim = 4 ) # TODO make dependent on model
7297 dpos , dquat , dvel , dang_vel , drotor_vel = model (
73- pos , quat , vel , ang_vel , commands , Constants .from_config (drone_name , xp ), rotor_vel = None
98+ pos , quat , vel , ang_vel , cmd , Constants .from_config (drone_name , xp ), rotor_vel = rotor_vel
7499 )
75100 # Check if the output is on the correct device, has the correct type and shape
101+ for dx , x in zip (
102+ [dpos , dquat , dvel , dang_vel , drotor_vel ], [pos , quat , vel , ang_vel , rotor_vel ], strict = True
103+ ):
104+ assert isinstance (dx , type (x ))
105+ assert xp_device (dx ) == xp_device (x )
106+ assert dx .shape == x .shape
107+
108+
109+ @pytest .mark .unit
110+ @pytest .mark .parametrize ("model_name, model" , available_models .items ())
111+ @pytest .mark .parametrize ("drone_name" , Constants .available_configs )
112+ def test_model_batched_no_rotor_dynamics (model_name : str , model : Callable , drone_name : str ):
113+ batch_shape = (10 ,)
114+ pos , quat , vel , ang_vel , _ , _ , _ = create_rnd_states (batch_shape )
115+ cmd = create_rnd_commands (batch_shape , dim = 4 ) # TODO make dependent on model
116+ if model_features (model )["rotor_dynamics" ]:
117+ with pytest .warns (UserWarning , match = "Rotor velocity is not provided" ):
118+ dpos , dquat , dvel , dang_vel , drotor_vel = model (
119+ pos , quat , vel , ang_vel , cmd , Constants .from_config (drone_name , xp ), rotor_vel = None
120+ )
121+ else :
122+ dpos , dquat , dvel , dang_vel , drotor_vel = model (
123+ pos , quat , vel , ang_vel , cmd , Constants .from_config (drone_name , xp ), rotor_vel = None
124+ )
125+ assert drotor_vel is None , "Model should not return rotor velocities without rotor_vel input"
126+ # Check if the output is on the correct device, has the correct type and shape
76127 for dx , x in zip ([dpos , dquat , dvel , dang_vel ], [pos , quat , vel , ang_vel ], strict = True ):
77128 assert isinstance (dx , type (x ))
78129 assert xp_device (dx ) == xp_device (x )
@@ -82,19 +133,14 @@ def test_model_single_input_no_rotor_dynamics(model_name: str, model: Callable,
82133@pytest .mark .unit
83134@pytest .mark .parametrize ("model_name, model" , available_models .items ())
84135@pytest .mark .parametrize ("drone_name" , Constants .available_configs )
85- def test_model_single_input_rotor_dynamics (model_name : str , model : Callable , drone_name : str ):
136+ def test_model_batched_rotor_dynamics (model_name : str , model : Callable , drone_name : str ):
86137 skip_models_without_features (model , ["rotor_dynamics" ])
87138
88- pos , quat , vel , ang_vel , rotor_vel , _ , _ = create_rnd_states ()
89- commands = create_rnd_commands (dim = 4 ) # TODO make dependent on model
139+ batch_shape = (10 ,)
140+ pos , quat , vel , ang_vel , rotor_vel , _ , _ = create_rnd_states (batch_shape )
141+ cmd = create_rnd_commands (batch_shape , dim = 4 ) # TODO make dependent on model
90142 dpos , dquat , dvel , dang_vel , drotor_vel = model (
91- pos ,
92- quat ,
93- vel ,
94- ang_vel ,
95- commands ,
96- Constants .from_config (drone_name , xp ),
97- rotor_vel = rotor_vel ,
143+ pos , quat , vel , ang_vel , cmd , Constants .from_config (drone_name , xp ), rotor_vel = rotor_vel
98144 )
99145 # Check if the output is on the correct device, has the correct type and shape
100146 for dx , x in zip (
0 commit comments