Skip to content

Conversation

@MultyXu
Copy link
Collaborator

@MultyXu MultyXu commented Oct 10, 2025

As a complement to this PR.

Copy link
Collaborator

@GoldenZephyr GoldenZephyr left a comment

Choose a reason for hiding this comment

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

Great progress! I left some specific comments. At a high level, I think there are two structural changes we should make.

  1. Let's plan to always have a mid-level planner, but the mid-level planner that is used is flexible. For the case where we want to fall back to the current behavior, the mid-level planner should just be an "identity" planner, i.e. it returns the high-level plan.
  2. I think we want to split up the mid-level planner, Occupancy Map, and ROS logic for updating the occupancy map slightly differently. The mid-level planner should be in charge of instantiating the occupancy map (and ROS pub/subs for updating the map)

Also, it would be great to add the instructions for how to test this functionality offboard the robot (either from a bag or the fake occupancy map), either here or in the ADT4 README

return [m, start, end]


def to_viz_msg(self, action, marker_ns):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there any reason that these functions for creating marker arrays from the action descriptions won't work here? https://github.com/robustrobotics/spot_tools/blob/main/robot_executor_interface/robot_executor_interface_ros/src/robot_executor_interface_ros/action_descriptions_ros.py

Copy link
Collaborator

Choose a reason for hiding this comment

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

When we use the existing functions we get this strange error Failed to publish path: Unknown action type <class 'robot_executor_msgs.msg._action_sequence_msg.ActionSequenceMsg the issue is for whatever reason our action type has _ before _action_sequence_msg and it should not be there?

@MultyXu
Copy link
Collaborator Author

MultyXu commented Oct 16, 2025

@GoldenZephyr Now the branch should be mostly refactored according to the suggestions you gave. But there might be further small changes needed.

Copy link
Collaborator

@GoldenZephyr GoldenZephyr left a comment

Choose a reason for hiding this comment

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

Good progress. Made a couple more comments (and I think there might be a small number of comments from last time to wrap up). Also,

  1. You'll want to rebase on main (and resolve the conflict, let me know if you need any help with that) so that CI will run
  2. I think some of the functions used to publish viz markers for the fake path can be used from the existing robot_executor action_description utilities rather than implemented here (see comment below).

I haven't had a chance to try running on my side yet, but thanks for adding the steps in the README

def clone_grid(self):
return self.occupancy_map.copy() if self.occupancy_map is not None else None

def set_grid(self, grid, resolution, origin, robot_pose, update_time):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Need to acquire the lock for the duration of this function (what if one thread tries accessing this information while it is being set, and only some of the fields have updated?)

from robot_executor_msgs.msg import ActionSequenceMsg, ActionMsg


def get_robot_pose(tf_buffer, parent_frame: str, child_frame: str):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's make a utils.py in this directory for now. We can think about a more centralized utils directory outside of spot_tools_ros later


def occupancy_map_callback(self, msg):

with self.occupancy_map:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would recommend handling the locking inside the implementation of set_grid, and then this function doesn't need to worry about acquiring the lock at all.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What if the ros updater updates the grid in the middle of the plan_path function?

Copy link
Collaborator

Choose a reason for hiding this comment

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

