Basic concepts

Field

A field describes the entire field operation in the system add-on web app. It exists out of shapefiles with the Coordinate Reference system (CRS) WGS84 or UTM.

A field includes:

  1. traject: A trajectory is mandatory when executing a field operation. The shapefile is of the shape type Linestring.

  2. geofence: A geofence is also mandatory when executing a field operation. The shapefile is of the shape type polygon.

  3. tasks: Tasks are optional and describe the robot’s actions at specific locations. The following task types exist

    • hitch: The hitch is activated (lowered) when a polygon of the task map contains the hitch centre.

    • continuous: The section is activated when it intersects with a polygon of the task map. A particular type of this is a cardan task, which activates the Power Take Off (PTO) when intersecting the polygon (s) in the task map.

    • intermittent: The section is activated when it contains a point on the task map.

    • discrete: The discrete action (mostly a measurement) is performed when the section is the closest to the point of action on the task map.

_images/fig_task_types.png

Figure 1. Task types

Platform

The platform abstracts the robot’s capabilities and includes information such as dimensions, the number and position of the hitches, the vehicle configuration, etc. The settings.json file describes the platform configuration and can be edited in the system add-on web app JSON editor.

Listing 1. settings.json file of the CIMAT robot
{
    "name": "cimat",
    "robot": {
        "width": 1.5,
        "length": 3.8,
        "wheel_diameter": 0.7,
        "transform": {
            "T": [
                0,
                -1.0029,
                -1.93
            ],
            "R": [
                0,
                0,
                0
            ]
        }
    },
    "auto_velocity": {
        "min": 0.2,
        "max": 1.7
    },
    "nav_modes": [
        {
            "id": 1,
            "name": "pp 90\u00b0 turn"
        },
        {
            "id": 2,
            "name": "pp 180\u00b0 turn"
        },
        {
            "id": 3,
            "name": "pure pp"
        },
        {
            "id": 4,
            "name": "pp rollback"
        },
        {
            "id": 5,
            "name": "external"
        }
    ],
    "auto_modes": [
        {
            "id": 1,
            "name": "auto"
        }
    ],
    "hitches": [
        {
            "id": 2,
            "name": "FB",
            "min": 10,
            "max": 65,
            "types": [
                "hitch",
                "continuous",
                "discrete",
                "intermittent"
            ],
            "transform": {
                "T": [
                    0,
                    0.3314,
                    -1.3831
                ],
                "R": [
                    0,
                    0,
                    0
                ]
            },
            "link_length": 0.601
        },
        {
            "id": 4,
            "name": "RB",
            "min": 10,
            "max": 58,
            "types": [
                "hitch",
                "continuous",
                "discrete",
                "intermittent"
            ],
            "transform": {
                "T": [
                    0,
                    -2.45,
                    -1.2
                ],
                "R": [
                    0,
                    0,
                    0
                ]
            },
            "link_length": 0.55
        }
    ],
    "gps": {
        "device": "serial",
        "utm_zone": 31,
        "usb_port": "/dev/septentrio0",
        "ntrip_server": "flepos.vlaanderen.be",
        "ntrip_mountpoint": "<mountpoint>",
        "ntrip_uname": "<username>",
        "ntrip_pwd": "<password>",
        "antenna_rotation": -90,
        "transform": {
            "T": [
                -0.68064,
                0,
                0
            ],
            "R": [
                0,
                0,
                0
            ]
        }
    }
}

The fields in the settings.json file are:

  1. name: The name of the robot platform

  2. robot: The robot platform configuration

    2.1. The dimensions of the robot platform (width, height, wheel_diameter)

    2.2. transform: The translation (in meters) and rotation in Euler angles (degrees) of the different robot components to the antenna reference point on the robot. The robot reference point is measured from the ground on and is used by the navigation algorithms, to calculate the robots position in respect to the trajectory.

    • For a 4WD4WS robot vehicle configuration, the robot reference coincides with its geometric center.

    • For an Ackerman robot, the robot reference coincides with the centre of mass. In the case this does not equal the robot center, this transformation is written under the transform_center field of the robot configuration object.

_images/fig_robot_transformations.png

Figure 2. Robot component transformations, expressed relative to the antenna reference

  1. auto_velocity: min and max velocity of the platform.

  2. nav_modes: The allowed navigation modes. Depending on the robot platforms’ vehicle configuration, different headland turns are possible. The navigation modes determine these headland turns and are shown in Figure 3 with their corresponding ID.

    • 90° spinning: When the robot reaches a corner, it rotates around its axis until its orientation matches the direction of the next trajectory line it needs to follow. This basic turning process applies to both regular corners and headland turns.

    • 180° spinning: When the robot reaches a headland turn, it first rotates around its axis until it aligns with the direction of the next row it needs to follow. After aligning with the new row’s trajectory, the robot moves sideways toward the corner where this new row begins. Once it reaches the corner, the robot continues driving along the trajectory line of the new row. The navigation mode 90° spinning is used when the turns are regular corners.

    • pure pursuit: When the robot encounters a corner, it will navigate the turn by driving in a circular path. The radius of this circle is determined by the turning radius specified in the platform.json configuration file. If the corner is part of a headland turn (where the two rows of the field are closer together than twice the minimum turning radius), the robot will instead drive along a circular arc that connects the two rows. This arc will have the minimum turning radius, allowing the robot to smoothly transition from one row to the next within the limited space.

    • rollback: When the robot reaches a headland turn, it initially turns by driving in a quarter-circle, using the minimum turning radius specified for the platform. If the robot doesn’t have enough space to align with the next trajectory line, it will move backward to create the necessary space. Once there is enough room, the robot makes another quarter-circle turn with the minimum turning radius, aligning itself with the new trajectory line and continuing on its path.

    • external: An external controller can take control of the robot’s navigation by updating the navigation_control parameters. This allows the external controller to direct the robot’s movements, overriding the robot’s default navigation system.

_images/fig_navigation_modes.png

Figure 3. Navigation modes with their navigation id

  1. auto_modes: The different modes for autonomous navigation.

    • Full auto: The robot operates fully autonomous, with autonomous velocity- and steering control.

    • Auto steer: The robot only performs autonomous steering, and no autonomous velocity control. This mode provides “steering guidance” functionality.

    • Auto throttle: The robot only performs autonomous velocity control, and no autonomous steering control. This mode provides “cruise control” functionality.

  2. hitches: The specifications of the different hitches on the platform.

    6.1. id: hitch id

    6.2. name: name of the hitch

    6.3. min: min height of the hitch (cm)

    6.4. max: max height of the hitch (cm)

    6.5. transform to the hitch hinge (see Figure 2)

    6.6. types: compatible task types with the hitch

  3. gps: The RTK GNSS configuration.

    7.1. device: The device field is set serial for USB devices and socket for network devices.

    7.2. utm_zone: The Universal Transverse Mercator (UTM) projection zone to complete the EPSG:326xx code whereby xx is the UTM zone.

    7.3. usb_port or ìp and port: These parameters are device interface dependent. For a USB device the device name needs to be provided, and for a socket device the network- ip and port.

    7.4. NTRIP configuration ntrip_server, ntrip_mountpoint, ntrip_uname, ntrip_pwd

    7.5. antenna_rotation: The antenna rotation describes an additional rotation of the antennas around the z-axis (in degrees).

    • 0 or 180 if the antennas are in the longitudinal direction of the platform.

    • 90 or -90 if the antennas are in the lateral direction of the platform.

    7.6. transform: The transform field describes the transformation to the antenna reference (see Figure 2).

Implements

The <implement-name>.json files describe the implement configuration and can be edited in the system add-on web app JSON editor.

The fields in the <implement-name>.json file are:

  1. name: The name of the implement.

  2. on_taskmap: This variable determines whether the task needs to be executed on the task map. If not, the sections can be operated by an add-on or program on another computer. If yes, the sections are operated according to the field configuration.

  3. types: The implement supports several types of operations: continuous, intermittent, and discrete, as illustrated in Figure 2. Some implements can handle multiple types of operations simultaneously. For example, the auto-label implement shown in Listing 2 is designed for data collection using two cameras. The sections are defined by the cameras’ fields of view. A camera can be activated in two scenarios: when the sections overlap with a specified polygon (a continuous task) or when a crop plant enters the field of view (an intermittent task).

