При помощи элементов управления (окна, слайдеры) обеспечить задание параметров ПИД-регулятора и задающего значения (уставки) по углу отклонения маятника. Простая программная реализация регулятора предполагает, что, подобно модели маятника, он будет описан в разностной форме.
В качестве управляющего воздействия (выход регулятора) принять момент силы, прикладываемый к маятнику в точке его подвеса.
При расчете состояния маятника на каждом шагу реализовывать управляющее воздействие, соблюдая принцип включения регулятора в контур с обратной связью.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body
{
text-align: center;
max-width: 50em;
margin: 0 auto;
padding: 8px;
}
h2
{
text-align: center;
}
#canvas
{
display: block;
margin: 0 auto;
padding: 0;
background: #f1f5f5;
pointer-events: none;
}
.quantity-block
{
display: grid;
grid-template-columns: 100px 250px;
justify-content: center;
align-items: flex-start;
gap: 1em;
padding: 10px ;
}
.quantity-block label
{
text-align:start;
}
.button
{
background-color: white;
color: black;
border: 2px solid #555555;
padding: 15px 20px;
text-align: center;
cursor: pointer;
border-radius: 8px;
}
input, select, button
{
font: inherit;
}
</style>
</head>
<body>
<canvas id="canvas" width="350" height="350"></canvas>
<div class="quantity-block">
<label>Начальный угол маятника</label>
<input name="α" type="range" value="0" min="-1.57" max="1.57" step="any">
<label> Масса шарика</label>
<input name="m" type="number" value="1" step="any">
<label>Длина маятника</label>
<input name="l" type="number" value="5" step="any">
<label>Коэффициент сопротивления окружающей среды</label>
<input name="kf" type="number" value="0.01" step="any">
</div>
<button class="button" onclick="toggleSimulation();">Моделировать</button>
<script>
var canvas = $("#canvas");
var context = canvas.getContext("2d");
context.lineWidth = 3;
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var α = 0;
var ω = 0;
var m = 0.1;
var g = 9.8;
var l = 0.5;
var kf = 1;
var M = 0;
var isSimulationOn = false;
var lastT = performance.now();
function $(selector, context)
{
return (context || document).querySelector(selector);
}
function $$(selector, context)
{
return Array.from((context || document).querySelectorAll(selector));
}
function draw(t)
{
requestAnimationFrame(draw);
var dt = Math.min(1 / 24, (t - lastT) / 1000);
lastT = t;
if(isSimulationOn)
{
α += dt * ω;
ω += dt * (- g / l * Math.sin(α) - kf * l / m * ω * ω * Math.sign(ω) + M / m / l / l);
}
var angle = α + Math.PI / 2;
var size = Math.min(WIDTH, HEIGHT);
var ox = WIDTH / 2;
var rPend = size * 0.45 * Math.tanh(0.5 * l);
var rBall = size * 0.05 * Math.tanh(0.5 * m);
var x = ox - rPend * Math.cos(angle);
var y = rPend * Math.sin(angle) + 2;
context.clearRect(0, 0, WIDTH, HEIGHT);
context.beginPath();
context.arc(x, y, rBall, 0, Math.PI * 2);
context.fill();
context.moveTo(0, 2);
context.lineTo(WIDTH, 2);
context.moveTo(ox, 0);
context.lineTo(x, y);
context.stroke();
}
function updateVariables()
{
$$(".quantity-block input[name]").forEach(function (input)
{
assignVariable(input.name, input.value);
});
}
function assignVariable(name, value)
{
if (name in window)
{
window[name] = Number(value);
if (name === "α") ω = 0;
if (name === "l" || name === "m") window[name] = Math.max(0.01, window[name]);
}
}
function toggleSimulation()
{
isSimulationOn = !isSimulationOn;
$(".button").textContent = isSimulationOn ? "Остановить" : "Моделировать"
updateVariables();
}
$(".quantity-block").addEventListener("input", function (event)
{
var input = event.target;
if (input.matches("input[name]"))
{
assignVariable(input.name, input.value);
}
});
requestAnimationFrame(draw);
toggleSimulation();
</script>
</body>
</html>