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

Costmap #639

Merged
merged 94 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
8691b67
Test
RyanAspen Sep 19, 2023
46a2479
First commit
RyanAspen Sep 21, 2023
ca4cb07
Initial Cost_map commit
RyanAspen Nov 1, 2023
f79037b
Removed costmap folder copy
RyanAspen Nov 5, 2023
01cca13
Initial README.md
RyanAspen Nov 5, 2023
c61599b
Update README.md
RyanAspen Nov 5, 2023
c3bd12f
Update README.md
RyanAspen Nov 5, 2023
6eb8d2b
Update README.md to account for clarification on project specifications
RyanAspen Nov 10, 2023
d945109
Added clarification
RyanAspen Nov 10, 2023
7c2c227
Created water_bottle_search.py
26pawarp Nov 12, 2023
6d08489
Updated code for new project scope
RyanAspen Nov 14, 2023
db012d2
Added more complete logic for stitching
RyanAspen Nov 17, 2023
5bf78fa
Removed Line which kills Clang servers
RyanAspen Nov 17, 2023
0e6360d
Added rostopic and started callback function
26pawarp Nov 19, 2023
95761c0
add costmap nodelet in cmakelists
alisonryckman Nov 19, 2023
d82f70a
Added a debug file for testng the occupency grid message
26pawarp Nov 19, 2023
2ff1505
changed costmap removed redundant code
MackenzieMurphy Nov 19, 2023
9a82b0c
add costmap to cmakelists, fix some includes compatibility issues
umroverPerception Nov 26, 2023
782e22a
Started publisher for msg debugging
26pawarp Nov 26, 2023
bf8c75f
Debug course publisher update with test, also added temporary subscri…
26pawarp Nov 29, 2023
de8ce87
Callback function done and tested, data retrieved properly
26pawarp Nov 29, 2023
234315f
Removed duplicate code
Depicar Nov 30, 2023
6a448eb
remove pcl from build
alisonryckman Dec 3, 2023
c06b6f3
worked on grid filling
Crypt1cG Dec 3, 2023
5e0f139
merge
Crypt1cG Dec 3, 2023
ea4471e
fixed build and header
alisonryckman Jan 16, 2024
70aa5f0
add normals to zed processing
alisonryckman Jan 19, 2024
d452ba4
Cartesian Coordinates COnverting Function
26pawarp Jan 21, 2024
8535c3e
Merge branch 'master' into obs-avoidance
Emerson-Ramey Jan 21, 2024
da29226
Merge remote-tracking branch 'origin/integration' into obs-avoidance
qhdwight Jan 23, 2024
4b4df3b
Merge remote-tracking branch 'origin/integration' into costmap
umroverPerception Jan 23, 2024
beae107
Fix compile
umroverPerception Jan 23, 2024
40acab3
Update
umroverPerception Jan 24, 2024
41c4681
costmap stuff
umroverPerception Jan 24, 2024
5342537
Merge remote-tracking branch 'origin/integration' into costmap
qhdwight Jan 24, 2024
3039c9c
Remove old file
qhdwight Jan 24, 2024
5c65ea4
Merge remote-tracking branch 'origin/integration' into obs-avoidance
kayleyge Jan 25, 2024
36c1351
costmap node
Crypt1cG Jan 26, 2024
3e9cbb0
Merge remote-tracking branch 'origin/obs-avoidance' into obs-avoidance
kayleyge Jan 26, 2024
996575b
normal tweaks
Crypt1cG Jan 26, 2024
52a5bd8
Conversion function (Real World -> Occupancy Grid) & (Occupancy Grid …
26pawarp Jan 26, 2024
4a4afa8
Added setup for the A* program
26pawarp Jan 27, 2024
04654f3
pointcloud working
Crypt1cG Jan 31, 2024
e987b17
fast costmap
Crypt1cG Jan 31, 2024
99bb271
fixed costmap
Crypt1cG Jan 31, 2024
227170c
faster costmap
Crypt1cG Feb 1, 2024
6b74531
Finished adding A-Start
26pawarp Feb 2, 2024
52df6b2
Added A-Star call, but we need to do something with the list returned
26pawarp Feb 2, 2024
be439f3
Added reset cur point in trajector.py and made small changes to conve…
26pawarp Feb 3, 2024
e8b314c
reorganize and add path logic to on_loop
Emerson-Ramey Feb 4, 2024
caec28b
change incrementing of paths
Emerson-Ramey Feb 4, 2024
3f9c09c
minor tweaks
Crypt1cG Feb 6, 2024
57a5b43
removed allocations from hot-path
Crypt1cG Feb 6, 2024
973f25a
cleaned up starter project stuff
Crypt1cG Feb 6, 2024
aea0665
small changes for nav
Crypt1cG Feb 7, 2024
6c8f040
Merge remote-tracking branch 'origin/obs-avoidance' into costmap
Crypt1cG Feb 8, 2024
3cfd481
fix errors when running and add transitions
umroverPerception Feb 10, 2024
890cb25
minor fixes
Crypt1cG Feb 11, 2024
17b3a55
fix driving to same target point
umroverPerception Feb 11, 2024
ef3c646
Merge branch 'costmap' of github.com:umrover/mrover-ros into costmap
umroverPerception Feb 11, 2024
ce364ac
small fixes
Crypt1cG Feb 11, 2024
d555242
Merge branch 'costmap' of github.com:umrover/mrover-ros into costmap
Crypt1cG Feb 11, 2024
2be051e
got rid of z limit (might be needed in real world though)
Crypt1cG Feb 11, 2024
572e818
not done - testing path planning
umroverPerception Feb 18, 2024
a2592d4
Merge remote-tracking branch 'origin/integration' into costmap
qhdwight Feb 20, 2024
bc8d1f3
Update to use manif
qhdwight Feb 20, 2024
a13a89d
Formatting
qhdwight Feb 20, 2024
60fa76b
Fix nav imports
qhdwight Feb 20, 2024
59fb619
Merge remote-tracking branch 'origin/integration' into costmap
qhdwight Feb 20, 2024
e5d9092
Correct frame
qhdwight Feb 20, 2024
b877028
Enable ekf
qhdwight Feb 20, 2024
4cb242e
Merge remote-tracking branch 'origin/integration' into costmap
qhdwight Feb 20, 2024
6b466e5
Merge remote-tracking branch 'origin/integration' into costmap
qhdwight Feb 22, 2024
3e3b545
changed debug course publisher for waterbottle
kayleyge Feb 23, 2024
3e27986
Costmap touchup (#652)
qhdwight Feb 26, 2024
1cbff99
fix zed frame name
alisonryckman Feb 27, 2024
1d277cd
Merge remote-tracking branch 'origin/integration' into costmap
qhdwight Mar 3, 2024
cd678cf
fix navigation merge conflicts
Emerson-Ramey Mar 4, 2024
3728c6d
add message file
umroverPerception Mar 4, 2024
fe8ab31
fix percep topic name and testing nav path planning
umroverPerception Mar 8, 2024
d794ae4
fix orientation of costmap and start to consider average of neighbor …
umroverPerception Mar 8, 2024
eaa6467
update printed map, add kernel, traversable terrian
umroverPerception Mar 12, 2024
e5179c7
add transition to approach object, add exceptions and reset trajectory
umroverPerception Mar 16, 2024
7e69d19
add extra points to water bottle search spiral
umroverPerception Mar 22, 2024
bb27616
create new file for astar, move costmap callback to context
umroverPerception Mar 26, 2024
40d8b3e
Added ground1 test One mountain test
26pawarp Mar 28, 2024
ca76f89
Adding ground2.fbx and ground2.blend for testing and there's some tes…
arleencheema Mar 28, 2024
3effc25
added the arena for the 70th hunger games as a map
kayleyge Mar 31, 2024
fb9b018
Merge remote-tracking branch 'refs/remotes/origin/costmap' into costmap
kayleyge Mar 31, 2024
f7c85ea
update grounds
umroverPerception Apr 4, 2024
93fce06
worked on moving costmap
Crypt1cG Apr 11, 2024
87bbb85
forgot to add something
Crypt1cG Apr 11, 2024
054b377
Merge remote-tracking branch 'origin/integration' into costmap
Apr 28, 2024
a29f629
Merge remote-tracking branch 'origin/integration' into costmap
umroverPerception Apr 25, 2024
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
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ mrover_add_esw_bridge_node(pdb_test_bridge src/esw/pdb_test_bridge/*.cpp)

## Perception

mrover_add_nodelet(costmap src/perception/cost_map/*.cpp src/perception/cost_map src/perception/cost_map/pch.hpp)
mrover_nodelet_link_libraries(costmap lie)

mrover_add_library(websocket_server src/esw/websocket_server/*.cpp src/esw/websocket_server)
target_compile_definitions(websocket_server PUBLIC BOOST_ASIO_NO_DEPRECATED)

Expand Down
6 changes: 6 additions & 0 deletions config/navigation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ search:
segments_per_rotation: 8
distance_between_spirals: 3

water_bottle_search:
stop_thresh: 0.5
drive_fwd_thresh: 0.34
coverage_radius: 10
segments_per_rotation: 10

object_search:
coverage_radius: 10
distance_between_spirals: 3
Expand Down
6 changes: 3 additions & 3 deletions config/rviz/zed_test.rviz
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Visualization Manager:
Value: false
zed2i_camera_center:
Value: false
zed2i_left_camera_frame:
zed_left_camera_frame:
Value: false
zed2i_left_camera_optical_frame:
Value: false
Expand Down Expand Up @@ -102,7 +102,7 @@ Visualization Manager:
zed2i_camera_center:
zed2i_baro_link:
{}
zed2i_left_camera_frame:
zed_left_camera_frame:
zed2i_left_camera_optical_frame:
{}
zed2i_temp_left_link:
Expand Down Expand Up @@ -145,7 +145,7 @@ Visualization Manager:
Show Axes: false
Show Trail: false
Value: true
zed2i_left_camera_frame:
zed_left_camera_frame:
Alpha: 1
Show Axes: false
Show Trail: false
Expand Down
1 change: 1 addition & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<!-- The export tag contains other, unspecified, tags -->
<export>
<nodelet plugin="${prefix}/plugins/object_detector_plugin.xml"/>
<nodelet plugin="${prefix}/plugins/costmap.xml" />
<nodelet plugin="${prefix}/plugins/tag_detector_plugin.xml"/>
<nodelet plugin="${prefix}/plugins/zed_plugin.xml"/>
<nodelet plugin="${prefix}/plugins/long_range_tag_detector_plugin.xml"/>
Expand Down
6 changes: 6 additions & 0 deletions plugins/costmap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<library path="lib/libcostmap_nodelet">
<class name="mrover/CostMapNodelet"
type="mrover::CostMapNodelet"
base_class_type="nodelet::Nodelet">
</class>
</library>
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies = [
"aenum==3.1.15",
"daphne==4.0.0",
"channels==4.0.0",
"scipy==1.12.0",
"pyubx2==1.2.35",
"opencv-python==4.9.0.80",
]
Expand Down
22 changes: 11 additions & 11 deletions scripts/debug_course_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@
convert_waypoint_to_gps(waypoint)
for waypoint in [
(
Waypoint(tag_id=0, type=WaypointType(val=WaypointType.NO_SEARCH)),
SE3(position=np.array([4, 4, 0])),
),
(
Waypoint(tag_id=0, type=WaypointType(val=WaypointType.POST)),
SE3(position=np.array([-2, -2, 0])),
),
(
Waypoint(tag_id=1, type=WaypointType(val=WaypointType.POST)),
SE3(position=np.array([11, -10, 0])),
),
Waypoint(tag_id=-1, type=WaypointType(val=WaypointType.WATER_BOTTLE)),
SE3(position=np.array([0, 0, 0])),
)#,
# (
# Waypoint(tag_id=0, type=WaypointType(val=WaypointType.POST)),
# SE3(position=np.array([-2, -2, 0])),
# ),
# (
# Waypoint(tag_id=1, type=WaypointType(val=WaypointType.POST)),
# SE3(position=np.array([11, -10, 0])),
# ),
]
]
)
47 changes: 47 additions & 0 deletions scripts/debug_water_bottle_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python3

import numpy as np

import rospy
import time
from nav_msgs.msg import OccupancyGrid, MapMetaData
from geometry_msgs.msg import Pose
from std_msgs.msg import Header
from util.course_publish_helpers import publish_waypoints


"""
The purpose of this file is for testing the water bottle search state.
Specifically the occupancy grid message.
"""

if __name__ == "__main__":
rospy.init_node("debug_water_bottle")
try:
# int8[] data
test_grid = OccupancyGrid()
test_grid.data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=np.int8)

# nav_msgs/MapMetaData info
metadata = MapMetaData()
metadata.map_load_time = rospy.Time.now()
metadata.resolution = 3
metadata.width = 3
metadata.height = 3
metadata.origin = Pose()
test_grid.info = metadata

# std_msgs/Header header
header = Header()
test_grid.header = header

# rospy.loginfo(f"Before publish")
# #costpub = rospy.Publisher("costmap", OccupancyGrid, queue_size=1)
# for i in range(10):
# costpub.publish(test_grid)
# time.sleep(1)
# rospy.loginfo(f"After publish")
# rospy.spin()

except rospy.ROSInterruptException as e:
print(f"Didn't work to publish or retrieve message from ros")
245 changes: 245 additions & 0 deletions src/navigation/astar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
from __future__ import annotations

import heapq
import random
from threading import Lock

import numpy as np

from navigation.context import Context

class SpiralEnd(Exception):
"""
Raise when there are no more points left in the search spiral
"""
pass

class NoPath(Exception):
"""
Raise when an A* path could not be found
"""
pass

class AStar:
origin: np.ndarray = np.array([]) # holds the inital rover pose (waypoint of water bottle)
context: Context
costmap_lock: Lock

def __init__(self, origin: np.ndarray, context: Context)-> None:
self.origin = origin
self.context = context
self.costmap_lock = Lock()

class Node:
"""
Node class for astar pathfinding
"""
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
self.g = 0
self.h = 0
self.f = 0

def __eq__(self, other):
return self.position == other.position

# defining less than for purposes of heap queue
def __lt__(self, other):
return self.f < other.f

# defining greater than for purposes of heap queue
def __gt__(self, other):
return self.f > other.f

def cartesian_to_ij(self, cart_coord: np.ndarray) -> np.ndarray:
"""
Convert real world cartesian coordinates (x, y) to coordinates in the occupancy grid (i, j)
using formula floor(v - (WP + [-W/2, H/2]) / r) * [1, -1]
v: (x,y) coordinate
WP: origin
W, H: grid width, height (meters)
r: resolution (meters/cell)
:param cart_coord: array of x and y cartesian coordinates
:return: array of i and j coordinates for the occupancy grid
"""
width = self.context.env.cost_map.width * self.context.env.cost_map.resolution
height = self.context.env.cost_map.height * self.context.env.cost_map.resolution
width_height = np.array([-1 * width / 2, height / 2]) # [-W/2, H/2]
converted_coord = np.floor((cart_coord[0:2] - (self.origin + width_height)) / self.context.env.cost_map.resolution)
return converted_coord.astype(np.int8) * np.array([1, -1])

def ij_to_cartesian(self, ij_coords: np.ndarray) -> np.ndarray:
"""
Convert coordinates in the occupancy grid (i, j) to real world cartesian coordinates (x, y)
using formula (WP - [W/2, H/2]) + [j * r, i * r] + [r/2, -r/2] * [1, -1]
WP: origin
W, H: grid width, height (meters)
r: resolution (meters/cell)
:param ij_coords: array of i and j occupancy grid coordinates
:return: array of x and y coordinates in the real world
"""
width = self.context.env.cost_map.width * self.context.env.cost_map.resolution
height = self.context.env.cost_map.height * self.context.env.cost_map.resolution
width_height = np.array([width / 2, height / 2]) # [W/2, H/2]
resolution_conversion = ij_coords * [self.context.env.cost_map.resolution, self.context.env.cost_map.resolution] # [j * r, i * r]
half_res = np.array([self.context.env.cost_map.resolution / 2, -1 * self.context.env.cost_map.resolution / 2]) # [r/2, -r/2]
return ((self.origin - width_height) + resolution_conversion + half_res) * np.array([1, -1])

def return_path(self, current_node: Node) -> list:
"""
Return the path given from A-Star in reverse through current node's parents
:param current_node: end point of path which contains parents to retrieve path
:param costmap2D: current costmap
:return: reversed path except the starting point (we are already there)
"""
costmap2D = np.copy(self.context.env.cost_map.data)
path = []
current = current_node
while current is not None:
path.append(current.position)
current = current.parent
reversed_path = path[::-1]
print("ij:", reversed_path[1:])

# Print visual of costmap with path and start (S) and end (E) points
# The lighter the block, the more costly it is
for step in reversed_path:
costmap2D[step[0]][step[1]] = 2 # path (.)
costmap2D[reversed_path[0][0]][reversed_path[0][1]] = 3 # start
costmap2D[reversed_path[-1][0]][reversed_path[-1][1]] = 4 # end

for row in costmap2D:
line = []
for col in row:
if col == 1.0:
line.append("\u2588")
elif col < 1.0 and col >= 0.8:
line.append("\u2593")
elif col < 0.8 and col >= 0.5:
line.append("\u2592")
elif col < 0.5 and col >= 0.2:
line.append("\u2591")
elif col < 0.2:
line.append(" ")
elif col == 2:
line.append(".")
elif col == 3:
line.append("S")
elif col == 4:
line.append("E")
print("".join(line))

return reversed_path[1:]

def a_star(self, start: np.ndarray, end: np.ndarray) -> list | None:
"""
A-STAR Algorithm: f(n) = g(n) + h(n) to find a path from the given start to the given end in the given costmap
:param costmap2d: occupancy grid in the form of an 2D array of floats
:param start: rover pose in cartesian coordinates
:param end: next point in the spiral from traj in cartesian coordinates
:return: list of A-STAR coordinates in the occupancy grid coordinates (i,j)
"""
with self.costmap_lock:
costmap2d = self.context.env.cost_map.data
# convert start and end to occupancy grid coordinates
startij = self.cartesian_to_ij(start)
endij = self.cartesian_to_ij(end)

# initialize start and end nodes
start_node = self.Node(None, (startij[0], startij[1]))
end_node = self.Node(None, (endij[0], endij[1]))

if start_node == end_node:
return None

print(f"start: {start}, end: {end}")
print(f"startij: {startij}, endij: {endij}")

# initialize both open and closed list
open_list = []
closed_list = []

# heapify the open_list and add the start node
heapq.heapify(open_list)
heapq.heappush(open_list, start_node)

# add a stop condition
outer_iterations = 0
max_iterations = costmap2d.shape[0] * costmap2d.shape[1] // 2

# movements/squares we can search
adjacent_squares = ((0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1))
adjacent_square_pick_index = [0, 1, 2, 3, 4, 5, 6, 7]

# loop until you find the end
while len(open_list) > 0:
# randomize the order of the adjacent_squares_pick_index to avoid a decision making bias
random.shuffle(adjacent_square_pick_index)

outer_iterations += 1

if outer_iterations > max_iterations:
# if we hit this point return the path such as it is. It will not contain the destination
print("giving up on pathfinding too many iterations")
return self.return_path(current_node)

# get the current node
current_node = heapq.heappop(open_list)
closed_list.append(current_node)

# found the goal
if current_node == end_node:
return self.return_path(current_node)

# generate children
children = []
for pick_index in adjacent_square_pick_index:
new_position = adjacent_squares[pick_index]
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])
# make sure within range
if (
node_position[0] > (costmap2d.shape[0] - 1)
or node_position[0] < 0
or node_position[1] > (costmap2d.shape[1] - 1)
or node_position[1] < 0
):
continue

# make sure it is traversable terrian (not too high of a cost)
if costmap2d[node_position[0]][node_position[1]] >= 0.2: # TODO: find optimal value
continue

# create new node and append it
new_node = self.Node(current_node, node_position)
children.append(new_node)

# loop through children
for child in children:
# child is on the closed list
if len([closed_child for closed_child in closed_list if closed_child == child]) > 0:
continue
# create the f (total), g (cost in map), and h (euclidean distance) values
child.g = current_node.g + costmap2d[child.position[0], child.position[1]]
child.h = ((child.position[0] - end_node.position[0]) ** 2) + (
(child.position[1] - end_node.position[1]) ** 2
)
child.f = child.g + child.h
# child is already in the open list
if (
len(
[
open_node
for open_node in open_list
if child.position == open_node.position and child.g > open_node.g
]
)
> 0
):
continue
# add the child to the open list
heapq.heappush(open_list, child)

print("Couldn't find a path to destination")
raise NoPath()

Loading
Loading