## Another approach to assembly solver (A2plus)

Discussion about the development of the Assembly workbench.
Mark Szlazak
Posts: 426
Joined: Tue Apr 04, 2017 6:06 pm
Location: SF Bay Area, California

### Re: Another approach to assembly solver (A2plus)

project4 wrote:
Wed Jul 25, 2018 2:59 pm
Mark Szlazak wrote:
Wed Jul 25, 2018 2:37 pm
project4 wrote:
Wed Jul 25, 2018 10:02 am
kbwbe wrote:
Wed Jul 25, 2018 9:50 am
project4 wrote:
Wed Jul 25, 2018 9:38 am

"draw a line connecting the centers of one hole, a second line connecting the centers of the second hole"
You mean one line between the 2 centers?

I lost you on the "perpendicular bisectors"
A perpendicular line in the middle of the line drawn before -> "perpendicular bisector"(Edit) (right angled line to line drawn before)
Ohhh... You mean like in the image below, points B and C are the rotation refPoints and point A is the intersection of the angles from those refPoint.
So the point D of perpendicular D-E will show the location of the rotation point, right?
In that case we'll need to calculate a lot of intersection points... My way is probably easier...
imgC.gif
Here is a video showing how to do it. The only exception is when the perpendiculars are parallel in which case you just rectilinearly translate the rigid body to it’s new location. All displacements can be done this way without exception.

phpBB [video]
Ohhh... I don't know how to transform that to math in the code...
Use 3d-vectors so you can do the 2d planar displacements on any orientation of reference plane in 3d-space. If you want something even more general like rigid-body displacement in 3-d space then one approach is screw displacements. Look up Chasles or Mozzi-Chasles theorem. A while back (over a year ago), I posted the Chasles theorem with a very rough demo code and some (boring) video. I would go over the vector math of that code and polish it up. Note that i did NOT control for edge cases. Also, I think i posted a paper on how to use this in constructing constrained motion but screw-displacement theory is not the only approach.
project4
Posts: 197
Joined: Fri Jul 12, 2013 12:53 pm

### Re: Another approach to assembly solver (A2plus)

kbwbe wrote:
Wed Jul 25, 2018 12:34 pm
@project4,
just tested new PR's.

I found some errors. Find attached a small assembly "group-001.fcstd".

When using partial solving, it is not able to solve and i get an error in console "nested recomputes".
Solving alltogether works.
.
group-001.png
.
The hierarchy file shows false constraint-types.
.
Hierarchy.png
Please take 2 PRs that should fix both issues.
project4
Posts: 197
Joined: Fri Jul 12, 2013 12:53 pm

### Re: Another approach to assembly solver (A2plus)

kbwbe wrote:
Wed Jul 25, 2018 1:35 pm
@project4,

tested again move+rotation steps.
find attached another test-assemby and the solversystem.py with which i assembled.

1) move steps where to big. i multiplied them with factor 0.5
2) rotation is correct, but much to small steps.
3) i weighted the rotation for "refPointAttractions" again factor 6. It is still to slow !

I you start solving with solversystem.py above: i activated 10 steps only, 1/2 second delay between steps, with visualisation per step.
You can see a 'video' of movement. (You will see that rotation is to slow)

PS: enlarged factor 6.0 to 100.0. Great ! 6DOF is as fast as it had been some versions ago. I think that's it ...
(tested with max 150.000 steps and no visualisation again)
That's a great model for rotation explorations, only few parts and takes lots of steps, even in partial mode.
I'll probably have time to play with it on the weekend.
kbwbe
Posts: 986
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

### Re: Another approach to assembly solver (A2plus)

project4 wrote:
Wed Jul 25, 2018 4:46 pm
kbwbe wrote:
Wed Jul 25, 2018 12:34 pm
@project4,
just tested new PR's.

I found some errors. Find attached a small assembly "group-001.fcstd".

When using partial solving, it is not able to solve and i get an error in console "nested recomputes".
Solving alltogether works.
.
group-001.png
.
The hierarchy file shows false constraint-types.
.
Hierarchy.png
Please take 2 PRs that should fix both issues.
Hi @project4,
both PR's are merged. I also pushed changes to the branch "solver-stabilization". Now rotation caused by "refPointAttraction" got a weight.
Also linearMove was weighted, as it was to big in some usecases. 6DOF is fast again. Main problems may exist within long chains of parts as Manuel's crankshaft. In total it solved more testcases for me.
KBWBE

