Skip to content
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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions docs/developer/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment on lines +55 to +60
Copy link
Member

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 a MyExamplePlanner instance, which is not a client (did you mean MyExampleClient?)
  • MyExamplePlanner is anyway not the name of the example class above
  • robot is never assigned the client instance, so line 61 wouldn't work either
  • The call to robot would not have robot as first param, that's only if you call the IK feature directly skipping what the robot class does for you. Maybe you meant ik_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:

# you will need instances of client and robot
planner = ExamplePlanner(client)
ik_result = planner.inverse_kinematics(robot, frame)

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):

# We will use ROS as an example of a client
with RosClient(planner_type=ExamplePlanner) as client:
   robot = client.load_robot()
   ik_result = robot.inverse_kinematics(frame)



These backend feature interfaces exist in part to enforce a common
Copy link
Member

Choose a reason for hiding this comment

The 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"

Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The 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 ClientA/ClientB example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Down
Loading