|
2 | 2 | import rclpy
|
3 | 3 | from rclpy.node import Node
|
4 | 4 | from rclpy.action import ActionClient
|
5 |
| -from sensor_msgs.msg import PointCloud2 |
6 |
| -import sensor_msgs_py.point_cloud2 as pc2 |
| 5 | +from geometry_msgs.msg import PoseArray |
7 | 6 | from geometry_msgs.msg import Point, PoseStamped
|
8 | 7 | from nav2_msgs.action import NavigateToPose
|
9 | 8 | import numpy as np
|
10 | 9 | from std_msgs.msg import Header
|
11 | 10 | from nav_msgs.msg import Odometry
|
12 |
| -import constants |
13 | 11 |
|
14 | 12 | # Node to handle when the robot loses connection with internet, stores waypoints to fall back to to try to get back connection
|
15 | 13 | class LostNetworkFallback(Node):
|
16 | 14 |
|
17 |
| - def __init__(self): |
18 |
| - self.NB_FALLBACKS=100 |
19 |
| - self.DISTANCE_BETWEEN_FALLBACKS = 2 # meters |
| 15 | + def __init__(self): |
| 16 | + self.DISTANCE_BETWEEN_FALLBACKS = 0.1 # meters |
20 | 17 | self.NB_MAX_FAILED_CONNS = 3
|
21 | 18 | self.nbFailedConns = 0
|
22 | 19 | self.fallbackIndex = 0
|
23 | 20 | self.PING_RATE = 1 # seconds
|
24 | 21 |
|
25 | 22 | self.ONLINE = 'ONLINE'
|
26 | 23 | self.OFFLINE = 'OFFLINE'
|
| 24 | + self.WAITING = 'WAITING' |
| 25 | + |
| 26 | + self.state = "ONLINE" |
| 27 | + self.traveling_to_fallback = False |
27 | 28 |
|
28 |
| - self.state = constants.OFFLINE |
29 | 29 | self.goal_handle = None
|
30 | 30 |
|
31 | 31 | super().__init__('lost_network_fallback')
|
| 32 | + self.get_logger().info('Initiating LostNetworkFallback node') |
32 | 33 | self.create_timer(self.PING_RATE, self.check_connection)
|
33 | 34 | self.odometry_subscription = self.create_subscription(
|
34 | 35 | Odometry, '/odometry/local', self.odometry_callback, 10)
|
35 |
| - self.action_client = ActionClient(self, LostNetworkFallback, 'lost_network_fallback') |
| 36 | + self.action_client = ActionClient(self, NavigateToPose, 'navigate_to_pose') |
36 | 37 |
|
37 |
| - #list of potential fallback positions, always will be of size self.NB_FALLBACKS, ascending order |
38 |
| - self.fallback_position = [Point(x=0.0, y=0.0, z=0.0) for _ in range(self.NB_FALLBACKS)] |
39 |
| - self.current_position = Point(x=0.0, y=0.0, z=0.0) |
| 38 | + #list of potential fallback positions, ascending order |
| 39 | + self.fallback_positions = [Point(x=0.0,y=0.0,z=0.0)] |
| 40 | + self.fallback_positions.pop() |
| 41 | + self.current_position: Point |
| 42 | + |
40 | 43 |
|
41 | 44 | # Check connection to google.com TODO: check connection to UI user instead
|
42 | 45 | def check_connection(self):
|
43 | 46 | try:
|
44 | 47 | socket.setdefaulttimeout(1) # in seconds
|
45 | 48 | socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect(("8.8.8.8", 53))
|
46 |
| - self.nbFailedConns = -1 |
47 |
| - if self.goal_handle != None: |
48 |
| - self.action_client._cancel_goal_async(self.goal_handle) |
49 |
| - self.goal_handle = None |
50 |
| - except: |
| 49 | + # if this following code runs, the connection was established |
| 50 | + self.nbFailedConns = 0 |
| 51 | + if self.state != self.ONLINE: |
| 52 | + self.get_logger().info('Connection re-established to server! terminating fallback state') |
| 53 | + self.fallback_positions[self.fallbackIndex:] |
| 54 | + if self.goal_handle != None: |
| 55 | + self.action_client._cancel_goal_async(self.goal_handle) |
| 56 | + self.goal_handle = None |
| 57 | + self.state = self.ONLINE |
| 58 | + except socket.error: |
51 | 59 | self.nbFailedConns += 1
|
52 |
| - if self.nbFailedConns == self.NB_MAX_FAILED_CONNS: |
53 |
| - # TODO: cancel current state |
54 |
| - self.navigate_to_fallback(self.fallbackIndex) |
55 |
| - |
56 |
| - def odometry_callback(self, msg : Odometry): |
57 |
| - # If robot at self.DISTANCE_BETWEEN_FALLBACKS distance or more from last fallback, add new fallback, delete the last one from the list |
58 |
| - self.current_position = Point(msg.pose.pose.position.x, msg.pose.pose.position.y, msg.pose.pose.position.z, ) |
59 |
| - if(np.linalg.norm(self.current_position-self.fallback_position[0]) >= self.DISTANCE_BETWEEN_FALLBACKS): |
60 |
| - self.fallback_position.insert(0, self.current_position) |
61 |
| - self.fallback_position.pop() |
| 60 | + if self.state != self.WAITING and self.nbFailedConns >= self.NB_MAX_FAILED_CONNS: |
| 61 | + self.state = self.OFFLINE |
| 62 | + if self.nbFailedConns == self.NB_MAX_FAILED_CONNS: |
| 63 | + self.get_logger().warn(f'Connection Error!!! ping to server failed {self.nbFailedConns} '+ |
| 64 | + 'times in a row, activating fallback state, the robot will try to retrace its steps.') |
| 65 | + # TODO: cancel current state of general state_handler |
| 66 | + if not self.traveling_to_fallback and self.fallbackIndex < len(self.fallback_positions): |
| 67 | + self.traveling_to_fallback = True |
| 68 | + self.navigate_to_fallback() |
62 | 69 |
|
63 |
| - def navigate_to_fallback(self, index): |
64 |
| - assert(index < self.DISTANCE_BETWEEN_FALLBACKS) |
| 70 | + |
| 71 | + def navigate_to_fallback(self): |
65 | 72 | # Prepare the goal message and send it
|
66 | 73 | goal_pose = PoseStamped()
|
67 | 74 | goal_pose.header = Header(frame_id='map')
|
68 | 75 |
|
69 |
| - goal_pose.pose.position = self.fallback_position[index] |
| 76 | + goal_pose.pose.position = self.fallback_positions[self.fallbackIndex] |
70 | 77 | goal_pose.pose.orientation.w = 1.0 # Assuming no orientation preference
|
71 | 78 |
|
72 |
| - self.get_logger().info(f'Navigating to fallback at ({self.fallback_position.__str__()})') |
| 79 | + self.get_logger().warn(f'Navigating to fallback at ({self.fallback_positions[self.fallbackIndex].__str__()})') |
73 | 80 | self.action_client.wait_for_server()
|
74 |
| - self.send_goal(goal_pose) |
75 |
| - |
76 |
| - def send_goal(self, goal_pose): |
| 81 | + |
77 | 82 | goal_msg = NavigateToPose.Goal()
|
78 | 83 | goal_msg.pose = goal_pose
|
79 |
| - self.goal_handle = self.action_client.send_goal_async(goal_msg,self.fallback_point_reached).result() |
80 |
| - self.get_logger().info('Goal sent to navigation system.') |
81 |
| - |
82 |
| - def fallback_point_reached(self): |
83 |
| - if self.state == self.OFFLINE: |
| 84 | + future = self.action_client.send_goal_async(goal_msg) |
| 85 | + self.goal_handle = future.result() |
| 86 | + future.add_done_callback(self.fallback_point_reached) |
| 87 | + |
| 88 | + |
| 89 | + def fallback_point_reached(self, _): |
| 90 | + self.traveling_to_fallback = False |
| 91 | + if self.state == self.OFFLINE and self.fallbackIndex < len(self.fallback_positions): |
| 92 | + self.get_logger().warn(f'Fallback {self.fallbackIndex} reached. Will attempt to reach next one if still offline.') |
84 | 93 | self.fallbackIndex+=1
|
85 |
| - self.navigate_to_fallback(self.fallbackIndex) |
| 94 | + if self.state == self.OFFLINE and self.fallbackIndex >= len(self.fallback_positions): |
| 95 | + self.state = self.WAITING |
| 96 | + self.get_logger().error(f'Last fallback reached, waiting and hoping for connection to come back :(') |
| 97 | + |
| 98 | + def odometry_callback(self, msg : Odometry): |
| 99 | + current_position = Point(x=msg.pose.pose.position.x, y=msg.pose.pose.position.y, z=msg.pose.pose.position.z) |
| 100 | + # Sets the first position |
| 101 | + if len(self.fallback_positions) == 0: |
| 102 | + self.fallback_positions = [current_position] |
| 103 | + self.get_logger().info(f'First fallback position initiated: {self.fallback_positions[0]}') |
| 104 | + |
| 105 | + # If robot at self.DISTANCE_BETWEEN_FALLBACKS distance or more from last fallback, add new fallback point |
| 106 | + if(self.state == self.ONLINE): |
| 107 | + if len(self.fallback_positions) == 0: |
| 108 | + self.fallback_positions.append(current_position) |
| 109 | + else: |
| 110 | + dist = [current_position.x-self.fallback_positions[0]._x, |
| 111 | + current_position.y-self.fallback_positions[0]._y, |
| 112 | + current_position.z-self.fallback_positions[0]._z] |
| 113 | + |
| 114 | + if(np.linalg.norm(dist) >= self.DISTANCE_BETWEEN_FALLBACKS): |
| 115 | + self.fallback_positions.insert(0, current_position) |
| 116 | + self.get_logger().info(f'New fallback position added: {self.fallback_positions}') |
86 | 117 |
|
87 | 118 | def main(args=None):
|
88 | 119 | rclpy.init(args=args)
|
|
0 commit comments