https://github.com/kbwbe/A2plus
kbwbe
Posts: 986
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

### Re: Another approach to assembly solver (A2plus)

@project4,

just tested your PR's. Everything is fine now. Good to have the button to toggle between solvermode "AllTogether" and "partial".
Manuels crankshaft is no problem anymore, using partial system.
Good work !
KBWBE

https://github.com/kbwbe/A2plus
project4
Posts: 197
Joined: Fri Jul 12, 2013 12:53 pm

### Re: Another approach to assembly solver (A2plus)

kbwbe wrote:
Wed Jul 25, 2018 5:52 pm
@project4,

just tested your PR's. Everything is fine now. Good to have the button to toggle between solvermode "AllTogether" and "partial".
Manuels crankshaft is no problem anymore, using partial system.
Good work !
I had the crankshaft assembled before all the changes. All other models didn't work for me.
Didn't test your latest changes yet. Probably could get back to that only on the weekend.
kbwbe
Posts: 986
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

### Re: Another approach to assembly solver (A2plus)

Hi @project4,

i updated branch "solver-stabilization".

Changes are:

bugfix in calculate chain, i recognized a double entry in worklist and system could not be solved. Now it looks as:

Code: Select all

``````    def calculateChain(self, doc):
self.stepCount = 0
rigCalcCount = 0
haveMore = True
workList = []

if a2plib.isPartialProcessing():
# start from fixed rigids and its children
for rig in self.rigids:
if rig.fixed:
workList.append(rig);
workList.extend(rig.getCandidates())
else:
FreeCAD.Console.PrintMessage( "Solvermode = solve all Parts at once !\n")
workList.extend(self.rigids)

while haveMore:
solutionFound = self.calculateWorkList(doc, workList)
if not solutionFound: return False

for rig in workList:
candidates = rig.getCandidates()
for candidate in candidates:

return True

``````
solveSystem() has been modified. Now it only loads once complete system and only once updates the solution.
SolveSystem now looks as:

Code: Select all

``````    def solveSystem(self,doc):
self.level_of_accuracy=1
FreeCAD.Console.PrintMessage( "\n===== Start Solving System ====== \n" )

startTime = int(round(time.time() * 1000))
self.assignParentship(doc)
while True:
systemSolved = self.calculateChain(doc)
totalTime = int(round(time.time() * 1000))
#FreeCAD.Console.PrintMessage( "Position Accuracy: %f\n" %  self.mySOLVER_POS_ACCURACY )
#FreeCAD.Console.PrintMessage( "Max positionerror: %f\n" %  poserror )
#FreeCAD.Console.PrintMessage( "Spin Accuracy: %f\n" %  self.mySOLVER_SPIN_ACCURACY )
#FreeCAD.Console.PrintMessage( "Max spinerror: %f\n" %  spinerror )
FreeCAD.Console.PrintMessage( "Total steps used: %d\n" %  self.stepCount)
FreeCAD.Console.PrintMessage( "TotalTime (ms): %d\n" % (totalTime - startTime) )
if systemSolved:
FreeCAD.Console.PrintMessage( "===== System solved ! ====== \n" )
self.mySOLVER_SPIN_ACCURACY *= 1e-1
self.mySOLVER_POS_ACCURACY *= 1e-1
self.level_of_accuracy+=1
if self.level_of_accuracy == 4:
self.solutionToParts(doc)
break
self.prepareRestart()
else:
FreeCAD.Console.PrintMessage( "===== Could not solve system ====== \n" )

msg = \
'''
Constraints inconsistent. Cannot solve System.
'''
QtGui.QMessageBox.information(  QtGui.QApplication.activeWindow(), "Constraint mismatch", msg )
break
self.mySOLVER_SPIN_ACCURACY = SOLVER_SPIN_ACCURACY
self.mySOLVER_POS_ACCURACY = SOLVER_POS_ACCURACY

``````
There are some small functions been added. solverSystem.prepareRestart(), rigid.prepareRestart(), dependency.disable()

I have attached a new assembled pump-testfile. I now can be solved in partial solvermode. In "alltogether mode" it will not work without adding some "helper" constraints.
Attachments
pump-2018-07-26.fcstd
KBWBE

