Skip to content

Commit 75518e6

Browse files
Fix auton latency (#507)
1 parent 4de475b commit 75518e6

File tree

7 files changed

+158
-49
lines changed

7 files changed

+158
-49
lines changed

launch/basestation.launch

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
<node name="rviz" pkg="rviz" type="rviz" args="-d $(arg rvizconfig)" required="true"/>
1616
<node name="topic_services" pkg="mrover" type="topic_services.py"/>
1717

18+
<!-- Run the auton enable forwarding program -->
19+
<node name="auton_enable_forward" pkg="mrover" type="auton_enable_forward.py" output="screen"></node>
20+
1821
<!-- network monitor node-->
1922
<node name="network_monitor" pkg="mrover" type="network_monitor.py"></node>
2023
</launch>

scripts/debug_service.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66

77
from typing import Any
88
import rospy
9-
from std_srvs.srv import Trigger, TriggerResponse
9+
from mrover.srv import PublishEnableAuton, PublishEnableAutonResponse
1010

1111
# Change these values for the service name and type definition to test different values
12-
SERVICE_NAME = "mcu_board_reset"
13-
SERVICE_TYPE = Trigger
12+
SERVICE_NAME = "enable_auton"
13+
SERVICE_TYPE = PublishEnableAuton
1414

1515

1616
def print_service_request(service_request: Any):
1717
rospy.loginfo(service_request)
18-
return TriggerResponse(success=True, message="")
18+
return PublishEnableAutonResponse(success=True)
1919

2020

2121
def main():

src/esw/network_monitor.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
# Credit: https://stackoverflow.com/questions/15616378/python-network-bandwidth-monitor
1010

11+
SLEEP_TIME_S = 3
12+
1113

1214
def get_bytes(t: str, interface: str) -> int:
1315
with open("/sys/class/net/" + interface + "/statistics/" + t + "_bytes", "r") as f:
@@ -27,21 +29,29 @@ def get_iface(default: str) -> Optional[str]:
2729
return eth_iface
2830

2931

30-
if __name__ == "__main__":
32+
def main():
3133
rospy.init_node("network_monitor")
3234
iface = get_iface(rospy.get_param("default_network_iface"))
3335

3436
if iface is not None:
3537
pub = rospy.Publisher("network_bandwidth", NetworkBandwidth, queue_size=1)
36-
while True:
38+
39+
while not rospy.is_shutdown():
3740
tx1 = get_bytes("tx", iface)
3841
rx1 = get_bytes("rx", iface)
39-
time.sleep(1)
42+
time.sleep(SLEEP_TIME_S)
4043
tx2 = get_bytes("tx", iface)
4144
rx2 = get_bytes("rx", iface)
42-
tx_speed = (tx2 - tx1) * 8.0 / 1000000.0 # Mbps
43-
rx_speed = (rx2 - rx1) * 8.0 / 1000000.0 # Mbps
45+
46+
# Convert to Mbps
47+
tx_speed = (tx2 - tx1) * 8.0 / (SLEEP_TIME_S * 1000000.0)
48+
rx_speed = (rx2 - rx1) * 8.0 / (SLEEP_TIME_S * 1000000.0)
49+
4450
pub.publish(NetworkBandwidth(tx_speed, rx_speed))
4551

4652
else:
4753
rospy.logerr(f"Node {rospy.get_name()} cannot locate valid network interface.")
54+
55+
56+
if __name__ == "__main__":
57+
main()

src/navigation/navigation.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from post_backup import PostBackupState, PostBackupTransitions
1919
from smach.log import set_loggers
2020
from smach.log import loginfo, logwarn, logerr
21+
from std_msgs.msg import String
2122

2223

2324
class Navigation(threading.Thread):
@@ -34,6 +35,7 @@ def __init__(self, context: Context):
3435
self.context = context
3536
self.sis = smach_ros.IntrospectionServer("", self.state_machine, "/SM_ROOT")
3637
self.sis.start()
38+
self.state_publisher = rospy.Publisher("/nav_state", String, queue_size=1)
3739
with self.state_machine:
3840
self.state_machine.add(
3941
"OffState", OffState(self.context), transitions=self.get_transitions(OffStateTransitions)
@@ -70,12 +72,19 @@ def __init__(self, context: Context):
7072
PostBackupState(self.context),
7173
transitions=self.get_transitions(PostBackupTransitions),
7274
)
75+
rospy.Timer(rospy.Duration(0.1), self.publish_state)
7376

7477
def get_transitions(self, transitions_enum):
7578
transition_dict = {transition.name: transition.value for transition in transitions_enum}
7679
transition_dict["off"] = "OffState" # logic for switching to offstate is built into OffState
7780
return transition_dict
7881

82+
def publish_state(self, event=None):
83+
with self.state_machine:
84+
active_states = self.state_machine.get_active_states()
85+
if len(active_states) > 0:
86+
self.state_publisher.publish(active_states[0])
87+
7988
def run(self):
8089
self.state_machine.execute()
8190

src/teleop/auton_enable_forward.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python3
2+
import rospy
3+
import threading
4+
5+
from mrover.msg import EnableAuton
6+
from mrover.srv import PublishEnableAuton, PublishEnableAutonRequest
7+
8+
from typing import Optional
9+
10+
11+
class AutonBridge:
12+
"""
13+
A class that manages the state of auton enable. This is necessary because Vue does not have a way to act as a
14+
persistent service client. We need persistence in order to quickly enable or disable autonomy, so this program
15+
acts as a bridge to send the messages.
16+
"""
17+
18+
service_client: rospy.ServiceProxy
19+
"""
20+
A persistent client to give navigation it's enable and course requests.
21+
"""
22+
23+
msg: Optional[EnableAuton]
24+
"""
25+
The last received message from the GUI. None if not available.
26+
"""
27+
28+
msg_lock: threading.Lock
29+
"""
30+
Mutex to access msg.
31+
"""
32+
33+
def __init__(self):
34+
"""
35+
Construct bridge object.
36+
"""
37+
self._connect_to_server()
38+
39+
self.msg = None
40+
self.msg_lock = threading.Lock()
41+
42+
def _connect_to_server(self):
43+
"""
44+
Create a service proxy, waiting as long as necessary for it to be advertised by auton.
45+
"""
46+
rospy.loginfo("Waiting for navigation to launch...")
47+
rospy.wait_for_service("enable_auton")
48+
49+
self.service_client = rospy.ServiceProxy("enable_auton", PublishEnableAuton, persistent=True)
50+
rospy.loginfo("Navigation service found!")
51+
52+
def handle_message(self, msg: EnableAuton) -> None:
53+
"""
54+
Receive an EnableAuton message from teleop and forward to navigation if it's updated.
55+
"""
56+
with self.msg_lock:
57+
# Guard against outdated messages.
58+
if self.msg == msg:
59+
return
60+
61+
# Attempt to make service request, updating msg state if successful.
62+
try:
63+
self.service_client(PublishEnableAutonRequest(msg))
64+
self.msg = msg
65+
66+
# Reconnect service client upon failure.
67+
except rospy.ServiceException as e:
68+
rospy.logerr(f"Could not forward enable auton message: {e}")
69+
70+
self.service_client.close()
71+
self._connect_to_server()
72+
73+
74+
def main():
75+
rospy.init_node("auton_enable_forward")
76+
77+
# Construct the bridge.
78+
bridge = AutonBridge()
79+
80+
# Configure subscriber.
81+
rospy.Subscriber("intermediate_enable_auton", EnableAuton, bridge.handle_message)
82+
83+
rospy.spin()
84+
85+
86+
if __name__ == "__main__":
87+
main()

src/teleop/gui/src/components/AutonTask.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ export default {
230230
created: function () {
231231
this.nav_status_sub = new ROSLIB.Topic({
232232
ros: this.$ros,
233-
name: "/smach/container_status",
234-
messageType: "smach_msgs/SmachContainerStatus"
233+
name: "/nav_state",
234+
messageType: "std_msgs/String"
235235
});
236236
237237
this.odom_sub = new ROSLIB.Topic({
@@ -268,7 +268,7 @@ export default {
268268
269269
this.nav_status_sub.subscribe((msg) => {
270270
// Callback for nav_status
271-
this.nav_status.nav_state_name = msg.active_states[0];
271+
this.nav_status.nav_state_name = msg.data;
272272
});
273273
274274
this.odom_sub.subscribe((msg) => {

src/teleop/gui/src/components/AutonWaypointEditor.vue

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ import _ from "lodash";
261261
import L from "leaflet";
262262
import ROSLIB from "roslib";
263263
264-
let interval;
264+
let stuck_interval, intermediate_publish_interval;
265265
266266
const WAYPOINT_TYPES = {
267267
NO_SEARCH: 0,
@@ -433,42 +433,53 @@ export default {
433433
},
434434
435435
beforeDestroy: function () {
436-
window.clearInterval(interval);
436+
window.clearInterval(stuck_interval);
437+
window.clearInterval(intermediate_publish_interval);
437438
this.autonEnabled = false;
438439
this.sendEnableAuton();
439440
},
440441
441442
created: function () {
442-
(this.course_pub = new ROSLIB.Service({
443+
this.course_pub = new ROSLIB.Topic({
443444
ros: this.$ros,
444-
name: "/enable_auton",
445-
serviceType: "mrover/PublishEnableAuton",
446-
})),
447-
(this.nav_status_sub = new ROSLIB.Topic({
448-
ros: this.$ros,
449-
name: "/smach/container_status",
450-
messageType: "smach_msgs/SmachContainerStatus",
451-
})),
452-
(this.rover_stuck_pub = new ROSLIB.Topic({
453-
ros: this.$ros,
454-
name: "/rover_stuck",
455-
messageType: "std_msgs/Bool",
456-
})),
457-
// Make sure local odom format matches vuex odom format
458-
(this.odom_format_in = this.odom_format);
445+
name: "/intermediate_enable_auton",
446+
messageType: "mrover/EnableAuton",
447+
});
448+
449+
this.nav_status_sub = new ROSLIB.Topic({
450+
ros: this.$ros,
451+
name: "/nav_state",
452+
messageType: "std_msgs/String",
453+
});
454+
455+
this.rover_stuck_pub = new ROSLIB.Topic({
456+
ros: this.$ros,
457+
name: "/rover_stuck",
458+
messageType: "std_msgs/Bool",
459+
});
460+
461+
// Make sure local odom format matches vuex odom format
462+
this.odom_format_in = this.odom_format;
459463
460464
this.nav_status_sub.subscribe((msg) => {
461-
if (msg.active_states[0] !== "OffState" && !this.autonEnabled) {
465+
// If still waiting for nav...
466+
if ((msg.data == "OffState" && this.autonEnabled) ||
467+
(msg.data !== "OffState" && !this.autonEnabled)) {
462468
return;
463469
}
470+
464471
this.waitingForNav = false;
465472
this.autonButtonColor = this.autonEnabled ? "green" : "red";
466473
});
467474
468475
// Interval for publishing stuck status for training data
469-
interval = window.setInterval(() => {
476+
stuck_interval = window.setInterval(() => {
470477
this.rover_stuck_pub.publish({ data: this.roverStuck });
471478
}, 100);
479+
480+
intermediate_publish_interval = window.setInterval(() => {
481+
this.sendEnableAuton();
482+
}, 1000);
472483
},
473484
474485
mounted: function () {
@@ -490,12 +501,9 @@ export default {
490501
}),
491502
492503
sendEnableAuton() {
493-
let course;
494-
495504
// If Auton Enabled send course
496505
if (this.autonEnabled) {
497-
course = {
498-
enable: true,
506+
this.course_pub.publish({
499507
// Map for every waypoint in the current route
500508
waypoints: _.map(this.route, (waypoint) => {
501509
const lat = waypoint.lat;
@@ -516,20 +524,12 @@ export default {
516524
id: parseInt(waypoint.id),
517525
};
518526
}),
519-
};
527+
enable: true
528+
});
520529
} else {
521530
// Else send false and no array
522-
course = {
523-
enable: false,
524-
waypoints: [],
525-
};
531+
this.course_pub.publish({waypoints: [], enable: false});
526532
}
527-
528-
const course_request = new ROSLIB.ServiceRequest({
529-
enableMsg: course,
530-
});
531-
532-
this.course_pub.callService(course_request, () => {});
533533
},
534534
535535
openModal: function () {
@@ -677,8 +677,8 @@ export default {
677677
678678
toggleAutonMode: function (val) {
679679
this.setAutonMode(val);
680-
// This will trigger the yellow "waiting for nav" state of the checkbox only if we are enabling the button
681-
this.autonButtonColor = val ? "yellow" : "red";
680+
// This will trigger the yellow "waiting for nav" state of the checkbox
681+
this.autonButtonColor = "yellow";
682682
this.waitingForNav = true;
683683
this.sendEnableAuton();
684684
},

0 commit comments

Comments
 (0)