Listing 2. Implement supporting multiple types
{
  "name": "auto-label",
  "on_taskmap": true,
  "types": [
    "continuous",
    "intermittent"
  ],
  "sections": [
    {
      "id": "L",
      "width": 0.25,
      "up": 0.075,
      "down": 0.075,
      "transform": {
        "T": [
          0.35,
          -0.98,
          -0.4
        ],
        "R": [
          0,
          0,
          0
        ]
      }
    },
    {
      "id": "R",
      "width": 0.25,
      "up": 0.075,
      "down": 0.075,
      "transform": {
        "T": [
          -0.35,
          -0.98,
          -0.4
        ],
        "R": [
          0,
          0,
          0
        ]
      }
    }
  ]
}
  1. sections: The sections are an array of the geometries and transformations of the implement sections.

    4.1. id: The id of the section (string).

    4.2. The dimensions width, up, down in meters.

    4.3. transform: The transformation of the center of the section to the hitch pen, visualized Figure 2.

    4.4. A section can be repeated over a specified distance by using the repeats and offset fields. This functionality allows for the configuration of implements with repetitive sections, such as the spray boom described in Listing 3. The spray boom consists of 24 sections, but only the transformation for the first section needs to be defined. The repeats and offset fields define how these sections are repeated along the boom.

Listing 3. Implement with an array of sections configured by the repeats and offset fields
{
  "name": "spray-boom",
  "on_taskmap": true,
  "types": [
    "continuous",
    "intermittent"
  ],
  "sections": [
    {
      "id": "",
      "width": 0.15,
      "up": 0.05,
      "down": 0.05,
      "repeats": 24,
      "offset": 0.25,
      "transform": {
        "T": [
          3,
          -0.98,
          -0.4
        ],
        "R": [
          0,
          0,
          0
        ]
      }
    }
  ]
}

Interfaces

The config.json file describes the system’s Redis variables and the mechatronic/operational layer interface configuration. You can edit the configuration file in the system add-on web app JSON editor.

Listing 4. The config.json file of the CIMAT robot
{
    "protocols": {
        "snap7": {
            "ip": "192.168.1.2",
            "rack": 0,
            "read_db": 201,
            "slot": 1,
            "write_db": 200
        },
        "redis": {
            "ip": "127.0.0.1",
            "port": 6379
        }
    },
    "variables": {
        "plc": {
            "control": {
                "navigation": "navigation_control",
                "hitch_fb": "hitch_control",
                "hitch_rb": "hitch_control",
                "substate": "substate"
            },
            "monitor": {
                "drive_fl": "drive_monitor",
                "wheel_fl": "wheel_monitor",
                "drive_fr": "drive_monitor",
                "wheel_fr": "wheel_monitor",
                "drive_rl": "drive_monitor",
                "wheel_rl": "wheel_monitor",
                "drive_rr": "drive_monitor",
                "wheel_rr": "wheel_monitor",
                "imu": "imu_monitor",
                "hitch_fb": "hitch_monitor",
                "hitch_rb": "hitch_monitor",
                "navigation": "navigation_monitor",
                "state": "state",
                "substate": "substate"
            }
        },
        "pc": {
            "gps": "gps",
            "simulation": "simulation",
            "navigation": "navigation",
            "purepursuit": "purepursuit",
            "pid_steady_state": "pid",
            "pid_rough": "pid",
            "path": "path",
            "field": "field",
            "implement": "implement",
            "execution": "execution"
        }
    }
}

The fields in the config.json file are:

  1. The protocols object defines the protocol configuration.

    1.1. The snap7 object provides the mechatronic/operational layer interface parameters:

    1.1.1. the ip address of the PLC

    1.1.2. the read_db and write_db data block (DB), which correspond with the higherLevelMonitor and higherLevelControl

    1.1.3. the rack and slot of these data blocks

    1.2 The redis object provides the Redis ARTOF interface parameters:

    1.2.1 ip and port: the IP address and port of the Redis server

  2. The variables object defines the plc and pc Redis variables on the system.

    2.1. plc variables: These variables are continuously synced between the operational and mechatronic layer.

    2.1.1 the monitor variables are read S7-communication protocol (Snap7) by the RobotPLC

    2.1.2 the control variables are written S7-communication protocol (Snap7) by the RobotPLC

    2.2. pc variables are solely used by the operational layer and require no synchronization with the mechatronic layer.

The configuration depends on the platform configuration (vehicle configuration, number of hitches, energy source, etc.).

The types.json file describes recurrent composite types in the config.json file. These types can be nested as can be seen in Listing 5.