https://github.com/kbwbe/A2plus
project4
Posts: 197
Joined: Fri Jul 12, 2013 12:53 pm

### Re: Another approach to assembly solver (A2plus)

kbwbe wrote:
Thu Jul 26, 2018 12:43 pm
Hi @project4,

i updated branch "solver-stabilization".

Changes are:

bugfix in calculate chain, i recognized a double entry in worklist and system could not be solved. Now it looks as:

Code: Select all

``````    def calculateChain(self, doc):
self.stepCount = 0
rigCalcCount = 0
haveMore = True
workList = []

if a2plib.isPartialProcessing():
# start from fixed rigids and its children
for rig in self.rigids:
if rig.fixed:
workList.append(rig);
workList.extend(rig.getCandidates())
else:
FreeCAD.Console.PrintMessage( "Solvermode = solve all Parts at once !\n")
workList.extend(self.rigids)

while haveMore:
solutionFound = self.calculateWorkList(doc, workList)
if not solutionFound: return False

for rig in workList:
candidates = rig.getCandidates()
for candidate in candidates:

return True

``````
solveSystem() has been modified. Now it only loads once complete system and only once updates the solution.
SolveSystem now looks as:

Code: Select all

``````    def solveSystem(self,doc):
self.level_of_accuracy=1
FreeCAD.Console.PrintMessage( "\n===== Start Solving System ====== \n" )

startTime = int(round(time.time() * 1000))
self.assignParentship(doc)
while True:
systemSolved = self.calculateChain(doc)
totalTime = int(round(time.time() * 1000))
#FreeCAD.Console.PrintMessage( "Position Accuracy: %f\n" %  self.mySOLVER_POS_ACCURACY )
#FreeCAD.Console.PrintMessage( "Max positionerror: %f\n" %  poserror )
#FreeCAD.Console.PrintMessage( "Spin Accuracy: %f\n" %  self.mySOLVER_SPIN_ACCURACY )
#FreeCAD.Console.PrintMessage( "Max spinerror: %f\n" %  spinerror )
FreeCAD.Console.PrintMessage( "Total steps used: %d\n" %  self.stepCount)
FreeCAD.Console.PrintMessage( "TotalTime (ms): %d\n" % (totalTime - startTime) )
if systemSolved:
FreeCAD.Console.PrintMessage( "===== System solved ! ====== \n" )
self.mySOLVER_SPIN_ACCURACY *= 1e-1
self.mySOLVER_POS_ACCURACY *= 1e-1
self.level_of_accuracy+=1
if self.level_of_accuracy == 4:
self.solutionToParts(doc)
break
self.prepareRestart()
else:
FreeCAD.Console.PrintMessage( "===== Could not solve system ====== \n" )

msg = \
'''
Constraints inconsistent. Cannot solve System.
'''
QtGui.QMessageBox.information(  QtGui.QApplication.activeWindow(), "Constraint mismatch", msg )
break
self.mySOLVER_SPIN_ACCURACY = SOLVER_SPIN_ACCURACY
self.mySOLVER_POS_ACCURACY = SOLVER_POS_ACCURACY

``````
There are some small functions been added. solverSystem.prepareRestart(), rigid.prepareRestart(), dependency.disable()

I have attached a new assembled pump-testfile. I now can be solved in partial solvermode. In "alltogether mode" it will not work without adding some "helper" constraints.
Hi,

You don't have to remove the duplicates yourself, that's done in the getCandidates function already:

Code: Select all

``````    def getCandidates(self):
candidates = []
for rig in self.childRigids:
if not rig.tempfixed and rig.areAllParentTempFixed():
candidates.append(rig)
return list(set(candidates))	# <-----  list(set(...)) removes the duplicates
``````
https://stackoverflow.com/questions/796 ... s-in-lists

Actually after reading the stackoverflow explanation again, we don't really need the list() call, which restores the order, since the order is not important in our case.

I've added the list(set()) to getCandidates in the last change you took from me...
kbwbe
Posts: 986
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

### Re: Another approach to assembly solver (A2plus)

project4 wrote:
Thu Jul 26, 2018 1:21 pm
I've added the list(set()) to getCandidates in the last change you took from me...
Yes. But only 1 duplicate in 1 testcase. It was the motor of the pump. Therefore i could not solve the pump assembly. After my changes, it worked. !?
KBWBE

https://github.com/kbwbe/A2plus