PX4-Autopilot/Tools/draw_path.sh
Samuel Sadok ee75ebac8c
Adds Smart Return To Home capability by recording the flight path in a memory optimimized format. This means the UAV can return to home by using only known-good flight paths.
The flight graph is stored as a series of delta elements at 1m resolution and a list of nodes. Both lists share the same buffer. Each delta element takes up 2 bytes or sometimes 6 bytes if the delta is large. The path can consist of multiple disconnected segments, in which case the gaps are stored as delta elements with a jump-bit set.

Once in a while or when required the graph is consolidated, which means:
 - Points that lie roughly in a line are replaced by a single line
 - Points that lie close to previously recorded lines are pruned
 - For lines that pass close to each other a node element is created

Furthermore:
 - The graph is recorded at a higher precision closer to home
 - If the graph becomes full, the overall precision is reduced and the whole graph is re-consolidated
 - If the graph becomes full once more, all data is removed except for the shortest path home at that moment. One of these actions is repeated at each subsequent fill-up.

Path finding information is generated/refreshed on demand and stored in the nodes. During return-to-home, the best direction to home is continuously evaluated by using the information stored in the nodes.

The graph recording and path finding is implemented in navigator/tracker.cpp.
The graph based return-to-home is implemented in navigator/smart_rtl.cpp.
2020-10-04 12:16:45 -04:00

242 lines
5.8 KiB
Bash
Executable File

