Source code for skydy.configuration.Configuration
#!/usr/bin/python3
import numpy as np
import sympy as sym
from .BaseSymbols import CoordinateSymbols
NUM_COORDS = 6
[docs]class Configuration(CoordinateSymbols):
[docs] def __init__(self, name):
"""A body's configuration is nothing other than a description of its pose
(where it is, and how it is oriented).
As such, it is decribed by a vector of positions, and a matrix of rotations.
All bodies can have up to 6 DOFs, i.e., directions in which it can move.
By applying constraints, a body can have as little as zero DOFs.
A Configuration inherits from CoordinateSymbols, as it is solely related to a
body's name, and the 6 CoordinateSymbols that describe it.
Args:
name (int or str): the name for the symbols. This will form the superscript, i.e., the body the symbols refer to.
Returns:
None
Example:
Configuration for body named "1".
>>> from skydy.configuration import Configuration
>>> body_name = "1"
>>> body_config = Configuration(body_name)
>>> # See the symbolic position and rotation of the body
>>> print(body_config.pos_body)
>>> print(body_config.rot_body)
"""
super().__init__(name)
# Get the coordinates
q = self.positions()
# Define the rotation matrices for each axis
Rx = sym.rot_axis1(q[3]).T
Ry = sym.rot_axis2(q[4]).T
Rz = sym.rot_axis3(q[5]).T
# Define the free, or unconstrained, configuration
self.__pos_free = sym.Matrix(q[:3])
self.__rot_free = sym.simplify(Rz @ Ry @ Rx)
self.pos_body = None
self.rot_body = None
self.free_idx = None
self.reset_constraints()
[docs] def accelerations(self):
"""Returns the acceleration of the coordinates of the body."""
return sym.Matrix([sym.diff(var, sym.Symbol("t")) for var in self.velocities()])
[docs] def apply_constraint(self, idx, const_value=0):
"""Apply a coordinate constraint to the free configuration of the body.
A constraint is nothing but a coordinate having constant value.
This indices for each coordinate are:
0 -> x
1 -> y
2 -> z
3 -> theta_x
4 -> theta_y
5 -> theta_z
Args:
idx (int): the index to apply the constriaint to.
const_value (int or float): the constant value to substitute in for the coordinate at the index. Default value is zero.
Returns:
None
Example:
Constrain some coordinate for a body named "1".
>>> from skydy.configuration import Configuration
>>> body_name = "1"
>>> body_config = Configuration(body_name)
>>> # Apply a translational constraint to the z-axis, at a height of 5 m.
>>> body_config.apply_constraint(2, 5)
>>> # Apply a rotational constraint about the y-axis
>>> body_config.apply_constraint(4, 0)
"""
q = self.positions()
self.assign_values(const_value, idx)
self.pos_body = self.pos_body.subs(q[idx], const_value)
self.rot_body = self.rot_body.subs(q[idx], const_value)
self.free_idx -= set([idx])
[docs] def reset_constraints(self):
"""Reset the constraints, i.e., remove any restrictions on movement.
In short, the position and rotation are the free configuration matrices
determined on object instantiation.
"""
self.assign_values(np.ones(self.values().shape))
self.pos_body = self.__pos_free.copy()
self.rot_body = self.__rot_free.copy()
self.free_idx = set([idx for idx in range(NUM_COORDS)])
def __free_symbols(self, symbols):
"""Helper method to return the free symbols for a sympy.matrix object.
Args:
symbols (sympy.matrices.dense.MutableDenseMatrix or list): a list of symbols.
Returns:
free_symbols (list): a list of free symbols from the input list, based on the free configuration indices.
Example:
See self.free_coordinates(), self.free_velocities(), self.free_accelerations() below.
"""
return [symbols[idx] for idx in self.free_idx]
[docs] def free_coordinates(self):
"""Return a list of free coordinates for the body."""
return self.__free_symbols(self.positions())
[docs] def free_velocities(self):
"""Return a list of free velocities for the body."""
return self.__free_symbols(self.velocities())
[docs] def free_accelerations(self):
"""Return a list of free accelerations for the body."""
return self.__free_symbols(self.accelerations())
@property
def pos_body(self):
return self._pos_body
@pos_body.setter
def pos_body(self, val):
if val is None:
self._pos_body = val
elif (
isinstance(val, sym.matrices.immutable.ImmutableDenseMatrix)
or isinstance(val, sym.Matrix)
) and val.shape == (3, 1):
self._pos_body = val
else:
raise TypeError("Body Position must be a 3 x 1 sym.Matrix.")
@property
def rot_body(self):
return self._rot_body
@rot_body.setter
def rot_body(self, val):
if val is None:
self._rot_body = val
elif (
isinstance(val, sym.matrices.immutable.ImmutableDenseMatrix)
or isinstance(val, sym.Matrix)
and val.shape == (3, 3)
):
self._rot_body = val
else:
raise TypeError("Body Rotation must be a 3 x 3 sym.Matrix.")