Skip to content

Commit

Permalink
ROS Work - ZED Camera Integration Pose/Object Detection
Browse files Browse the repository at this point in the history
  • Loading branch information
JMUJacart committed Jun 16, 2022
1 parent 8e35ab0 commit e913c66
Show file tree
Hide file tree
Showing 28 changed files with 246,019 additions and 105 deletions.
3,394 changes: 3,394 additions & 0 deletions Graph: 2022-04-16 11:24:48.102358.gml

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions cart_control/launch/autoware_localization.launch
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,19 @@
<arg name="gazebo" default="false"/>
<arg name="lidar" default="true"/>
<arg name="use_sim_time" default="false"/>
<arg name="map_arg" default="$(env HOME)/AVData/speedBoiMap.pcd" />
<param name="use_sim_time" type="bool" value="$(arg use_sim_time)"/>


<!-- Location of Point Cloud Map file -->
<arg name="map_arg" default="$(env HOME)/AVData/speedBoiMap.pcd"/>
<!--<<group unless="$(arg full_map)">
<arg name="map_arg" default="$(env HOME)/AVData/speedBoiMap.pcd" />
</group>
<group if="$(arg full_map)">
<arg name="map_arg" value="$(env HOME)/AVData/fullmap-compressed.pcd"/>
</group>-->


<group>
<!-- Start Gazebo with an empty world -->
Expand Down Expand Up @@ -78,4 +87,4 @@
<!-- Throttle position messages so nodes that need positional updates dont't call to often -->
<node name="throttle" pkg="topic_tools" type="throttle" args="messages /ndt_pose 60.0 /limited_pose"/>
</group>
</launch>
</launch>
5 changes: 4 additions & 1 deletion cart_control/launch/navigation.launch
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
<arg name="pose" default="false"/>
<arg name="obstacle_detection" default="true"/>
<arg name="visualize_pose" default="false"/>
<arg name="map_arg" default="$(env HOME)/AVData/speedBoiMap.pcd"/>

<!-- Configuration for real-world driving -->
<group if="$(arg realtime)">
<include file="$(find cart_control)/launch/autoware_localization.launch"/>
<include file="$(find cart_control)/launch/autoware_localization.launch">
<arg name="map_arg" value="$(arg map_arg)" />
</include>
</group>

<!-- Configuration for running simulator -->
Expand Down
9 changes: 5 additions & 4 deletions cart_endpoints/launch/hardware_interface.launch
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
<arg name="visualize_pose" default="false"/>
<arg name="obstacle_detection" default="true"/>
<arg name="lidar" default="true"/>
<arg name="use_front_camera" default="true"/>
<arg name="use_front_camera" default="false"/>


<include file="$(find cart_endpoints)/launch/zed.launch"/>
<!--<include file="$(find cart_endpoints)/launch/zed.launch"/>-->

<!-- Start lidar code -->
<group if="$(arg lidar)">
Expand All @@ -36,13 +36,14 @@
</group>
<group if="$(arg obstacle_detection)">

<node name="pointcloud_to_laserscan_node" pkg="pointcloud_to_laserscan" type="pointcloud_to_laserscan_node" args="cloud_in:=/velodyne_points scan:=/pcd_to_scan" output="screen">
<node name="lidar_pointcloud_to_laserscan_node" pkg="pointcloud_to_laserscan" type="pointcloud_to_laserscan_node" args="cloud_in:=/velodyne_points scan:=/pcd_to_scan" output="screen">
<param name="min_height" type="double" value="-1.0" />
<param name="max_height" type="double" value="0" />
<param name="range_min" type="double" value="0.8" />
</node>

