-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
APPARENTLY CANT CALL NAVIGATOR IN MULTITHREAD :)
- Loading branch information
Showing
6 changed files
with
231 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import os | ||
|
||
from ament_index_python.packages import get_package_share_directory | ||
from launch import LaunchDescription | ||
from launch.actions import IncludeLaunchDescription, GroupAction | ||
from launch.launch_description_sources import PythonLaunchDescriptionSource | ||
from launch.substitutions import PathJoinSubstitution | ||
from launch_ros.substitutions import FindPackageShare | ||
|
||
from launch_ros.actions import Node | ||
from launch_ros.actions import SetRemap | ||
|
||
def generate_launch_description(): | ||
|
||
mule_behavior = Node( | ||
package="rove_navigation", | ||
executable='mule_behavior', | ||
name='mule_behavior', | ||
output='screen', | ||
) | ||
|
||
person_following = Node( | ||
package="rove_navigation", | ||
executable='person_following', | ||
name='navigate_to_person', | ||
output='screen', | ||
) | ||
|
||
return LaunchDescription([ | ||
#person_following_node, | ||
mule_behavior, | ||
person_following | ||
]) |
13 changes: 13 additions & 0 deletions
13
src/rove_navigation/rove_navigation/behavior/mule_constants.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# states | ||
START = 'START' # At the beginning, exiting this state sets the startpoint | ||
FOLLOWING = 'FOLLOWING' # make person_following behaviour active | ||
REWIND = 'REWIND' # Initiates the rewind phase | ||
PROCESSED_REWIND = 'PROCESSED_REWIND' # Continues the rewind phase until another TPOSE is detected | ||
PAUSE = 'PAUSE' # Freezes the robot in place (TODO: nice to have, make it still oriente itself towards the target, but not move) | ||
END = 'END' | ||
|
||
# poses | ||
FIGURE_IDLE = "IDLE" # Continue state | ||
FIGURE_TPOSE = "TPOSE" # Start the follow | ||
FIGURE_BUCKET = "BUCKET" # Pause/UnPause | ||
FIGURE_UP = "UP" # start the rewind phase |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import time | ||
import socket | ||
from threading import Thread | ||
|
||
from sympy import Point | ||
import rclpy | ||
from rclpy.duration import Duration | ||
from rclpy.node import Node | ||
from geometry_msgs.msg import PoseStamped | ||
import numpy as np | ||
from std_msgs.msg import String, Header | ||
from nav_msgs.msg import Odometry, Path | ||
from nav2_msgs.action import NavigateThroughPoses | ||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult | ||
import rove_navigation.behavior.mule_constants as consts | ||
|
||
# Node to handle when the robot loses connection with internet, stores waypoints to fall back to to try to get back connection | ||
class MuleBehavior(Node): | ||
def __init__(self): | ||
super().__init__('mule_behavior') | ||
self.subscription_person_position = self.create_subscription( | ||
String, | ||
'/tracking/state', | ||
self.state_listener, | ||
10) | ||
self.pub_mule_state = self.create_publisher( | ||
String, | ||
'/mule_state', | ||
10) | ||
self.odom_subscriber = self.create_subscription( | ||
Odometry, | ||
'/odometry/local', | ||
self.odom_listener, | ||
10) | ||
|
||
# nav variables | ||
self.startPoint: PoseStamped | ||
self.endpoint: PoseStamped | ||
self.goal_point: PoseStamped | ||
self.currentPos: PoseStamped | ||
self.navigator = BasicNavigator() | ||
|
||
|
||
self.state = '' | ||
self.change_state(consts.START) | ||
self.navigate_to_goal_point() # returns only when goal reached/cancelled/failed | ||
|
||
self.get_logger().info('Initiating MuleBehavior node') | ||
|
||
# Thread to execute goal pose posting for rewind | ||
self.connection_thread = Thread(target=self.rewind_behavior) | ||
self.connection_thread.start() | ||
|
||
def change_state(self, state): | ||
if(state != self.state): | ||
self.state = state | ||
self.pub_mule_state.publish(String(data=state)) | ||
|
||
# Subscribed to /odometry/local | ||
def odom_listener(self, msg:Odometry): | ||
self.currentPos = PoseStamped() | ||
self.currentPos.header.frame_id = msg.header.frame_id | ||
self.currentPos.header.stamp = msg.header.stamp | ||
self.currentPos.pose = msg.pose.pose | ||
|
||
# Subscribed to /tracking/state, handles state/figure changes | ||
def state_listener(self, msg:String): | ||
if self.state != msg.data: | ||
returnState = msg.data | ||
match msg.data: | ||
case consts.FIGURE_TPOSE: | ||
if self.state == consts.START: | ||
self.startPoint = self.currentPos | ||
self.navigator.setInitialPose(self.startPoint) | ||
self.get_logger().info(f'Mule behavior engaged, point A noted at coordonates {self.startPoint.pose.position}') | ||
returnState = consts.FOLLOWING | ||
|
||
case consts.FIGURE_IDLE: | ||
if self.state != consts.REWIND and self.state != consts.PROCESSED_REWIND: | ||
returnState=self.state | ||
|
||
case consts.FIGURE_UP: | ||
if self.state != consts.REWIND and self.state != consts.PROCESSED_REWIND: | ||
if self.state == consts.PAUSE: returnState = consts.FOLLOWING | ||
else: returnState = consts.PAUSE | ||
|
||
case consts.FIGURE_BUCKET: | ||
if self.state != consts.REWIND and self.state != consts.PROCESSED_REWIND: | ||
self.endpoint = self.currentPos | ||
returnState = consts.REWIND | ||
|
||
case _: | ||
self.get_logger().error(f'Unimplemented pose returned: {msg.data}') | ||
|
||
self.change_state(returnState) | ||
|
||
# runs on a thread continuously | ||
def rewind_behavior(self): | ||
while self.state != consts.END: | ||
if self.state == consts.REWIND: | ||
self.get_logger().info(f'Rewind phase engaged, point B noted at coordonates {self.endpoint.pose.position}') | ||
self.change_state(consts.PROCESSED_REWIND) | ||
self.goal_point = self.startPoint | ||
while self.state == consts.PROCESSED_REWIND: | ||
self.navigate_to_goal_point() # returns only when goal reached/cancelled/failed | ||
result = self.navigator.getResult() | ||
goal = 'A' | ||
nextgoal = 'B' | ||
if self.goal_point == self.startPoint: | ||
goal = 'B' | ||
nextgoal = 'A' | ||
if result == TaskResult.SUCCEEDED: | ||
self.get_logger().info(f'Goal {goal} reached! Initiating nav towards point {nextgoal}') | ||
self.goal_point = self.startPoint if self.goal_point == self.endpoint else self.endpoint | ||
elif result == TaskResult.FAILED: | ||
self.get_logger().error(f'Failed to reach {goal}! Initiating nav towards point {nextgoal}') | ||
self.goal_point = self.startPoint if self.goal_point == self.endpoint else self.endpoint | ||
elif result == TaskResult.CANCELED: | ||
self.get_logger().error(f'Rewinding to point {goal} was cancelled! Initiating nav towards point {nextgoal}') | ||
else : | ||
self.get_logger().warn(f'nav2 navigator task returned with unknown TaskResult') | ||
|
||
# Tells navigator to go to current goal Point and processes feedback | ||
def navigate_to_goal_point(self): | ||
i = 0 | ||
self.navigator.goToPose(self.goal_point) | ||
while not self.navigator.isTaskComplete(): | ||
if self.state != consts.PROCESSED_REWIND: | ||
self.navigator.cancelTask() | ||
|
||
feedback:NavigateThroughPoses.Feedback = self.navigator.getFeedback() | ||
self.poses_remaining = feedback.number_of_poses_remaining | ||
if feedback and i % 5 == 0: | ||
# navigation timeout | ||
if Duration.from_msg(feedback.navigation_time) > 1.5 * Duration(seconds=feedback.estimated_time_remaining): | ||
self.navigator.cancelTask() | ||
i+=1 | ||
|
||
def main(args=None): | ||
rclpy.init(args=args) | ||
node = MuleBehavior() | ||
rclpy.spin(node) | ||
node.destroy_node() | ||
rclpy.shutdown() | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters