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

Refactoring code to be more modular #6

Closed
wants to merge 62 commits into from

Conversation

jeremysee2
Copy link
Contributor

This continues work on the ROS1 version of the Spot wrapper to modularize the code and add testing to the wrapper, from heuristicus/spot_ros#104

List of breaking changes to the ROS interface:

  1. front/side/rear images now follow the ROS2 implementation of synchronous calls instead of AsyncPeriodicQuery
  2. All spot_arm calls have to be updated (e.g. self.spot_wrapper.spot_arm.arm_stow() instead of self.spot_wrapper.arm_stow(), adding the module name inbetween
  3. All docking calls have to be updated
  4. All GraphNav calls have to be updated
  5. All Images calls have to be updated
  6. The list_world_objects call has to be updated
  7. graph_nav_util has been folded into the GraphNav class, with the BD copyright notice marking where the copied code starts

New features:

  1. GraphNav has been significantly refactored into navigate_init, navigate_to and navigate_route
  2. Spot Check has been added

Currently, the only unit tests are for some parts of graph_nav_util, written in unittest and triggered automatically via Github Actions CI.

The PR is not ready to merge, requires testing and comments.

@jeremysee2
Copy link
Contributor Author

Hand images use the old AsyncPeriodicQuery, because I don't want the ROS interface to be aware of whether the arm exists; this should be handled directly by the Wrapper.

@heuristicus
Copy link
Collaborator

Thanks for the work you've put in on this. I'll take some time to review soon.

* use pytest instead

* add pytest dependency

* fail test on purpose

* remove failing test
@jeremysee2
Copy link
Contributor Author

I've made PR's to prepare for the breaking changes, in both the ROS1 and ROS2 implementations of spot_ros:

ROS1: heuristicus/spot_ros#126
ROS2: bdaiinstitute/spot_ros2#41

@jeremysee2 jeremysee2 changed the title Draft: Refactoring code to be more modular Refactoring code to be more modular Apr 21, 2023
@jeremysee2
Copy link
Contributor Author

Tested the code with the ROS1 and ROS2 wrapper, appears to work on the surface level (claim, stand, sit).

@amessing-bdai
Copy link
Collaborator

Ran the BDAII tests with that and the associated HIL tests

  • The bike repair test succeeded
  • Graph Nav test failed with the below error @kaiyu-zheng
[spot_ros2-1]   File "/workspaces/bdai/ws/build/spot_wrapper/spot_wrapper/spot_graph_nav.py", line 544, in clear_graph                                                                                             
[spot_ros2-1]     result = self._graph_nav_client.clear_graph(lease=self._lease.lease_proto)                                                                                                                       
[spot_ros2-1] AttributeError: 'SpotGraphNav' object has no attribute '_lease'                                                                                                                                      

@kzheng-bdai
Copy link
Contributor

Ran the BDAII tests with that and the associated HIL tests

  • The bike repair test succeeded
  • Graph Nav test failed with the below error @kaiyu-zheng
[spot_ros2-1]   File "/workspaces/bdai/ws/build/spot_wrapper/spot_wrapper/spot_graph_nav.py", line 544, in clear_graph                                                                                             
[spot_ros2-1]     result = self._graph_nav_client.clear_graph(lease=self._lease.lease_proto)                                                                                                                       
[spot_ros2-1] AttributeError: 'SpotGraphNav' object has no attribute '_lease'                                                                                                                                      

Looks like SpotWrapper originally has a self._lease field, and it has functions like getLease that set that field. How is this handled now?

@amessing-bdai
Copy link
Collaborator

Also, I want to reiterate that all of the arm functions should use block_until_arm_arrives instead of time.sleep

@jeremysee2
Copy link
Contributor Author

Ran the BDAII tests with that and the associated HIL tests

  • The bike repair test succeeded
  • Graph Nav test failed with the below error @kaiyu-zheng
[spot_ros2-1]   File "/workspaces/bdai/ws/build/spot_wrapper/spot_wrapper/spot_graph_nav.py", line 544, in clear_graph                                                                                             
[spot_ros2-1]     result = self._graph_nav_client.clear_graph(lease=self._lease.lease_proto)                                                                                                                       
[spot_ros2-1] AttributeError: 'SpotGraphNav' object has no attribute '_lease'                                                                                                                                      

Looks like SpotWrapper originally has a self._lease field, and it has functions like getLease that set that field. How is this handled now?

Currently, the self._lease field is created when initializing GraphNav with a map, or when telling the robot to navigate to a specific waypoint. Clearing the graph (currently) does not attempt to claim the lease.

It's not clear if clear_graph requires the lease.

If clearing the graph doesn't require claiming the lease, I recommend wrapping that call in an if statement to avoid this problem. If it does, we can add in the following snippet to claim the lease:

self._lease = self._lease_wallet.get_lease()

@jeremysee2
Copy link
Contributor Author

Ran the BDAII tests with that and the associated HIL tests

  • The bike repair test succeeded
  • Graph Nav test failed with the below error @kaiyu-zheng
[spot_ros2-1]   File "/workspaces/bdai/ws/build/spot_wrapper/spot_wrapper/spot_graph_nav.py", line 544, in clear_graph                                                                                             
[spot_ros2-1]     result = self._graph_nav_client.clear_graph(lease=self._lease.lease_proto)                                                                                                                       
[spot_ros2-1] AttributeError: 'SpotGraphNav' object has no attribute '_lease'                                                                                                                                      

Looks like SpotWrapper originally has a self._lease field, and it has functions like getLease that set that field. How is this handled now?

Currently, the self._lease field is created when initializing GraphNav with a map, or when telling the robot to navigate to a specific waypoint. Clearing the graph (currently) does not attempt to claim the lease.

It's not clear if clear_graph requires the lease.

If clearing the graph doesn't require claiming the lease, I recommend wrapping that call in an if statement to avoid this problem. If it does, we can add in the following snippet to claim the lease:

self._lease = self._lease_wallet.get_lease()

Addressed with the addition of self._get_lease in the clear_graph method 651fed6

@heuristicus
Copy link
Collaborator

Also, I want to reiterate that all of the arm functions should use block_until_arm_arrives instead of time.sleep

Addressed this in e6df481. The grasp_3d command comes from the manipulation API and doesn't seem to have an equivalent function.

@kzheng-bdai
Copy link
Contributor

kzheng-bdai commented Jun 10, 2023

Ran the BDAII tests with that and the associated HIL tests

  • The bike repair test succeeded
  • Graph Nav test failed with the below error @kaiyu-zheng
[spot_ros2-1]   File "/workspaces/bdai/ws/build/spot_wrapper/spot_wrapper/spot_graph_nav.py", line 544, in clear_graph                                                                                             
[spot_ros2-1]     result = self._graph_nav_client.clear_graph(lease=self._lease.lease_proto)                                                                                                                       
[spot_ros2-1] AttributeError: 'SpotGraphNav' object has no attribute '_lease'                                                                                                                                      

Looks like SpotWrapper originally has a self._lease field, and it has functions like getLease that set that field. How is this handled now?

Currently, the self._lease field is created when initializing GraphNav with a map, or when telling the robot to navigate to a specific waypoint. Clearing the graph (currently) does not attempt to claim the lease.

It's not clear if clear_graph requires the lease.

If clearing the graph doesn't require claiming the lease, I recommend wrapping that call in an if statement to avoid this problem. If it does, we can add in the following snippet to claim the lease:

self._lease = self._lease_wallet.get_lease()

Since only one node can possess the lease to a Spot at a time, would it be better if the lease wallet is owned by the SpotWrapper object, instead of by the individual components? Correct me if I am misunderstanding something.

@heuristicus
Copy link
Collaborator

heuristicus commented Jun 11, 2023

Since only one node can possess the lease to a Spot at a time, would it be better if the lease wallet is owned by the SpotWrapper object, instead of by the individual components? Correct me if I am misunderstanding something.

I think that because it's part of the same node, getting a lease object should just return the same values. But I'm not certain. I tried doing some stuff with the lease wallet previously and it didn't behave the way I expected, so there may be some complications.

@amessing-bdai
Copy link
Collaborator

Also, I want to reiterate that all of the arm functions should use block_until_arm_arrives instead of time.sleep

Addressed this in e6df481. The grasp_3d command comes from the manipulation API and doesn't seem to have an equivalent function.

That's fine. You are basically doing the same thing that they do internally. Also, spot_ros2 doesn't use the grasp_3d command and instead uses a more general manipulation_api command. I might still recommend that you have a timeout option similar to the existing block commands.

@amessing-bdai
Copy link
Collaborator

amessing-bdai commented Jun 11, 2023

Since only one node can possess the lease to a Spot at a time, would it be better if the lease wallet is owned by the SpotWrapper object, instead of by the individual components? Correct me if I am misunderstanding something.

I think that because it's part of the same node, getting a lease object should just return the same values. But I'm not certain. I tried doing some stuff with the lease wallet previously and it didn't behave the way I expected, so there may be some complications.

I don't think whether it is part of the same node matters so much. I think because we created the lease client from a single robot instance, all leases acquired/taken have the same owner. I think it will be the same lease, but possibly have an updated request time. The only time it would be a new lease would be if something else took the lease (e.g. the tablet or another program) and then you took it back.

I'm not sure there is a difference between using the get_lease or just leaving the default value of None which says it will use the client's lease. Probably depends on whether they mean the lease_client's lease or the last lease that was passed into the graph_nav_client. When the various functions create subleases and pass those to the graph_nav_client is when I think having the lease_wallet matters.

Also, I am also curious about having the lease_keepalive internally and if there are any conflicts with the lease_keepalive that is held in SpotWrapper. Potentially there are times when there are two threads that are trying to keep the lease alive. Probably doesn't hurt in terms of messing with the lease, but just potentially extra computation and communication. Also, there are times where various functions shutdown the internal keepalive, usually just before they pass a sublease to the graph_nav_client, so I'd be curious if the external keepalive (which presumably is still running) creates a conflict.

self._init_current_graph_nav_state()
self._estop_endpoint = None
self._estop_keepalive = None
self._robot_id = None
Copy link
Collaborator

@amessing-bdai amessing-bdai Jun 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property down below uses the robot_params dict instead of self._robot_id. If this isn't used anywhere they it should be removed to avoid confusion

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made it so that the wrapper class no longer uses the convenience dict for accessing these values or returning them from properties. It still has the dict to pass to things which it instantiates.

@heuristicus
Copy link
Collaborator

I've fixed up some of the issues which were mentioned by @amessing-bdai and @myeatman-bdai. I'd like to get this merged and it would be good to know if there are any other concerns which would be considered blocking so I can try and fix them.

@heuristicus
Copy link
Collaborator

Per the discussion on slack, I will close this PR and split it out into individual PRs for each module which achieves the same result but is easier to test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants