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// Figure eight (lemniscate of Bernoulli) about a centre: two lobes reaching
103// `size` from the centre along the `heading` axis, flown `count` times, entered
104// at the point of the curve nearest the vehicle. Yaw is left to the follower
105// (tangent): the camera looks along the path.
107{
111 float size = 5.0f; // centre to lobe tip (m); lobes are ~0.35 * size wide
112 float heading = pattern_detail::kNaN; // NED yaw of the lobe axis; NaN = entry heading
113 int count = 1; // loops; 0 -> single vertex at the entry point, immediate success
115 Turn direction = Turn::RIGHT; // sense of travel along the curve
116};
117std::vector<NedPoint> generateFlightPatternFigureEight(
118 const FlightPatternFigureEightParams & p, const NedPoint & current);
120
121// ---------------------------------------------------------------------------
122// Straight transit with sinusoidal altitude about the base altitude
132std::vector<NedPoint> generateFlightPatternSineWaveVertical(
133 const FlightPatternSineWaveVerticalParams & p, const NedPoint & current);
134
135// ---------------------------------------------------------------------------
136// Straight transit with sinusoidal lateral weave about the leg line
146std::vector<NedPoint> generateFlightPatternSineWaveHorizontal(
147 const FlightPatternSineWaveHorizontalParams & p, const NedPoint & current);
148
149// ---------------------------------------------------------------------------
150// Expanding square spiral: legs s, s, 2s, 2s, 3s, 3s, ... with 90 deg turns
152{
157 float initialHeading = pattern_detail::kNaN; // first leg heading; NaN = entry heading
158 float spacing = 10.0f; // track spacing (m)
159 int numLegs = 12;
160 float maxLegLength = 0.0f; // 0 = unbounded; otherwise stop before a longer leg
161};
162std::vector<NedPoint> generateFlightPatternSquareSpiral(
163 const FlightPatternSquareSpiralParams & p, const NedPoint & current);
165
166// ---------------------------------------------------------------------------
167// Archimedean spiral r = (spacing / 2 pi) * theta between two radii
169{
173 float startRadius = 0.0f;
174 float endRadius = 50.0f;
175 float spacing = 10.0f; // radial gap between successive turns (m)
177 bool inward = false; // fly from endRadius in to startRadius
178 float sampleSpacing = 2.0f; // arc length between vertices (m)
179};
180std::vector<NedPoint> generateFlightPatternSpiral(
181 const FlightPatternSpiralParams & p, const NedPoint & current);
183
184// ---------------------------------------------------------------------------
185// Boustrophedon lawnmower: parallel lanes along `laneHeading`, stepping
186// sideways by laneSpacing across `width`. With originIsCenter the origin is
187// the centre of the covered rectangle (pins are area centres); otherwise it is
188// the start corner of the first lane.
190{
193 bool originIsCenter = true;
195 float laneHeading = pattern_detail::kNaN; // NED yaw of the lanes; NaN = entry heading
196 float laneLength = 50.0f;
197 float width = 30.0f;
198 float laneSpacing = 10.0f;
199 Turn firstTurn = Turn::RIGHT; // side the lanes step toward
200};
201std::vector<NedPoint> generateFlightPatternLawnmower(
202 const FlightPatternLawnmowerParams & p, const NedPoint & current);
205
206// ---------------------------------------------------------------------------
207// Crosshatch grid: a lawnmower pass, then a second pass rotated 90 degrees
208// over the same rectangle, starting from the corner nearest the end of pass 1
214std::vector<NedPoint> generateFlightPatternGridPattern(
215 const FlightPatternGridPatternParams & p, const NedPoint & current);
217
218// ---------------------------------------------------------------------------
219// Victor Sierra (IAMSAR sector search): nine legs of length `radius` from a
220// datum with 120 degree turns; the craft passes back through the datum after
221// legs 3, 6 and 9, covering three 60 degree sectors. Extra cycles rotate the
222// pattern by `reorientation` (classic 30 degrees) to fill the gaps.
224{
228 float radius = 30.0f;
229 float initialHeading = pattern_detail::kNaN; // first leg heading; NaN = entry heading
231 int cycles = 1;
232 float reorientation = static_cast<float>(M_PI) / 6.0f;
233};
234std::vector<NedPoint> generateFlightPatternVSSearch(
235 const FlightPatternVSSearchParams & p, const NedPoint & current);
237
238// heading (NED yaw) of the leg from `from` to `to`
239inline float legHeading(const NedPoint & from, const NedPoint & to)
240{
241 return std::atan2(to.y - from.y, to.x - from.x);
242}
243
244} // 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 > generateFlightPatternFigureEight(const FlightPatternFigureEightParams &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)
float flightPatternFigureEightLength(const FlightPatternFigureEightParams &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)