Regression improvements

This commit is contained in:
Ryan Brott
2023-03-02 00:16:35 -08:00
parent 9651735df3
commit 80ea431b29
4 changed files with 119 additions and 61 deletions

View File

@ -79,6 +79,26 @@ function fixVels(ts, xs, vs) {
return numDerivOffline(ts, xs).map((est, i) => inverseOverflow(vs[i + 1], est));
}
// see https://github.com/FIRST-Tech-Challenge/FtcRobotController/issues/617
function fixAngVels(vs) {
if (vs.length === 0) {
return [];
}
let offset = 0;
lastV = vs[0];
const vsFixed = [lastV];
for (let i = 1; i < vs.length; i++) {
if (Math.abs(vs[i] - lastV) > Math.PI) {
offset -= Math.sign(vs[i] - lastV) * 2 * Math.PI;
}
vsFixed.push(offset + vs[i]);
lastV = vs[i];
}
return vsFixed;
}
// data comes in pairs
function newLinearRegressionChart(container, xs, ys, options, onChange) {
if (xs.length !== ys.length) {
@ -103,6 +123,7 @@ function newLinearRegressionChart(container, xs, ys, options, onChange) {
const maxX = xs.reduce((a, b) => Math.max(a, b), 0);
const chartDiv = document.createElement('div');
const width = Math.max(0, window.innerWidth - 50);
Plotly.newPlot(chartDiv, [{
type: 'scatter',
mode: 'markers',
@ -124,10 +145,11 @@ function newLinearRegressionChart(container, xs, ys, options, onChange) {
dragmode: 'select',
showlegend: false,
hovermode: false,
width: 600,
width,
height: width * 9 / 16,
}, {
// 'select2d' left
modeBarButtonsToRemove: ['zoom2d', 'pan2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'],
// 'select2d', 'zoom2d', 'pan2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d' left
modeBarButtonsToRemove: [],
});
const results = document.createElement('p');
@ -161,6 +183,10 @@ function newLinearRegressionChart(container, xs, ys, options, onChange) {
let pendingSelection = null;
chartDiv.on('plotly_selected', function(eventData) {
if (eventData === undefined) {
return;
}
pendingSelection = eventData;
});

View File

@ -9,6 +9,10 @@ body {
}
.content {
margin: auto;
}
header {
max-width: 600px;
margin: auto;
}
@ -44,28 +48,30 @@ details, a {
</head>
<body>
<div class="content">
<h1>RR Dead Wheel Angular Ramp Regression</h1>
<details></details>
<header>
<h1>RR Dead Wheel Angular Ramp Regression</h1>
<details></details>
<div id="download"></div>
<div id="download"></div>
<fieldset>
<legend>Feedforward Parameters</legend>
<div>
kV: <input id="kv" type="text" />
</div>
<div>
kS: <input id="ks" type="text" />
</div>
<input id="update" type="button" value="update" />
</fieldset>
<fieldset>
<legend>Feedforward Parameters</legend>
<div>
kV: <input id="kv" type="text" />
</div>
<div>
kS: <input id="ks" type="text" />
</div>
<input id="update" type="button" value="update" />
</fieldset>
<div id="trackWidthChart">
<p>
<button id="latest">Latest</button>
<input id="browse" type="file" accept="application/json">
</p>
</div>
</header>
<div id="trackWidthChart"></div>
<div id="deadWheelCharts"></div>
</div>
@ -73,7 +79,7 @@ details, a {
<script>
function loadRegression(data) {
const [_, angVels] = data.angVels.reduce((acc, vsArg) => {
const vs = vsArg.map(v => Math.abs(v));
const vs = fixAngVels(vsArg.slice(0, -1)).map(v => Math.abs(v));
const maxV = vs.reduce((acc, v) => Math.max(acc, v), 0);
const [accMaxV, _] = acc;
if (maxV >= accMaxV) {
@ -86,28 +92,29 @@ function loadRegression(data) {
data.parEncVels.forEach((vs, i) => {
const div = document.createElement('div');
newLinearRegressionChart(div,
angVels.slice(1, angVels.length - 1),
fixVels(data.encTimes, data.parEncPositions[i], vs),
{title: `Parallel Wheel ${i} Regression`, slope: 'y-position'});
angVels.slice(1, -1),
fixVels(data.encTimes.slice(0, -1), data.parEncPositions[i].slice(0, -1), vs.slice(0, -1)),
{title: `Parallel Wheel ${i} Regression`, slope: 'y-position', noIntercept: true});
deadWheelCharts.appendChild(div);
});
data.perpEncVels.forEach((vs, i) => {
const div = document.createElement('div');
newLinearRegressionChart(div,
angVels.slice(1, angVels.length - 1),
fixVels(data.encTimes, data.perpEncPositions[i], vs),
{title: `Perpendicular Wheel ${i} Regression`, slope: 'x-position'});
angVels.slice(1, -1),
fixVels(data.encTimes.slice(0, -1), data.perpEncPositions[i].slice(0, -1), vs.slice(0, -1)),
{title: `Perpendicular Wheel ${i} Regression`, slope: 'x-position', noIntercept: true});
deadWheelCharts.appendChild(div);
});
const setParams = (() => {
const appliedVoltages = data.voltages.map((v, i) =>
[...data.leftPowers, ...data.rightPowers].reduce((acc, ps) => Math.max(acc, ps[i]), 0) * v);
const allPowers = [...data.leftPowers, ...data.rightPowers];
const appliedVoltages = data.voltages.slice(0, -1).map((v, i) =>
allPowers.reduce((acc, ps) => Math.max(acc, ps[i]), 0) * v);
const setTrackWidthData = newLinearRegressionChart(
document.getElementById('trackWidthChart'),
[], [],
{title: 'Track Width Regression', slope: 'track width'}
{title: 'Track Width Regression', slope: 'track width', noIntercept: true}
);
return (kV, kS) => setTrackWidthData(angVels, appliedVoltages.map((v, i) =>
@ -119,6 +126,8 @@ function loadRegression(data) {
document.getElementById('update').addEventListener('click', () => {
setParams(parseFloat(kvInput.value), parseFloat(ksInput.value));
});
setParams(parseFloat(kvInput.value), parseFloat(ksInput.value));
}
const latestButton = document.getElementById('latest');
@ -130,10 +139,12 @@ latestButton.addEventListener('click', function() {
const a = document.createElement('a');
a.innerText = 'Download';
a.href = `/tuning/forward-ramp/${filename}`;
a.download = `forward-ramp-${filename}`;
a.href = `/tuning/angular-ramp/${filename}`;
a.download = `angular-ramp-${filename}`;
document.getElementById('download').appendChild(a);
const download = document.getElementById('download');
download.innerHTML = '';
download.appendChild(a);
return res.json();
} else {
@ -142,7 +153,12 @@ latestButton.addEventListener('click', function() {
}
})
.then(loadRegression)
.catch(console.log.bind(console));
.catch((e) => {
const deadWheelCharts = document.getElementById('deadWheelCharts');
deadWheelCharts.innerHTML = '';
console.log(e);
});
});
const browseInput = document.getElementById('browse');

View File

@ -9,6 +9,10 @@ body {
}
.content {
margin: auto;
}
header {
max-width: 600px;
margin: auto;
}
@ -39,48 +43,50 @@ details, a {
</head>
<body>
<div class="content">
<h1>RR Drive Encoder Angular Ramp Regression</h1>
<details></details>
<header>
<h1>RR Drive Encoder Angular Ramp Regression</h1>
<details></details>
<div id="download"></div>
<div id="download"></div>
<div id="rampChart">
<p>
<button id="latest">Latest</button>
<input id="browse" type="file" accept="application/json">
</p>
</div>
</header>
<div id="rampChart"></div>
<div id="trackWidthChart"></div>
</div>
<script>
function loadRegression(data) {
const leftEncVels = data.leftEncVels.map((vs, i) =>
fixVels(data.encTimes, data.leftEncPositions[i], vs));
fixVels(data.encTimes.slice(0, -1), data.leftEncPositions[i].slice(0, -1), vs.slice(0, -1)));
const rightEncVels = data.rightEncVels.map((vs, i) =>
fixVels(data.encTimes, data.rightEncPositions[i], vs));
fixVels(data.encTimes.slice(0, -1), data.rightEncPositions[i].slice(0, -1), vs.slice(0, -1)));
newLinearRegressionChart(
document.getElementById('rampChart'),
[
...leftEncVels.flatMap(vs => vs.map(v => -v)),
...rightEncVels.flatMap(vs => vs),
...leftEncVels.flatMap(vs => vs.slice(0, -1).map(v => -v)),
...rightEncVels.flatMap(vs => vs.slice(0, -1)),
],
[
...data.leftPowers.flatMap(ps => {
const psNew = ps.map((p, i) => -p * data.voltages[i]);
return psNew.slice(1, psNew.length - 1);
const psNew = ps.slice(0, -1).map((p, i) => -p * data.voltages[i]);
return psNew.slice(1, -1);
}),
...data.rightPowers.flatMap(ps => {
const psNew = ps.map((p, i) => p * data.voltages[i]);
return psNew.slice(1, psNew.length - 1);
const psNew = ps.slice(0, -1).map((p, i) => p * data.voltages[i]);
return psNew.slice(1, -1);
}),
],
{title: 'Ramp Regression', slope: 'kV', intercept: 'kS'}
);
const p = data.angVels.reduce((acc, vsArg) => {
const vs = vsArg.map(v => Math.abs(v));
const vs = fixAngVels(vsArg).map(v => Math.abs(v));
const maxV = vs.reduce((acc, v) => Math.max(acc, v), 0);
const [accMaxV, _] = acc;
if (maxV >= accMaxV) {
@ -88,7 +94,7 @@ function loadRegression(data) {
}
return acc;
}, [0, []]);
const angVels = p[1].slice(1, p[1].length - 1);
const angVels = p[1].slice(1, -1);
newLinearRegressionChart(
document.getElementById('trackWidthChart'),
@ -98,7 +104,7 @@ function loadRegression(data) {
+ rightEncVels.reduce((acc, vs) => acc + vs[i], 0) / data.rightEncVels.length)
* (data.type === 'mecanum' ? 0.5 : 1)
),
{title: 'Track Width Regression', slope: 'track width'}
{title: 'Track Width Regression', slope: 'track width', noIntercept: true}
);
}
@ -111,10 +117,12 @@ latestButton.addEventListener('click', function() {
const a = document.createElement('a');
a.innerText = 'Download';
a.href = `/tuning/forward-ramp/${filename}`;
a.download = `forward-ramp-${filename}`;
a.href = `/tuning/angular-ramp/${filename}`;
a.download = `angular-ramp-${filename}`;
document.getElementById('download').appendChild(a);
const download = document.getElementById('download');
download.innerHTML = '';
download.appendChild(a);
return res.json();
} else {

View File

@ -9,6 +9,10 @@ body {
}
.content {
margin: auto;
}
header {
max-width: 600px;
margin: auto;
}
@ -39,25 +43,27 @@ details, a {
</head>
<body>
<div class="content">
<h1>RR Forward Ramp Regression</h1>
<details></details>
<header>
<h1>RR Forward Ramp Regression</h1>
<details></details>
<div id="download"></div>
<div id="download"></div>
<div id="rampChart">
<p>
<button id="latest">Latest</button>
<input id="browse" type="file" accept="application/json">
</p>
</div>
</header>
<div id="rampChart"></div>
</div>
<script>
function loadRegression(data) {
const forwardEncVels = data.forwardEncVels.flatMap((vs, i) =>
fixVels(data.encTimes, data.forwardEncPositions[i], vs));
fixVels(data.encTimes.slice(0, -1), data.forwardEncPositions[i].slice(0, -1), vs.slice(0, -1)));
const appliedVoltages = data.forwardEncVels.flatMap(vs => {
const voltages = data.voltages.map((v, i) =>
const voltages = data.voltages.slice(0, -1).map((v, i) =>
data.powers.reduce((acc, ps) => Math.max(acc, ps[i]), 0) * v);
return voltages.slice(1, voltages.length - 1);
@ -66,7 +72,7 @@ function loadRegression(data) {
newLinearRegressionChart(
document.getElementById('rampChart'),
forwardEncVels, appliedVoltages,
{title: 'Ramp Regression', slope: 'kV', intercept: 'kS'}
{title: 'Ramp Regression', slope: 'kV', intercept: 'kS', noIntercept: false}
);
}
@ -82,7 +88,9 @@ latestButton.addEventListener('click', function() {
a.href = `/tuning/forward-ramp/${filename}`;
a.download = `forward-ramp-${filename}`;
document.getElementById('download').appendChild(a);
const download = document.getElementById('download');
download.innerHTML = '';
download.appendChild(a);
return res.json();
} else {