Listing 5. types.json file
{
    "xyz": {
        "x": "float",
        "y": "float",
        "z": "float"
    },
    "direction": {
        "longitudinal": "float",
        "angular": "float",
        "lateral": "float"
    },
    "drive_monitor": {
        "power": "float",
        "voltage": "float",
        "current": "float",
        "rpm": "float",
        "temperature": "float",
        "torque": "float"
    },
    "wheel_monitor": {
        "speed": "float",
        "rpm": "float",
        "angle": "float"
    },
    "battery_monitor": {
        "soc": "float",
        "current": "float",
        "voltage": "float",
        "power": "float",
        "capacity": "float",
        "temperature": "float"
    },
    "fuel_monitor": {
        "temperature": "float",
        "level": "float",
        "volume": "float",
        "type": "string"
    },
    "generator_monitor": {
        "temperature": "float",
        "rpm": "float",
        "fuel": "fuel_monitor"
    },
    "imu_monitor": {
        "acceleration": "xyz",
        "rotation": "xyz"
    }, 
    "hitch_monitor": {
        "busy": "bool",
        "height": "float",
        "angle": "float",
        "feedback_sections": "array[32] of uint8"
    },
    "state": {
        "safe": "bool",
        "normal": "bool",
        "auto": "bool",
        "aware": "bool",
        "steer": "bool",
        "throttle": "bool",
        "error": "bool"
    },
    "substate": {
        "programming": "bool",
        "dummy": "bool"
    },
    "hitch_control": {
        "activate": "bool",
        "activate_discrete": "bool",
        "activate_continuous": "bool",
        "activate_cardan": "bool",
        "setpoint": "int16",
        "activate_sections": "array[32] of uint8"
    },
    "navigation_control": {
        "velocity": "direction",
        "sideways": "bool",
        "end_reached": "bool",
        "heartbeat": "bool"
    },
    "navigation_monitor": {
        "velocity": "direction"
    },
    "gps": {
        "fix": "int",
        "hrp_mode": "int",
        "ground_speed": "float"
    },
    "simulation": {
        "fix": "int",
        "active": "bool",
        "auto": "bool",
        "factor": "float"
    },
    "navigation": {
        "non_operational_velocity": "float",
        "operational_velocity": "float",
        "sideways_velocity": "float",
        "spinning_velocity": "float",
        "max_angular_velocity": "float",
        "mode": "int",
        "spin_angle": "float",
        "turning_radius": "float"
    },
    "purepursuit": {
        "carrot_distance": "float",
        "inter_point_distance": "float",
        "weight_factor": "float"
    },
    "pid": {
        "p": "float",
        "i": "float",
        "d": "float",
        "integral": "float",
        "derivative": "float",
        "proportional": "float"
    },
    "path": {
        "orientation": "float",
        "deviation": "float"
    },
    "field": {
        "name": "string",
        "updated": "bool"
    },
    "implement": {
        "slow_down": "bool",
        "disable": "bool"
    },
    "execution": {
        "notification": "string"
    }
}

The names of the Redis variables or keys are the composition of the nested object keys. Some examples of how these Redis keys look like are: plc.monitor.drive_fl.current, plc.monitor.drive_fr.temperature, plc.monitor.hitch_fb.feedback_sections.27, plc.control.navigation.heartbeat, plc.control.hitch_fb.activate_sections.30, pc.navigation.turning_radius, pc.purepursuit.carrot_distance

Redis variables that need configuration are listed in the redis.init.json file, as shown in the variables object in Listing 4. The initialization is only done at system installation. Redis variables are persistent. So these values are preserved on system reboot.

Besides, the redis.init.json file contains information related to the processes and jobs that need to run. The latter is discussed in the Jobs section.