<node name="obstacle_detector" pkg="cart_endpoints" type="obstacle_detector.py" output="screen"/>
<node name="lidar_obstacle_detector" pkg="cart_endpoints" type="obstacle_detector.py" output="screen"/>
<node name="obstacle_merger" pkg="cart_endpoints" type="obstacle_merger.py"/>
<node name="collision_detector" pkg="cart_planning" type="collision_detector.py" output="screen"/>
</group>
<group if="$(arg use_front_camera)">
Expand Down
236 changes: 236 additions & 0 deletions cart_endpoints/scripts/broken_obstacle_merger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#!/usr/bin/env python
'''
Obstacle Merger class. Takes in many lists of obstacles and combines them to remove duplicates of similar
obstacles for detectors with overlapping fields of view (i.e. LiDAR and ZED see the same person).
The best way to picture this calculation in your head is to imagine the obstacles over a tile floor.
All the readings that are on the same tile get collapsed into the same obstacle located at the center
of the tile (TODO: Consider averaging them together). This also implies that this node will only merge
obstacles on the world's XZ plane since we do not care about the height.
Obstacle coordinates will all be translated to the same coordinate frame (default := base_link)
before they are collapsed.
TODO: Do some ascii art describing the process
+--------------------------------------------------+
| |
| |
| |
| |
| |
| |
+--------------------------------------------------+
'''


import rospy
import numpy as np
import math

# Messages
from navigation_msgs.msg import VehicleState, Obstacle, ObstacleArray
from geometry_msgs.msg import TwistStamped, Vector3, PointStamped
from std_msgs.msg import Header

# Display
from visualization_msgs.msg import Marker



class ObstacleMerger(object):

def __init__(self):

# ----- Constants -----
self.NS_TO_SEC = 1 / (10 ** 9)

# ----- Parameters -----
# Name of the node
self.name = rospy.get_param("name", "obstacle_merger")
# Space delimited list of topics that publish ObstacleArray's for input
self.obstacles_in = rospy.get_param("obstacles_in", "front_camera_obstacles")
# Name of the topic to output obstacles to
self.obstacles_out = rospy.get_param("obstacles_out", "/test_obstacles")
# Name of the topic to output obstacle markers to
self.obstacle_markers_out = rospy.get_param("obstacle_markers_out", "/test_obstacle_markers")
# Name of the coordinate frame to convert everything to
self.target_frame = rospy.get_param("target_frame", "/base_link")
# Size in meters of a single "tile"
self.tile_size_m = rospy.get_param("tile_size", 0.25)
# self.tile_size_m = rospy.get_param("tile_size", 1.0)
# How many times per second should data be outputted from the node
self.output_hz = rospy.get_param("output_hz", 10)
# How long should we keep considering old data in seconds
self.stale_data_sec = rospy.get_param("stale_data_sec", 1.0)
# Whether or not you should use the center of the tile or average the points together
self.use_center = rospy.get_param("use_center", True)

if self.tile_size_m == 0:
rospy.logfatal("Tile size cannot be 0!")

rospy.init_node(self.name)

rospy.loginfo("obstacles_in: '%s'" % (self.obstacles_in))

# ----- Node State -----
# Maps the topic name to the latest message received from that topic
self.message_map = {}
# Maps the topic name to the subscriber object
self.subscribers = {}
# Array of objects we want to publish
self.objects = ObstacleArray()
# Publishers for output topics
self.obstacles_pub = rospy.Publisher(self.obstacles_out, ObstacleArray, queue_size=10)
self.display_pub = rospy.Publisher(self.obstacle_markers_out, Marker, queue_size=10)


# Check if we have any input topics
if not self.obstacles_in:
rospy.logerr("Not subscribing to any topics!")
else:
# Subscribe to all of the input messages
for topic in self.obstacles_in.split(" "):
rospy.loginfo("Subscribing to %s" % (topic))
self.subscribers[topic] = rospy.Subscriber(topic, ObstacleArray, \
callback=self.receiveObstacleArray, \
callback_args=topic, \
queue_size=10)


# Repeatedly merge obstacles and publish results until shutdown
r = rospy.Rate(self.output_hz)
while not rospy.is_shutdown():
self.voxelize()
self.obstacles_pub.publish(self.objects)
self.local_display()
r.sleep()


def receiveObstacleArray(self, msg, topic):
"""
Receives ObstacleArray data from a topic we've subscribed to.
Converts the message to our desired coordinate frame.
@param self
@param msg ObstacleArray message
@param topic Topic that the message came from
"""
rospy.loginfo("Received message from %s" % (topic));

# Check if we need to translate it to our coordinate frame
self.message_map[topic] = msg


def voxelize(self):
"""
Groups the obstacles together within their cell (voxel). Obstacles within the same
cell will have their locations averaged into one obstacle depending on a flag.
@param self
"""
local_message_map = self.message_map

# Clear obstacles
self.objects = ObstacleArray()

# vx and vy are the voxel coordinates
# This structure keeps track of what we need to calculate the average
# (vx, vy) -> (x_coord_sum, z_coord_sum, num_coords)
voxel_map = {}

for topic, msg in local_message_map.items():
rospy.loginfo("I have %d obstacles from %s" % ( len(msg.obstacles), topic ))
# If the message is recent enough
# if rospy.get_rostime().to_sec() - msg.header.stamp.to_sec() < self.stale_data_sec:
# Calculate voxel coordinates
for obstacle in msg.obstacles:
vx = math.floor(obstacle.pos.point.x / self.tile_size_m)
vy = math.floor(obstacle.pos.point.y / self.tile_size_m)
rospy.loginfo("(%f, %f, %f) obs pos (%d, %d) voxel coords" % (obstacle.pos.point.x, obstacle.pos.point.y ,obstacle.pos.point.z, vx, vy))

# Insert the current sum into the voxel map
key = (vx, vy)
if self.use_center:
voxel_map[key] = ( vx + (self.tile_size_m / 2), vy + (self.tile_size_m / 2), 1 )
else:
if not key in voxel_map:
voxel_map[key] = ( obstacle.pos.point.x, obstacle.pos.point.z, 1 )
else:
voxel_map[key] = ( voxel_map[key][0] + obstacle.pos.point.x, \
voxel_map[key][1] + obstacle.pos.point.z, \
voxel_map[key][2] + 1 )



# Calculates the average of all points gathered in each voxel
for coords, data in voxel_map.items():
vx = coords[0]
vy = coords[1]

x_coord_sum = data[0]
y_coord_sum = data[1]
num_coords = data[2]


obs = Obstacle()
obs.header.frame_id = self.target_frame
obs.header.stamp = rospy.Time(0)
obs.pos.point.x = x_coord_sum / num_coords
obs.pos.point.y = y_coord_sum / num_coords
obs.pos.point.z = 0
obs.radius = self.tile_size_m

rospy.loginfo("Obstacle at: (%f, %f, %f) from voxel (%d, %d)" % (obs.pos.point.x, obs.pos.point.y, obs.pos.point.z, vx, vy))

self.objects.obstacles.append(obs)




def local_display(self):
"""
Publish markers for RViz to display, may appear jittery due to lack of interpolation.
@param self
"""
object_list = self.objects.obstacles
for i in range(len(object_list)):
marker = Marker()
marker.header = Header()
marker.header.frame_id = self.target_frame

marker.ns = "Object_NS"
marker.id = i
marker.type = Marker.CYLINDER
marker.action = 0
marker.color.r = 1.0
marker.color.g = 0.0
marker.color.b = 1.0
marker.color.a = 1.0
marker.lifetime = rospy.Duration.from_sec(0.1)

marker.pose.position.x = object_list[i].pos.point.x
marker.pose.position.y = object_list[i].pos.point.y
marker.pose.position.z = 0.0

radius = object_list[i].radius
marker.scale.x = radius
marker.scale.y = radius
marker.scale.z = 0.1

self.display_pub.publish(marker)






