From 45d6982d17c468a2a734b2898ad116d8b82e5f3f Mon Sep 17 00:00:00 2001 From: mss Date: Mon, 26 Oct 2026 15:19:12 +0400 Subject: [PATCH] =?UTF-8?q?ai:=20=D1=81=D1=82=D1=83=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D1=87=D0=B5=D1=81=D0=BA=D0=B0=D1=8F=20=D0=BA=D0=BE=D0=BF=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BB=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BD=D0=BE=D0=B9=205=20(DDS-=D0=B3=D0=B5=D0=BD=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=D1=82=D0=BE=D1=80=20RP2040=20+=20=D0=90=D0=A7=D0=A5=20?= =?UTF-8?q?=D0=B8=D0=BD=D1=82=D0=B5=D0=B3=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B0)=20=D0=B8=D0=B7=20=D0=A0=D0=9F=D0=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 112 ++++++++ ...peredatochnaya-funkciya-na-ou-student.docx | Bin 0 -> 42221 bytes stub/01-dds-generator-rp2040.ino | 243 ++++++++++++++++++ stub/02-izmerenie-achh.py | 138 ++++++++++ stub/03-blank-otchyota.md | 150 +++++++++++ task.md | 236 +++++++++++++++++ 6 files changed, 879 insertions(+) create mode 100644 README.md create mode 100644 print/methodical-lab5-peredatochnaya-funkciya-na-ou-student.docx create mode 100644 stub/01-dds-generator-rp2040.ino create mode 100644 stub/02-izmerenie-achh.py create mode 100644 stub/03-blank-otchyota.md create mode 100644 task.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..a116da8 --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# Лабораторная: интегратор и дифференциатор на ОУ (АЧХ/ФЧХ + Monte Carlo + автосвип) + +Комплект к теме `topics/lab5-peredatochnaya-funkciya-na-ou.md` +(«Электронные устройства мехатронных и робототехнических систем», +Б1.В.06, 5 семестр, 4 ч). + +> AI-сгенерировано (2026-10-26), требует проверки преподавателем +> перед использованием на стенде. + +## Контракт + +Оформлено по контракту `labs/`: + +``` +labs/lab5-peredatochnaya-funkciya-na-ou/ +├── meta.yaml ← контракт +├── task.md ← задание студенту +├── method.md ← методичка преподавателю +├── README.md ← этот файл +├── print/ ← брошюры DOCX (rpd methodical) +└── stub/ + ├── 01-dds-generator-rp2040.ino ← генератор-свип (Arduino, RP2040) + ├── 02-izmerenie-achh.py ← автоснятие АЧХ по Serial + └── 03-blank-otchyota.md ← бланк отчёта студента +``` + +## Новое относительно `topics/lab5-peredatochnaya-funkciya-na-ou.md` + +1. **Monte Carlo по допускам** (`.step param`) — новый для курса анализ + в симуляторе: разброс K и f_c при ±5 % на компонентах, доля годных. +2. **Генератор-свип на RP2040** — автономный генератор для натурной + части: прямоугольник (ШИМ) и синус (DDS), логарифмический свип, + синхроимпульс для осциллографа, измерение амплитуды на ADC0. +3. **Хост-скрипт автоснятия АЧХ** — управление генератором по Serial, + сбор точек, экспорт CSV, построение графика. +4. **Явное разделение практики и лаборатории:** симуляция — на практическом + занятии (2 ч), железо — на лабораторном (4 ч), отчёт — СРС. + +## Предупреждения по стенду + +- **ADC0 RP2040: 0…3,3 В.** Сигнал с выхода исследуемой цепи ≥ 3,3 В + подавать **только через делитель** или пиковый детектор. +- **Питание ОУ:** LM358 работает при однополярном +5…+12 В, TL081 — + при ±12 В. Уточнить по фактическому стенду. +- **Синус DDS:** качество зависит от внешнего ФНЧ после ШИМ (см. схему + ниже). Без фильтра на выходе будет меандр переменной скважности, + а не синус. + +## Схема DDS-генератора (внешняя обвязка) + +ШИМ сам по себе даёт **меандр переменной скважности**, а не синус. +Синус получается выделением огибающей фильтром. Поэтому на выходе +GP15 нужен внешний **ФНЧ**: + +``` +GP15 (ШИМ 62,5 кГц) ──[R1 1к]──┬──[R2 1к]──┬──────► OUT (вход DUT) + │ │ + [C1 47н] [C2 47н] + │ │ + GND GND +``` + +Пассивный ФНЧ 2-го порядка, **f_c ≈ 3,4 кГц**: + +- подавление несущей: `(62,5 / 3,4)² ≈ 338 ≈ 50 дБ` → пульсации ≈ 10 мВ; +- рабочий диапазон генератора: **≈ 1 Гц … 1 кГц** (для звена + с f_c = 16…34 Гц — с запасом). + +### Две оговорки + +1. **Согласование сопротивлений.** Выход фильтра ≈ 1 кОм, вход + интегратора — R1 = 10 кОм → делитель 10/11, просадка ≈ 9 %. + Если нужна точность — поставить буфер-повторитель на ОУ + (**Sallen–Key**: тот же 2-й порядок + низкое выходное). + +2. **Постоянная составляющая.** При скважности 50 % среднее = `Uпит/2` + = 1,65 В, синус смещён. Где смещение недопустимо — разделительный + `C = 1 мкФ` последовательно + `R = 47 кОм` на землю (ФВЧ ≈ 3,4 Гц), + либо уменьшить амплитуду командой `M`. + +### Варианты фильтра под другую несущую + +| SAMPLE_HZ | R | C | f_c | +|---|---|---|---| +| 62,5 кГц | 1 кОм | 47 нФ | 3,4 кГц | +| 62,5 кГц | 2,2 кОм | 22 нФ | 3,3 кГц | +| 31,25 кГц | 2,2 кОм | 47 нФ | 1,5 кГц | + +## Как использовать + +1. `task.md` + `stub/03-blank-otchyota.md` — студенту. +2. `stub/01-dds-generator-rp2040.ino` — прошить в RP2040 заранее + (или дать студенту в задании 4). +3. `stub/02-izmerenie-achh.py` — запускается на ПК студента (нужен pyserial). +4. `method.md` — только преподавателю. +5. Брошюры: `uv run rpd methodical lab5-peredatochnaya-funkciya-na-ou`. + +## Связи + +- **Практика** `pr5-raschet-analogovyh-ustrojstv` — расчётная часть + (K, R1, R2, фильтры) выполняется там. +- **Лекция** `le5-metody-modelirovaniya` — типы анализа; Monte Carlo + и `.step` — прямое применение. +- **Лекция** `le5-proektirovanie-analogovyh-ustrojstv` — топология ОУ. +- **ТАУ** (`b1-o-23`) — передаточные функции и типовые звенья идут + параллельно в 5 семестре; эта работа даёт им физическую реализацию. + +## Статус + +Черновик (draft). Тема `lab5-peredatochnaya-funkciya-na-ou` существует +в `topics/`; комплект в `labs/` — подготовлен для интеграции после +проверки преподавателем и согласования сквозной работы по мехатронике. diff --git a/print/methodical-lab5-peredatochnaya-funkciya-na-ou-student.docx b/print/methodical-lab5-peredatochnaya-funkciya-na-ou-student.docx new file mode 100644 index 0000000000000000000000000000000000000000..16305c23fb129beb10319f03fa1c5d14ac7980eb GIT binary patch literal 42221 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 literal 0 HcmV?d00001 diff --git a/stub/01-dds-generator-rp2040.ino b/stub/01-dds-generator-rp2040.ino new file mode 100644 index 0000000..e0b5f73 --- /dev/null +++ b/stub/01-dds-generator-rp2040.ino @@ -0,0 +1,243 @@ +/* + * DDS-генератор сигналов для лабораторных работ + * Платформа: RP2040 (Raspberry Pi Pico, Arduino core — earlephilhower) + * + * ЧЕРНОВИК (AI-сгенерировано, 2026-10-26). Требует проверки преподавателем + * на реальном стенде: номиналы RC-фильтра, диапазоны частот, точность ADC. + * + * Возможности: + * - прямоугольник : аппаратный ШИМ, точная частота, независимая скважность; + * - синус : таблица 256 точек + аккумулятор фазы (DDS) в таймерном + * ISR -> ШИМ (несущая = SAMPLE_HZ = 62,5 кГц) -> внешний + * ФНЧ (схема ниже); амплитуда — командой M; + * - свип : логарифмический (f0..f1, N точек) с синхроимпульсом + * на отдельном пине — для осциллографа/хост-скрипта; + * - измерение : ADC0 (размах за окно) — амплитуда на выходе + * исследуемой цепи (DUT) для автоматической АЧХ. + * + * ─── СХЕМА СИНУСА (обязательна: ШИМ даёт меандр, а не синус) ─── + * + * GP15 ──[R1]──┬──[R2]──┬──────► OUT (вход DUT) + * │ │ + * [C1] [C2] + * │ │ + * GND GND + * + * Пассивный ФНЧ 2-го порядка. При несущей 62,5 кГц: + * R1 = R2 = 1 кОм, C1 = C2 = 47 нФ -> f_c ≈ 3,4 кГц + * подавление несущей ≈ (62,5/3,4)^2 ≈ 50 дБ -> пульсации ~10 мВ + * Номиналы для другой несущей: f_c и постоянную RC смотреть в README. + * + * ВНИМАНИЕ (важно): выходное сопротивление фильтра ~1 кОм. Вход + * интегратора — R1 = 10 кОм, получается делитель 10/11 (просадка ~9 %). + * Если это критично — поставить буфер-повторитель на ОУ (Sallen-Key): + * тот же фильтр 2-го порядка + низкое выходное сопротивление. + * + * Постоянная составляющая: при скважности 50 % среднее = 1,65 В, синус + * смещён на Uпит/2. Где смещение недопустимо — разделительный C = 1 мкФ + * последовательно + R = 47 кОм на землю (ФВЧ ≈ 3,4 Гц), либо уменьшить + * амплитуду командой M. + * + * Протокол команд (Serial, 115200, строка + \n): + * F <Гц> — установить частоту (1..500000) + * W Q | W S — режим: Q — прямоугольник, S — синус + * D <0..255> — скважность прямоугольника (128 = 50 %) + * M <0..127> — амплитуда синуса (127 = максимум) + * A — однократно измерить амплитуду на ADC0 + * -> ответ "A <мВ размах>" + * SWEEP + * — логарифмический свип; на каждой точке выдаёт + * "P <Гц> <мВ>" и импульс синхро на PIN_TRIG + * STOP — прервать свип + */ + +#include +#include + +// ─────────────────────────── пины ─────────────────────────── +#define PIN_OUT 15 // выход сигнала (прямоугольник — прямо; синус — через RC) +#define PIN_TRIG 14 // синхроимпульс (для осциллографа) +#define PIN_ADC 26 // ADC0 = A0 — вход измерения (выход DUT через делитель!) + +// ─────────────────────── параметры DDS ────────────────────── +// ВАЖНО: несущая ШИМ и частота обновления выборок должны СОВПАДАТЬ. +// Значение скважности применяется один раз за период ШИМ; если обновлять +// выборки с другой частотой, появляется джиттер и биения на выходе фильтра. +// При 16 мкс период ISR занимает ~3 % CPU RP2040; при перегрузке — +// снизить до 31250 (тогда f_c фильтра пересчитать вниз пропорционально). +#define CARRIER_HZ 62500UL // несущая ШИМ для синуса (= частота выборок) +#define SAMPLE_HZ 62500UL // частота обновления выборок синуса +#define PWM_RANGE 255 // 8-бит (для синуса); прямоугольнику хватает +#define AMP_MAX 127 // максимальная амплитуда выборки (единиц duty) + +// ───────────────────────── состояние ──────────────────────── +enum WaveMode : uint8_t { MODE_SQUARE = 0, MODE_SINE = 1 }; + +static volatile uint32_t g_freq = 1000; // текущая частота, Гц +static volatile uint8_t g_duty = 128; // 0..255 (прямоугольник) +static volatile uint8_t g_amp = AMP_MAX;// амплитуда синуса, 0..AMP_MAX +static volatile WaveMode g_mode = MODE_SQUARE; +static volatile uint32_t g_phase = 0; // аккумулятор фазы (32 бит) +static volatile bool g_sweep = false; // флаг активного свипа +static int8_t g_lut[256]; // sin: -127..127 + +// ─────────────────────── таблица синуса ───────────────────── +static void buildLut() { + for (int i = 0; i < 256; ++i) { + g_lut[i] = (int8_t)lroundf(127.0f * sinf(2.0f * (float)M_PI * i / 256.0f)); + } +} + +// ───────────── ISR: обновление ШИМ для синуса (DDS) ───────── +static bool onSample(repeating_timer_t *) { + if (g_mode == MODE_SINE) { + // шаг фазы = f * 2^32 / Fs (64-бит, чтобы не переполнить) + uint32_t step = + (uint32_t)((uint64_t)g_freq * (1ULL << 32) / SAMPLE_HZ); + g_phase += step; + uint8_t idx = (uint8_t)(g_phase >> 24); + // центрируем на 128 и масштабируем амплитудой (g_amp 0..127) + int32_t s = ((int32_t)g_lut[idx] * g_amp) / 127; + analogWrite(PIN_OUT, (uint8_t)(128 + s)); + } + return true; +} + +static repeating_timer_t g_timer; + +// ─────────────────── применить режим/частоту ──────────────── +static void applySquare() { + analogWriteFreq(g_freq); + analogWriteRange(PWM_RANGE); + // после analogWriteFreq/analogWriteRange текущее значение сбрасывается: + analogWrite(PIN_OUT, g_duty); +} + +static void applySine() { + // частота ШИМ = SAMPLE_HZ (совпадает с ISR), размах — 8 бит + analogWriteFreq(CARRIER_HZ); + analogWriteRange(PWM_RANGE); +} + +static void setFrequency(uint32_t hz) { + if (hz < 1) hz = 1; + if (hz > 500000) hz = 500000; + g_freq = hz; + if (g_mode == MODE_SQUARE) applySquare(); +} + +static void setMode(WaveMode m) { + g_mode = m; + if (m == MODE_SQUARE) applySquare(); + else applySine(); +} + +// ─────────────────── измерение амплитуды (ADC) ────────────── +// Возвращает размах (пик-пик) в милливольтах за окно window_ms. +// TODO (студенту): заменить окно по времени на окно по числу периодов +// измеряемой частоты, чтобы размах считался по целому числу периодов. +static uint32_t measureAmplitudeMv(uint32_t window_ms = 50) { + uint16_t vmin = 4095, vmax = 0; + uint32_t t0 = millis(); + while ((millis() - t0) < window_ms) { + uint16_t v = analogRead(PIN_ADC); + if (v < vmin) vmin = v; + if (v > vmax) vmax = v; + } + // ADC 12 бит, Vref = 3,3 В + return (uint32_t)((uint32_t)(vmax - vmin) * 3300UL / 4095UL); +} + +// ───────────────────────── свип ───────────────────────────── +static void triggerPulse() { + digitalWrite(PIN_TRIG, HIGH); + delayMicroseconds(50); + digitalWrite(PIN_TRIG, LOW); +} + +static void runSweep(float f0, float f1, int n, uint32_t dwell_ms) { + if (n < 2) n = 2; + if (f0 < 1) f0 = 1; + if (f1 <= f0) f1 = f0 * 10.0f; + g_sweep = true; + + const float ratio = powf(f1 / f0, 1.0f / (float)(n - 1)); + float f = f0; + for (int i = 0; i < n && g_sweep; ++i) { + setFrequency((uint32_t)lroundf(f)); + triggerPulse(); // метка точки для осциллографа + delay(dwell_ms); // дать DUT/фильтру устояться + uint32_t mv = measureAmplitudeMv(); + Serial.print("P "); // точка АЧХ: частота + размах + Serial.print((uint32_t)lroundf(f)); + Serial.print(' '); + Serial.println(mv); + f *= ratio; + } + Serial.println("DONE"); + setFrequency((uint32_t)f0); // вернуться к началу +} + +// ─────────────────────── разбор команд ────────────────────── +static void handleLine(char *line) { + char *cmd = strtok(line, " \t"); + if (!cmd) return; + + if (!strcasecmp(cmd, "F")) { + char *a = strtok(nullptr, " \t"); + if (a) setFrequency((uint32_t)strtoul(a, nullptr, 10)); + Serial.print("OK F "); Serial.println(g_freq); + } else if (!strcasecmp(cmd, "W")) { + char *a = strtok(nullptr, " \t"); + if (a) setMode((!strcasecmp(a, "S")) ? MODE_SINE : MODE_SQUARE); + Serial.print("OK W "); Serial.println(g_mode == MODE_SINE ? "S" : "Q"); + } else if (!strcasecmp(cmd, "D")) { + char *a = strtok(nullptr, " \t"); + if (a) { g_duty = (uint8_t)constrain(atoi(a), 0, 255); applySquare(); } + Serial.print("OK D "); Serial.println(g_duty); + } else if (!strcasecmp(cmd, "M")) { + char *a = strtok(nullptr, " \t"); + if (a) g_amp = (uint8_t)constrain(atoi(a), 0, AMP_MAX); + Serial.print("OK M "); Serial.println(g_amp); + } else if (!strcasecmp(cmd, "A")) { + Serial.print("A "); Serial.println(measureAmplitudeMv()); + } else if (!strcasecmp(cmd, "SWEEP")) { + char *a0 = strtok(nullptr, " \t"); + char *a1 = strtok(nullptr, " \t"); + char *a2 = strtok(nullptr, " \t"); + char *a3 = strtok(nullptr, " \t"); + if (a0 && a1 && a2 && a3) { + runSweep(atof(a0), atof(a1), atoi(a2), strtoul(a3, nullptr, 10)); + } + } else if (!strcasecmp(cmd, "STOP")) { + g_sweep = false; + } +} + +// ────────────────────────── setup/loop ────────────────────── +void setup() { + Serial.begin(115200); + buildLut(); + pinMode(PIN_TRIG, OUTPUT); + digitalWrite(PIN_TRIG, LOW); + analogReadResolution(12); // ADC0 — 12 бит + add_repeating_timer_us(-(1000000L / SAMPLE_HZ), onSample, nullptr, &g_timer); + setMode(MODE_SQUARE); + setFrequency(1000); + Serial.println("# DDS generator ready (RP2040). help: F/W/D/A/SWEEP/STOP"); +} + +void loop() { + static char buf[96]; + static size_t len = 0; + while (Serial.available()) { + char c = (char)Serial.read(); + if (c == '\n' || c == '\r') { + buf[len] = '\0'; + if (len) handleLine(buf); + len = 0; + } else if (len < sizeof(buf) - 1) { + buf[len++] = c; + } + } +} diff --git a/stub/02-izmerenie-achh.py b/stub/02-izmerenie-achh.py new file mode 100644 index 0000000..fcb3ca4 --- /dev/null +++ b/stub/02-izmerenie-achh.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +""" +Хост-скрипт: автоматическое снятие АЧХ с DDS-генератора (RP2040). + +ЧЕРНОВИК (AI-сгенерировано, 2026-10-26). Требует проверки преподавателем. + +Управляет генератором 01-dds-generator-rp2040.ino по Serial, запускает +логарифмический свип, собирает точки "P <Гц> <мВ>" и строит АЧХ в дБ. + +Использование: + python 02-izmerenie-achh.py --port /dev/ttyACM0 \ + --f0 10 --f1 100000 --n 25 --dwell 150 --out achh.csv + +Зависимости: pyserial (обязательно), matplotlib (опционально, для графика). + pip install pyserial matplotlib + +ПРОТОКОЛ (строки, \n): + -> W S режим синуса + -> SWEEP + <- P <Гц> <мВ> точка АЧХ + <- DONE свип завершён + +ВНИМАНИЕ: вход ADC0 генератора — 0..3,3 В. Сигнал с выхода исследуемой +цепи выше 3,3 В подавать только через делитель/пиковый детектор. +Нормировка на выходе: gain_dB = 20*log10(Uout/Uin). Входное напряжение +Uin берётся из аргумента --uin (действующая или амплитудная — как задано, +скрипт работает с размахом, мВ). +""" + +from __future__ import annotations + +import argparse +import csv +import math +import sys +import time + +try: + import serial # type: ignore +except ImportError: # pragma: no cover + print("Нужен pyserial: pip install pyserial", file=sys.stderr) + raise SystemExit(2) + + +def open_port(port: str, baud: int = 115200, timeout: float = 0.2): + return serial.Serial(port, baud, timeout=timeout) + + +def send(ser, line: str) -> None: + ser.write((line + "\n").encode("ascii")) + ser.flush() + + +def collect_sweep(ser, f0: float, f1: float, n: int, dwell: int, + uin_mv: float, timeout_s: float = 120.0): + """Запустить свип и вернуть список (f, uout_mv, gain_db).""" + send(ser, "W S") # синус + time.sleep(0.2) + send(ser, f"SWEEP {f0:.3f} {f1:.3f} {n} {dwell}") + + points: list[tuple[float, float, float]] = [] + t0 = time.time() + while True: + if time.time() - t0 > timeout_s: + print("таймаут свипа", file=sys.stderr) + break + raw = ser.readline().decode("ascii", "replace").strip() + if not raw: + continue + if raw == "DONE": + break + if raw.startswith("P "): + _, fs, mv = raw.split() + f = float(fs) + uout = float(mv) + gain = 20.0 * math.log10(max(uout, 1e-6) / max(uin_mv, 1e-6)) + points.append((f, uout, gain)) + print(f" {f:>9.1f} Гц {uout:>7.1f} мВ {gain:+6.2f} дБ") + return points + + +def write_csv(path: str, points) -> None: + with open(path, "w", newline="", encoding="utf-8") as fh: + w = csv.writer(fh) + w.writerow(["freq_hz", "uout_mv", "gain_db"]) + for row in points: + w.writerow([f"{row[0]:.3f}", f"{row[1]:.3f}", f"{row[2]:.4f}"]) + print(f"CSV: {path}") + + +def plot(points) -> None: + try: + import matplotlib.pyplot as plt # type: ignore + except ImportError: + print("matplotlib нет — график пропущен (CSV сохранён)", file=sys.stderr) + return + xs = [p[0] for p in points] + ys = [p[2] for p in points] + plt.semilogx(xs, ys, "o-") + plt.xlabel("Частота, Гц") + plt.ylabel("Усиление, дБ") + plt.grid(True, which="both") + plt.title("АЧХ (автоснятие с RP2040 DDS)") + plt.tight_layout() + plt.show() + + +def main() -> int: + ap = argparse.ArgumentParser(description="Автоснятие АЧХ через RP2040 DDS") + ap.add_argument("--port", required=True, help="например /dev/ttyACM0 или COM5") + ap.add_argument("--f0", type=float, default=10.0) + ap.add_argument("--f1", type=float, default=100000.0) + ap.add_argument("--n", type=int, default=25) + ap.add_argument("--dwell", type=int, default=150, help="мс на точку") + ap.add_argument("--uin", type=float, default=500.0, + help="входное напряжение (размах), мВ") + ap.add_argument("--out", default="achh.csv") + ap.add_argument("--plot", action="store_true") + args = ap.parse_args() + + ser = open_port(args.port) + time.sleep(0.5) + ser.reset_input_buffer() + try: + points = collect_sweep(ser, args.f0, args.f1, args.n, args.dwell, args.uin) + finally: + ser.close() + if not points: + print("точек не получено", file=sys.stderr) + return 1 + write_csv(args.out, points) + if args.plot: + plot(points) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/stub/03-blank-otchyota.md b/stub/03-blank-otchyota.md new file mode 100644 index 0000000..d308d52 --- /dev/null +++ b/stub/03-blank-otchyota.md @@ -0,0 +1,150 @@ +# Бланк отчёта — интегратор/дифференциатор на ОУ + +Лабораторная `lab5-peredatochnaya-funkciya-na-ou`, 4 ч. +ФИО / группа / вариант: _____________________________ + +> Файл заполняется студентом. Все значения — с единицами измерения. + +--- + +## 1. Параметры варианта + +| Параметр | Значение | +|---|---| +| R1 | кОм | +| R2 | кОм | +| C1 | мкФ | +| U_пит | ± В | + +**Расчёт (до эксперимента):** + +- K = R2/R1 = _____ +- τ = R2·C1 = _____ мс +- f_c = 1/(2π·τ) = _____ Гц + +--- + +## 2. Симуляция: АЧХ/ФЧХ (задание 1) + +Директива: `.ac dec 20 1 100k` + +| Параметр | Расчёт | Из симуляции | Расхождение | +|---|---|---|---| +| Усиление на плоском участке, дБ | | | | +| f_c, Гц | | | | +| Наклон за f_c, дБ/дек | −20 | | | + +Вставить графики АЧХ и ФЧХ ↓ + +``` +(место для скриншота LTSpice) +``` + +--- + +## 3. Monte Carlo (задание 2) + +Директивы: + +``` +.step param tol ... +``` + +Число прогонов: ____ + +| Параметр | Среднее | СКО | Мин | Макс | Доля в ±10 % | +|---|---|---|---|---|---| +| K | | | | | | +| f_c, Гц | | | | | | + +Гистограммы ↓ + +``` +(место для гистограмм) +``` + +**Вывод о вкладе компонентов:** + +- + +--- + +## 4. Железо: переходные процессы (задание 3) + +### 4.1. Интегратор (прямоугольник 1 кГц) + +| Параметр | Расчёт | Измерение | +|---|---|---| +| Размах пилы U_m, В | | | +| Частота входного сигнала, кГц | 1,0 | | + +Осциллограмма ↓ + +``` +(место для осциллограммы) +``` + +### 4.2. Дифференциатор (пила 1 кГц) + +| Параметр | Расчёт | Измерение | +|---|---|---| +| Амплитуда импульса, В | | | + +### 4.3. Роль резистора утечки + +Что меняется без R2: ______________________________ + +--- + +## 5. Железо: автоснятие АЧХ (задание 4) + +Команда запуска: + +``` +python 02-izmerenie-achh.py --port ____ --f0 10 --f1 100000 --n 25 --dwell 150 --uin ___ --out achh.csv +``` + +Таблица точек (первые 5 и последние 5): + +| f, Гц | U_вых, мВ | K, дБ | +|---|---|---| +| | | | +| | | | + +Совмещённый график (расчёт / модель / эксперимент) ↓ + +``` +(место для графика) +``` + +--- + +## 6. Сводная таблица (задание 5) + +| Параметр | Расчёт | Симуляция | Железо | +|---|---|---|---| +| K | | | | +| τ, мс | | | | +| f_c, Гц | | | | + +**Причины расхождений:** + +1. Расчёт vs симуляция: ______________________________ +2. Симуляция vs железо: ______________________________ +3. Роль Monte Carlo: ______________________________ + +--- + +## 7. Выводы + +- +- +- + +--- + +## 8. Ответы на контрольные вопросы + +1. +2. +3. diff --git a/task.md b/task.md new file mode 100644 index 0000000..e18dbaf --- /dev/null +++ b/task.md @@ -0,0 +1,236 @@ +# Лабораторная работа + +## Интегратор и дифференциатор на ОУ: передаточная функция, АЧХ/ФЧХ и разброс по допускам + +**Дисциплина:** Электронные устройства мехатронных и робототехнических +систем (Б1.В.06, очная форма) +**Семестр:** 5 · **Объём:** 4 часа · **Тема:** `lab5-peredatochnaya-funkciya-na-ou` +**Форма:** черновик — в учебный план пока не привязана + +> AI-сгенерировано (2026-10-26), требует проверки преподавателем. + +--- + +## 1. Легенда + +В мехатронном модуле сигнал датчика проходит через аналоговую обработку: +фильтрацию, интегрирование, выделение скорости (дифференцирование). +Каждое такое звено описывается передаточной функцией, и от его параметров +(K, τ) зависит поведение всей системы управления. + +**Задача работы:** убедиться, что аналоговое звено на операционном +усилителе действительно реализует расчётную передаточную функцию — сначала +в симуляторе, затем на макетной плате. И понять, как **допуски** +резисторов и конденсаторов влияют на параметры звена в серии. + +Работа состоит из двух частей: + +- **Симуляция** (LTSpice): АЧХ/ФЧХ и **новый анализ — Monte Carlo** по допускам; +- **Железо** (макет + генератор): переходные процессы и **автоматическое + снятие АЧХ** генератором-свипом на RP2040. + +--- + +## 2. Теория кратко + +**Интегратор на ОУ** (R1 на входе, C1 в ООС): + +W(s) = −1 / (R1·C1·s) + +**Реальный интегратор** (резистор утечки R2 параллельно C1) — это +**апериодическое звено первого порядка**: + +W(s) = −R2 / (R1 · (1 + R2·C1·s)) = −K / (τ·s + 1) + +где K = R2/R1, τ = R2·C1, f_c = 1/(2π·τ). + +**Дифференциатор** (C1 на входе, R1 в ООС): + +W(s) = −R1·C1·s + +**Модель для работы:** K = −10, τ = 10 мс, f_c ≈ 16 Гц +(R1 = 10 кОм, R2 = 100 кОм, C1 = 0,1 мкФ). + +--- + +## 3. Задания + +### Задание 1 (1 ч). Симуляция: АЧХ и ФЧХ + +В LTSpice собрать интегратор с резистором утечки: R1 = 10 кОм, +R2 = 100 кОм, C1 = 0,1 мкФ (R2 || C1 в ООС). + +1. Задать анализ `.ac dec 20 1 100k`. +2. Снять АЧХ в децибелах и ФЧХ. +3. По графику определить: + - усиление на плоском участке (должно быть ≈ 20 дБ, т. е. K = 10); + - частоту сопряжения f_c (где спад −3 дБ); + - наклон спада (ожидается −20 дБ/дек). +4. Сравнить с расчётом по формулам из п. 2. + +### Задание 2 (1 ч). Симуляция: Monte Carlo по допускам ⭐ + +**Новый анализ.** Реальные резисторы и конденсаторы имеют допуск ±5 % +(ряд E24 — не лучше). Оценим разброс параметров звена. + +1. Задать разброс: `.step param tol list -5 -2.5 0 2.5 5` и применить + его к номиналам (например `{100k*(1+tol/100)}` для R2, аналогично R1 и C1). + Либо, для полноценного статистического анализа, использовать + `.step param run 1 100 1` с генератором случайных отклонений. +2. Прогнать **не менее 100 прогонов**. +3. Для каждого прогона зафиксировать K (усиление на плоском участке) и f_c. +4. Построить гистограммы распределения K и f_c. +5. Посчитать **долю экземпляров**, укладывающихся в полосу ±10 % от + номинального значения K и f_c. +6. Сделать вывод: **какой компонент** вносит наибольший вклад в разброс + и почему. + +### Задание 3 (1 ч). Железо: переходные процессы + +Собрать на макетной плате: + +**3.1. Интегратор** (R1 = 10 кОм, C1 = 0,1 мкФ): + +- подать прямоугольник 1 кГц (от RP2040-генератора или лабораторного); +- наблюдать пилообразный сигнал на выходе; +- замерить размах пилы и сравнить с + U_m = U_вх · T / (2 · R1 · C1); +- убедиться, что при росте частоты размах падает обратно пропорционально. + +**3.2. Дифференциатор** (C1 = 0,1 мкФ на входе, R1 = 10 кОм в ООС): + +- подать пилу 1 кГц; +- наблюдать импульсы в моменты перепада; +- сравнить амплитуду с U_m = R1·C1 · dU_вх/dt. + +**3.3. Роль резистора утечки:** собрать интегратор с R2 = 100 кОм +параллельно C1 и без него; объяснить, что меняется на постоянном токе. + +### Задание 4 (1 ч). Железо: автоматическое снятие АЧХ + +Использовать подготовленный генератор-свип: + +- **`stub/01-dds-generator-rp2040.ino`** — прошить в RP2040 (Raspberry Pi + Pico). Прямоугольник (ШИМ) и синус (DDS) + логарифмический свип + + измерение амплитуды на ADC0. +- **`stub/02-izmerenie-achh.py`** — хост-скрипт: запускает свип по Serial, + собирает точки, строит АЧХ. + +Порядок: + +1. Прошить генератор, проверить команды `F`, `W S`, `M`, `A` в мониторе + порта. +2. **Собрать внешний ФНЧ** на выходе генератора (без него будет меандр, + а не синус): + + ``` + GP15 ──[R1 1к]──┬──[R2 1к]──┬──────► вход схемы + │ │ + [C1 47н] [C2 47н] + │ │ + GND GND + ``` + + Это пассивный ФНЧ 2-го порядка, f_c ≈ 3,4 кГц; он подавляет несущую + ШИМ 62,5 кГц примерно на 50 дБ. **Зарисовать фильтр в отчёт.** + +3. Выход фильтра → вход схемы; выход схемы → вход измерения (ADC0). + **Если сигнал превышает 3,3 В — только через делитель.** +4. Запустить: + ``` + python 02-izmerenie-achh.py --port /dev/ttyACM0 \ + --f0 10 --f1 100000 --n 25 --dwell 150 --uin 500 --out achh.csv --plot + ``` +5. Импортировать CSV, нанести точки на модельную кривую из задания 1. +6. Объяснить расхождения. + +> **Смещение:** при скважности 50 % среднее на выходе ШИМ = `Uпит/2` +> = 1,65 В, поэтому синус смещён относительно земли. Если это мешает +> измерению — поставьте разделительный `C = 1 мкФ` + `R = 47 кОм` +> на землю, либо уменьшите амплитуду командой `M`. + +> Свип-генератор — студенческая заготовка (черновик). Если на стенде +> есть серийный функциональный генератор с режимом свипа/логарифмической +> развёртки — можно использовать его, принцип тот же. + +### Задание 5 (СРС / отчёт). Сведение результатов + +Свести в одну таблицу «**расчёт / симуляция / железо**» параметры +K, τ, f_c. Построить совмещённый график АЧХ (три кривые). + +Сделать выводы: + +- причины расхождения расчёта и симуляции; +- причины расхождения симуляции и железа; +- что даёт анализ Monte Carlo для реального проектирования. + +--- + +## 4. Варианты + +| № | R1, кОм | R2, кОм | C1, мкФ | K | τ, мс | f_c, Гц | +|---|---------|---------|---------|---|-------|---------| +| 1 | 10 | 100 | 0,1 | 10 | 10 | 15,9 | +| 2 | 10 | 47 | 0,1 | 4,7 | 4,7 | 33,9 | +| 3 | 4,7 | 100 | 0,1 | 21 | 10 | 15,9 | +| 4 | 10 | 100 | 0,047 | 10 | 4,7 | 33,9 | +| 5 | 1 | 47 | 1,0 | 47 | 47 | 3,4 | +| 6 | 22 | 220 | 0,022 | 10 | 4,84 | 32,9 | + +*Примечание. Номиналы приведены к ряду E24. Вариант 2 и 4 дают одинаковую +f_c при разном K — удобно для сравнения вклада параметров.* + +--- + +## 5. Оформление и оценивание + +Отчёт содержит: + +1. Схему интегратора и дифференциатора (KiCad или от руки). +2. АЧХ и ФЧХ из LTSpice. +3. Гистограммы Monte Carlo (K и f_c) + доля годных. +4. Осциллограммы переходных процессов (пила, импульсы). +5. CSV-точки автоматического свипа + совмещённый график АЧХ. +6. Итоговую таблицу «расчёт / симуляция / железо». +7. Выводы. + +| Задание | Баллы | +|---------|-------| +| 1. Симуляция АЧХ/ФЧХ | 2,0 | +| 2. Monte Carlo | 2,5 | +| 3. Железо: переходные процессы | 2,0 | +| 4. Железо: автоснятие АЧХ | 2,0 | +| 5. Отчёт: сведение | 1,5 | +| **Итого** | **10** | + +Зачёт — при **6 и более** баллах. Расхождение с эталоном ±10 % +(допуски компонентов). + +--- + +## 6. Контрольные вопросы + +1. Чему равна передаточная функция идеального интегратора и почему без + резистора утечки она нереализуема на практике? +2. Как по АЧХ отличить идеальный интегратор от апериодического звена + первого порядка? +3. Почему частота сопряжения определяется R2·C1, а не R1·C1? +4. Как связаны наклон АЧХ в дБ/дек и порядок звена? +5. Какой компонент даёт наибольший вклад в разброс f_c при допуске ±5 % + и почему? +6. Как на форме переходного процесса сказывается конечная полоса + пропускания операционного усилителя? + +--- + +## 7. Литература + +1. Хоровиц П., Хилл У. Искусство схемотехники. 3-е изд. М.: Бином, 2014. + Гл. 4. +2. Титце У., Шенк К. Полупроводниковая схемотехника. 12-е изд. М.: ДМК + Пресс, 2008. Т. 1, гл. 11. +3. Бесекерский В.А., Попов Е.П. Теория систем автоматического + регулирования. М.: Наука, 2010. Гл. 2. +4. LTSpice XVII User Guide. Analog Devices. +5. LM358 / TL081 datasheet. Texas Instruments. +6. RP2040 Datasheet. Raspberry Pi Ltd.