Skip to content

Thrust stand calibration tools #1

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
# thrust_map_estimation
# Thrust Map estimation

This repository holds all the tools and procedures used to estimate a Thrust Map using a thrust stand for offline data recording.

## Thrust Stand utils

The [thrust_stand](./thrust_stand/) folder contains the scripts used to carry out the experiments on the thrust stand so they can be replicated, as well as some test data obtained for a specific motor model and all the scripts used to fit and test the polynomial surface for the Thrust Map.
Binary file added figures/multirotor-surface.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added figures/ramp-exp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added figures/single-rotor-surface.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added figures/steps-exp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added figures/stepup-exp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions thrust_stand/RCbenchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Experiments using RCbenchmark

The thrust stand used to record the test data of this repository works with the [RCbenchmark](https://www.tytorobotics.com/pages/rcbenchmark-software?srsltid=AfmBOoplVTaXTnj7UVUPZncaaGceY2aUXAr7xT3j2jto4vNWKTdj7LbD) software application. This software allows the loading of scripts to automate experiments. The experiment scripts used in the tests are these:

- [rcb_ThrustMap_ramp](./rcb_ThrustMap_ramp.js): performs a continuous ramp between specified minimum and maximum throttle values in a given time. Additional options allow to also perform a descending ramp, or perform consecutive ramps.
- [rcb_ThrustMap_steps](./rcb_ThrustMap_steps.js): performs a series of steps, returning to a low throttle level after each step. The steps are performed at increasingly high levels. The maximum and minimum values, as well as the increment between steps can be configured.
- [rcb_ThrustMap_stepsUp](./rcb_ThrustMap_stepsUp.js) and [rcb_ThrustMap_stepsDown](./rcb_ThrustMap_stepsDown.js): performs a series of steps without returning to a low level throttle between steps. The steps in `stepsUp` are increasingly high, while the ones in `stepsDown` decrease along the experiment.


<img src="../../figures/ramp-exp.png" alt="ramp" width="320"/> <img src="../../figures/steps-exp.png" alt="steps" width="320"/> <img src="../../figures/stepup-exp.png" alt="stepsup" width="320"/>
99 changes: 99 additions & 0 deletions thrust_stand/RCbenchmark/rcb_ThrustMap_ramp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* //////////////// Ramp response measure ////////////////

The script will perform an up and down ramp function with values between "minVal" and "maxVal". Around each ramp the script will keep the output constant (plateau). The ramp will be of duration "t". Data will be continuously recorded during the test.

^ Motor Input
| ___maxVal
| / \
| / \ <-> waitTime
| minVal___/ \___
|_________ <t>___<t>_____________> Time

///////////// User defined variables //////////// */

var minVal = 1000; // Min. input value [700us, 2300us]
var maxVal = 1600; // Max. input value [700us, 2300us]
var t = 30; // Time of the ramp input (seconds)
var samplesAvg = 5; // Number of samples to average at each reading (reduces noise and number of CSV rows)
var repeat = 1; // How many times to repeat the same sequence
var filePrefix = "xNova-TM";
var rampGoDown = true; // If set to true, the ramp will go up and down.

///////////////// Beginning of the script //////////////////

//Reading sensors and writing to file continuously
rcb.files.newLogFile({prefix: filePrefix});
readSensor(); // Start the loop. After readSensor(), readDone() followed by readSensor(), etc.

function readSensor(){
rcb.console.setVerbose(false);
rcb.sensors.read(readDone, samplesAvg);
rcb.console.setVerbose(true);
}

function readDone(result){
rcb.console.setVerbose(false);
rcb.files.newLogEntry(result,readSensor);
rcb.console.setVerbose(true);
}

//ESC initialization
rcb.console.print("Initializing ESC...");
rcb.output.set("esc",1000);
rcb.wait(startPlateau, 4);

//Start plateau
function startPlateau(){
rcb.console.print("Start Plateau...");
rcb.output.set("esc",minVal);
rcb.wait(rampUp, 2.0);
rcb.console.print("This should be at the end");
}

//Ramp up
function rampUp(){
rcb.console.print("Ramping Up...");
rcb.output.ramp("esc", minVal, maxVal, t, upPlateau);
}

//Up Plateau
function upPlateau() {
rcb.console.print("Up Plateau...");
rcb.wait(rampDown, 0.5);
}

//Ramp down
function rampDown() {
if(rampGoDown){
rcb.console.print("Ramping Down...");
rcb.output.ramp("esc", maxVal, minVal, t, endPlateau);
}else
endScript();
}

//End Plateau
function endPlateau() {
rcb.console.print("End Plateau...");
rcb.wait(endScript, 2.0);
}

//Ends or loops the script
function endScript() {
if(--repeat > 0){
if(repeat === 0){
rcb.console.print("Repeating one last time...");
}else{
rcb.console.print("Repeating " + repeat + " more times...");
}
startPlateau();
}else{
rcb.endScript();
}
}







58 changes: 58 additions & 0 deletions thrust_stand/RCbenchmark/rcb_ThrustMap_steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
var minEsc = 1100;
var maxVal = 2000;
var waitTime = 2.0;
var samplesAvg = 5;
var repeat = 1.0;
var throttleIncrement = 50;
var filePrefix = "xNova-TM";

function readSensor(){
rcb.console.setVerbose(false);
rcb.sensors.read(readDone, samplesAvg);
rcb.console.setVerbose(true);
}

function readDone(result){
rcb.console.setVerbose(false);
rcb.files.newLogEntry(result, readSensor);
rcb.console.setVerbose(true);
}

function performRampStep(min_value, max_value, maxLimit, increment) {
if (max_value > maxLimit) {
rcb.console.print("Script done. Closing down...");
return;
}

rcb.console.print("Up from " + min_value + " to " + max_value);
rcb.output.ramp("esc", min_value, max_value, waitTime, function () {
rcb.console.print("Plateau at max...");
rcb.wait(function () {
rcb.console.print("Down from " + max_value + " to " + min_value);
rcb.output.ramp("esc", max_value, min_value, waitTime, function () {
rcb.console.print("Plateau at min...");
rcb.wait(function () {
performRampStep(min_value, max_value + increment, maxLimit, increment);
}, waitTime);
});
}, waitTime);
});
}

function runScript() {
rcb.output.set("esc", minEsc);
var min_value = 1100;
var maxLimit = 1800;
var increment = throttleIncrement;

performRampStep(min_value, min_value + increment, maxLimit, increment);
}

// Inicio de la ejecución
rcb.files.newLogFile({prefix: filePrefix});
readSensor();

rcb.console.print("Initializing ESC...");
rcb.output.set("esc", 1000);

rcb.wait(runScript, 3);
53 changes: 53 additions & 0 deletions thrust_stand/RCbenchmark/rcb_ThrustMap_stepsDown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
var minEsc = 1100;
var maxVal = 2000;
var waitTime = 2.0;
var samplesAvg = 5;
var repeat = 1.0;
var throttleIncrement = 50;
var filePrefix = "xNova-TM";

function readSensor(){
rcb.console.setVerbose(false);
rcb.sensors.read(readDone, samplesAvg);
rcb.console.setVerbose(true);
}

function readDone(result){
rcb.console.setVerbose(false);
rcb.files.newLogEntry(result, readSensor);
rcb.console.setVerbose(true);
}

function performRampStep(min_value, max_value, maxLimit, increment) {
if (max_value > maxLimit) {
rcb.console.print("Script done. Closing down...");
return;
}

rcb.console.print("Down from " + max_value + " to " + min_value);
rcb.output.ramp("esc", max_value, min_value, waitTime, function () {
rcb.console.print("Plateau at min...");
rcb.wait(function () {
max_value = min_value;
performRampStep(max_value, max_value + increment, maxLimit, increment);
}, waitTime);
});
}

function runScript() {
rcb.output.set("esc", minEsc);
var max_value = 1600;
var minLimit = 1100;
var increment = throttleIncrement;

performRampStep(max_value, max_value - increment, minLimit, increment);
}

// Inicio de la ejecución
rcb.files.newLogFile({prefix: filePrefix});
readSensor();

rcb.console.print("Initializing ESC...");
rcb.output.set("esc", 1000);

rcb.wait(runScript, 3);
53 changes: 53 additions & 0 deletions thrust_stand/RCbenchmark/rcb_ThrustMap_stepsUp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
var minEsc = 1100;
var maxVal = 2000;
var waitTime = 2.0;
var samplesAvg = 5;
var repeat = 1.0;
var throttleIncrement = 50;
var filePrefix = "xNova-TM";

function readSensor(){
rcb.console.setVerbose(false);
rcb.sensors.read(readDone, samplesAvg);
rcb.console.setVerbose(true);
}

function readDone(result){
rcb.console.setVerbose(false);
rcb.files.newLogEntry(result, readSensor);
rcb.console.setVerbose(true);
}

function performRampStep(min_value, max_value, maxLimit, increment) {
if (max_value > maxLimit) {
rcb.console.print("Script done. Closing down...");
return;
}

rcb.console.print("Up from " + min_value + " to " + max_value);
rcb.output.ramp("esc", min_value, max_value, waitTime, function () {
rcb.console.print("Plateau at max...");
rcb.wait(function () {
min_value = max_value;
performRampStep(min_value, min_value + increment, maxLimit, increment);
}, waitTime);
});
}

function runScript() {
rcb.output.set("esc", minEsc);
var min_value = 1100;
var maxLimit = 1600;
var increment = throttleIncrement;

performRampStep(min_value, min_value + increment, maxLimit, increment);
}

// Inicio de la ejecución
rcb.files.newLogFile({prefix: filePrefix});
readSensor();

rcb.console.print("Initializing ESC...");
rcb.output.set("esc", 1000);

rcb.wait(runScript, 3);
59 changes: 59 additions & 0 deletions thrust_stand/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Thrust stand experiments for Thrust Map estimation

The scripts that process the thrust stand data and turn them into a polynomial Thrust Map are found here.

<img src="../figures/single-rotor-surface.png" alt="SR-TM" width="335"/> <img src="../figures/multirotor-surface.png" alt="MR-TM" width="320"/>

## Usage

To obtain the coefficients for the polynomial surface that characterizes the Thrust Map, simply run:

```
python3 thrust_map_fit.py -d <path/to/data/directory> -c <path/to/config/file>
```

This will take all the data `.csv` files in the given directory and combine them to use them all for the surface fitting. Alternatively, you can run:

```
python3 thrust_map_fit.py -f <path/to/data/file1> <path/to/data/file2> ... -c <path/to/config/file>
```

to pass a list of specific files you want to use for the polynomial surface fitting.

By default, this script will perform the fitting and output the resulting coefficientes to a text file. A configuration file can be used to set data filters and activate more options. The [default configuration](config/default_config.yaml) looks like this:

```
combined_data_file: null # combined data filename. If null, no data is saved.
coefficients_file: null # coefficients file name. Default is coefficients.txt
poly_deg: 2nd # degree of the desired polynomial to fit
compute_error: true # save a report file with fitting error and stddev
plot_results: true # plot resulting fitted surface

data_filter:
min_thrust: 0.0 # discard all data rows with thrust below this threshold
max_thrust: 11.0 # discard all data rows with thrust above this threshold
min_volt: 20.4 # discard all data rows with voltage below this threshold
max_volt: 25.2 # discard all data rows with voltage above this threshold
min_throttle: 1300 # discard all data rows with throttle below this threshold
max_throttle: 1500 # discard all data rows with throttle above this threshold

plotting:
color: orange # color for the plotted data and surface
```

- `combined_data_file`: saves all the combined data from the different input `.csv` files into a file with the given name. If `null` (default) the combined data will not be saved.
- `coefficients_file`: name of the file with the stored coefficients resulted from the polynomial fitting that will be saved to the 'results' folder. Default name is 'coefficients.txt'.
- `poly_deg`: degree of the polynomial surface to be fitted. Default is a 2nd degree polynomial. Only polynomial between 1st and 4th degree are valid.
- `compute_error`: if true, generates a report with the Mean Absolute Error and the Standard Deviation of the fitting of the polynomial to the input data in the 'results' folder. Default is false.
- `plot_results`: if true, shows a 3D plot of the fitted surface and the input data.
- `data_filter`: allows to specify maximum and minimum values of thrust, voltage and throttle to filter the data.
- `plotting`: options for the plots, like the color of the data and the surface.

## Recording data form the thrust stand

The scripts in this repository assume that your data is stored in `.csv` files that have, at least, the following three columns with these exact same names:
- Voltage (V)
- Thrust (N)
- ESC signal (µs)

This is the same format of the data recorded by RCbenchmark software used to record the test data provided with this repository. For more information on the recording of data using RCbenchmark see the [RCbenchmark](./RCbenchmark/) section.
16 changes: 16 additions & 0 deletions thrust_stand/config/default_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
combined_data_file: null # combined data filename. If null, no data is saved.
coefficients_file: null # coefficients file name. Default is coefficients.txt
poly_deg: 2nd # degree of the desired polynomial to fit
compute_error: true # save a report file with fitting error and stddev
plot_results: true # plot resulting fitted surface

data_filter:
min_thrust: 0.0 # discard all data rows with thrust below this threshold
max_thrust: 11.0 # discard all data rows with thrust above this threshold
min_volt: 20.4 # discard all data rows with voltage below this threshold
max_volt: 25.2 # discard all data rows with voltage above this threshold
min_throttle: 1300 # discard all data rows with throttle below this threshold
max_throttle: 1500 # discard all data rows with throttle above this threshold

plotting:
color: orange # color for the plotted data and surface
Loading