-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update Backends Documentation #438
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,37 +19,45 @@ but for now, such methods and attributes will be left with the | |
client. | ||
|
||
The ``PlannerInterface`` serves as a template for any client-specific | ||
planner, providing default behavior for each of the | ||
methods listed within. When a developer wishes to override any | ||
of these defaults, they should make use of the appropriate backend | ||
feature interface from ``backends/interfaces.py``. The file | ||
``interfaces.py`` consists of a collection of classes, any | ||
implementation of which is callable through its ``__call__`` magic | ||
method. For example: | ||
planner and contains all unified planning function signatures. | ||
Developers wishing to develop new planner features should inherit from the | ||
appropriate backend feature interface from ``backends/interfaces/backend_features.py``. | ||
These features are then composed into a single class that inherits from | ||
``PlannerInterface``. | ||
For example: | ||
|
||
.. code-block:: python | ||
|
||
from compas.geometry import Frame | ||
from compas_fab.backends.interfaces import InverseKinematics | ||
from compas_fab.backends.interfaces import PlannerInterface | ||
|
||
class ExampleInverseKinematics(InverseKinematics): | ||
def inverse_kinematics(self, robot, | ||
frame_WCF, | ||
start_configuration=None, | ||
group=None, | ||
options=None): | ||
# The backend features have access to the instance of the client | ||
print(type(self.client)) | ||
# insert fancy code here | ||
pass | ||
|
||
can be instantiated and called in the following manner: | ||
|
||
# Now we can create a custom planner using our newly implemented backend feature | ||
class ExamplePlanner(ExampleInverseKinematics, PlannerInterface): | ||
pass | ||
|
||
The planner can be instantiated and called in the following manner: | ||
|
||
.. code-block:: python | ||
|
||
calculate_example_ik = ExampleInverseKinematics() | ||
# Instantiate the client and change its planner to the ExamplePlanner | ||
client = MyExamplePlanner(robot) | ||
client.planner = ExamplePlanner(client) | ||
# Call the inverse kinematics method as usual | ||
frame = Frame([0, 0, 0], [1, 0, 0], [0, 1, 0]) | ||
ik_result = calculate_example_ik(robot, frame) | ||
# or equivalently: | ||
ik_result = calculate_example_ik.inverse_kinematics(robot, frame) | ||
ik_result = robot.inverse_kinematics(robot, frame) | ||
|
||
|
||
These backend feature interfaces exist in part to enforce a common | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a typo in line 70 (not in your changes, but if you're updating, it, it makes sense to change. It says "These interfaces as exist to allow", but I guess it should read "These interfaces also exist to allow" |
||
|
@@ -67,9 +75,16 @@ backend of ``ClientB`` is slow to compute inverse kinematics but can plan motion | |
|
||
.. code-block:: python | ||
|
||
with ClientA() as client_a, ClientB() as client_b: | ||
inverse_kinematics = ClientAInverseKinematics(client_a) | ||
plan_motion = ClientBPlanMotion(client_b) | ||
with ClientA(robot) as client_a, ClientB(robot) as client_b: | ||
inverse_kinematics = client_a.planner.inverse_kinematics | ||
plan_motion = client_b.planner.plan_motion | ||
|
||
frame = Frame([0, 0, 0], [1, 0, 0], [0, 1, 0]) | ||
ik_result = inverse_kinematics(frame) | ||
|
||
start_configuration = robot.zero_configuration() | ||
goal_configuration = robot.random_configuration() | ||
motion_plan = plan_motion(start_configuration, goal_configuration) | ||
Comment on lines
+78
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This second example in general has never made much sense. I think we could kill the entire There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So do we remove the rest starting from "These interface also exist to allow mixing..."? |
||
|
||
Here we can assign the inverse kinematics to be calculated by the backend | ||
of ``ClientA``, while the motion planning is calculated by the backend of | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example doesn't work for multiple reasons:
client
is assigned aMyExamplePlanner
instance, which is not a client (did you meanMyExampleClient
?)MyExamplePlanner
is anyway not the name of the example class aboverobot
as first param, that's only if you call the IK feature directly skipping what the robot class does for you. Maybe you meantik_result = robot.inverse_kinematics(frame)
But I think the example is not the best anyway. I would suggest changing it to limit it to a more concrete example of what actually works if the person reading up to here implements (and understand) only this part.
So, as a first applied example -in my opinion- the correct thing is to show only what was coded above and not more, ie. how to instantiate the planner and use the backend feature:
Right after this semi-trivial example, we could a more elaborate one, but as you implied on the conversation on slack, it's kind of tricky to find one because the current client <-> planner <-> robot relationship is kind of convoluted.
One example using the current implementation could be this (notice that it depends on the tiny PR that I just sent):