UAV Models and Usage#
Using the UAV Models#
All UAV models, e.g., Hummingbird and Firefly are subclassed from MultirotorBase.
An instance of MultirotorBase does not correspond to a specific UAV, but acts like a view
(think of torch.Tensor.view) that holds the states of a group of UAVs of that type in torch.Tensor for reading
and writing. It implements the multirotor dynamics and provides interfaces for common interactions such as getting the
kinematic information and applying rotor commands.
The following example demonstrates:
How to create and control a UAV using the
MultirotorBaseclass.How to use a position controller to track a circular trajectory.
How to attach cameras to UAVs to record images and hence videos.
Importing/Creating New UAVs#
Since MultirotorBase implements the commonly use multirotor dynamics, adding an ordinary UAV is easy:
from omni_drones.robots.drone.multirotor import MultirotorBase
from omni_drones.robots.robot import ASSET_PATH
class Iris(MultirotorBase):
usd_path: str = ASSET_PATH + "/usd/iris.usd"
param_path: str = ASSET_PATH + "/usd/iris.yaml"
which effectively comes to 1. providing a description file in .usd format, and 2. specifying its parameters.
Adding an UAV model with more complex dynamics may require extending the corresponding methods.
For example, the omnidirectional UAV Omav has 6 tilt units. So we extend Omav.apply_action()
to control the tilt units via velocity targets, and Omav._reset_idx() to reset the tilting angle.
1import torch
2from torchrl.data import BoundedTensorSpec, UnboundedContinuousTensorSpec
3
4from omni_drones.robots.drone import MultirotorBase
5from omni_drones.robots.robot import ASSET_PATH
6
7class Omav(MultirotorBase):
8
9 usd_path: str = ASSET_PATH + "/usd/omav.usd"
10 param_path: str = ASSET_PATH + "/usd/omav.yaml"
11
12 def __init__(self, name: str = "Omav", cfg=None) -> None:
13 super().__init__(name, cfg)
14 self.action_spec = BoundedTensorSpec(-1, 1, 12 + 6, device=self.device)
15 self.tilt_dof_indices = torch.arange(0, 6, device=self.device)
16 self.rotor_dof_indices = torch.arange(6, 18, device=self.device)
17 self.max_tilt_velocity = 10
18
19 self.action_spec = BoundedTensorSpec(-1, 1, self.num_rotors + 6, device=self.device)
20 self.state_spec = UnboundedContinuousTensorSpec(
21 19 + self.num_rotors + 12, device=self.device
22 )
23
24 def initialize(self, prim_paths_expr: str = None):
25 if not self.is_articulation:
26 raise NotImplementedError
27 super().initialize(prim_paths_expr)
28 self.init_joint_positions = self._view.get_joint_positions()
29 self.init_joint_velocities = self._view.get_joint_velocities()
30
31 def apply_action(self, actions: torch.Tensor) -> torch.Tensor:
32 rotor_cmds, tilt_cmds = actions.expand(*self.shape, 18).split([12, 6], dim=-1)
33 super().apply_action(rotor_cmds)
34
35 velocity_targets = tilt_cmds.clamp(-1, 1) * self.max_tilt_velocity
36 self._view.set_joint_velocity_targets(
37 velocity_targets, joint_indices=self.tilt_dof_indices
38 )
39
40 return self.throttle.sum(-1)
41
42 def _reset_idx(self, env_ids: torch.Tensor, train: bool=True):
43 env_ids = super()._reset_idx(env_ids, train)
44 self._view.set_joint_positions(
45 self.init_joint_positions[env_ids],
46 env_indices=env_ids,
47 )
48 self._view.set_joint_velocities(
49 self.init_joint_velocities[env_ids],
50 env_indices=env_ids,
51 )
52 return env_ids
53
54 def get_state(self, env=True):
55 state = super().get_state(env)
56 joint_positions = self.get_joint_positions()[..., self.tilt_dof_indices]
57 joint_velocities = self.get_joint_velocities()[..., self.tilt_dof_indices]
58 state = torch.cat([state, joint_positions, joint_velocities], dim=-1)
59 assert not torch.isnan(state).any()
60 return state
61
Customized Dynamics#
Note
To be refactored.
Building New Configurations#
Note
To be refactored.