Skip to content

Commit

Permalink
Regression improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
rbrott committed Mar 2, 2023
1 parent 9651735 commit 80ea431
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 64 deletions.
32 changes: 29 additions & 3 deletions TeamCode/src/main/assets/tuning/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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',
Expand All @@ -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');
Expand Down Expand Up @@ -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;
});

Expand Down
80 changes: 48 additions & 32 deletions TeamCode/src/main/assets/tuning/dead-wheel-angular-ramp.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
}

.content {
margin: auto;
}

header {
max-width: 600px;
margin: auto;
}
Expand Down Expand Up @@ -44,36 +48,38 @@
</head>
<body>
<div class="content">
<h1>RR Dead Wheel Angular Ramp Regression</h1>
<details></details>

<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>

<div id="trackWidthChart">
<header>
<h1>RR Dead Wheel Angular Ramp Regression</h1>
<details></details>

<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>

<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>

<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) {
Expand All @@ -86,28 +92,29 @@ <h1>RR Dead Wheel Angular Ramp Regression</h1>
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) =>
Expand All @@ -119,6 +126,8 @@ <h1>RR Dead Wheel Angular Ramp Regression</h1>
document.getElementById('update').addEventListener('click', () => {
setParams(parseFloat(kvInput.value), parseFloat(ksInput.value));
});

setParams(parseFloat(kvInput.value), parseFloat(ksInput.value));
}

const latestButton = document.getElementById('latest');
Expand All @@ -130,10 +139,12 @@ <h1>RR Dead Wheel Angular Ramp Regression</h1>

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 {
Expand All @@ -142,7 +153,12 @@ <h1>RR Dead Wheel Angular Ramp Regression</h1>
}
})
.then(loadRegression)
.catch(console.log.bind(console));
.catch((e) => {
const deadWheelCharts = document.getElementById('deadWheelCharts');
deadWheelCharts.innerHTML = '';

console.log(e);
});
});

const browseInput = document.getElementById('browse');
Expand Down
48 changes: 28 additions & 20 deletions TeamCode/src/main/assets/tuning/drive-encoder-angular-ramp.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
}

.content {
margin: auto;
}

header {
max-width: 600px;
margin: auto;
}
Expand Down Expand Up @@ -39,56 +43,58 @@
</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) {
return [maxV, vs];
}
return acc;
}, [0, []]);
const angVels = p[1].slice(1, p[1].length - 1);
const angVels = p[1].slice(1, -1);

newLinearRegressionChart(
document.getElementById('trackWidthChart'),
Expand All @@ -98,7 +104,7 @@ <h1>RR Drive Encoder Angular Ramp Regression</h1>
+ 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}
);
}

Expand All @@ -111,10 +117,12 @@ <h1>RR Drive Encoder Angular Ramp Regression</h1>

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 {
Expand Down
26 changes: 17 additions & 9 deletions TeamCode/src/main/assets/tuning/forward-ramp.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
}

.content {
margin: auto;
}

header {
max-width: 600px;
margin: auto;
}
Expand Down Expand Up @@ -39,25 +43,27 @@
</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);
Expand All @@ -66,7 +72,7 @@ <h1>RR Forward Ramp Regression</h1>
newLinearRegressionChart(
document.getElementById('rampChart'),
forwardEncVels, appliedVoltages,
{title: 'Ramp Regression', slope: 'kV', intercept: 'kS'}
{title: 'Ramp Regression', slope: 'kV', intercept: 'kS', noIntercept: false}
);
}

Expand All @@ -82,7 +88,9 @@ <h1>RR Forward Ramp Regression</h1>
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 {
Expand Down

0 comments on commit 80ea431

Please sign in to comment.