I am working on a new version of the “dynamics workbench”, applying several changes I have had in mind for a while now. In fact, I consider the currently available code only a proof of concept. A successful proof of concept, but only a proof of concept anyway. It is time to move to something more refined, and the first thing to do, in my opinion, is to use FreeCAD local coordinate systems as nodes and joints for MBDyn. The purpose of this post is to share what I have done so far, and hopefully hear your opinions.
There are two main flaws in the current version of the current workbench: 1) When I programed it I assumed all the nodes are orthonormal to the global reference frame. 2) The code is unnecessarily long because it does not make much use of the tools FreeCAD has to handle vectors and matrices.
I would like to mention the approach I am taking to solve these flaws:
The main thing is, as mentioned, to use FreeCAD LCSs as dynamic nodes and joints. Let me talk about nodes firs. In MBDyn, to define a dynamic node, one has to provide at least four parameters that define the initial conditions of the node: initial position, initial orientation, initial velocity and initial angular velocity. Of these, initial velocity and initial angular velocity present no complications, because these two are defined by the user. But, in order to integrate FreeCAD and MBDyn, the initial position and the initial orientation must be connected to the CAD geometry, because the user will want to place and orient the nodes at points and orientation of interest of the CAD assembly or mechanism to be simulated. This is one of the reasons to use LCSs as nodes. There are already several mechanisms to position and orient LCSs, by providing references to components (faces, edges) of the CAD geometry. This is my class for a structural dynamic node:
Code: Select all
import FreeCAD
class Structuraldynamicnode:
def __init__(self, obj, baseBody):
obj.addProperty("App::PropertyDistance","absolute_position_X","Initial conditions: absolute position",'The X component of the absolute position of the node. To access this variable type "Px_" followed by the number of node, for instance "Px_1".',1)
obj.addProperty("App::PropertyDistance","absolute_position_Y","Initial conditions: absolute position",'The Y component of the absolute position of the node. To access this variable type "Px_" followed by the number of node, for instance "Py_1".',1)
obj.addProperty("App::PropertyDistance","absolute_position_Z","Initial conditions: absolute position",'The Z component of the absolute position of the node. To access this variable type "Px_" followed by the number of node, for instance "Pz_1".',1)
obj.addProperty("App::PropertyString","absolute_orientation_matrix","Initial conditions: absolute orientation","Orientation of the node in the absolute frame. The absolute orientation matrix is automatically computed from the global placement.",1)
obj.addProperty("App::PropertySpeed","absolute_velocity_X","Initial conditions: absolute velocity",'To access this variable type "Vx_" followed by the number of node, for instance "Vx_1".')
obj.addProperty("App::PropertySpeed","absolute_velocity_Y","Initial conditions: absolute velocity",'To access this variable type "Vy_" followed by the number of node, for instance "Vy_1".')
obj.addProperty("App::PropertySpeed","absolute_velocity_Z","Initial conditions: absolute velocity",'To access this variable type "Vz_" followed by the number of node, for instance "Vz_1".')
obj.addProperty("App::PropertyQuantity","absolute_angular_velocity_X","Initial conditions: absolute angular velocity",'To access this variable type "Wx_" followed by the number of node, for instance "Wx_1".')
obj.absolute_angular_velocity_X = FreeCAD.Units.Unit('deg/s')
obj.addProperty("App::PropertyQuantity","absolute_angular_velocity_Y","Initial conditions: absolute angular velocity",'To access this variable type "Wy_" followed by the number of node, for instance "Wy_1".')
obj.absolute_angular_velocity_Y = FreeCAD.Units.Unit('deg/s')
obj.addProperty("App::PropertyQuantity","absolute_angular_velocity_Z","Initial conditions: absolute angular velocity",'To access this variable type "Wz_" followed by the number of node, for instance "Wz_1".')
obj.absolute_angular_velocity_Z = FreeCAD.Units.Unit('deg/s')
def execute(self, fp):
return
def onDocumentRestored(self, fp):
fp.absolute_angular_velocity_X = FreeCAD.Units.Unit('deg/s')
fp.absolute_angular_velocity_Y = FreeCAD.Units.Unit('deg/s')
fp.absolute_angular_velocity_Z = FreeCAD.Units.Unit('deg/s')
Here, absolute_velocity_X, Y and Z; and absolute_angular_velocity_X, Y, and Z; are initialized with a value of 0.0, and the user simply types other value if needed. The absolute_position_X, Y and Z; and the absolute_orientation_matrix must be obtained from the placement of the LCS that represents the node, but before, I create the node as follows:
Code: Select all
node = FreeCAD.ActiveDocument.addObject("PartDesign::CoordinateSystem", "label")
Structuraldynamicnode (node, baseBody)
Where label is simply a unique label automatically generated to identify the node, for example “structural_1”, “structural_2”, “structural_3”, etc. baseBody is the rigid body to which the node is connected. I am quite happy with how rigid bodies work on the current version of the workbench, so not planning to do many changes here.
And here comes the first problem. The “onDocumentRestored” method is never executed when FreeCAD is started, and I would appreciate some help on why.
After the node has been created, it is basically a local coordinate system with some added properties, and whose placement is:
Code: Select all
Placement [Pos=(0,00), Yaw-Pitch-Roll=(0,0,0)]
The user now positions the node using the tools FreeCAD already has to position a LCS, or some other positioning mechanisms I have added; and the initial absolute position and absolute orientation matrix are obtained, from the LCS placement, as follows:
Code: Select all
absolute_position_X = node.placement.Matrix.A14
absolute_position_Y = node.placement.Matrix.A24
absolute_position_Z = node.placement.Matrix.A34
absolute_orientation_matrix = "matr, " + str(node.placement.Matrix.A11) + ", " + str(node.placement.Matrix.A12) + ", " + str(node.placement.Matrix.A13) + ", " + str(node.placement.Matrix.A21) + ", " + str(node.placement.Matrix.A22) + ", " + str(node.placement.Matrix.A23) + ", " + str(node.placement.Matrix.A31) + ", " + str(node.placement.Matrix.A32) + ", " + str(node.placement.Matrix.A33)
This is so the position and orientation match the format needed by MBDyn:
Code: Select all
<additional_args> ::= , { static | dynamic | modal } ,
[ position , ] (Vec3) <absolute_position> ,
[ orientation , ] (OrientationMatrix) <absolute_orientation_matrix> ,
[ velocity , ] (Vec3) <absolute_velocity> ,
[ angular velocity , ] (Vec3) <absolute_angular_velocity>
Where Vec3 is simply a vector, exactly the same as in FreeCAD, and orientation is the general-case orientation matrix, as explained in:
https://www.sky-engin.jp/en/MBDynTutori ... hap13.html
Except for the problem that the onDocumentRestored method is not executed when FreeCAD is loaded, the above approach to model a node using a LCS seems to work just fine, but anyway I would love to hear what people think, and any ideas on how to improve this part of the project will be much appreciated.