mirror of
https://github.com/CopterExpress/clover.git
synced 2026-05-26 11:43:25 +00:00
blocks: implement global navigation
This commit is contained in:
@@ -30,6 +30,7 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app
|
|||||||
Parameters read by frontend:
|
Parameters read by frontend:
|
||||||
|
|
||||||
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2).
|
||||||
|
* `~navigate_global_tolerance` (*float*) – distance tolerance for global coordinates navigation (default: 1).
|
||||||
* `~yaw_tolerance` (*float*) – yaw angle tolerance in degrees, used in set_yaw block (default: 1).
|
* `~yaw_tolerance` (*float*) – yaw angle tolerance in degrees, used in set_yaw block (default: 1).
|
||||||
* `~sleep_time` (*float*) – duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
|
* `~sleep_time` (*float*) – duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
|
||||||
* `~confirm_run` (*bool*) – enable confirmation to run the program (default: true).
|
* `~confirm_run` (*bool*) – enable confirmation to run the program (default: true).
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ function considerFrameId(e) {
|
|||||||
this.getInput('Y').fieldRow[0].setValue('y');
|
this.getInput('Y').fieldRow[0].setValue('y');
|
||||||
this.getInput('Z').fieldRow[0].setValue('z');
|
this.getInput('Z').fieldRow[0].setValue('z');
|
||||||
}
|
}
|
||||||
|
if (this.getInput('LAT')) { // block has global coordinates
|
||||||
|
let global = frameId.startsWith('GLOBAL');
|
||||||
|
this.getInput('LAT').setVisible(global);
|
||||||
|
this.getInput('LON').setVisible(global);
|
||||||
|
this.getInput('X').setVisible(!global);
|
||||||
|
this.getInput('Y').setVisible(!global);
|
||||||
|
this.getInput('Z').fieldRow[0].setValue(frameId == 'GLOBAL' ? 'altitude' : 'z');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.getInput('ID')) { // block has marker id field
|
if (this.getInput('ID')) { // block has marker id field
|
||||||
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
|
this.getInput('ID').setVisible(frameId == 'ARUCO'); // toggle id field
|
||||||
@@ -65,6 +73,9 @@ function updateSetpointBlock(e) {
|
|||||||
|
|
||||||
Blockly.Blocks['navigate'] = {
|
Blockly.Blocks['navigate'] = {
|
||||||
init: function () {
|
init: function () {
|
||||||
|
let navFrameId = frameIds.slice();
|
||||||
|
navFrameId.push(['global', 'GLOBAL_LOCAL'])
|
||||||
|
navFrameId.push(['global, WGS 84 alt.', 'GLOBAL'])
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("navigate to point");
|
.appendField("navigate to point");
|
||||||
this.appendValueInput("X")
|
this.appendValueInput("X")
|
||||||
@@ -73,12 +84,20 @@ Blockly.Blocks['navigate'] = {
|
|||||||
this.appendValueInput("Y")
|
this.appendValueInput("Y")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("left");
|
.appendField("left");
|
||||||
|
this.appendValueInput("LAT")
|
||||||
|
.setCheck("Number")
|
||||||
|
.appendField("latitude")
|
||||||
|
.setVisible(false);
|
||||||
|
this.appendValueInput("LON")
|
||||||
|
.setCheck("Number")
|
||||||
|
.appendField("longitude")
|
||||||
|
.setVisible(false)
|
||||||
this.appendValueInput("Z")
|
this.appendValueInput("Z")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("up");
|
.appendField("up");
|
||||||
this.appendDummyInput()
|
this.appendDummyInput()
|
||||||
.appendField("relative to")
|
.appendField("relative to")
|
||||||
.appendField(new Blockly.FieldDropdown(frameIds), "FRAME_ID");
|
.appendField(new Blockly.FieldDropdown(navFrameId), "FRAME_ID");
|
||||||
this.appendValueInput("ID")
|
this.appendValueInput("ID")
|
||||||
.setCheck("Number")
|
.setCheck("Number")
|
||||||
.appendField("with ID")
|
.appendField("with ID")
|
||||||
|
|||||||
@@ -52,6 +52,8 @@
|
|||||||
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="X"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="Y"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="Z"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
|
<value name="LAT"><shadow type="math_number"><field name="NUM">47.397503</field></shadow></value>
|
||||||
|
<value name="LON"><shadow type="math_number"><field name="NUM">8.544945</field></shadow></value>
|
||||||
<value name="SPEED"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
|
<value name="SPEED"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
|
||||||
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
<value name="ID"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
|
||||||
</block>
|
</block>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ var workspace = Blockly.inject('blockly', {
|
|||||||
function readParams() {
|
function readParams() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
ros.readParam('navigate_tolerance', true, 0.2),
|
ros.readParam('navigate_tolerance', true, 0.2),
|
||||||
|
ros.readParam('navigate_global_tolerance', true, 1),
|
||||||
ros.readParam('yaw_tolerance', true, 1),
|
ros.readParam('yaw_tolerance', true, 1),
|
||||||
ros.readParam('sleep_time', true, 0.2),
|
ros.readParam('sleep_time', true, 0.2),
|
||||||
ros.readParam('confirm_run', true, true),
|
ros.readParam('confirm_run', true, true),
|
||||||
|
|||||||
@@ -33,6 +33,18 @@ const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame
|
|||||||
return
|
return
|
||||||
rospy.sleep(${params.sleep_time})\n`;
|
rospy.sleep(${params.sleep_time})\n`;
|
||||||
|
|
||||||
|
const NAVIGATE_GLOBAL_WAIT = () => `\ndef navigate_global_wait(lat, lon, z, speed=0.5):
|
||||||
|
res = navigate_global(lat=lat, lon=lon, z=z, yaw=float('inf'), speed=speed)
|
||||||
|
|
||||||
|
if not res.success:
|
||||||
|
raise Exception(res.message)
|
||||||
|
|
||||||
|
while not rospy.is_shutdown():
|
||||||
|
telem = get_telemetry(frame_id='navigate_target')
|
||||||
|
if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_global_tolerance}:
|
||||||
|
return
|
||||||
|
rospy.sleep(${params.sleep_time})\n`;
|
||||||
|
|
||||||
const LAND_WAIT = () => `\ndef land_wait():
|
const LAND_WAIT = () => `\ndef land_wait():
|
||||||
land()
|
land()
|
||||||
while get_telemetry().armed:
|
while get_telemetry().armed:
|
||||||
@@ -68,6 +80,9 @@ function generateROSDefinitions() {
|
|||||||
if (rosDefinitions.offboard) {
|
if (rosDefinitions.offboard) {
|
||||||
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
|
||||||
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
|
code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
|
||||||
|
if (rosDefinitions.navigateGlobal) {
|
||||||
|
code += `navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)\n`;
|
||||||
|
}
|
||||||
if (rosDefinitions.setVelocity) {
|
if (rosDefinitions.setVelocity) {
|
||||||
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
|
||||||
}
|
}
|
||||||
@@ -94,6 +109,10 @@ function generateROSDefinitions() {
|
|||||||
Blockly.Python.definitions_['import_math'] = 'import math';
|
Blockly.Python.definitions_['import_math'] = 'import math';
|
||||||
code += NAVIGATE_WAIT();
|
code += NAVIGATE_WAIT();
|
||||||
}
|
}
|
||||||
|
if (rosDefinitions.navigateGlobalWait) {
|
||||||
|
Blockly.Python.definitions_['import_math'] = 'import math';
|
||||||
|
code += NAVIGATE_GLOBAL_WAIT();
|
||||||
|
}
|
||||||
if (rosDefinitions.landWait) {
|
if (rosDefinitions.landWait) {
|
||||||
code += LAND_WAIT();
|
code += LAND_WAIT();
|
||||||
}
|
}
|
||||||
@@ -161,24 +180,48 @@ Blockly.Python.navigate = function(block) {
|
|||||||
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
|
||||||
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
|
let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
|
||||||
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
|
let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
|
||||||
let frameId = buildFrameId(block);
|
let lat = Blockly.Python.valueToCode(block, 'LAT', Blockly.Python.ORDER_NONE);
|
||||||
|
let lon = Blockly.Python.valueToCode(block, 'LON', Blockly.Python.ORDER_NONE);
|
||||||
|
let wait = block.getFieldValue('WAIT') == 'TRUE';
|
||||||
|
let frameId = block.getFieldValue('FRAME_ID');
|
||||||
let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
|
let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
|
||||||
|
|
||||||
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
|
||||||
|
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
|
|
||||||
if (block.getFieldValue('WAIT') == 'TRUE') {
|
// global coordinates
|
||||||
rosDefinitions.navigateWait = true;
|
if (frameId.startsWith('GLOBAL')) {
|
||||||
|
rosDefinitions.navigateGlobal = true;
|
||||||
simpleOffboard();
|
simpleOffboard();
|
||||||
|
|
||||||
return `navigate_wait(${params.join(', ')})\n`;
|
if (frameId == 'GLOBAL') {
|
||||||
|
z = `${z} + get_telemetry().alt - get_telemetry().z`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
rosDefinitions.navigateGlobalWait = true;
|
||||||
|
simpleOffboard();
|
||||||
|
return `navigate_global_wait(lat=${lat}, lon=${lon}, z=${z}, speed=${speed})\n`;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return `navigate_global(lat=${lat}, lon=${lon}, z=${z}, yaw=float('inf'), speed=${speed})\n`;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (frameId != 'body') {
|
frameId = buildFrameId(block);
|
||||||
params.push(`yaw=float('nan')`);
|
let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
rosDefinitions.navigateWait = true;
|
||||||
|
simpleOffboard();
|
||||||
|
|
||||||
|
return `navigate_wait(${params.join(', ')})\n`;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (frameId != 'body') {
|
||||||
|
params.push(`yaw=float('nan')`);
|
||||||
|
}
|
||||||
|
return `navigate(${params.join(', ')})\n`;
|
||||||
}
|
}
|
||||||
return `navigate(${params.join(', ')})\n`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ This block allows to specify the [coordinate frame](frames.md) of the target poi
|
|||||||
* *marker* – coordinates, relative to an [ArUco-marker](aruco_marker.md); marker's ID input fields appears.
|
* *marker* – coordinates, relative to an [ArUco-marker](aruco_marker.md); marker's ID input fields appears.
|
||||||
* *last navigate target* – coordinates, relative to the last specified navigate point.
|
* *last navigate target* – coordinates, relative to the last specified navigate point.
|
||||||
* *map* – drone's local coordinate system, linked with the point of its initialization.
|
* *map* – drone's local coordinate system, linked with the point of its initialization.
|
||||||
|
* *global* – global coordinates system (latitude and longitude) and relative altitude.
|
||||||
|
* *global, WGS 84 alt.* – global coordinates system and [WGS 84](https://en.wikipedia.org/wiki/WGS_84) altitude.
|
||||||
|
|
||||||
### land
|
### land
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,8 @@
|
|||||||
* *markers map* – система координат, связанная с [картой ArUco-маркеров](aruco_map.md).
|
* *markers map* – система координат, связанная с [картой ArUco-маркеров](aruco_map.md).
|
||||||
* *marker* – система координат, связанная с [ArUco-маркером](aruco_marker.md); появляется поле для ввода ID маркеа.
|
* *marker* – система координат, связанная с [ArUco-маркером](aruco_marker.md); появляется поле для ввода ID маркеа.
|
||||||
* *last navigate target* – координаты относительно последней заданной точки для навигации.
|
* *last navigate target* – координаты относительно последней заданной точки для навигации.
|
||||||
* *map* – локальная система координат коптера, связана с местом его инициализации.
|
* *global* – глобальная система координат (широта и долгота) и относительная высота.
|
||||||
|
* *global, WGS 84 alt.* – глобальная система координат и высота в [системе WGS 84](https://ru.wikipedia.org/wiki/WGS_84).
|
||||||
|
|
||||||
### land
|
### land
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user