Source code for genesis.engine.entities.drone_entity

import os
import xml.etree.ElementTree as ET

import torch
import gstaichi as ti

import genesis as gs
from genesis.utils.misc import get_assets_dir

from .rigid_entity import RigidEntity


[docs]@ti.data_oriented class DroneEntity(RigidEntity): def _load_scene(self, morph, surface): super()._load_scene(morph, surface) # additional drone specific attributes properties = ET.parse(os.path.join(get_assets_dir(), morph.file)).getroot()[0].attrib self._KF = float(properties["kf"]) self._KM = float(properties["km"]) self._n_propellers = len(morph.propellers_link_name) propellers_link = gs.List([self.get_link(name) for name in morph.propellers_link_name]) self._propellers_link_idx = torch.tensor( [link.idx for link in propellers_link], dtype=gs.tc_int, device=gs.device ) try: self._propellers_vgeom_idxs = torch.tensor( [link.vgeoms[0].idx for link in propellers_link], dtype=gs.tc_int, device=gs.device ) self._animate_propellers = True except Exception: gs.logger.warning("No visual geometry found for propellers. Skipping propeller animation.") self._animate_propellers = False self._propellers_spin = torch.tensor(morph.propellers_spin, dtype=gs.tc_float, device=gs.device) self._model = morph.model def _build(self): super()._build() self._propellers_revs = torch.zeros((self._n_propellers, self.solver._B), dtype=gs.tc_float, device=gs.device) self._prev_prop_t = None
[docs] def set_propellels_rpm(self, propellels_rpm): """ Set the RPM (revolutions per minute) for each propeller in the drone. Parameters ---------- propellels_rpm : array-like or torch.Tensor A tensor or array of shape (n_propellers,) or (n_envs, n_propellers) specifying the desired RPM values for each propeller. Must be non-negative. Raises ------ RuntimeError If the method is called more than once per simulation step, or if the input shape does not match the number of propellers, or contains negative values. """ if self._prev_prop_t == self.sim.cur_step_global: gs.raise_exception("`set_propellels_rpm` can only be called once per step.") self._prev_prop_t = self.sim.cur_step_global assert propellels_rpm is not None propellels_rpm, *_ = self._solver._sanitize_io_variables( propellels_rpm, self._propellers_link_idx, self._n_propellers, "propellers_link_idx" ) if self._scene.n_envs == 0: propellels_rpm = propellels_rpm[None] # FIXME: This check is too expensive # if (propellels_rpm < 0.0).any(): # gs.raise_exception("`propellels_rpm` cannot be negative.") self._propellers_revs = (self._propellers_revs + propellels_rpm.T) % (60 / self.solver.dt) self.solver.set_drone_rpm( self._propellers_link_idx, propellels_rpm, self._propellers_spin, self.KF, self.KM, self._model == "RACE", )
[docs] def update_propeller_vgeoms(self): """ Update the visual geometry of the propellers for animation based on their current rotation. This method is a no-op if animation is disabled due to missing visual geometry. """ if self._animate_propellers: self.solver.update_drone_propeller_vgeoms( self._propellers_vgeom_idxs, self._propellers_revs, self._propellers_spin )
@property def model(self): """The model type of the drone.""" return self._model @property def KF(self): """The drone's thrust coefficient.""" return self._KF @property def KM(self): """The drone's moment coefficient.""" return self._KM @property def n_propellers(self): """The number of propellers on the drone.""" return self._n_propellers @property def COM_link_idx(self): """The index of the center-of-mass (COM) link of the drone.""" return self._COM_link_idx @property def propellers_idx(self): """The indices of the drone's propeller links.""" return self._propellers_link_idx @property def propellers_spin(self): """The spin direction for each propeller.""" return self._propellers_spin