Skip to content

Commit 984d102

Browse files
authored
Merge pull request #1175 from AVSLab/feature/translating-bodies-branching
Add effector branching to linear translation 1DOF state effector
2 parents df1cbd2 + 3f02548 commit 984d102

File tree

7 files changed

+197
-55
lines changed

7 files changed

+197
-55
lines changed

docs/source/Learn/bskPrinciples/bskPrinciples-11.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ special case of prescribed effector branching explained in :ref:`prescribedMotio
227227
</tr>
228228
<tr>
229229
<td class="label">Linear Translating Bodies 1DOF</td>
230-
<td class="yellow"></td><td class="yellow"></td><td class="yellow"></td><td class="yellow"></td>
231-
<td class="yellow"></td><td class="yellow"></td><td class="yellow"></td><td class="yellow"></td>
230+
<td class="green"></td><td class="yellow"></td><td class="green"></td><td class="yellow"></td>
231+
<td class="green"></td><td class="yellow"></td><td class="yellow"></td><td class="yellow"></td>
232232
<td class="red"></td><td class="red"></td><td class="red"></td><td class="red"></td>
233233
<td class="red"></td><td class="red"></td><td class="red"></td><td class="red"></td>
234234
</tr>

docs/source/Support/bskReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Version |release|
3333
- Added :ref:`bskPrinciples-11` capability. Now enabled for :ref:`extForceTorque`, :ref:`constraintDynamicEffector`,
3434
and :ref:`thrusterDynamicEffector` attachable to :ref:`spinningBodyOneDOFStateEffector`,
3535
:ref:`spinningBodyTwoDOFStateEffector`, and :ref:`spinningBodyNDOFStateEffector`.
36+
- Added support for :ref:`linearTranslationOneDOFStateEffector` to attach dependent effectors.
3637
- Added custom reaction wheel: "NanoAvionics RW0" to ``src/utilities/simIncludeRW.py``
3738
- Added TLE handling utilities in :ref:`tleHandling` to parse TLE files and convert to orbital elements
3839
- Removed deprecated use of astro constants from ``src/utilities/astroFunction.py``.

src/simulation/dynamics/_GeneralModuleFiles/_UnitTestDynamics/test_effectorBranching_integrated.py

Lines changed: 97 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
("spinningBodiesOneDOF", True),
8181
("spinningBodiesTwoDOF", True),
8282
("spinningBodiesNDOF", True),
83-
# ("linearTranslationBodiesOneDOF", True),
83+
("linearTranslationBodiesOneDOF", True),
8484
# ("linearTranslationBodiesNDOF", True),
8585
("linearSpringMassDamper", False),
8686
# ("sphericalPendulum", False),
@@ -117,17 +117,8 @@ def test_effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dy
117117
addDynamicEffector() method is called. The dynamic effector compatibility is checked at
118118
simulation initialization when linkInProperties() is called.
119119
120-
State effectors that are expected to be able to host dynamic effectors (isParent) include:
121-
122-
- :ref:`spinningBodyOneDOFStateEffector`
123-
- :ref:`spinningBodyTwoDOFStateEffector`
124-
- :ref:`spinningBodyNDOFStateEffector`
125-
126-
Dynamic effectors that are expected to be able to attach to state effectors (isChild) include:
127-
128-
- :ref:`extForceTorque`
129-
- :ref:`thrusterDynamicEffector`
130-
- :ref:`constraintDynamicEffector`
120+
A summary of which state effectors and dynamic effectors are expected to be able to act as a
121+
parent or child effector to another is summarized in :ref:`bskPrinciples-11`.
131122
132123
Note that the constraint effector is tested in two configurations: once where one vehicle's
133124
state effector is attached to the hub on another vehicle (``constraintEffectorOneHub``), and
@@ -259,6 +250,9 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
259250
elif stateEffector == "spinningBodiesNDOF":
260251
stateEff, stateEffProps = setup_spinningBodiesNDOF()
261252
segment = 4
253+
elif stateEffector == "linearTranslationBodiesOneDOF":
254+
stateEff, stateEffProps = setup_translatingBodiesOneDOF()
255+
segment = 1
262256
elif stateEffector == "linearSpringMassDamper":
263257
stateEff, stateEffProps = setup_linearSpringMassDamper()
264258
segment = 1
@@ -340,13 +334,27 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
340334
assert isChild, "FAILED: attached an incompatible dynamic effector without erroring"
341335

342336
# Check that properties are being handed correctly from state effector to dynamic effector
343-
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "Position") == getStateEffInertialPropName(scObject, segment, stateEff, "Position"), (
337+
if (stateEffector == "spinningBodiesNDOF" or stateEffector == "linearTranslationBodiesOneDOF"
338+
or stateEffector == "linearTranslationBodiesNDOF"):
339+
# Newer effector classes keep all variables private and so we check with the dynParamManager
340+
positionName = getModernStateEffInertialPropName(scObject, segment, stateEff, "Position")
341+
velocityName = getModernStateEffInertialPropName(scObject, segment, stateEff, "Velocity")
342+
attitudeName = getModernStateEffInertialPropName(scObject, segment, stateEff, "Attitude")
343+
angvelocityName = getModernStateEffInertialPropName(scObject, segment, stateEff, "AngVelocity")
344+
else:
345+
# older effector classes have public variable names that are simply checked directly
346+
positionName = getStateEffInertialPropName(segment, stateEff, "Position")
347+
velocityName = getStateEffInertialPropName(segment, stateEff, "Velocity")
348+
attitudeName = getStateEffInertialPropName(segment, stateEff, "Attitude")
349+
angvelocityName = getStateEffInertialPropName(segment, stateEff, "AngVelocity")
350+
351+
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "Position") == positionName, (
344352
"FAILED: inertialPositionProperty not handed correctly between state and dynamic effectors")
345-
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "Velocity") == getStateEffInertialPropName(scObject, segment, stateEff, "Velocity"), (
353+
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "Velocity") == velocityName, (
346354
"FAILED: inertialVelocityProperty not handed correctly between state and dynamic effectors")
347-
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "Attitude") == getStateEffInertialPropName(scObject, segment, stateEff, "Attitude"), (
355+
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "Attitude") == attitudeName, (
348356
"FAILED: inertialAttitudeProperty not handed correctly between state and dynamic effectors")
349-
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "AngVelocity") == getStateEffInertialPropName(scObject, segment, stateEff, "AngVelocity"), (
357+
assert getDynEffInertialPropName(dynamicEffector, dynamicEff, "AngVelocity") == angvelocityName, (
350358
"FAILED: inertialAngVelocityProperty not handed correctly between state and dynamic effectors")
351359

352360
# Run the sim for a few timesteps to confirm execution without error
@@ -379,11 +387,18 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
379387
dV[idx,:] = (dV[idx-1,:] + (dcm_NS @ np.array(dynamicEff.extForce_B).flatten())
380388
/ (scObject.hub.mHub + stateEffProps.totalMass) * timestep)
381389
# Compute the total external torque on the vehicle
382-
extTorque[idx,:] = (dcm_NS @ np.array(dynamicEff.extTorquePntB_B).flatten()
383-
+ np.cross(r_ScN_N_log[idx+1,:] - dcm_NS
384-
@ np.array(stateEffProps.r_PcP_P).flatten()
385-
- datLog.r_CN_N[idx,:], dcm_NS
386-
@ np.array(dynamicEff.extForce_B).flatten()))
390+
if stateEffector == "linearTranslationBodiesOneDOF" or stateEffector == "linearTranslationBodiesNDOF":
391+
extTorque[idx,:] = (dcm_NS @ np.array(dynamicEff.extTorquePntB_B).flatten()
392+
+ np.cross(r_ScN_N_log[idx+1,:] - dcm_NS
393+
@ np.array(stateEffProps.r_PcP_P).flatten()
394+
- datLog.r_CN_N[idx,:], dcm_NS
395+
@ np.array(dynamicEff.extForce_B).flatten()))
396+
else:
397+
extTorque[idx,:] = (dcm_NS @ np.array(dynamicEff.extTorquePntB_B).flatten()
398+
+ np.cross(r_ScN_N_log[idx+1,:] - dcm_NS
399+
@ np.array(stateEffProps.r_PcP_P).flatten()
400+
- datLog.r_CN_N[idx,:], dcm_NS
401+
@ np.array(dynamicEff.extForce_B).flatten()))
387402

388403
# Integrate the torque to find accumulated change in angular momentum
389404
dx = np.ones(n-1)*timestep
@@ -437,12 +452,16 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
437452

438453
# Check angular momentum difference against sim truth
439454
angMom_accuracy = 1e-3
440-
np.testing.assert_allclose(rotAngMom_N[:-1,:]-rotAngMom_N[0,:] - dH, 0, atol=angMom_accuracy,
455+
# np.testing.assert_allclose(np.linalg.norm(rotAngMom_N[:-1,:]-rotAngMom_N[0,:] - dH, axis=1), 0, atol=angMom_accuracy,
456+
# err_msg="angular momentum difference beyond accuracy limits")
457+
np.testing.assert_allclose(rotAngMom_N[:-1,:]-rotAngMom_N[0,:], dH, atol=angMom_accuracy,
441458
err_msg="angular momentum difference beyond accuracy limits")
442459

443460
# Check deltaV difference against sim truth
444461
deltaV_accuracy = 1e-6
445-
np.testing.assert_allclose(totAccumDV_N[:-1,:] - dV, 0, atol=deltaV_accuracy,
462+
# np.testing.assert_allclose(np.linalg.norm(totAccumDV_N[:-1,:] - dV, axis=1), 0, atol=deltaV_accuracy,
463+
# err_msg="deltaV difference beyond accuracy limits")
464+
np.testing.assert_allclose(totAccumDV_N[:-1,:], dV, 0, atol=deltaV_accuracy,
446465
err_msg="deltaV difference beyond accuracy limits")
447466

448467
return
@@ -456,7 +475,7 @@ def getDynEffInertialPropName(dynamicEffector, dynamicEff, propType):
456475
else:
457476
return getattr(dynamicEff, f"getPropName_inertial{propType}")()
458477

459-
def getStateEffInertialPropName(scObject, segment, stateEff, propType):
478+
def getStateEffInertialPropName(segment, stateEff, propType):
460479
if segment == 1:
461480
return getattr(stateEff, f"nameOfInertial{propType}Property")
462481
elif segment == 2:
@@ -469,6 +488,18 @@ def getStateEffInertialPropName(scObject, segment, stateEff, propType):
469488
return "notHandedCorrectly"
470489
return propName
471490

491+
def getModernStateEffInertialPropName(scObject, segment, stateEff, propType):
492+
try:
493+
if segment == 1:
494+
propName = stateEff.ModelTag + "Inertial" + propType + "1"
495+
scObject.dynManager.getPropertyReference(propName)
496+
elif segment == 4:
497+
propName = stateEff.ModelTag + "Inertial" + propType + "1_4"
498+
scObject.dynManager.getPropertyReference(propName)
499+
except BasiliskError:
500+
return "notHandedCorrectly"
501+
return propName
502+
472503
def setup_extForceTorque():
473504
extFT = extForceTorque.ExtForceTorque()
474505
extFT.extForce_B = [[1.0], [1.0], [1.0]]
@@ -621,7 +652,7 @@ def setup_spinningBodiesOneDOF():
621652
# Compute COM offset contribution, to be divided by the hub mass
622653
mr_ScB_B = -(spinningBody.r_SB_B + np.transpose(spinningBody.dcm_S0B) @ spinningBody.r_ScS_S) * spinningBody.mass
623654

624-
stateEffProps = stateEfectorProperties()
655+
stateEffProps = stateEffectorProperties()
625656
stateEffProps.totalMass = spinningBody.mass
626657
stateEffProps.mr_PcB_B = mr_ScB_B
627658
stateEffProps.r_PB_B = spinningBody.r_SB_B
@@ -640,10 +671,10 @@ def setup_spinningBodiesTwoDOF():
640671
spinningBody.IS2PntSc2_S2 = [[50.0, 0.0, 0.0], [0.0, 30.0, 0.0], [0.0, 0.0, 40.0]]
641672
spinningBody.dcm_S10B = [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]
642673
spinningBody.dcm_S20S1 = [[0.0, -1.0, 0.0], [0.0, .0, -1.0], [1.0, 0.0, 0.0]]
643-
spinningBody.r_Sc1S1_S1 = [[2.0], [-0.5], [0.0]]
674+
spinningBody.r_Sc1S1_S1 = [[1.0], [-0.5], [0.0]]
644675
spinningBody.r_Sc2S2_S2 = [[1.0], [0.0], [-1.0]]
645-
spinningBody.r_S1B_B = [[-2.0], [0.5], [-1.0]]
646-
spinningBody.r_S2S1_S1 = [[0.5], [-1.5], [-0.5]]
676+
spinningBody.r_S1B_B = [[-1.0], [0.5], [-1.0]]
677+
spinningBody.r_S2S1_S1 = [[0.5], [-0.5], [-0.5]]
647678
spinningBody.s1Hat_S1 = [[0], [0], [1]]
648679
spinningBody.s2Hat_S2 = [[0], [-1], [0]]
649680
spinningBody.theta1DotInit = 1.0 * macros.D2R
@@ -661,7 +692,7 @@ def setup_spinningBodiesTwoDOF():
661692
np.transpose(spinningBody.dcm_S20S1) @ spinningBody.r_Sc2S2_S2) )
662693
* spinningBody.mass2)
663694

664-
stateEffProps = stateEfectorProperties()
695+
stateEffProps = stateEffectorProperties()
665696
stateEffProps.totalMass = spinningBody.mass1 + spinningBody.mass2
666697
stateEffProps.mr_PcB_B = mr_ScB_B
667698
stateEffProps.r_PB_B = spinningBody.r_S1B_B + np.transpose(spinningBody.dcm_S10B) @ spinningBody.r_S2S1_S1
@@ -729,7 +760,7 @@ def setup_spinningBodiesNDOF():
729760

730761
spinningBodyEffector.ModelTag = "spinningBody"
731762

732-
stateEffProps = stateEfectorProperties()
763+
stateEffProps = stateEffectorProperties()
733764
stateEffProps.totalMass = massSubPanel * numberOfSegments
734765
stateEffProps.mr_PcB_B = mr_ScB_B
735766
stateEffProps.r_PB_B = r_ScB_B - dcm_SB.transpose() @ spinningBody.getR_ScS_S()
@@ -738,6 +769,39 @@ def setup_spinningBodiesNDOF():
738769

739770
return(spinningBodyEffector, stateEffProps)
740771

772+
def setup_translatingBodiesOneDOF():
773+
translatingBody = linearTranslationOneDOFStateEffector.LinearTranslationOneDOFStateEffector()
774+
775+
# Define properties of translating body
776+
translatingBody.setMass(20.0)
777+
translatingBody.setK(100.0)
778+
translatingBody.setC(50.0)
779+
translatingBody.setRhoInit(1.0)
780+
translatingBody.setRhoDotInit(0.05)
781+
translatingBody.setFHat_B([[3.0 / 5.0], [4.0 / 5.0], [0.0]])
782+
translatingBody.setR_FcF_F([[-1.0], [1.0], [0.0]])
783+
translatingBody.setR_F0B_B([[-1.0], [1.0], [0.0]])
784+
translatingBody.setIPntFc_F([[50.0, 0.0, 0.0],
785+
[0.0, 80.0, 0.0],
786+
[0.0, 0.0, 60.0]])
787+
translatingBody.setDCM_FB([[0.0, -1.0, 0.0],
788+
[0.0, 0.0, -1.0],
789+
[1.0, 0.0, 0.0]])
790+
translatingBody.ModelTag = "linearTranslation"
791+
792+
mr_ScB_B = -(translatingBody.getR_F0B_B() + np.transpose(translatingBody.getDCM_FB()) @
793+
(translatingBody.getR_FcF_F() + translatingBody.getRhoInit() *
794+
np.array(translatingBody.getFHat_B()))) * translatingBody.getMass()
795+
796+
stateEffProps = stateEffectorProperties()
797+
stateEffProps.totalMass = translatingBody.getMass()
798+
stateEffProps.mr_PcB_B = mr_ScB_B
799+
stateEffProps.r_PB_B = translatingBody.getR_F0B_B()
800+
stateEffProps.r_PcP_P = translatingBody.getR_FcF_F()
801+
stateEffProps.inertialPropLogName = "translatingBodyConfigLogOutMsg"
802+
803+
return(translatingBody, stateEffProps)
804+
741805
def setup_linearSpringMassDamper():
742806
linearSpring = linearSpringMassDamper.LinearSpringMassDamper()
743807
linearSpring.massInit = 50.0
@@ -752,14 +816,14 @@ def setup_linearSpringMassDamper():
752816
# Compute COM offset contribution, to be divided by the hub mass
753817
mr_ScB_B = -(linearSpring.r_PB_B + linearSpring.rhoInit * np.array(linearSpring.pHat_B)) * linearSpring.massInit
754818

755-
stateEffProps = stateEfectorProperties()
819+
stateEffProps = stateEffectorProperties()
756820
stateEffProps.totalMass = linearSpring.massInit
757821
stateEffProps.mr_PcB_B = mr_ScB_B
758822
stateEffProps.r_PB_B = linearSpring.r_PB_B
759823

760824
return(linearSpring, stateEffProps)
761825

762-
class stateEfectorProperties:
826+
class stateEffectorProperties:
763827
# to be used in joint COM calculation
764828
totalMass = 0.0 # total mass of the effector (sum of all linkages)
765829
mr_PcB_B = [[0.0], [0.0], [0.0]] # sum(m_i * r_SiB_B) for i linkages, see rst documentation
@@ -769,4 +833,4 @@ class stateEfectorProperties:
769833
r_PcP_P = [[0.0], [0.0], [0.0]] # individual COM for linkage that dynEff will be attached to
770834

771835
if __name__ == "__main__":
772-
effectorBranchingIntegratedTest(True, "spinningBodiesOneDOF", True, "extForceTorque", True)
836+
effectorBranchingIntegratedTest(True, "linearTranslationBodiesOneDOF", True, "extForceTorque", True)

src/simulation/dynamics/constraintEffector/constraintDynamicEffector.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ void ConstraintDynamicEffector::computeForceTorque(double integTime, double time
317317
rDot_B1N_N = this->hubVelocity[parent1.idx]->getState();
318318
omega_B1N_B1 = this->hubOmega[parent1.idx]->getState();
319319
sigma_B1N = (Eigen::Vector3d)this->hubSigma[parent1.idx]->getState();
320-
} else if (this->parent2.parentType == "effector") {
320+
} else if (this->parent1.parentType == "effector") {
321321
// - Collect properties from parent effector
322322
r_B1N_N = *this->inertialPositionProperty[parent1.idx];
323323
rDot_B1N_N = *this->inertialVelocityProperty[parent1.idx];

0 commit comments

Comments
 (0)