SMACC2
Loading...
Searching...
No Matches
cb_px4_path_follower_base.hpp
Go to the documentation of this file.
1// Copyright 2026 RobosoftAI Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/*****************************************************************************************************************
16 *
17 * Authors: Brett Aldrich
18 *
19 ******************************************************************************************************************/
20
21#pragma once
22
26
27#include <atomic>
28#include <chrono>
29#include <limits>
30#include <vector>
31
32namespace cl_px4_mr
33{
34
35enum class YawMode
36{
37 TANGENT, // along the current path segment
38 FIXED, // PathFollowerParams::fixedYaw
39 PER_VERTEX, // NedPoint::yaw of the segment start (NaN -> tangent)
40 HOLD_ENTRY // heading at entry
41};
42
44{
45 float groundSpeed = 3.0f; // m/s, carrot advance rate along the polyline
46 // Max 3D distance the carrot may lead the vehicle. PX4 offboard position
47 // mode flies at vel = MPC_XY_P (0.95) * (setpoint - position), saturated at
48 // MPC_XY_VEL_MAX, so the steady-state lag is groundSpeed / 0.95 and the leash
49 // must exceed that or it throttles the effective speed to ~0.95 * leash.
50 float leash = 6.0f;
51 float arrivalXyTol = 1.0f; // final-vertex acceptance, metres
52 float arrivalZTol = 0.5f;
54 float fixedYaw = std::numeric_limits<float>::quiet_NaN();
55 bool prependCurrentPosition = true; // insert the entry position as vertex 0
56 float minSegmentLength = 0.05f; // drop degenerate segments
57 // If the state machine armed no timeout: timeout = max(30 s, factor * length / speed).
58 // 0 disables the auto-timeout.
59 float autoTimeoutFactor = 2.5f;
60 // Add CpTunnelCentering's lateral / vertical offsets to every carrot (the
61 // component is looked up in any orthogonal; silently off if absent or stale).
62 // The completion test then compares the vehicle with the offset carrot.
63 bool useTunnelCentering = false;
64};
65
66// Streaming polyline follower. A derived behavior supplies buildPath() (a pure
67// function of its params and the entry point); this base streams a moving
68// position setpoint ("carrot") along that polyline at a commanded ground
69// speed, throttled by a leash so the carrot never runs away from a lagging
70// vehicle, and posts success once the carrot has reached the end AND the
71// vehicle is within the arrival tolerances of the last vertex.
72//
73// It never uses CpGoalChecker (one global goal per client; closed paths would
74// self-trigger it at entry). Completion is an update() predicate.
75//
76// Threading: components come from the inherited allocation (state machine
77// thread). onEntry() (async thread) builds the path, arms the auto-timeout and
78// commands vertex 0, then sets active_. update() (SignalDetector thread,
79// ~20 Hz) only touches the path after observing active_. onExit() re-issues
80// the last carrot so the offboard keep-alive holds there.
82{
83public:
84 explicit CbPx4PathFollowerBase(PathFollowerParams params = {});
86
87 template <typename TOrthogonal, typename TSourceObject>
89 {
90 // optional, may live in another orthogonal (state machine thread, see the locking rule)
93 }
94
95 void setFollowerParams(const PathFollowerParams & params) { followerParams_ = params; }
97
98 void onEntry() override;
99 void onExit() override;
100 void update() override;
101
102protected:
103 // Called once from onEntry (async thread). Must depend only on params and
104 // `current` (x, y, z, yaw = heading at entry). May adjust followerParams_
105 // (e.g. yaw mode). Empty -> failure.
106 virtual std::vector<NedPoint> buildPath(const NedPoint & current) = 0;
107
108 // logging hooks
109 virtual const char * behaviorName() const { return "CbPx4PathFollowerBase"; }
110 virtual void onPathStarted(const std::vector<NedPoint> & /*path*/) {}
111 virtual void onPathCompleted() {}
112
113 float totalLength() const { return totalLen_; }
114 float progressFraction() const { return totalLen_ > 0.0f ? sCarrot_ / totalLen_ : 1.0f; }
115 // Index into the followed polyline (vertex 0 = the prepended entry position
116 // when prependCurrentPosition is set) of the vertex the carrot has passed
117 // most recently. Atomic: written from update(), readable from the state
118 // machine thread (e.g. a state's onExit, where updates are barred).
119 size_t carrotVertexIndex() const { return carrotVertex_.load(); }
120
123
124private:
125 NedPoint commandFor(float s);
126 void applyCentering(NedPoint & cmd) const;
127 float tangentYawAt(size_t segmentIndex) const;
128
129 std::vector<NedPoint> path_;
130 std::vector<float> cumLen_;
131 float totalLen_ = 0.0f;
132 float sCarrot_ = 0.0f;
133 std::atomic<size_t> carrotVertex_{0};
134 float entryHeading_ = 0.0f;
137 std::atomic<bool> active_{false};
138 std::chrono::steady_clock::time_point lastUpdateTime_;
139};
140
141} // namespace cl_px4_mr
std::chrono::steady_clock::time_point lastUpdateTime_
CbPx4PathFollowerBase(PathFollowerParams params={})
virtual std::vector< NedPoint > buildPath(const NedPoint &current)=0
virtual void onPathStarted(const std::vector< NedPoint > &)
float tangentYawAt(size_t segmentIndex) const
void setFollowerParams(const PathFollowerParams &params)
const PathFollowerParams & followerParams() const
void requiresComponent(SmaccComponentType *&storage, ComponentRequirement requirementType=ComponentRequirement::SOFT)