Repository navigation
1.4.0
Added
- Traversability rules:
config/traversability.yamldecides from OSM tags which ways the robot may drive on (first matching rule wins, each with areasonthe operator sees in the node log) and what they cost.MapDataapplies the rules when a map is loaded for planning —exclude_waysis now the shortcut for rules that only denyhighwayvalues — andGraphPlannerweighs its edges by the same highway/surface cost tables as the grid planner through the sharedpathsolver.way_costhelper, while the reported route length stays geometric. The file is selectable per run (traversability_fileparameter,traversability:=,--traversability) and is part of the map and planner cache keys, mtime included - Offline route planning:
map_data.pathsolver.route.plan_route(the Planner screen's graph/grid planning as a library call, withdensify, failure reasons and snap distances),map_data.annotations(annotation-store merge without the web app,load_mapdata_with_annotations), themap_data_planCLI, theroute_plannerROS 2 action server with the newmap_data_interfacespackage (PlanRoute.action),route_planner.launch.py,GraphPlanner.snap_distanceandgpx.create_gpx_track. The viewer's/api/create_replannow delegates toplan_routeand reports areasonon failure config/route_planner.yaml: everyroute_plannerparameter in one documented file, loaded byroute_planner.launch.py(params_file:=takes an absolute path or a name inconfig/). The launch arguments now default to empty and are applied on top of the file, so an argument left unset keeps the file's value;highway_types:=footway,roadlets the graph planner use roads as well as footways.map_data.utils.launchholds the argument helperskeep_goal(default on) ends a route at the requested goal itself rather than at its projection onto the network, which can be tens of metres short; the final off-network leg is densified like the rest. The goal has its own snap limit (goal_max_snap_distance, 30 m, failing withsnap_too_far) separate from the start's 100 m, which is the robot's own fixexclude_highway(defaultsteps): stairs stay in the saved map for the viewer, butMapData.exclude_waysdrops them at load time andGraphPlannerfilters them for callers passing a raw map, so no route is ever planned over them- Explicit annotations switch —
--annotations auto|none|FILEand theannotationsparameter — because a store may delete a large part of the map (Stromovka's deletes 838 ways) and planning has to be able to opt out of it - Goal QR codes for Robotour:
map_data.utils.qrencodes a goal as a geo URI, and the viewer serves one per waypoint (/api/qr,/api/qr.svg) with a full-screen modal and PNG download osm_cloudgeodetic placement of map data via the ECEF → local TF: newtransform_mode(tf|auto|geodetic) andearth_frameparameters. In geodetic mode UTM points are converted to lat/lon → ECEF and placed inlocal_framethrough theearth_frame→local_frametransform (FP_ECEF→FP_ENU0on Helhest), which is exact where a UTM translation is off by grid convergence (5–8 m per km). Grid bounds use all four UTM corners and the frames are launch arguments- Latched
osm_cloudpublishing (the grid and intersections are published once at start-up and after parameter rebuilds via transient-local publishers;republish_periodrestores periodic re-publishing) andMapData.geometric_intersections(), which finds crossings and endpoint T-junctions geometrically so that annotated viewer paths — which share no OSM node ids and were therefore invisible to node-based detection — reach export andosm_cloudas crossroads - Viewer tracker for the Helhest field stack: poses in any TF frame are converted to lat/lon through
earth_frame(ECEF) instead of requiring autmframe; new inputsBatteryState,Temperature, e-stopBool,DiagnosticArray,Joy, crl_commander state string,road_followerstate, goal (PoseStamped), waypoint sequence (Path/PoseArray) and visual road path; heading fromImu,Vector3Stampedyaw orOdometry(heading_type). The sidebar gained E-Stop / Follower / Diagnostics rows and battery percentage; the map draws the sequence (blue), road path (cyan) and goal (orange).config/helhest.yamlnow targets this stack (verified against the Stromovka bags) - Tracker map:
osm_cloudintersections, theroad_followeractive intersection with its enter/exit radii, the follower's waypoint window, a robot trail and a fix-age / stale indicator; the sidebar has a map legend geodesy.ecef_to_latlon_array- Documentation: offline route planning, the
route_plannerconfig file, the traversability rules and the cost tables the graph planner now shares, and the goal QR codes
Changed
route_plannerpreloads the map and caches its graph planners, converts ECEF in a vectorised form and subscribes to static TF only, so a plan request no longer pays for the map on every callsetup.pyinstalls.mapdatafiles and annotation stores, so a planning node finds its map in an installed workspacedata/is no longer tracked (the directory is kept): local working datasets do not belong in the packageconfig/helhest.yamluses the real crl_commander topic names, and the follower threshold now lives inroad_and_gps_follower.yaml- Nodes log through
warning(); on Kiltedwarn()shares one caller id, which collapses distinct warnings into one throttled message - Repo hygiene: the stale
todo_fixes_plan.mdis gone, the unusedpython3-joblibdependency is dropped frompackage.xml,.mypy_cache/is ignored, and a test now asserts that the version stays in step acrosspyproject.toml,package.xml,map_data_interfaces/package.xmlandCITATION.cffand that the released version has a dated changelog section
Fixed
GraphPlanner.plan()re-inserted the raw clicked coordinate at both ends of every segment, so each via point became a degenerate out-and-back off the network. It now returns on-network points only and collapses coincident vertices and sub-metre spurs — a 900 m loop goes from 49 to 37 vertices — whilekeep_start(start_from_robot) still keeps waypoint 0 verbatim, because planning from the robot's pose has to begin where the robot isannotations:apply_added_nodesinserts each added node after its anchor instead of at a running offset, so added nodes land in the right place when a map is loaded for planning- The viewer re-maps segment annotations when a way's split points change
- The viewer's sidebar mode panels scroll when their body is too long: as
flex:1childrenmin-height:autolet them grow past the sidebar and#mainclipped the overflow, leaving the bottom of the planner unreachable osm_cloudno longer clips intersections to the auto grid bounds: those bounds come from the map's query bbox, but ways crossing it are downloaded whole, so crossroads on the route were silently droppedosm_cloudloads the same map as the planner (annotations,exclude_highway); a junction on a retagged or deleted way published a ring nothing routes over- Two field fixes ported from the robot:
TransformListener(..., spin_thread=True)so TF lookups do not depend on the node's own executor, and theutm_to_localparameter reshaped to 4×4 — a flat 16-element list otherwise breaks every transform - Goal codes are served as vectors (
/api/qr.svg) and the modal caption renders as HTML - CI: the OpenCV runtime dependency is declared and the new mypy errors are fixed
Full Changelog: v1.3.0...v1.4.0 · changelog