SMACC2
Loading...
Searching...
No Matches
pattern_generators.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
24
25#include <cmath>
26#include <limits>
27#include <vector>
28
29// Pure trajectory-pattern generators: params + the vehicle's entry point in,
30// an ordered NED polyline out. No ROS, no components - unit-testable and
31// reusable by any follower.
32//
33// Naming convention: every pattern carries the FlightPattern prefix, e.g. for
34// a pattern called Lawnmower:
35// struct FlightPatternLawnmowerParams { ... };
36// std::vector<NedPoint> generateFlightPatternLawnmower(
37// const FlightPatternLawnmowerParams &, const NedPoint & current);
38// float flightPatternLawnmowerLength(const FlightPatternLawnmowerParams &); // watchdog sizing
39// The generator must stay pure: no ROS, no components, no clock.
40//
41// Conventions:
42// - NED: x north, y east, z down (altitude params are positive metres AGL,
43// converted to z = -altitude)
44// - NaN origin / altitude / heading = "use the entry point's value"
45// - Turn::RIGHT = clockwise viewed from above = increasing yaw
46// - yaw radians, NaN = leave to the follower's yaw mode
47
48namespace cl_px4_mr
49{
50
51enum class Turn
52{
53 RIGHT,
54 LEFT
55};
56
57inline float turnSign(Turn t) { return t == Turn::RIGHT ? 1.0f : -1.0f; }
58inline const char * turnName(Turn t) { return t == Turn::RIGHT ? "RIGHT" : "LEFT"; }
59
60namespace pattern_detail
61{
62constexpr float kNaN = std::numeric_limits<float>::quiet_NaN();
63inline float pick(float param, float fallback) { return std::isnan(param) ? fallback : param; }
64inline float altitudeToZ(float altitudeAgl, float currentZ)
65{
66 return std::isnan(altitudeAgl) ? currentZ : -altitudeAgl;
67}
68} // namespace pattern_detail
69
70// ---------------------------------------------------------------------------
71// Rate-controlled vertical move to an altitude (no horizontal motion)
73{
74 float altitudeAgl = 10.0f; // target, metres AGL
75 float climbRate = 1.5f; // m/s, becomes the follower ground speed
76 float tolerance = 0.5f; // already within this -> single-vertex path (immediate success)
77 // XY to hold during the climb/descent; NaN = the entry position. Set it to
78 // land precisely on a point (pre-landing descent).
81};
82std::vector<NedPoint> generateFlightPatternAscend(
83 const FlightPatternAscendParams & p, const NedPoint & current);
84
85// ---------------------------------------------------------------------------
86// N circles about a centre, entered at the nearest point
88{
92 float radius = 5.0f;
93 int count = 1; // 0 -> no circles: single vertex at the entry point, immediate success
96 bool faceCenter = true; // per-vertex yaw toward the centre; else tangent
97};
98std::vector<NedPoint> generateFlightPatternLoiter(
99 const FlightPatternLoiterParams & p, const NedPoint & current);
100
101// ---------------------------------------------------------------------------
102// Straight transit with sinusoidal altitude about the base altitude
112std::vector<NedPoint> generateFlightPatternSineWaveVertical(
113 const FlightPatternSineWaveVerticalParams & p, const NedPoint & current);
114
115// ---------------------------------------------------------------------------
116// Straight transit with sinusoidal lateral weave about the leg line
126std::vector<NedPoint> generateFlightPatternSineWaveHorizontal(
127 const FlightPatternSineWaveHorizontalParams & p, const NedPoint & current);
128
129// ---------------------------------------------------------------------------
130// Expanding square spiral: legs s, s, 2s, 2s, 3s, 3s, ... with 90 deg turns
132{
137 float initialHeading = pattern_detail::kNaN; // first leg heading; NaN = entry heading
138 float spacing = 10.0f; // track spacing (m)
139 int numLegs = 12;
140 float maxLegLength = 0.0f; // 0 = unbounded; otherwise stop before a longer leg
141};
142std::vector<NedPoint> generateFlightPatternSquareSpiral(
143 const FlightPatternSquareSpiralParams & p, const NedPoint & current);
145
146// ---------------------------------------------------------------------------
147// Archimedean spiral r = (spacing / 2 pi) * theta between two radii
149{
153 float startRadius = 0.0f;
154 float endRadius = 50.0f;
155 float spacing = 10.0f; // radial gap between successive turns (m)
157 bool inward = false; // fly from endRadius in to startRadius
158 float sampleSpacing = 2.0f; // arc length between vertices (m)
159};
160std::vector<NedPoint> generateFlightPatternSpiral(
161 const FlightPatternSpiralParams & p, const NedPoint & current);
163
164// ---------------------------------------------------------------------------
165// Boustrophedon lawnmower: parallel lanes along `laneHeading`, stepping
166// sideways by laneSpacing across `width`. With originIsCenter the origin is
167// the centre of the covered rectangle (pins are area centres); otherwise it is
168// the start corner of the first lane.
170{
173 bool originIsCenter = true;
175 float laneHeading = pattern_detail::kNaN; // NED yaw of the lanes; NaN = entry heading
176 float laneLength = 50.0f;
177 float width = 30.0f;
178 float laneSpacing = 10.0f;
179 Turn firstTurn = Turn::RIGHT; // side the lanes step toward
180};
181std::vector<NedPoint> generateFlightPatternLawnmower(
182 const FlightPatternLawnmowerParams & p, const NedPoint & current);
185
186// ---------------------------------------------------------------------------
187// Crosshatch grid: a lawnmower pass, then a second pass rotated 90 degrees
188// over the same rectangle, starting from the corner nearest the end of pass 1
194std::vector<NedPoint> generateFlightPatternGridPattern(
195 const FlightPatternGridPatternParams & p, const NedPoint & current);
197
198// ---------------------------------------------------------------------------
199// Victor Sierra (IAMSAR sector search): nine legs of length `radius` from a
200// datum with 120 degree turns; the craft passes back through the datum after
201// legs 3, 6 and 9, covering three 60 degree sectors. Extra cycles rotate the
202// pattern by `reorientation` (classic 30 degrees) to fill the gaps.
204{
208 float radius = 30.0f;
209 float initialHeading = pattern_detail::kNaN; // first leg heading; NaN = entry heading
211 int cycles = 1;
212 float reorientation = static_cast<float>(M_PI) / 6.0f;
213};
214std::vector<NedPoint> generateFlightPatternVSSearch(
215 const FlightPatternVSSearchParams & p, const NedPoint & current);
217
218// heading (NED yaw) of the leg from `from` to `to`
219inline float legHeading(const NedPoint & from, const NedPoint & to)
220{
221 return std::atan2(to.y - from.y, to.x - from.x);
222}
223
224} // namespace cl_px4_mr
float altitudeToZ(float altitudeAgl, float currentZ)
float pick(float param, float fallback)
std::vector< NedPoint > generateFlightPatternSpiral(const FlightPatternSpiralParams &p, const NedPoint &current)
std::vector< NedPoint > generateFlightPatternSineWaveVertical(const FlightPatternSineWaveVerticalParams &p, const NedPoint &current)
std::vector< NedPoint > generateFlightPatternVSSearch(const FlightPatternVSSearchParams &p, const NedPoint &current)
std::vector< NedPoint > generateFlightPatternSquareSpiral(const FlightPatternSquareSpiralParams &p, const NedPoint &current)
std::vector< NedPoint > generateFlightPatternLoiter(const FlightPatternLoiterParams &p, const NedPoint &current)
float flightPatternSquareSpiralLength(const FlightPatternSquareSpiralParams &p)
float legHeading(const NedPoint &from, const NedPoint &to)
std::vector< NedPoint > generateFlightPatternSineWaveHorizontal(const FlightPatternSineWaveHorizontalParams &p, const NedPoint &current)
float flightPatternGridPatternLength(const FlightPatternGridPatternParams &p)
std::vector< NedPoint > generateFlightPatternLawnmower(const FlightPatternLawnmowerParams &p, const NedPoint &current)
const char * turnName(Turn t)
int flightPatternLawnmowerLaneCount(const FlightPatternLawnmowerParams &p)
float flightPatternVSSearchLength(const FlightPatternVSSearchParams &p)
std::vector< NedPoint > generateFlightPatternGridPattern(const FlightPatternGridPatternParams &p, const NedPoint &current)
float turnSign(Turn t)
float flightPatternSpiralLength(const FlightPatternSpiralParams &p)
float flightPatternLawnmowerLength(const FlightPatternLawnmowerParams &p)
std::vector< NedPoint > generateFlightPatternAscend(const FlightPatternAscendParams &p, const NedPoint &current)