if __name__ == "__main__":
try:
ObstacleMerger()
except rospy.ROSInterruptException:
pass
13 changes: 7 additions & 6 deletions cart_endpoints/scripts/motor_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(self):
self.vel = 0
self.vel_cart_units = 0
self.angle = 0
self.steering_tolerance = 50 # default was 45
""" Set up the node. """
rospy.init_node('motor_endpoint')
rospy.loginfo("Starting motor node!")
Expand Down Expand Up @@ -126,11 +127,11 @@ def endpoint_calc(self):
#adjust the target_angle range from (-45 <-> 45) to (0 <-> 100)
# rospy.loginfo("Angle before adjustment: " + str(self.cmd_msg.angle))

if(self.angle < -45):
self.angle = -45
if(self.angle > 45):
self.angle = 45
target_angle = 100 - int(( (self.angle + 45) / 90 ) * 100)
if(self.angle < -40):
self.angle = self.steering_tolerance * -1
if(self.angle > 40):
self.angle = self.steering_tolerance
target_angle = 100 - int(( (self.angle + self.steering_tolerance) / 90 ) * 100)

#if debug printing is requested print speed and angle info
if self.debug:
Expand Down Expand Up @@ -179,7 +180,7 @@ def endpoint_calc(self):

def pack_send(self, throttle, brake, steer_angle):
data = bytearray(b'\x00' * 5)
bitstruct.pack_into('u8u8u8u8u8', data, 0, 42, 21, abs(throttle), brake, steer_angle)
bitstruct.pack_into('u8u8u8u8u8', data, 0, 42, 21, abs(throttle), brake, steer_angle + 10)
self.speed_ser.write(data)

if __name__ == "__main__":
Expand Down
11 changes: 8 additions & 3 deletions cart_endpoints/scripts/obstacle_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def __init__(self):
# Max distance between points to consider for clustering
self.dist_threshold = 0.10

self.obstacles_pub = rospy.Publisher('/obstacles', ObstacleArray, queue_size=10)
self.display_pub = rospy.Publisher('/obstacle_display', Marker, queue_size=10)
self.obstacles_pub = rospy.Publisher('/lidar_obstacles', ObstacleArray, queue_size=10)
self.display_pub = rospy.Publisher('/lidar_obstacle_display', Marker, queue_size=10)

# self.rplidar_sub = rospy.Subscriber('/scan_rplidar', LaserScan, self.lidar_callback, queue_size=1)
self.velodyne_laserscan_sub = rospy.Subscriber('/pcd_to_scan', LaserScan, self.lidar_callback, queue_size=1)
Expand Down Expand Up @@ -167,6 +167,8 @@ def cluster_points(self):
#Put circles around our clusters(really segments)
def circularize(self):
self.objects = ObstacleArray()
self.objects.header.frame_id = "velodyne"
self.objects.header.stamp = rospy.Time().now()

for cluster in self.cluster_list:
cur_circle = Obstacle()
Expand All @@ -186,7 +188,7 @@ def circularize(self):
self.t.waitForTransform("/base_link", "/velodyne", rospy.Time(0), rospy.Duration(0.01))
global_point = PointStamped()
global_point.header.frame_id = "velodyne"
global_point.header.stamp = rospy.Time(0)
global_point.header.stamp = rospy.Time().now()
global_point.point.x = centerX
global_point.point.y = centerY
global_point.point.z = 0.0
Expand All @@ -197,8 +199,11 @@ def circularize(self):

#Create new circle around obstacle and add to current list of obstacles
cur_circle = Obstacle()
cur_circle.header.frame_id = "velodyne"
cur_circle.header.stamp = rospy.Time().now()
cur_circle.pos = prepared_point
cur_circle.radius = radius
cur_circle.followable = True

self.objects.obstacles.append(cur_circle)

Expand Down
Loading

0 comments on commit e913c66

Please sign in to comment.