Fields2Cover

repository·main·Indexed 21 days ago

https://github.com/fields2cover/fields2cover

A 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.

Tokens
12.5K
Snippets
52
Records
63
Agent score
74%

What's inside fields2cover

  1. Overview of Fields2Cover

    main
    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.
  2. Overview of Fields2Cover capabilities

    main

    Fields2Cover 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-tools to 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 NSwathModified cost function for efficient computation.
  3. How basic types and shared pointers work

    main

    Classes 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->() or p1.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 an F2CPoint without 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))
  4. New features in Fields2Cover version 2.0

    main

    Version 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-tools for 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 NSwathModified which uses an approximation to reduce computation costs.
  5. How swath generators work in Fields2Cover

    main

    Swath 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 F2CCells or 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());
  6. Connect decomposed cells for Route Planning

    main

    A direct decomposition workflow can break the connectivity of headland rings between decomposed cells, which prevents the RoutePlanner from working correctly.

    To ensure headland rings are connected for a continuous route, use the following pattern:

    1. Generate the initial middle headland ring using generateHeadlands on the original field.
    2. Decompose that middle headland ring using your decomposition object.
    3. Generate the inner headlands (the 'mainland') from the decomposed middle headland ring.
    4. Generate swaths from these inner headlands.
    5. Pass the original middle headland ring (mid_hl) and the generated swaths to the RoutePlanner.
    # 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)
  7. Understand Swath and Collection types

    main

    Fields2Cover uses a hierarchy of types to represent coverage paths:

    • F2CSwath: A single path (AB line) used by an agricultural vehicle. It consists of a F2CLineString (the path) and a width.
    • F2CSwaths: A collection of F2CSwath objects grouped on a single F2CCell.
    • F2CSwathsByCells: A collection of F2CSwaths for each cell within a F2CCells collection.
  8. Sort swaths using known patterns

    main

    For 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 genSortedSwaths repeatedly will cycle through these variants.

    # Python Example of pattern sorting
    boustrophedon_sorter = f2c.RP_Boustrophedon()
    swaths = boustrophedon_sorter.genSortedSwaths(swaths)
  9. New classes and functionalities in F2C v2

    main

    Version 2.0 introduces several new classes to extend the library's capabilities:

    • Graphs: f2c::types::Graph and f2c::types::Graph2D for simple graph logic.
    • Routes: f2c::types::Route implements new logic for route representation.
    • Swaths: f2c::types::SwathsByCells is now a formal class instead of a std::vector<Swaths> alias.
    • Route Planning: f2c::rp::RoutePlannerBase allows for route planning without predefined patterns.
    • Cell Decomposition: f2c::decomp::DecompositionBase, f2c::decomp::TrapezoidalDecomp, and f2c::decomp::BoustrophedonDecomp provide decomposition functions.
    • Path Planning: f2c::pp::PathPlanning provides smooth connections for f2c::types::Route to ensure proper coverage.
    • Objective Functions: f2c::obj::NSwathModified provides a faster approximation of f2c::obj::NSwath for computing the number of swaths.
  10. Use F2CRoute to define a sequence of swaths

    main

    An F2CRoute defines a sequence of coverage movements. It is composed of a sequence of std::vector<F2CSwaths> and std::vector<F2CMultiPoint>.

    The logical flow of a route is:

    1. Follow the first F2CMultiPoint (if not empty).
    2. Cover the first set of F2CSwaths (moving from the end of one swath to the start of the next).
    3. Use the next F2CMultiPoint to transition from the end of the last covered swath to the start of the next set of swaths.
    4. Repeat until all swaths are covered.

    Note: An F2CRoute is a high-level sequence and does not contain the specific turns or velocities required for a vehicle; for those, use F2CPath.

  11. Access and modify elements in collections

    main

    To access an element in a collection (like F2CMultiLineString, F2CCell, F2CCells, or F2CMultiPoint), use the getGeometry(int n) method, where n is 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 use setGeometry(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)