#!/bin/sh
#
# Script to draw 2D flight paths. The z-component is always 0.
# The purpose is to test the flight graph tracker by handing it a predesigned path.
#
# Usage: draw_path.sh [file]
# If the file already exists, new points are appended.
# arrow keys: move cursor
# spacebar: add current cursor position to path
# q: quit
# Shows a status message at the end of the screen
function show_status() {
tput cup $(expr $SCREEN_WIDTH-1) 0
echo $1
move_cursor
STATUS_CLEAR="no"
}
# Clears the status message
function clear_status() {
[ "$STATUS_CLEAR" = "no" ] || return 0
tput cup 0 0
tput el && tput el1
move_cursor
STATUS_CLEAR="yes"
}
# Terminates the script
function quit() {
tput rmcup
stty echo
exit 0
}
# Moves the terminal cursor to position ($CURSOR_X, $CURSOR_Y)
function move_cursor() {
#echo "\033[$CURSOR_Y;$CURSOR_X"'f'
#echo -e "\033[$CURSOR_Y;3f"
tput cup $CURSOR_Y $CURSOR_X
}
# Draws a line from the last point to the current cursor position
function go_here() {
draw_line $LAST_POINT_X $LAST_POINT_Y $CURSOR_X $CURSOR_Y
echo "o"
move_cursor
LAST_POINT_X=$CURSOR_X
LAST_POINT_Y=$CURSOR_Y
}
# Adds the current cursor position to the file $FILE
function store_point() {
echo "$CURSOR_X,$CURSOR_Y,0," >> $FILE
}
# Loads all points from the file $FILE
# Each line stores a point in the format "x,y,z".
# Empty lines and lines starting with "/" are ignored.
function load_file() {
while IFS='' read -r line || [[ -n "$line" ]]; do
CURSOR_X=$(echo "$line" | awk -F, "/^[^\/]/{print \$1}")
CURSOR_Y=$(echo "$line" | awk -F, "/^[^\/]/{print \$2}")
#echo "X=$CURSOR_X Y=$CURSOR_Y"
[ "$CURSOR_X" = "" ] || [ "$CURSOR_Y" = "" ] || go_here
done < "$FILE"
}
# Draws a line using the Bresenham algorithm
# usage: draw_line x1 y1 x2 y2
# The point (x1, y1) is not filled.
# The point (x2, y2) is filled and the cursor is placed there.
function draw_line() {
X1=$1
Y1=$2
X2=$3
Y2=$4
LINE_CHAR="\\"
# The algorithm requires that delta-y is smaller than delta-x
SWAP_XY=$(echo "define delta(a, b) {if (a>b) return a-b; return b-a;}; delta($X1,$X2) < delta($Y1,$Y2)" | bc)
[ "$SWAP_XY" = "1" ] && {
TEMP=$X1; X1=$Y1; Y1=$TEMP
TEMP=$X2; X2=$Y2; Y2=$TEMP
}
# Delta-x must be positive
REVERSE_DIR=$(echo "$X1 > $X2" | bc)
[ "$REVERSE_DIR" = "1" ] && {
TEMP=$X1; X1=$X2; X2=$TEMP
TEMP=$Y1; Y1=$Y2; Y2=$TEMP
}
# Now the slope is in [-45°, +45], in positive x-direction. The update update differs for positive and negative slopes.
POS_SLOPE=$(echo "$Y2 > $Y1" | bc)
DELTA_X=$(($X2-$X1))
DELTA_Y=$(($Y2-$Y1))
# init update criterion
if [ "$POS_SLOPE" = "1" ]; then
D=$(echo "$DELTA_Y * ($X1+1) - $DELTA_X * ($Y1+0.5) + $X2 * $Y1 - $X1 * $Y2" | bc)
else
D=$(echo "$DELTA_Y * ($X1+1) - $DELTA_X * ($Y1-0.5) + $X2 * $Y1 - $X1 * $Y2" | bc)
fi
XP=$X1
YP=$Y1
while [ $XP -lt $X2 ]; do
[ "$SWAP_XY" = "0" ] && LINE_CHAR="-" || LINE_CHAR="|"
# Move to next pixel, according to update criterion
XP=$(($XP+1))
if [ "$POS_SLOPE" = "1" ]; then
[ $(echo "$D > 0" | bc) = "1" ] && {
YP=$(($YP+1))
D=$(echo "$D - $DELTA_X" | bc)
LINE_CHAR="\\"
}
D=$(echo "$D + $DELTA_Y" | bc)
else
[ $(echo "$D < 0" | bc) = "1" ] && {
YP=$(($YP-1))
D=$(echo "$D + $DELTA_X" | bc)
LINE_CHAR="/"
}
D=$(echo "$D + $DELTA_Y" | bc)
fi
# Draw pixel
[ "$SWAP_XY" = "0" ] && { CURSOR_X=$XP; CURSOR_Y=$YP; } || { CURSOR_X=$YP; CURSOR_Y=$XP; }
move_cursor;
([ "$REVERSE_DIR" = "1" ] && [ "$XP" = "$X2" ]) || echo "$LINE_CHAR"
done
[ "$REVERSE_DIR" = "1" ] && { [ "$SWAP_XY" = "0" ] && { CURSOR_X=$X1; CURSOR_Y=$Y1; } || { CURSOR_X=$Y1; CURSOR_Y=$X1; } }
move_cursor;
}
# Moves the cursor up if possible
function up() {
[ $CURSOR_Y -gt 0 ] && let CURSOR_Y=$CURSOR_Y-1
move_cursor
}
# Moves the cursor down if possible
function down() {
let CURSOR_Y=$CURSOR_Y+1
[ $CURSOR_Y -lt $SCREEN_HEIGHT ] || let CURSOR_Y=$SCREEN_HEIGHT-1
move_cursor
}
# Moves the cursor left if possible
function left() {
[ $CURSOR_X -gt 0 ] && let CURSOR_X=$CURSOR_X-1
move_cursor
}
# Moves the cursor right if possible
function right() {
let CURSOR_X=$CURSOR_X+1
[ $CURSOR_X -lt $SCREEN_WIDTH ] || let CURSOR_X=$SCREEN_WIDTH-1
move_cursor
}
# Set up globals
SCREEN_WIDTH=$(tput cols)
SCREEN_HEIGHT=$(tput lines)
let CURSOR_X=$SCREEN_WIDTH/2
let CURSOR_Y=$SCREEN_HEIGHT/2
LAST_POINT_X=$CURSOR_X
LAST_POINT_Y=$CURSOR_Y
KEY_SEQUENCE=""
FILE=$1
touch $FILE 2>/dev/null || { echo "could not open file $FILE"; exit 1; }
# Switch to alternate screen
tput smcup
#x=$(tput lines)
#while [ $x -gt 0 ]; do echo ""; let x=$x-1; done;
tput clear
stty -echo
move_cursor
load_file
# draws two crosses
#draw_line 20 3 50 15
#draw_line 20 15 50 3
#draw_line 5 5 20 30
#draw_line 20 5 5 30
# draws the same two crosses (with swapped start end end points)
#draw_line 50 15 20 3
#draw_line 50 3 20 15
#draw_line 20 30 5 5
#draw_line 5 30 20 5
while true; do
IFS=""
read -r -n 1 CHAR
KEY_SEQUENCE=$KEY_SEQUENCE$CHAR
CARRY_SEQUENCE=""
clear_status
case $KEY_SEQUENCE in
$'\033'|$'\033[')
# incomplete escape sequence - read another char
CARRY_SEQUENCE=$KEY_SEQUENCE
#show_status "press q to exit"
;;
'q') quit ;;
$'\033[A') up ;;
$'\033[B') down ;;
$'\033[C') right ;;
$'\033[D') left ;;
$" ") go_here; store_point ;;
*)
show_status "unknown key"
;;
esac
KEY_SEQUENCE=$CARRY_SEQUENCE
done