Fields2Cover
repository·main·Indexed 21 days ago
https://github.com/fields2cover/fields2coverA modular and extensible Coverage Path Planning (CPP) library designed for autonomous agricultural vehicles, as well as cleaning and surveillance robotics. Version 2.0.0 provides algorithms for decomposing complex fields (including non-convex fields and those with obstacles) using Trapezoidal and Boustrophedon decomposition, generating swaths, and optimizing routes via OR-tools integration.
What's inside fields2cover
- Fields2Cover is a library designed for robust and efficient Coverage Path Planning (CPP). Its primary goal is to generate paths that allow one or several vehicles to cover a specific area. While the development is currently focused on offline planning for autonomous agricultural vehicles, the library is designed to be extensible and accepts contributions for other types of coverage planners.
Overview of Fields2Cover capabilities
mainFields2Cover is a library for Coverage Path Planning (CPP), primarily focused on autonomous agricultural vehicles but applicable to cleaning robots, surveillance, and de-mining.
Key Features (v2.0+)
- Non-convex field support: Handles complex field shapes and obstacles.
- Decomposition: Includes Trapezoidal and Boustrophedon decomposition to split concave fields into convex sub-fields.
- Route Optimization: Uses
OR-toolsto optimize swath ordering and supports specific start/end points. - Path Planning: Supports routes generated by the route optimizer.
- Swath Generation: High-speed generation with a new
NSwathModifiedcost function for efficient computation.
Use F2CPath for vehicle-ready coverage paths
mainAnF2CPathdefines a concrete coverage path suitable for a vehicle. It consists of a vector of steps, where each step contains a point, angle, length, and velocity. It also includes metadata regarding the direction of travel and whether the path is traversing through the mainland.How basic types and shared pointers work
mainClasses derived from GDAL types, such as
F2CPoint, use a compound structure of shared pointers.While you can access the underlying GDAL pointers directly using
p1->()orp1.get(), Fields2Cover provides a simplified interface that allows you to call GDAL methods directly on the F2C object. For example, you can call.distance()directly on anF2CPointwithout manually accessing the pointer.// C++: Direct access vs pointer access std::cout << p4->Distance(p5.get()) << std::endl; std::cout << p4.distance(p5) << std::endl; // Preferred# Python: Direct access vs pointer access print(p4.distance(p5))New features in Fields2Cover version 2.0
mainVersion 2.0 introduces support for non-convex fields and fields containing obstacles. Key enhancements include:
- Decomposition algorithms: Support for Trapezoidal and Boustrophedon decomposition to split concave fields into convex sub-fields.
- Route planner: Integration with
OR-toolsfor route optimization (ordering swaths) and support for specifying custom start and end points. - Path planner: Ability to utilize routes provided by the route optimizer.
- Swath generator: Improved generation speed and a new cost function
NSwathModifiedwhich uses an approximation to reduce computation costs.
How swath generators work in Fields2Cover
mainSwath generators are used to plan coverage paths (swaths) across a field to ensure complete coverage. Most generators include a search method to find the optimal coverage angle. To find this angle, the generator requires a Global objective function that defines what constitutes a 'best' path (e.g., minimizing the number of swaths or the total distance traveled).
Key components involved in swath generation:
- Robot: Defines the coverage width.
- Objective Function: A class (e.g.,
NSwath,SwathLength) that provides a cost value for a given set of swaths. - Geometry: The field area to be covered (often derived from
F2CCellsor headland generation). - Swath Generator: The algorithm (e.g.,
BruteForce) that executes the search or path generation.
# Example setup for swath generation f2c::Random rand(42); f2c::F2CRobot robot (2.0, 6.0); f2c::hg::ConstHL const_hl; f2c::F2CCells cells = rand.generateRandField(1e4, 5).getField(); f2c::F2CCells no_hl = const_hl.generateHeadlands(cells, 3.0 * robot.getWidth());Connect decomposed cells for Route Planning
mainA direct decomposition workflow can break the connectivity of headland rings between decomposed cells, which prevents the
RoutePlannerfrom working correctly.To ensure headland rings are connected for a continuous route, use the following pattern:
- Generate the initial middle headland ring using
generateHeadlandson the original field. - Decompose that middle headland ring using your decomposition object.
- Generate the inner headlands (the 'mainland') from the decomposed middle headland ring.
- Generate swaths from these inner headlands.
- Pass the original middle headland ring (
mid_hl) and the generated swaths to theRoutePlanner.
# Python pattern for connected route planning mid_hl = const_hl.generateHeadlands(cells, 1.5 * r_w) decomp_mid_hl = decomp.decompose(mid_hl) no_hl = const_hl.generateHeadlands(decomp_mid_hl, 1.5 * r_w) swaths = bf.generateBestSwaths(obj, r_w, no_hl) route_planner = f2c.RP_RoutePlannerBase() route = route_planner.genRoute(mid_hl, swaths)- Generate the initial middle headland ring using
Understand Swath and Collection types
mainFields2Cover uses a hierarchy of types to represent coverage paths:
F2CSwath: A single path (AB line) used by an agricultural vehicle. It consists of aF2CLineString(the path) and a width.F2CSwaths: A collection ofF2CSwathobjects grouped on a singleF2CCell.F2CSwathsByCells: A collection ofF2CSwathsfor each cell within aF2CCellscollection.
Sort swaths using known patterns
mainFor swaths created in order (such as in convex fields), you can sort them more efficiently using known patterns instead of metaheuristics. These patterns include Boustrophedon, Snake, and Spiral orders.
Each of these patterns typically has 4 variants based on the starting point. Calling
genSortedSwathsrepeatedly will cycle through these variants.# Python Example of pattern sorting boustrophedon_sorter = f2c.RP_Boustrophedon() swaths = boustrophedon_sorter.genSortedSwaths(swaths)New classes and functionalities in F2C v2
mainVersion 2.0 introduces several new classes to extend the library's capabilities:
- Graphs:
f2c::types::Graphandf2c::types::Graph2Dfor simple graph logic. - Routes:
f2c::types::Routeimplements new logic for route representation. - Swaths:
f2c::types::SwathsByCellsis now a formal class instead of astd::vector<Swaths>alias. - Route Planning:
f2c::rp::RoutePlannerBaseallows for route planning without predefined patterns. - Cell Decomposition:
f2c::decomp::DecompositionBase,f2c::decomp::TrapezoidalDecomp, andf2c::decomp::BoustrophedonDecompprovide decomposition functions. - Path Planning:
f2c::pp::PathPlanningprovides smooth connections forf2c::types::Routeto ensure proper coverage. - Objective Functions:
f2c::obj::NSwathModifiedprovides a faster approximation off2c::obj::NSwathfor computing the number of swaths.
- Graphs:
Use F2CRoute to define a sequence of swaths
mainAn
F2CRoutedefines a sequence of coverage movements. It is composed of a sequence ofstd::vector<F2CSwaths>andstd::vector<F2CMultiPoint>.The logical flow of a route is:
- Follow the first
F2CMultiPoint(if not empty). - Cover the first set of
F2CSwaths(moving from the end of one swath to the start of the next). - Use the next
F2CMultiPointto transition from the end of the last covered swath to the start of the next set of swaths. - Repeat until all swaths are covered.
Note: An
F2CRouteis a high-level sequence and does not contain the specific turns or velocities required for a vehicle; for those, useF2CPath.- Follow the first
Access and modify elements in collections
mainTo access an element in a collection (like
F2CMultiLineString,F2CCell,F2CCells, orF2CMultiPoint), use thegetGeometry(int n)method, wherenis the index.Important: Modifying an object returned by
getGeometry()does not update the object inside the collection because the returned object is often a copy or a separate handle. To update an element within a collection, you must usesetGeometry(int n, geometry)to set the modified object back into the collection at the specified index.// C++: Access and Update F2CPoint p_0 = points.getGeometry(0); p_0 *= 1e5; // Modify local copy points.setGeometry(0, p_0); // Update collection# Python: Access and Update p_0 = points.getGeometry(0) p_0 *= 1e5 points.setGeometry(0, p_0)