diff --git a/config/ouster_jackal_default.yaml b/config/ouster_jackal_default.yaml
index e9b4b08d..28aea4e8 100644
--- a/config/ouster_jackal_default.yaml
+++ b/config/ouster_jackal_default.yaml
@@ -55,6 +55,7 @@
origin_lng: -79.4661
origin_theta: 1.3
scale: 1.0
+ gps_topic: "/novatel/fix"
tactic:
enable_parallelization: true
preprocessing_skippable: false
diff --git a/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/goal/GoalForm.js b/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/goal/GoalForm.js
index 5de5531f..6eb59062 100644
--- a/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/goal/GoalForm.js
+++ b/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/goal/GoalForm.js
@@ -43,7 +43,7 @@ class GoalForm extends React.Component {
}
render() {
- const { waypointsMap, goalType, goalWaypoints, setNewGoalWaypoints } = this.props;
+ const { goalType, goalWaypoints, setNewGoalWaypoints } = this.props;
const { goal_form_open, goal_waypoints_str, goal_waypoints_invalid, pause_after, pause_before } = this.state;
return (
<>
diff --git a/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/graph/GraphMap.js b/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/graph/GraphMap.js
index 7fd8b6e0..f81a022c 100644
--- a/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/graph/GraphMap.js
+++ b/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/graph/GraphMap.js
@@ -20,7 +20,7 @@ import React from "react";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import "leaflet-rotatedmarker"; // enable marker rotation
-import { Card, IconButton, TextField } from "@mui/material";
+import { Card, IconButton, TextField, Switch, Box, FormControlLabel, Typography } from "@mui/material";
import { MapContainer, TileLayer, ZoomControl, ImageOverlay, Marker, Popup } from "react-leaflet";
import { kdTree } from "kd-tree-javascript";
@@ -31,7 +31,6 @@ import GoalManager from "../goal/GoalManager";
import TaskQueue from "../task_queue/TaskQueue";
import NewGoalWaypointSVG from "../../images/new-goal-waypoint.svg";
-import RunningGoalWaypointSVG from "../../images/running-goal-waypoint.svg";
import RobotIconSVG from "../../images/arrow.svg";
import TargetIconSVG from "../../images/arrow-merge.svg";
import SelectorCenterSVG from "../../images/selector-center.svg";
@@ -47,9 +46,12 @@ import AddIcon from "@mui/icons-material/Add";
import DeleteIcon from "@mui/icons-material/Delete";
/// pose graph constants
-const ROUTE_TYPE_COLOR = ["#ff0000", "#fd4a18", "#ffa500", "#fecd29", "#ffff00", "#6aaf1e", "#008000"];
+const ROUTE_TYPE_COLOR = ["#ff0000", "#fd4a18", "#ffa500", "#fecd29", "#ffff00", "#6aaf1e", "#008000", "#000000"];
+const ROUTE_TYPE_WIDTH = [0.4, 1.0, 2.0, 3.0, 4.0, 5.0, 20.0, 0.05];
+const ROUTE_TYPE_OPACITY = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.9];
+const ANNOTATE_LINE_COLOR = "#000000";
+const ANNOTATE_LINE_WIDTH = 0.5;
const GRAPH_OPACITY = 0.9;
-const GRAPH_WEIGHT = 7;
/// robot constants
const ROBOT_OPACITY = 0.8;
@@ -73,11 +75,6 @@ const NEW_GOAL_WAYPOINT_ICON = new L.Icon({
iconAnchor: [15, 30],
iconSize: new L.Point(30, 30),
});
-const RUNNING_GOAL_WAYPOINT_ICON = new L.Icon({
- iconUrl: RunningGoalWaypointSVG,
- iconAnchor: [15, 30],
- iconSize: new L.Point(30, 30),
-});
const WAYPOINT_OPACITY = 0.9;
/// selector constants
@@ -143,6 +140,9 @@ class GraphMap extends React.Component {
move_robot_vertex: { lng: 0, lat: 0, id: -1 },
// map center
map_center: {lat: 43.78220, lng: -79.4661},
+ /// whether the path selection will be the whole path (for annotation)
+ annotate_full: false,
+ in_select_mode: false,
};
this.fetchMapCenter()
/// leaflet map
@@ -309,7 +309,6 @@ class GraphMap extends React.Component {
const {
server_state,
waypoints_map,
- display_waypoints_map,
goals,
curr_goal_idx,
new_goal_type,
@@ -366,6 +365,31 @@ class GraphMap extends React.Component {
// move robot
moveRobotVertex={move_robot_vertex}
/>
+
+ this.setState({annotate_full: !this.state.annotate_full})}
+ inputProps={{ 'aria-label': 'controlled' }}
+ />
+ } label={
+
+ SELECT FULL PATH
+
+ }
+ />
+
{
+ this.fixed_routes.forEach((route) => {
+ route.polyline.setStyle({
+ color: ROUTE_TYPE_COLOR[route.type % ROUTE_TYPE_COLOR.length],
+ weight: this.metres2pix(ROUTE_TYPE_WIDTH[route.type % ROUTE_TYPE_COLOR.length]),
+ opacity: ROUTE_TYPE_OPACITY[route.type % ROUTE_TYPE_COLOR.length],
+ lineCap: "butt",
+ })
+ });
+ this.active_routes.forEach((route) => {
+ route.polyline.setStyle({
+ color: ROUTE_TYPE_COLOR[route.type % ROUTE_TYPE_COLOR.length],
+ weight: this.metres2pix(ROUTE_TYPE_WIDTH[route.type % ROUTE_TYPE_COLOR.length]),
+ opacity: ROUTE_TYPE_OPACITY[route.type % ROUTE_TYPE_COLOR.length],
+ lineCap: "butt",
+ })
+ });
+ }
+
/** @brief Leaflet map creationg callback */
mapCreatedCallback(map) {
console.debug("Leaflet map created.");
@@ -420,6 +463,8 @@ class GraphMap extends React.Component {
//
this.map.on("click", this.handleMapClick.bind(this));
//
+ this.map.on("zoomend", this.zoomEnd, this);
+ //
this.fetchGraphState(true);
}
@@ -469,7 +514,6 @@ class GraphMap extends React.Component {
if (this.state.new_goal_type === "repeat") {
this.setState({ new_goal_waypoints: [...this.state.new_goal_waypoints, best.target.id] });
}
- console.log(this.state.waypoints_map);
}
}
@@ -486,37 +530,42 @@ class GraphMap extends React.Component {
fetchGraphState(center = false) {
console.info("Fetching the current pose graph state (full).");
fetchWithTimeout("/vtr/graph")
- .then((response) => {
- if (response.status !== 200) throw new Error("Failed to fetch pose graph state: " + response.status);
- response.json().then((data) => {
- console.info("Received the pose graph state (full): ", data);
- this.loadGraphState(data, center);
- // fetch robot and following route after graph has been set
- this.fetchRobotState();
- this.fetchFollowingRoute();
- this.fetchServerState();
- });
- })
- .catch((error) => {
- console.error(error);
+ .then((response) => {
+ if (response.status !== 200) throw new Error("Failed to fetch pose graph state: " + response.status);
+ response.json().then((data) => {
+ console.info("Received the pose graph state (full): ", data);
+ this.loadGraphState(data, center);
+ // fetch robot and following route after graph has been set
+ this.fetchRobotState();
+ this.fetchFollowingRoute();
+ this.fetchServerState();
});
+ })
+ .catch((error) => {
+ console.error(error);
+ });
}
-
+
graphStateCallback(graph_state) {
console.info("Received graph state: ", graph_state);
this.loadGraphState(graph_state);
}
-
+
/** @brief Helper function to convert a pose graph route to a leaflet polyline, and add it to map */
route2Polyline(route) {
// fixed_routes format: [{type: 0, ids: [id, ...]}, ...]
let color = ROUTE_TYPE_COLOR[route.type % ROUTE_TYPE_COLOR.length];
- let width = route.type + 2
let latlngs = route.ids.map((id) => {
let v = this.id2vertex.get(id);
return [v.lat, v.lng];
});
- let polyline = L.polyline(latlngs, { color: color, opacity: GRAPH_OPACITY, weight: width, pane: "graph" });
+ let polyline = L.polyline(latlngs, {
+ color: color,
+ weight: this.metres2pix(ROUTE_TYPE_WIDTH[route.type % ROUTE_TYPE_COLOR.length]),
+ opacity: ROUTE_TYPE_OPACITY[route.type % ROUTE_TYPE_COLOR.length],
+ pane: "graph",
+ lineCap: "butt",
+ });
polyline.addTo(this.map);
return polyline;
}
@@ -538,24 +587,30 @@ class GraphMap extends React.Component {
wps_map.set(v.id, v.name);
}
});
- this.setState({waypoints_map: wps_map, display_waypoints_map: wps_map});
+ this.setState({waypoints_map: wps_map, display_waypoints_map: wps_map});
this.kdtree = new kdTree(graph.vertices, (a, b) => b.distanceTo(a), ["lat", "lng"]);
// fixed routes
this.fixed_routes.forEach((route) => {
route.polyline.remove();
});
- this.fixed_routes = graph.fixed_routes.map((route) => {
+ this.fixed_routes = graph.fixed_routes.flatMap((route) => {
let polyline = this.route2Polyline(route);
- return { ...route, polyline: polyline };
+ let route_centre = structuredClone(route);
+ route_centre.type = 7;
+ let polyline_centre = this.route2Polyline(route_centre);
+ return [{ ...route, polyline: polyline}, {...route_centre, polyline: polyline_centre}];
});
// active routes
this.active_routes.forEach((route) => {
route.polyline.remove();
});
- this.active_routes = graph.active_routes.map((route) => {
+ this.active_routes = graph.active_routes.flatMap((route) => {
let polyline = this.route2Polyline(route);
- return { ...route, polyline: polyline };
+ let route_centre = structuredClone(route);
+ route_centre.type = 7;
+ let polyline_centre = this.route2Polyline(route_centre);
+ return [{ ...route, polyline: polyline}, {...route_centre, polyline: polyline_centre}];
});
// center set to root vertex
@@ -730,14 +785,23 @@ class GraphMap extends React.Component {
let active_route = { ids: [vf.id], type: vf.type };
active_route = { ...active_route, polyline: this.route2Polyline(active_route) };
this.active_routes.push(active_route);
+ let active_route_centre = { ids: [vf.id], type: 7 };
+ active_route_centre = { ...active_route_centre, polyline: this.route2Polyline(active_route_centre) };
+ this.active_routes.push(active_route_centre);
}
- let active_route = this.active_routes[this.active_routes.length - 1];
+ let active_route = this.active_routes[this.active_routes.length - 2];
active_route.ids.push(vt.id);
active_route.polyline.addLatLng([vt.lat, vt.lng]);
+ let active_route_centre = this.active_routes[this.active_routes.length - 1];
+ active_route_centre.ids.push(vt.id);
+ active_route_centre.polyline.addLatLng([vt.lat, vt.lng]);
if (active_route.type !== vt.type) {
let new_active_route = { ids: [vt.id], type: vt.type };
new_active_route = { ...new_active_route, polyline: this.route2Polyline(new_active_route) };
this.active_routes.push(new_active_route);
+ let new_active_route_centre = { ids: [vf.id], type: 7 };
+ new_active_route_centre = { ...new_active_route_centre, polyline: this.route2Polyline(new_active_route_centre) };
+ this.active_routes.push(new_active_route_centre);
}
}
@@ -906,9 +970,17 @@ class GraphMap extends React.Component {
let interm_pos = { s: null, c: null, e: null };
let getSelectedPath = () => {
- let paths = this.breadthFirstSearch(selector.vertex.c.id, [selector.vertex.s.id, selector.vertex.e.id]);
- paths[0].reverse();
- return paths[0].concat(paths[1].slice(1));
+ if (!this.state.annotate_full) {
+ let paths = this.breadthFirstSearch(selector.vertex.c.id, [selector.vertex.s.id, selector.vertex.e.id]);
+ paths[0].reverse();
+ return paths[0].concat(paths[1].slice(1));
+ }
+ else {
+ let end_id = Math.max(...Array.from(this.id2vertex.keys()));
+ let paths = this.breadthFirstSearch(selector.vertex.c.id, [0, end_id]);
+ paths[0].reverse();
+ return paths[0].concat(paths[1].slice(1));
+ }
};
let getRotationAngle = (p1, p2) => {
@@ -969,6 +1041,7 @@ class GraphMap extends React.Component {
// set up and add the center marker
let closest_vertices = this.kdtree.nearest(center_pos, 1);
selector.vertex.c = closest_vertices ? closest_vertices[0][0] : center_pos;
+ selector.vertex.c = this.id2vertex.get(0);
selector.marker.c = L.marker(selector.vertex.c, {
draggable: true,
icon: SELECTOR_CENTER_ICON,
@@ -1068,6 +1141,7 @@ class GraphMap extends React.Component {
/// Annotate Route
startAnnotateRoute() {
console.info("Start annotating route");
+ this.setState({in_select_mode: true});
this.annotate_route_selector = { marker: { s: null, c: null, e: null }, vertex: { s: null, c: null, e: null } };
this.annotate_polyline = null;
let selectPathCallback = (ids) => {
@@ -1078,10 +1152,11 @@ class GraphMap extends React.Component {
});
if (this.annotate_polyline === null) {
this.annotate_polyline = L.polyline(latlngs, {
- color: ROUTE_TYPE_COLOR[0],
+ color: ANNOTATE_LINE_COLOR,
opacity: GRAPH_OPACITY,
- weight: GRAPH_WEIGHT,
+ weight: this.metres2pix(ANNOTATE_LINE_WIDTH),
pane: "graph",
+ lineCap: "butt",
});
this.annotate_polyline.addTo(this.map);
} else {
@@ -1090,10 +1165,28 @@ class GraphMap extends React.Component {
});
};
this.createSelector(this.annotate_route_selector, selectPathCallback);
+
+ this.annotateRouteZoomEnd = () => {
+
+ let type = this.state.annotate_route_type;
+ this.setState({ annotate_route_type: type }, () =>
+ this.annotate_polyline.setStyle({
+ color: ANNOTATE_LINE_COLOR,
+ opacity: GRAPH_OPACITY,
+ weight: this.metres2pix(ANNOTATE_LINE_WIDTH),
+ pane: "graph",
+ lineCap: "butt",
+ })
+ );
+ };
+ this.map.on("zoomend", this.annotateRouteZoomEnd, this);
}
finishAnnotateRoute() {
console.info("Finish annotating route");
+ this.map.off("zoomend", this.annotateRouteZoomEnd, this);
+ this.moveGraphZoomEnd = null;
+
if (this.annotate_polyline !== null) {
this.annotate_polyline.remove();
this.annotate_polyline = null;
@@ -1102,12 +1195,19 @@ class GraphMap extends React.Component {
this.removeSelector(this.annotate_route_selector);
this.annotate_route_selector = null;
}
+ this.setState({in_select_mode: false});
}
handleAnnotateRouteSliderChange(type) {
if (this.annotate_polyline === null) return;
this.setState({ annotate_route_type: type }, () =>
- this.annotate_polyline.setStyle({ color: ROUTE_TYPE_COLOR[type % ROUTE_TYPE_COLOR.length] })
+ this.annotate_polyline.setStyle({
+ color: ANNOTATE_LINE_COLOR,
+ opacity: GRAPH_OPACITY,
+ weight: this.metres2pix(ANNOTATE_LINE_WIDTH),
+ pane: "graph",
+ lineCap: "butt",
+ })
);
}
@@ -1243,7 +1343,7 @@ class GraphMap extends React.Component {
let new_diff_rot_p = new_rot_loc_p.subtract(trans_loc_p);
let rot_mag = Math.sqrt(Math.pow(diff_rot_p.x, 2) + Math.pow(diff_rot_p.y, 2));
let new_rot_mag = Math.sqrt(Math.pow(new_diff_rot_p.x, 2) + Math.pow(new_diff_rot_p.y, 2));
- if (new_rot_mag == 0) return;
+ if (new_rot_mag === 0) return;
new_rot_loc_p = trans_loc_p.add(new_diff_rot_p.multiplyBy(rot_mag / new_rot_mag));
// Scale marker moves with rot marker
@@ -1280,7 +1380,7 @@ class GraphMap extends React.Component {
let new_diff_scale_p = new_scale_loc_p.subtract(trans_loc_p);
let scale_mag = Math.sqrt(Math.pow(diff_scale_p.x, 2) + Math.pow(diff_scale_p.y, 2));
let new_scale_mag = Math.sqrt(Math.pow(new_diff_scale_p.x, 2) + Math.pow(new_diff_scale_p.y, 2));
- if (new_scale_mag == 0) return;
+ if (new_scale_mag === 0) return;
new_scale_loc_p = trans_loc_p.add(diff_scale_p.multiplyBy(new_scale_mag / scale_mag));
// Scale marker moves with rot marker
@@ -1333,6 +1433,14 @@ class GraphMap extends React.Component {
this.map.getPane("graph").style.zIndex = -100;
};
this.moveGraphZoomEnd = () => {
+ this.fixed_routes.forEach((route) => {
+ route.polyline = this.route2Polyline(route);
+ });
+ this.active_routes.forEach((route) => {
+ route.polyline = this.route2Polyline(route);
+ });
+ // display markers
+ this.scale_marker.setOpacity(MOVE_GRAPH_MARKER_OPACITY2);
// Maintain the relative position of rotMarker and transMarker
let trans_loc_p = this.map.latLngToLayerPoint(trans_loc);
let new_rot_loc_p = trans_loc_p.add(diff_rot_p_zoom);
@@ -1422,6 +1530,14 @@ class GraphMap extends React.Component {
// add the robot marker back
this.robot_marker.marker.addTo(this.map);
}
+
+ metres2pix(metres) {
+ let origin = L.latLng(this.id2vertex.get(this.root_vid));
+ let origin_plus_metre = this.map.latLngToLayerPoint(L.latLng(origin.lat - 1, origin.lng));
+ let metre_standard = origin_plus_metre.subtract(this.map.latLngToLayerPoint(origin));
+ let factor = metre_standard.y * 0.000009;
+ return metres * factor;
+ }
}
-export default GraphMap;
+export default GraphMap;
\ No newline at end of file
diff --git a/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/setup/StartMenu.js b/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/setup/StartMenu.js
index 434bba4b..c564b7d8 100644
--- a/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/setup/StartMenu.js
+++ b/main/src/vtr_gui/vtr_gui/vtr-gui/src/components/setup/StartMenu.js
@@ -92,6 +92,7 @@ class StartMenu extends React.Component {constructor(props) {
width: "90%",
margin: "5% 10% 0% 0%",
}}
+ alt="ASRL"
/>
#include
+#include
+#include
#include "rclcpp/rclcpp.hpp"
@@ -28,6 +31,7 @@
#include "vtr_navigation_msgs/msg/annotate_route.hpp"
#include "vtr_navigation_msgs/msg/update_waypoint.hpp"
+#include "vtr_navigation_msgs/msg/mission_command.hpp"
#include "vtr_navigation_msgs/msg/graph_route.hpp"
#include "vtr_navigation_msgs/msg/graph_state.hpp"
#include "vtr_navigation_msgs/msg/graph_update.hpp"
@@ -38,6 +42,7 @@
#include "vtr_navigation_msgs/srv/robot_state.hpp"
#include "vtr_pose_graph_msgs/msg/map_info.hpp"
#include "vtr_pose_graph_msgs/srv/map_info.hpp"
+#include "sensor_msgs/msg/nav_sat_fix.hpp"
namespace vtr {
namespace navigation {
@@ -65,6 +70,8 @@ class GraphMapServer : public tactic::Graph::Callback,
using MoveGraphMsg = vtr_navigation_msgs::msg::MoveGraph;
using AnnotateRouteMsg = vtr_navigation_msgs::msg::AnnotateRoute;
using UpdateWaypointMsg = vtr_navigation_msgs::msg::UpdateWaypoint;
+ using MissionCommandMsg = vtr_navigation_msgs::msg::MissionCommand;
+ using GoalHandle = vtr_navigation_msgs::msg::GoalHandle;
using VertexPtr = tactic::Graph::VertexPtr;
using EdgePtr = tactic::Graph::EdgePtr;
@@ -76,6 +83,8 @@ class GraphMapServer : public tactic::Graph::Callback,
using GraphWeakPtr = tactic::Graph::WeakPtr;
using GraphBasePtr = tactic::GraphBase::Ptr;
+ using NavSatFix = sensor_msgs::msg::NavSatFix;
+
using VertexId2TransformMap = std::unordered_map;
using VertexId2IdxMap = std::unordered_map;
using ProjectVertex =
@@ -105,7 +114,9 @@ class GraphMapServer : public tactic::Graph::Callback,
std::shared_ptr response) const;
void moveGraphCallback(const MoveGraphMsg::ConstSharedPtr msg);
void annotateRouteCallback(const AnnotateRouteMsg::ConstSharedPtr msg);
+ void poseCallback(const NavSatFix::ConstSharedPtr msg);
void updateWaypointCallback(const UpdateWaypointMsg::ConstSharedPtr msg);
+ void updateMissionCallback(const MissionCommandMsg::ConstSharedPtr msg);
private:
/// these functions are called with graph mutex locked
@@ -135,6 +146,10 @@ class GraphMapServer : public tactic::Graph::Callback,
void updateRobotProjection();
+ float haversineDist(float lat1, float lat2, float lon1, float lon2);
+ float deltaLongToMetres(float lat1, float lat2, float lon1, float lon2);
+ float deltaLatToMetres(float lat1, float lat2);
+
private:
/** \brief Graph that generates the callbacks */
GraphWeakPtr graph_;
@@ -149,6 +164,8 @@ class GraphMapServer : public tactic::Graph::Callback,
/** \brief Vertices and routes */
GraphState graph_state_;
+ uint goal_;
+
/**
* \brief Cached robot persistent & target localization used after graph
* relaxation and calibration
@@ -189,6 +206,13 @@ class GraphMapServer : public tactic::Graph::Callback,
rclcpp::Subscription::SharedPtr move_graph_sub_;
rclcpp::Subscription::SharedPtr annotate_route_sub_;
rclcpp::Subscription::SharedPtr update_waypoint_sub_;
+ rclcpp::Subscription::SharedPtr mission_command_sub_;
+
+ rclcpp::Subscription::SharedPtr pose_pub_;
+
+ const float dist_thres_ = 0.1;
+ bool initial_pose_set_ = false;
+ std::deque> gps_coords_ {2};
};
class RvizGraphMapServer : public GraphMapServer,
diff --git a/main/src/vtr_navigation/src/graph_map_server.cpp b/main/src/vtr_navigation/src/graph_map_server.cpp
index 8ef88fd7..871eb70d 100644
--- a/main/src/vtr_navigation/src/graph_map_server.cpp
+++ b/main/src/vtr_navigation/src/graph_map_server.cpp
@@ -66,6 +66,7 @@ void GraphMapServer::start(const rclcpp::Node::SharedPtr& node,
const auto lng = node->declare_parameter("graph_projection.origin_lng", -79.466092);
const auto theta = node->declare_parameter("graph_projection.origin_theta", 0.);
const auto scale = node->declare_parameter("graph_projection.scale", 1.);
+ const auto pose_pub_topic_ = node->declare_parameter("graph_projection.gps_topic", "/novatel/fix");
/// Publishers and services
callback_group_ = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
@@ -80,14 +81,18 @@ void GraphMapServer::start(const rclcpp::Node::SharedPtr& node,
following_route_pub_ = node->create_publisher("following_route", 10);
following_route_srv_ = node->create_service("following_route_srv", std::bind(&GraphMapServer::followingRouteSrvCallback, this, std::placeholders::_1, std::placeholders::_2), rmw_qos_profile_services_default, callback_group_);
+
// graph manipulation
auto sub_opt = rclcpp::SubscriptionOptions();
sub_opt.callback_group = callback_group_;
annotate_route_sub_ = node->create_subscription("annotate_route", rclcpp::QoS(10), std::bind(&GraphMapServer::annotateRouteCallback, this, std::placeholders::_1), sub_opt);
move_graph_sub_ = node->create_subscription("move_graph", rclcpp::QoS(10), std::bind(&GraphMapServer::moveGraphCallback, this, std::placeholders::_1), sub_opt);
update_waypoint_sub_ = node->create_subscription("update_waypoint", rclcpp::QoS(10), std::bind(&GraphMapServer::updateWaypointCallback, this, std::placeholders::_1), sub_opt);
+ mission_command_sub_ = node->create_subscription("mission_command", rclcpp::QoS(10), std::bind(&GraphMapServer::updateMissionCallback, this, std::placeholders::_1), sub_opt);
// clang-format on
+ pose_pub_ = node->create_subscription(pose_pub_topic_, rclcpp::QoS(10), std::bind(&GraphMapServer::poseCallback, this, std::placeholders::_1), sub_opt);
+
// initialize graph mapinfo if working on a new map
auto map_info = graph->getMapInfo();
if (!map_info.set) {
@@ -151,6 +156,11 @@ void GraphMapServer::followingRouteSrvCallback(
response->following_route = following_route_;
}
+void GraphMapServer::updateMissionCallback(
+ const MissionCommandMsg::ConstSharedPtr msg) {
+ goal_ = msg->goal_handle.type;
+}
+
void GraphMapServer::annotateRouteCallback(
const AnnotateRouteMsg::ConstSharedPtr msg) {
CLOG(DEBUG, "navigation.graph_map_server")
@@ -206,6 +216,52 @@ void GraphMapServer::moveGraphCallback(const MoveGraphMsg::ConstSharedPtr msg) {
graph_state_pub_->publish(graph_state_);
}
+float GraphMapServer::haversineDist(float lat1, float lat2, float lon1, float lon2) {
+ float R = 6378137.0; // Radius of earth in metres
+ float dLat = lat2 * M_PI / 180 - lat1 * M_PI / 180;
+ float dLon = lon2 * M_PI / 180 - lon1 * M_PI / 180;
+ float a = std::sin(dLat/2) * std::sin(dLat/2) +
+ std::cos(lat1 * M_PI / 180) * std::cos(lat2 * M_PI / 180) *
+ std::sin(dLon/2) * std::sin(dLon/2);
+ float c = 2 * atan2(std::sqrt(a), std::sqrt(1-a));
+ float d = R * c;
+ return d;
+}
+
+float GraphMapServer::deltaLongToMetres(float lat1, float lat2, float lon1, float lon2) {
+ float avg_lat = (lat1 + lat2) / 2;
+ float dist = (lon2 - lon1) * std::cos(avg_lat * M_PI / 180) * 111000;
+ return dist;
+}
+
+float GraphMapServer::deltaLatToMetres(float lat1, float lat2) {
+ float dist = (lat2 - lat1) * 111000;
+ return dist;
+}
+
+void GraphMapServer::poseCallback(const NavSatFix::ConstSharedPtr msg) {
+ gps_coords_.push_back(std::make_pair(msg->longitude, msg->latitude));
+ gps_coords_.pop_front();
+ const auto graph = getGraph();
+ auto map_info = graph->getMapInfo();
+
+ auto prev_coords = gps_coords_[0];
+ auto dist = haversineDist(prev_coords.second, msg->latitude, prev_coords.first, msg->longitude);
+
+ if (goal_ == GoalHandle::TEACH && gps_coords_.size() >= 2 && !initial_pose_set_ && dist > dist_thres_) {
+ auto delta_lng = deltaLongToMetres(prev_coords.second, msg->latitude, prev_coords.first, msg->longitude);
+ auto delta_lat = deltaLatToMetres(prev_coords.second, msg->latitude);
+
+ map_info.root_vid = 0;
+ map_info.lng = (float) msg->longitude;
+ map_info.lat = (float) msg->latitude;
+ map_info.theta = (float) atan2(delta_lat, delta_lng);
+ map_info.set = true;
+ graph->setMapInfo(map_info);
+ initial_pose_set_ = true;
+ }
+}
+
void GraphMapServer::updateWaypointCallback(
const UpdateWaypointMsg::ConstSharedPtr msg) {
CLOG(DEBUG, "navigation.graph_map_server")