Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bendy Ruler Lua Script Added #28903

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Bendy Ruler Lua Script Added #28903

wants to merge 5 commits into from

Conversation

TeteJK
Copy link

@TeteJK TeteJK commented Dec 18, 2024

Bendy Ruler Lua Script Implementation

Remaining issues:

  • needs to cover fence items (both inclusion and exclusion items)
  • need to split the GLOBAL_POSITION_INT and POSITION_TARGET_GLOBAL_INT targets
  • need to check against the old CUAV-OBC-2018 for behaviour
  • cleanup verbose messages like "Mission clear"
  • add logging to aid in understanding and debugging avoidance behaviour
  • document need for higher SCR_VM_I_COUNT and SCR_HEAP_SIZE
  • add autotest for CI, will need APIs for genobstacles when running headless
  • add airspace clear checking for takeoff with MAV_CMD_NAV_DELAY_AIRSPACE_CLEAR and for jumps with CONDITION_TYPE_AIRSPACE_CLEAR and CONDITION_TYPE_AIRSPACE_NOT_CLEAR

Problem framing from @NDevDrone and @tridge

This implementation aimed to integrate the Bendy Ruler obstacle avoidance algorithm into the latest version of ArduPilot by using a Lua script for evaluation and calculations.

SingleDAA MultiDAA


1. Enhancements to the AP_Avoidance Module

Bindings and functions related to obstacle detection using ADS-B were implemented within the AP_Avoidance module to interact with Lua. The following methods were added:

num_obstacles()
Returns the total number of obstacles detected via ADS-B (requires ADS-B to be enabled).

get_obstacle_loc(instance)
Retrieves the last known position of a detected obstacle as a Location userdata type. The instance parameter specifies the obstacle.

get_obstacle_vel(instance)
Provides the velocity of a detected obstacle as a 3D vector (Vector3f) in NED (North-East-Down) coordinates, measured in meters per second.

get_obstacle_id(instance)
Returns the unique ID of an obstacle as reported by ADS-B, using the instance parameter.

get_obstacle_time(instance)
Returns the timestamp of the ADS-B message for a specific obstacle as an integer value.

These methods, accessible via the AP_Avoidance singleton instance, enable real-time interaction with ADS-B data. Where existing ArduPilot methods sufficed for Lua script interactions, they were reused. Missing or underperforming methods were hardcoded into the Lua script.


2. Lua Implementation of the Bendy Ruler Logic

The Bendy Ruler avoidance algorithm was implemented in Lua, with modifications to adapt to the scripting environment:

  • Exclusions: Logic for fences and margin areas was omitted for simplicity. The current focus is solely on avoiding obstacles.
  • Simplified Task Logic: Instead of using threads and schedulers, the avoidance logic is invoked within the update() function. This ensures computations only occur when a potential threat is detected, optimizing limited computational resources when working with Lua scripts.
  • Navigation Commands: The Lua script does not interact directly with navigation and command libraries. Instead, it sends commands to fly to a new position when avoidance is required using the already implemented Lua methods.

3. Addressing Altitude Handling Issues

The GenObstacles library, used for terrain altitude calculations, had a bug preventing accurate obstacle generation. To enable horizontal avoidance testing:

  • All altitude-dependent logic was temporarily commented out.
  • Obstacles were generated without considering altitude.

This workaround allows testing of horizontal avoidance logic but requires future fixes to handle altitude data correctly.


4. STIL Simulation for Testing the Lua Script

Follow these steps to test the Lua script implementation for the Bendy Ruler obstacle avoidance algorithm:

1. Prepare the Simulation Environment

  • Navigate to the directory where the simulation will be run.
  • Create a folder named scripts in this directory (if not already present).
  • Copy the latest version of the Lua script containing the Bendy Ruler code (Bendy_ruler.lua) into the scripts folder.

2. Start the Simulation

Run the simulation using the following command, replacing IP with the appropriate IP address:

python sim_vehicle.py -w -v ArduPlane -f plane-elevon --console --map -C --load-module asterix,genobstacles --out=udp:IP:14550

3. Set Bendy Ruler Parameters

param set SCR_ENABLE 1 
param set AVD_ENABLE 1 
param set ADSB_TYPE 1
param set SCR_VM_I_COUNT 1000000
param set AVD_W_ACTION 2 
param set AVD_W_DIST_XY 5000

4. Clear Initial Obstacles

Delete all obstacles generated during startup to avoid overloading the Lua script.

  • Right-click on the map.
  • Select Obstacles > Clear All.

5. Reset Bendy Ruler Lua Script

Something like

script stop bendy-ruler.lua
script load bendy-ruler.lua

@Hwurzburg Hwurzburg added the WikiNeeded needs wiki update label Dec 18, 2024
always maintain original target location

local avoid_dist = orig_target_loc:get_distance(new_target_loc)

gcs:send_named_float("AVD_DIST", avoid_dist)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this do?

@timtuxworth
Copy link
Contributor

It would be really useful to have some logging.

int32_t get_obstacle_time(uint8_t instance) const {
return _obstacles[instance].timestamp_ms;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking it might be better to have this as a single method that returns all these values in a single call right @tpwrules ?
Apart from the simplicity and flash savings isn't there also the risk of a race condition?

@@ -104,3 +104,5 @@ Egge=60.215720,10.324071,198,303
Gundaroo=-35.02349196,149.26496411,576.8,0
Kaga=36.3268982,136.3316638,44,0
UCSB=34.413963,-119.848946,0,0
California=37.4275449,-122.1697,0,0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

California is a big place - just saying.

-- Returns the number of detected obstacles via ADS-B
-- the ADS-B Must be enabled.
---@return integer -- number of obstacles
function avoid:num_obstacles() end-- Returns a Location userdata for the last position of the obstacle observed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment does't seem to match the function.


---@param instance integer -- instance number
---@return Vector3f_ud -- 3D velocity in m/s, in NED format
function avoid:get_obstacle_vel(instance) end-- Obstacle id as obtained from ADS-B.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this be other obstacles? Not just ADS-B? What about Remote ID vehicles or something identified by an off board AI/camera?

-- MAV_COLLISION_SRC
MAV_COLLISION_SRC = {
ADSB = 0, -- Source is ADSB_VEHICLE packets
MAVLINK_GPS_GLOBAL_INT = 1, -- Source is MAVLink GPS_GLOBAL_INT
Copy link
Contributor

@timtuxworth timtuxworth Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remote ID?
Offboard AI?
Fixed obstacles like cell towers or power lines?

}

local PARAM_TABLE_KEY = 7
local PARAM_TABLE_PREFIX = "BENDY_"
Copy link
Contributor

@timtuxworth timtuxworth Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a suggestion, for parameter names, I've started using this convention:
prefix =
Z - identifies a scripting parameter
XX - scripting function - e.g. PF = Plane Follow - so BR = Bendy Ruler

ZPF_ Plane Follow parameters
ZTA_ Terrain Avoidance parameters
(possibly?) ZAL_ Auto Land parameters ...

so
ZBR_ for bendy ruler parameters

This means:

  • its easy to find any scripted parameters by just searching or "Z" (or Z* in mavproxy).
  • the maximum hit for bendy ruler (for up to 99 parameters) is 3 characters.

Which leaves room for coding some reasonable meaning into the remaining characters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you get the idea we can't have more than nine? A table can have 62 or so.

Copy link
Contributor

@timtuxworth timtuxworth Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hit this in writing quadplane_terrain_avoid.lua. An error definitely gets thrown if you try to add more than 10 parameters to a table. (I guess it's 10, not 9)

Copy link
Contributor

@timtuxworth timtuxworth Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its in AP_Param.h - AP_PARAM_MAX_DYNAMIC = 10, even though there is

 if (num_params > 63) {
        return false;
    }

at line 2999 of AP_Param.cpp, I think all the code in add_table() uses AP_PARAM_MAX_DYNAMIC as the limit, and I have found that you definitely can't add more than 10 parameters in a table.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out I was wrong about the 9 parameter limit. Thanks for setting me straight on this.

// @Description: Avoidance margin for fence
// @User: Advanced
--]]
BENDY_MARGIN_FENCE = bind_add_param('MARGIN_FENCE', 2, 50)
Copy link
Contributor

@timtuxworth timtuxworth Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about MRG - jus thinking we might need the characters later so
MRG_FEN or MRG_FNC
MRG_DYN
MRG_EXC

@@ -0,0 +1,871 @@
local UPDATE_RATE_HZ = 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't CAPITALs supposed to be reserved for GLOBALS?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s a global considering it’s in the outermost scope of the script and a setting at the top. Marking it as a local is probably marginally better for performance.

end

function longitude_scale(lat)
local DEG_TO_RAD = math.pi / 180
Copy link
Contributor

@timtuxworth timtuxworth Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is already a math.rad() function in Lua

loc2:lat(lat)
loc2:lng(lon)
loc2:alt(loc1:alt())
--gcs:send_text(0, string.format("IN: Lat: %.2f, Lon: %.2f", loc1:lat(),loc1:lng()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need the frame

// @Param: BENDY_OPTIONS
// @DisplayName: ADS-B avoidance options
// @Description: Bitmask of behaviour options
// @Values: 0:Disabled,1:Enabled
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is enabled/disabled it should be _ENABLE

if full_distance < 20 then
return nil
end
-- Try 5 degree increments around a circle, alternating left and right. Check each one to see if flying in that direction would avoid all obstacles.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be 15 degrees (or am I reading it wrong)?
bearing_inc_cd = 1500

if new_target_loc then
-- tell the vehicle to fly to the new calculated target
vehicle:update_target_location(target_loc, new_target_loc)
last_updated_target_loc = new_target_loc:copy()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update_target_location() is probably going go fail sometimes, so maybe last_updated_target_loc should only get updated if it returns true?

gcs:send_named_float("AVD_DIST", avoid_dist)
else
-- revert to the original target location, no avoidance needed
vehicle:update_target_location(target_loc, orig_target_loc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear why you are doing this. It seems like it would already be going here, so why tell it to go where it's already going?

end

local orig_target_loc = nil
local last_updated_target_loc = nil
Copy link
Contributor

@timtuxworth timtuxworth Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for? It doesn't seem to get used anywhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants