Another approach to assembly solver (A2plus)

Discussion about the development of the Assembly workbench.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Mark Szlazak
Posts: 439
Joined: Tue Apr 04, 2017 6:06 pm
Location: SF Bay Area, California

Re: Another approach to assembly solver (A2plus)

Post by Mark Szlazak »

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" :oops:
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: 237
Joined: Fri Jul 12, 2013 12:53 pm

Re: Another approach to assembly solver (A2plus)

Post by project4 »

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: 237
Joined: Fri Jul 12, 2013 12:53 pm

Re: Another approach to assembly solver (A2plus)

Post by project4 »

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
Veteran
Posts: 1052
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

Re: Another approach to assembly solver (A2plus)

Post by kbwbe »

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
latest release: v0.4.56, installable via FreeCAD's addon manager
Tutorial: gripper assembly https://www.youtube.com/watch?v=QMxcQ5tssWk
Documentation: https://www.freecadweb.org/wiki/A2plus_Workbench
kbwbe
Veteran
Posts: 1052
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

Re: Another approach to assembly solver (A2plus)

Post by kbwbe »

@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
latest release: v0.4.56, installable via FreeCAD's addon manager
Tutorial: gripper assembly https://www.youtube.com/watch?v=QMxcQ5tssWk
Documentation: https://www.freecadweb.org/wiki/A2plus_Workbench
project4
Posts: 237
Joined: Fri Jul 12, 2013 12:53 pm

Re: Another approach to assembly solver (A2plus)

Post by project4 »

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
Veteran
Posts: 1052
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

Re: Another approach to assembly solver (A2plus)

Post by kbwbe »

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():
            FreeCAD.Console.PrintMessage( "Solvermode = partialProcessing !\n")
            # 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

            addList = []
            for rig in workList:
                candidates = rig.getCandidates()
                for candidate in candidates:
                    if candidate not in addList:
                        addList.append(candidate)
            workList.extend(addList)
            haveMore = (len(addList) > 0)
            self.printList("AddList", addList)


        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.loadSystem(doc)
        self.assignParentship(doc)
        loadTime = int(round(time.time() * 1000))
        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( "LoadTime (ms): %d\n" % (loadTime - startTime) )
            FreeCAD.Console.PrintMessage( "CalcTime (ms): %d\n" % (totalTime - loadTime) )
            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. 
    Please delete your last created constraint !
    '''
                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
(760.88 KiB) Downloaded 31 times
KBWBE

https://github.com/kbwbe/A2plus
latest release: v0.4.56, installable via FreeCAD's addon manager
Tutorial: gripper assembly https://www.youtube.com/watch?v=QMxcQ5tssWk
Documentation: https://www.freecadweb.org/wiki/A2plus_Workbench
project4
Posts: 237
Joined: Fri Jul 12, 2013 12:53 pm

Re: Another approach to assembly solver (A2plus)

Post by project4 »

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():
            FreeCAD.Console.PrintMessage( "Solvermode = partialProcessing !\n")
            # 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

            addList = []
            for rig in workList:
                candidates = rig.getCandidates()
                for candidate in candidates:
                    if candidate not in addList:
                        addList.append(candidate)
            workList.extend(addList)
            haveMore = (len(addList) > 0)
            self.printList("AddList", addList)


        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.loadSystem(doc)
        self.assignParentship(doc)
        loadTime = int(round(time.time() * 1000))
        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( "LoadTime (ms): %d\n" % (loadTime - startTime) )
            FreeCAD.Console.PrintMessage( "CalcTime (ms): %d\n" % (totalTime - loadTime) )
            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. 
    Please delete your last created constraint !
    '''
                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.

Are you sure you saw the duplicates on latest version?
I've added the list(set()) to getCandidates in the last change you took from me...
kbwbe
Veteran
Posts: 1052
Joined: Tue Apr 10, 2018 3:12 pm
Location: Germany, near Köln (Cologne)

Re: Another approach to assembly solver (A2plus)

Post by kbwbe »

project4 wrote: Thu Jul 26, 2018 1:21 pm Are you sure you saw the duplicates on latest version?
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
latest release: v0.4.56, installable via FreeCAD's addon manager
Tutorial: gripper assembly https://www.youtube.com/watch?v=QMxcQ5tssWk
Documentation: https://www.freecadweb.org/wiki/A2plus_Workbench
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: Another approach to assembly solver (A2plus)

Post by triplus »

Just wanted to say it's nice to see such ongoing developer collaboration on a project like assembly solver for FreeCAD.
Post Reply