nova.actions
1from nova.actions.base import Action 2from nova.actions.container import CombinedActions, MovementController, MovementControllerContext 3from nova.actions.io import io_write 4from nova.actions.motions import cir, collision_free, jnt, lin, ptp 5 6__all__ = [ 7 "Action", 8 "lin", 9 "ptp", 10 "cir", 11 "jnt", 12 "collision_free", 13 "io_write", 14 "MovementController", 15 "CombinedActions", 16 "MovementControllerContext", 17]
7class Action(pydantic.BaseModel, ABC): 8 @abstractmethod 9 @pydantic.model_serializer 10 def serialize_model(self): 11 """Serialize the model to a dictionary""" 12 13 @abstractmethod 14 def is_motion(self) -> bool: 15 """Return whether the action is a motion"""
Usage docs: https://docs.pydantic.dev/2.10/concepts/models/
A base class for creating Pydantic models.
Attributes:
__class_vars__: The names of the class variables defined on the model.
__private_attributes__: Metadata about the private attributes of the model.
__signature__: The synthesized __init__
[Signature
][inspect.Signature] of the model.
__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
__args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.
__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.
__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.
8 @abstractmethod 9 @pydantic.model_serializer 10 def serialize_model(self): 11 """Serialize the model to a dictionary"""
Serialize the model to a dictionary
123def lin( 124 target: PoseOrVectorTuple, 125 settings: MotionSettings = MotionSettings(), 126 collision_scene: wb.models.CollisionScene | None = None, 127) -> Linear: 128 """Convenience function to create a linear motion 129 130 Args: 131 target: the target pose or vector. If the target is a vector, the orientation is set to (0, 0, 0). 132 settings: the motion settings 133 134 Returns: the linear motion 135 136 Examples: 137 >>> ms = MotionSettings(tcp_velocity_limit=10) 138 >>> assert lin((1, 2, 3, 4, 5, 6), settings=ms) == Linear(target=Pose((1, 2, 3, 4, 5, 6)), settings=ms) 139 >>> assert lin((1, 2, 3)) == lin((1, 2, 3, 0, 0, 0)) 140 >>> assert lin(Pose((1, 2, 3, 4, 5, 6)), settings=ms) == lin((1, 2, 3, 4, 5, 6), settings=ms) 141 142 """ 143 if not isinstance(target, Pose): 144 t = (*target, 0.0, 0.0, 0.0) if len(target) == 3 else target 145 target = Pose(t) 146 147 return Linear(target=target, settings=settings, collision_scene=collision_scene)
Convenience function to create a linear motion
Args: target: the target pose or vector. If the target is a vector, the orientation is set to (0, 0, 0). settings: the motion settings
Returns: the linear motion
Examples:
>>> ms = MotionSettings(tcp_velocity_limit=10)
>>> assert lin((1, 2, 3, 4, 5, 6), settings=ms) == Linear(target=Pose((1, 2, 3, 4, 5, 6)), settings=ms)
>>> assert lin((1, 2, 3)) == lin((1, 2, 3, 0, 0, 0))
>>> assert lin(Pose((1, 2, 3, 4, 5, 6)), settings=ms) == lin((1, 2, 3, 4, 5, 6), settings=ms)
180def ptp( 181 target: PoseOrVectorTuple, 182 settings: MotionSettings = MotionSettings(), 183 collision_scene: wb.models.CollisionScene | None = None, 184) -> PTP: 185 """Convenience function to create a point-to-point motion 186 187 Args: 188 target: the target pose or vector. If the target is a vector, the orientation is set to (0, 0, 0). 189 settings: the motion settings 190 191 Returns: the point-to-point motion 192 193 Examples: 194 >>> ms = MotionSettings(tcp_acceleration_limit=10) 195 >>> assert ptp((1, 2, 3, 4, 5, 6), settings=ms) == PTP(target=Pose((1, 2, 3, 4, 5, 6)), settings=ms) 196 >>> assert ptp((1, 2, 3)) == ptp((1, 2, 3, 0, 0, 0)) 197 >>> assert ptp(Pose((1, 2, 3, 4, 5, 6)), settings=ms) == ptp((1, 2, 3, 4, 5, 6), settings=ms) 198 199 """ 200 if not isinstance(target, Pose): 201 t = (*target, 0.0, 0.0, 0.0) if len(target) == 3 else target 202 target = Pose(t) 203 204 return PTP(target=target, settings=settings, collision_scene=collision_scene)
Convenience function to create a point-to-point motion
Args: target: the target pose or vector. If the target is a vector, the orientation is set to (0, 0, 0). settings: the motion settings
Returns: the point-to-point motion
Examples:
>>> ms = MotionSettings(tcp_acceleration_limit=10)
>>> assert ptp((1, 2, 3, 4, 5, 6), settings=ms) == PTP(target=Pose((1, 2, 3, 4, 5, 6)), settings=ms)
>>> assert ptp((1, 2, 3)) == ptp((1, 2, 3, 0, 0, 0))
>>> assert ptp(Pose((1, 2, 3, 4, 5, 6)), settings=ms) == ptp((1, 2, 3, 4, 5, 6), settings=ms)
240def cir( 241 target: PoseOrVectorTuple, 242 intermediate: PoseOrVectorTuple, 243 settings: MotionSettings = MotionSettings(), 244 collision_scene: wb.models.CollisionScene | None = None, 245) -> Circular: 246 """Convenience function to create a circular motion 247 248 Args: 249 target: the target pose or vector. If the target is a vector, the orientation is set to (0, 0, 0). 250 intermediate: the intermediate pose or vector. If the intermediate is a vector, the orientation is set to 251 (0, 0, 0). 252 settings: the motion settings 253 254 Returns: the circular motion 255 256 Examples: 257 >>> ms = MotionSettings(tcp_acceleration_limit=10) 258 >>> assert cir((1, 2, 3, 4, 5, 6), (7, 8, 9, 10, 11, 12), settings=ms) == Circular(target=Pose((1, 2, 3, 4, 5, 6)), intermediate=Pose((7, 8, 9, 10, 11, 12)), settings=ms) 259 >>> assert cir((1, 2, 3), (4, 5, 6)) == cir((1, 2, 3, 0, 0, 0), (4, 5, 6, 0, 0, 0)) 260 261 """ 262 if not isinstance(target, Pose): 263 t = (*target, 0.0, 0.0, 0.0) if len(target) == 3 else target 264 target = Pose(t) 265 266 if not isinstance(intermediate, Pose): 267 i = (*intermediate, 0.0, 0.0, 0.0) if len(intermediate) == 3 else intermediate 268 intermediate = Pose(i) 269 270 return Circular( 271 target=target, intermediate=intermediate, settings=settings, collision_scene=collision_scene 272 )
Convenience function to create a circular motion
Args: target: the target pose or vector. If the target is a vector, the orientation is set to (0, 0, 0). intermediate: the intermediate pose or vector. If the intermediate is a vector, the orientation is set to (0, 0, 0). settings: the motion settings
Returns: the circular motion
Examples:
>>> ms = MotionSettings(tcp_acceleration_limit=10)
>>> assert cir((1, 2, 3, 4, 5, 6), (7, 8, 9, 10, 11, 12), settings=ms) == Circular(target=Pose((1, 2, 3, 4, 5, 6)), intermediate=Pose((7, 8, 9, 10, 11, 12)), settings=ms)
>>> assert cir((1, 2, 3), (4, 5, 6)) == cir((1, 2, 3, 0, 0, 0), (4, 5, 6, 0, 0, 0))
304def jnt( 305 target: tuple[float, ...], 306 settings: MotionSettings = MotionSettings(), 307 collision_scene: wb.models.CollisionScene | None = None, 308) -> JointPTP: 309 """Convenience function to create a joint PTP motion 310 311 Args: 312 target: the target joint configuration 313 settings: the motion settings 314 315 Returns: the joint PTP motion 316 317 Examples: 318 >>> ms = MotionSettings(tcp_acceleration_limit=10) 319 >>> assert jnt((1, 2, 3, 4, 5, 6), settings=ms) == JointPTP(target=(1, 2, 3, 4, 5, 6), settings=ms) 320 321 """ 322 return JointPTP(target=target, settings=settings, collision_scene=collision_scene)
Convenience function to create a joint PTP motion
Args: target: the target joint configuration settings: the motion settings
Returns: the joint PTP motion
Examples:
>>> ms = MotionSettings(tcp_acceleration_limit=10)
>>> assert jnt((1, 2, 3, 4, 5, 6), settings=ms) == JointPTP(target=(1, 2, 3, 4, 5, 6), settings=ms)
24def io_write(key: str, value: Any, device_id: str | None = None) -> WriteAction: 25 """Create a WriteAction 26 27 Args: 28 key: The key to write 29 value: The value to write 30 device_id: The device id 31 32 Returns: 33 The WriteAction 34 35 """ 36 return WriteAction(key=key, value=value, device_id=device_id)
Create a WriteAction
Args: key: The key to write value: The value to write device_id: The device id
Returns: The WriteAction
25class CombinedActions(pydantic.BaseModel): 26 """A trajectory of motions and actions""" 27 28 # See: https://docs.pydantic.dev/latest/concepts/serialization/#serialize_as_any-runtime-setting 29 items: tuple[ 30 Annotated[ 31 pydantic.SerializeAsAny[ActionContainerItem], pydantic.Field(discriminator="type") 32 ], 33 ..., 34 ] = () 35 36 def __len__(self): 37 return len(self.items) 38 39 def __getitem__(self, item): 40 return self.items[item] 41 42 def __setattr__(self, key, value): 43 if key == "items": 44 raise TypeError("Cannot set items directly") 45 super().__setattr__(key, value) 46 47 def __iter__(self): 48 return iter(self.items) 49 50 def append(self, item: ActionContainerItem): 51 super().__setattr__("items", self.items + (item,)) 52 53 def _generate_trajectory( 54 self, 55 ) -> tuple[list[Motion | CollisionFreeMotion], list[ActionLocation]]: 56 """Generate two lists: one of Motion objects and another of ActionContainer objects, 57 where each ActionContainer wraps a non-Motion action with its path parameter. 58 59 The path parameter is the index of the last Motion object in the list of Motion objects. 60 S - M - M - A - A - M - M - A - M - M 61 0 - 1 - 2 - 3 - 3 - 3 - 4 - 5 - 5 - 6 62 63 Returns: 64 tuple: A tuple containing: 65 - list of Motion objects from self.items. 66 - list of ActionContainer objects with indexed path parameters. 67 """ 68 motions = [] 69 actions = [] 70 last_motion_index = 0 71 72 for item in self.items: 73 if isinstance(item, Motion) or isinstance(item, CollisionFreeMotion): 74 motions.append(item) 75 last_motion_index += 1 # Increment the motion index for each new Motion 76 else: 77 # Assign the current value of last_motion_index as path_parameter for actions 78 actions.append(ActionLocation(path_parameter=last_motion_index, action=item)) 79 80 return motions, actions 81 82 @property 83 def motions(self) -> list[Motion | CollisionFreeMotion]: 84 motions, _ = self._generate_trajectory() 85 return motions 86 87 @property 88 def actions(self) -> list[ActionLocation]: 89 _, actions = self._generate_trajectory() 90 return actions 91 92 @property 93 def start(self) -> ActionContainerItem | None: 94 return self.motions[0] if self.motions else None 95 96 @property 97 def end(self) -> ActionContainerItem | None: 98 return self.motions[-1] if self.motions else None 99 100 def poses(self) -> list[Pose]: 101 """Returns the positions of all motions. If a motion is not a cartesian motion, the position is ignored 102 103 Returns: the positions 104 105 """ 106 motions, _ = self._generate_trajectory() 107 return [ 108 Pose(position=motion.target.position, orientation=motion.target.orientation) 109 for motion in motions 110 if isinstance(motion.target, Pose) 111 ] 112 113 def positions(self): 114 """Returns the positions of all motions. If a motion is not a cartesian motion, the position is ignored 115 116 Returns: the positions 117 118 """ 119 return [pose.position for pose in self.poses()] 120 121 def orientations(self): 122 """Returns the orientations of all motions. If a motion is not a cartesian motion, the orientation is ignored 123 124 Returns: the orientations 125 126 """ 127 return [pose.orientation for pose in self.poses()] 128 129 def __add__(self, other: CombinedActions) -> CombinedActions: 130 return CombinedActions(items=self.items + other.items) 131 132 def to_motion_command(self) -> list[api.models.MotionCommand]: 133 motion_commands = [] 134 for motion in self.motions: 135 path = api.models.MotionCommandPath.from_dict(motion.model_dump()) 136 blending = ( 137 motion.settings.as_blending_setting() 138 if motion.settings.has_blending_settings() 139 else None 140 ) 141 limits_override = ( 142 motion.settings.as_limits_settings() 143 if motion.settings.has_limits_override() 144 else None 145 ) 146 motion_commands.append( 147 api.models.MotionCommand( 148 path=path, blending=blending, limits_override=limits_override 149 ) 150 ) 151 return motion_commands 152 153 def to_set_io(self) -> list[api.models.SetIO]: 154 return [ 155 api.models.SetIO( 156 io=api.models.IOValue(**action.action.model_dump(exclude_unset=True)), 157 location=action.path_parameter, 158 ) 159 for action in self.actions 160 ]
A trajectory of motions and actions
100 def poses(self) -> list[Pose]: 101 """Returns the positions of all motions. If a motion is not a cartesian motion, the position is ignored 102 103 Returns: the positions 104 105 """ 106 motions, _ = self._generate_trajectory() 107 return [ 108 Pose(position=motion.target.position, orientation=motion.target.orientation) 109 for motion in motions 110 if isinstance(motion.target, Pose) 111 ]
Returns the positions of all motions. If a motion is not a cartesian motion, the position is ignored
Returns: the positions
113 def positions(self): 114 """Returns the positions of all motions. If a motion is not a cartesian motion, the position is ignored 115 116 Returns: the positions 117 118 """ 119 return [pose.position for pose in self.poses()]
Returns the positions of all motions. If a motion is not a cartesian motion, the position is ignored
Returns: the positions
121 def orientations(self): 122 """Returns the orientations of all motions. If a motion is not a cartesian motion, the orientation is ignored 123 124 Returns: the orientations 125 126 """ 127 return [pose.orientation for pose in self.poses()]
Returns the orientations of all motions. If a motion is not a cartesian motion, the orientation is ignored
Returns: the orientations
132 def to_motion_command(self) -> list[api.models.MotionCommand]: 133 motion_commands = [] 134 for motion in self.motions: 135 path = api.models.MotionCommandPath.from_dict(motion.model_dump()) 136 blending = ( 137 motion.settings.as_blending_setting() 138 if motion.settings.has_blending_settings() 139 else None 140 ) 141 limits_override = ( 142 motion.settings.as_limits_settings() 143 if motion.settings.has_limits_override() 144 else None 145 ) 146 motion_commands.append( 147 api.models.MotionCommand( 148 path=path, blending=blending, limits_override=limits_override 149 ) 150 ) 151 return motion_commands
164class MovementControllerContext(pydantic.BaseModel): 165 combined_actions: CombinedActions 166 motion_id: str
Usage docs: https://docs.pydantic.dev/2.10/concepts/models/
A base class for creating Pydantic models.
Attributes:
__class_vars__: The names of the class variables defined on the model.
__private_attributes__: Metadata about the private attributes of the model.
__signature__: The synthesized __init__
[Signature
][inspect.Signature] of the model.
__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
__args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.
__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.
__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.