What specifically is the worry? During the time when plan_path assumes the occupancy map isn't changing, it should acquire the lock. If it has the lock and we receive a message here to update the occupancy map, we wait here until we can get the lock. That should work fine, although we do need to make sure that spot_executor_ros has its ROS callbacks setup up properly to ensure that blocking in the occupancy map callback doesn't block other ROS updates (specifically, we need to make sure spot_executor_ros has a MultiThreadedExecutor and also puts this callback in its own mutually exclusive callback group.

As a starting point, make sure that only the necessary lines are within this locking block. I think its should only be the call to set_map. And then I think it will be clear that we can just move the acquisition of the lock into the top of the set_map function (as opposed to right before calling the function).

ma.markers.append(m2)
return ma

def build_marker(pt, ns, frame, mid=0, color=[1,0,0]):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just to follow up on this, can the two marker-building functions be combined into one? If not, they need docstrings explaining the difference.

@MultyXu
Copy link
Collaborator Author

MultyXu commented Oct 20, 2025

There is small one line change in hydra_ros for the active window sink config. Do we want to make a PR on that repo as well?

Copy link
Collaborator

@GoldenZephyr GoldenZephyr left a comment

Choose a reason for hiding this comment

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

Looks very close, just a couple of things:

  1. I think there might still be a couple of comments unaddressed from last time. Let me know if you have any questions on those.
  2. Most of the time, there's no reason to commit commented out code. For any commented out line of code, I would either delete it, uncomment it, or leave a more extensive comment discussing the code, what it does, and why it's there as a comment.
  3. any TODOs or similar notes in comments should either be deleted (if they aren't adding much information / aren't very important), or enough context should be added to understand what's involved with implementing the TODO.
  4. I think this was an unresolved comment, but in the PR there's some code for converting an ActionDescription message to an RVIZ marker message. The robot_executor package has implementations of this already, can we use those instead of adding code here?

a_star_path_execute = a_star_path_metric

# project global point to local index
# TODO: add comment to explain code
Copy link
Collaborator

Choose a reason for hiding this comment

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

should resolve this TODO

if frontier_cells.size == 0:
return free_cell_candidate

# dists_frontier = np.linalg.norm(frontier_cells - np.array(goal).T, axis=1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove?

return 0 <= cell[0] < rows and 0 <= cell[1] < cols

def is_obstacle(cell):
# return self.occupancy_map[cell[0], cell[1]] != 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove?

# return self.occupancy_map[cell[0], cell[1]] != 0
# -1 unknown, 100 occupied, 0 free
return self.occupancy_map[cell[0], cell[1]] > 0 # threshold
# TODO: might be the case
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove? or clarify?

Input: high level path in <robot>/odom frame, Nx2 numpy array
Output: (bool, path) -> (success, path in odom frame)
'''
# self.feedback.print("INFO", f"{high_level_path_metric[-1]}")
Copy link
Collaborator

Choose a reason for hiding this comment

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

uncomment or remove

x=target_point.x, y=target_point.y, angle=yaw_angle
)
feedback.print("INFO", f"Navigating to waypoint {current_waypoint}")
# feedback.print("INFO", f"Navigating to waypoint {target_point}")
Copy link
Collaborator

Choose a reason for hiding this comment

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

uncomment or remove

occ_map = np.array(msg.data, dtype=np.int8).reshape((h, w))

# put test code here to see status
# robot_pose = self.spot_interface.get_pose() # not working in sim ??
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this resolved?


def occupancy_map_callback(self, msg):

with self.occupancy_map:
Copy link
Collaborator

Choose a reason for hiding this comment

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

What specifically is the worry? During the time when plan_path assumes the occupancy map isn't changing, it should acquire the lock. If it has the lock and we receive a message here to update the occupancy map, we wait here until we can get the lock. That should work fine, although we do need to make sure that spot_executor_ros has its ROS callbacks setup up properly to ensure that blocking in the occupancy map callback doesn't block other ROS updates (specifically, we need to make sure spot_executor_ros has a MultiThreadedExecutor and also puts this callback in its own mutually exclusive callback group.

As a starting point, make sure that only the necessary lines are within this locking block. I think its should only be the call to set_map. And then I think it will be clear that we can just move the acquisition of the lock into the top of the set_map function (as opposed to right before calling the function).

@GoldenZephyr
Copy link
Collaborator

Oh also I think CI is failing because of formatting or code linting -- I would recommend running the pre-commit hooks locally to check what the issues are.

@GoldenZephyr
Copy link
Collaborator

There is small one line change in hydra_ros for the active window sink config. Do we want to make a PR on that repo as well?

I think we should be able to just include the active window sinks in the hydra config that's already in ADT4. If you put the contents of this file (https://github.com/MIT-SPARK/Hydra-ROS/blob/3d7d24ed88232ab6173c7e009504a5f762539d6c/hydra_ros/config/sinks/active_window_sinks.yaml) (plus whatever modifications you have) in the ADT4 Hydra config under the active_window yaml namespace, it should work (and then you don't need to extra link in master.launch that refers to the extra yaml file at all).

@GoldenZephyr GoldenZephyr merged commit f99f1dd into main Oct 22, 2025
2 checks passed
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.

5 participants