Listing 6. redis.init.json file of the CIMAT robot
{
    "system": {
        "ilvoProcesses": [
            {
                "Name": "ilvo-navigation",
                "Running": true,
                "SoftwareUpdate": true,
                "CheckHeartbeat": true
            },
            {
                "Name": "ilvo-operation",
                "Running": true,
                "SoftwareUpdate": true,
                "CheckHeartbeat": true
            },
            {
                "Name": "ilvo-simulation",
                "Running": true,
                "SoftwareUpdate": true,
                "CheckHeartbeat": true
            },
            {
                "Name": "ilvo-robot-plc",
                "Running": true,
                "SoftwareUpdate": true,
                "CheckHeartbeat": false
            },
            {
                "Name": "ilvo-gps",
                "Running": true,
                "SoftwareUpdate": true,
                "CheckHeartbeat": false
            }
        ],
        "ilvoAddons": [
            {
                "Name": "system",
                "Running": true,
                "SoftwareUpdate": true,
                "DockerRegistry": {
                    "username": "artof-ilvo",
                    "password": "glpat-ZZs4PkN86nbopeXdeGpy",
                    "serveraddress": "https://gitlab.ilvo.be:5050/v2/"
                },
                "DockerConfig": {
                    "Image": "gitlab.ilvo.be:5050/artof-ilvo/addons/system",
                    "HostConfig": {
                        "Binds": [
                            "/var/lib/ilvo:/var/lib/ilvo"
                        ],
                        "NetworkMode": "host",
                        "RestartPolicy": {
                            "Name": "on-failure",
                            "MaximumRetryCount": 3
                        }
                    }
                }
            },
            {
                "Name": "node-red",
                "Running": true,
                "SoftwareUpdate": true,
                "DockerRegistry": {
                    "serveraddress": "https://registry.hub.docker.com/v2/"
                },
                "DockerConfig": {
                    "Image": "nodered/node-red",
                    "HostConfig": {
                        "Binds": [
                            "/var/lib/ilvo/node-red:/data",
                            "/var/lib/ilvo/field:/var/lib/ilvo/field",
                            "/var/lib/ilvo/implement:/var/lib/ilvo/implement",
                            "/var/lib/ilvo/config.json:/var/lib/ilvo/config.json",
                            "/var/lib/ilvo/settings.json:/var/lib/ilvo/settings.json",
                            "/var/lib/ilvo/types.json:/var/lib/ilvo/types.json"
                        ],
                        "NetworkMode": "host",
                        "RestartPolicy": {
                            "Name": "on-failure",
                            "MaximumRetryCount": 3
                        }
                    }
                }
            }
        ]
    },
    "variables": {
        "pc.simulation.fix": 4,
        "pc.simulation.factor": 1.0,
        "pc.navigation.non_operational_velocity": 1.0,
        "pc.navigation.operational_velocity": 1.0,
        "pc.navigation.sideways_velocity": 0.2,
        "pc.navigation.spinning_velocity": 0.2,
        "pc.navigation.max_angular_velocity": 10.0,
        "pc.navigation.mode": 1,
        "pc.navigation.spin_angle": 25.0,
        "pc.navigation.turning_radius": 3.0,
        "pc.purepursuit.carrot_distance": 3.5,
        "pc.purepursuit.inter_point_distance": 0.1,
        "pc.purepursuit.weight_factor": 1.0,
        "pc.field.name": "example"
    }
}

Jobs

The SystemManager class is responsible for managing all other processes (Process) and the add-ons (Addon) in the operational layer. A Job is a higher-level abstraction that implements the common functionality of the Process and Addon class.

A Process is a component managed by the SystemManager within the operational layer. These processes are further discussed in the Operational layer section.

An Addon is an entry for the SystemManager process, which maintains the system’s add-ons. The add-on configuration uses the Docker Engine API (1.46) syntax. These add-ons are further discussed in the Add-ons section.

The redis.init.json file describes the job configurations at installation. After installation, the add-on configuration can be adjusted using the system add-on web app.

States

The ARTOF framework continuously updates the state of various platform components in Redis JSON variables, using the GNSS coordinates along with the robot and implement geometry. Each state consists of a position in UTM coordinates and an orientation. Internally, this information is represented as a 4x4 affine transformation matrix, as affines allow for efficient calculations. For easier human readability, the affine matrix is converted into a translation vector (in meters) and an orientation vector (in degrees). This translation vector provides the position, while the orientation vector details the component’s alignment in space.

  • gps.raw.state: The GPS raw state provides the UTM position, heading, and roll as measured by the GNSS receiver. This is the state of the dominant GNSS antenna.

  • gps.ref.state: The GPS raw state is transformed to the center point between the two antennas. This adjusted coordinate is referred to as the GPS reference state.

    • Configuration of gps transform in the settings.json file.

  • robot.ref.state: The robot reference state, typically located at the robot’s center of mass, serves as the reference point for calculating its relative position to the trajectory. This information is used for the navigation controller.

    • Configuration of robot transform in the settings.json file.

  • robot.center.state: The geometric centre of the robot is used for visualization.

    • Configuration of robot center in the settings.json file.

  • robot.head.state: The front of the robot, or the “head,” is utilized by certain navigation controllers that require deviation measurements from a point at the robot’s front.

    • Configuration of robot head in the settings.json file.

  • hitch.states: The hitch states describe the states of the hitches. This is the state of the hitch hinge.

    • Configuration of hitches hitch_<name> transform in the settings.json file.

  • implement.states: The implement states describe the states of the different implements and their sections.

    • Configuration of the <implement-name>.json file.