_二级继电器程控放大倍数自动设置

简介

        在开发项目中,有时会遇到需要使用程控放大的情况,如果没有opa那种可编程放大器,那么就需要通过继电器来控制放大倍数。而在继电器程控中,常用的是二级程控,三级程控相较于二级就复杂了许多。

        在二级程控中,每级继电器都有两种状态,因此每级都可以设置两个倍数,两级级联共4种放大倍数。对于具体应用场景来说,难的是如何设置合适的不同放大倍数划分不同的子区间,使其可以对输入区间的不同子区间进行选择性放大,并且每个档位都能合理覆盖这些子区间,进而使得增益后的子区间均处于适合的范围。

具体场景

        举个具体例子,现在ADC采集电压峰峰值范围为0~2V,而输入的电压为30mV到600mV。对于电压30mV和600mV来说,放大倍数应分别接近66.7和3.3,那么就需要对其不同区间进行选择性放大。那么这就设计到了如何选取区间,4个放大倍数自然需要把输入区间划分为4个连续子区间。放大之后还需要确保,增益的子区间应在ADC采集的范围内。

        也就是说,我们能获得的信息是输入的区间范围(如30mV~600mV)和输出的区间范围(如0~2V),而我们需要的信息则是不同的放大倍数和划分的子区间。在此例中,为了能及时察觉到放大倍数是否过大,那么应让每个区间放大后的上限小于ADC的上限(2V),以便可以通过检测ADC电压是否大于子区间上限并接近上限电压,进而及时调整程控倍数。下限亦是如此。

建模

        根据具体的应用场景,可以很轻松地为其建模:

        对于一个输入电压范围x1,x2,可以把它分为4个连续区间范围(左闭右开),而现在有一级放大倍数O1min和O1max,二级放大倍数O2min和O2max,这四个数可以排列组合得到不同的乘积a,b,c,d。那么如何找到四个乘积a、b、c、d(由一级放大倍数O1和二级放大倍数O2的组合产生),以及四个区间的分割点,使得每个分割后的区间内的x乘以对应的乘积后落在[Vmin, Vmax]内,并且分割后的四个区间连续覆盖输入范围。

解决

        进行建模后,并不好从数学角度快速解出4个放大倍数和4个连续子区间。但可以通过编程来遍历所有合适的条件。如图,这是个简单的C++程序,可以向其指定输入区间、输出区间、步长和线程数。不过需要注意的是,找到的结果是对称的,也就是说两级放大倍数互换,在程序里是两种情况,而非一种情况。

小工具的使用

简介 

       使用方法很简单,与一般工具无异,通过指定参数来进行相应操作。此处,小工具的名称为auto_OPA.exe

输入区间 

       以前面的例子而言,输入区间为30mV到600mV,那么可以写成下面形式(这里以mV为单位,也可以以V为单位,即-i 0.03 0.6。需要确保后面也使用相同单位)

.\auto_OPA.exe -i 30 600

输出区间

        各个子区间增益后的区间范围

.\auto_OPA.exe -o 875 1900

步长

        决定了放大倍数的最小分辨率

.\auto_OPA.exe -s 0.5

        步长不建议选择太小,比如0.1,其耗时远远高于步长为0.5用时的5倍,并且消耗内存会会大幅增加,因为找到的结果会先保存起来

        另一方面,整数放大或者半整数放大在实际电路设计中较为常见,也比较好设计。

线程数

        决定了程序运行快慢。由于程序里内嵌套了多个for循环,建议有多少核就用多少核,如果满核的话,CPU占用率会达到100%,这很正常。

.\auto_OPA.exe -j 16

说明

参数也可以联合指定,不区分先后顺序

.\auto_OPA.exe -i 30 600 -o 875 1900 -s 0.5 -j 16

        此外,程序还有默认值(就是任何参数都不指定的情况下),可自行到源码里设置,然后编译运行

        其中放大倍数的上下限是根据输入区间范围和输出区间范围动态决定的,并不需要额外指定。

如果需要,那么可以显示设定上下限

vector<double> generate_O_values(double x_min, double x_max, double Vmin, double Vmax, double step) {vector<double> values;double min_O = ceil((Vmin/x_max)/step)*step;  // 显式下限double max_O = floor((Vmax/x_min)/step)*step;  // 精确上限for (double v = min_O; v <= max_O + 1e-9; v += step) {values.push_back(v);}return values;
}

运行结果

程序运行

保存的结果,其中O1为一级放大器的两个放大倍数,O2位二级放大器的放大倍数

源代码

gitcode:GitCode - 全球开发者的开源社区,开源代码托管平台

github:ichliebedich-DaCapo/auto_OPA

#include <iostream>
#include <vector>
#include <algorithm>
#include <thread>
#include <mutex>
#include <atomic>
#include <fstream>
#include <string>
#include <cmath>
#include <iomanip>
#include <getopt.h>using namespace std;// ANSI颜色代码
#define BLUE    "\033[34m"
#define GREEN   "\033[32m"
#define RESET   "\033[0m"struct Result
{double O1min{}, O1max{}, O2min{}, O2max{};vector<double> gains;vector<double> split_points;
};vector<Result> global_results;
mutex results_mutex;
atomic<int> processed(0);
atomic<int> found_results(0);vector<double> generate_O_values(double x, double Vmax, double step)
{vector<double> values;double max_O = Vmax / x;for (double v = step; v <= max_O + 1e-9; v += step){values.push_back(v);}return values;
}void display_progress(int total, int found)
{const float progress = static_cast<float>(processed) / total;const int bar_width = 50;cout << BLUE << "\r[";const int pos = bar_width * progress;for (int i = 0; i < bar_width; ++i){if (i < pos) cout << "=";else if (i == pos) cout << ">";else cout << " ";}cout << "] " << static_cast<int>(progress * 100.0) << "%" << RESET;cout << " " << GREEN << "Found: " << found << RESET << flush;
}void display_thread_func(int total)
{while (processed < total){this_thread::sleep_for(chrono::milliseconds(100));int current_processed = processed.load();int current_found = found_results.load();display_progress(total, current_found);}display_progress(total, found_results.load());cout << endl;
}void worker(const vector<pair<int, int> > &O1_combs, const vector<pair<int, int> > &O2_combs,const vector<double> &O1_values, const vector<double> &O2_values,double x1, double x2, double Vmin, double Vmax, int total_O1_combs)
{while (true){int idx = processed.fetch_add(1);if (idx >= total_O1_combs) break;const auto &[fst, snd] = O1_combs[idx];const double O1min = O1_values[fst];const double O1max = O1_values[snd];for (auto &O2_pair: O2_combs){const double O2min = O2_values[O2_pair.first];const double O2max = O2_values[O2_pair.second];const double k1 = O1min * O2min;const double k2 = O1min * O2max;const double k3 = O1max * O2min;const double k4 = O1max * O2max;if (k1 == k2 || k1 == k3 || k1 == k4 || k2 == k3 || k2 == k4 || k3 == k4) continue;vector<double> gains = {k1, k2, k3, k4};ranges::sort(gains);do{if (gains[3] < Vmin / x2 - 1e-9 || gains[3] > Vmax / x2 + 1e-9) continue;if (gains[0] < Vmin / x1 - 1e-9) continue;double d0 = x1;double d1_low = max(d0, Vmin / gains[1]);double d1_high = Vmax / gains[0];if (d1_low > d1_high + 1e-9) continue;double d1 = d1_low;double d2_low = max(d1, Vmin / gains[2]);double d2_high = Vmax / gains[1];if (d2_low > d2_high + 1e-9) continue;double d2 = d2_low;const double d3_low = max(d2, Vmin / gains[3]);const double d3_high = min(Vmax / gains[2], x2);if (d3_low > d3_high + 1e-9) continue;double d3 = d3_low;if (d3 > x2 + 1e-9) continue;Result res;res.O1min = O1min;res.O1max = O1max;res.O2min = O2min;res.O2max = O2max;res.gains = gains;res.split_points = {d1, d2, d3};lock_guard<mutex> lock(results_mutex);global_results.push_back(res);found_results.fetch_add(1);} while (ranges::next_permutation(gains).found);}}
}int main(int argc, char *argv[])
{double x1 = 30, x2 = 600, Vmin = 875, Vmax = 1950, step = 0.5;int threads = 16;// 解析命令行参数int opt;while ((opt = getopt(argc, argv, "i:o:s:j:h")) != -1){switch (opt){case 'i':x1 = stod(optarg);x2 = stod(argv[optind++]);break;case 'o':Vmin = stod(optarg);Vmax = stod(argv[optind++]);break;case 's':step = stod(optarg);break;case 'j':threads = stoi(optarg);break;case 'h':// 中文会乱码// cout << "[-i]:输入区间范围,比如-i 0.03 0.6\n"//         << "[-o]:输出区间范围,比如-o 0.9 2\n"//         << "[-s]:步长,比如-s 0.5\n"//         << "[-j]:线程数,比如-j 16\n"//         << "[-h]:帮助信息\n";;cout << "Two-Stage Programmable Amplifier Configuration Finder\n\n"<< "Usage:\n"<< "  ./auto_OPA -i <x_low> <x_high> -o <Vmin> <Vmax> [options]\n\n"<< "Required Parameters:\n"<< "  -i  Input voltage range (left-closed right-open interval)\n"<< "      Example: -i 0.03 0.6\n"<< "  -o  Desired output voltage range\n"<< "      Example: -o 1.0 3.3\n\n"<< "Options:\n"<< "  -s  Step size for gain search (default: 0.1)\n"<< "  -j  Number of parallel threads (default: CPU core count)\n"<< "  -h  Display this help message\n\n"<< "Validation Criteria:\n"<< "  1. Input coverage: [x_low, x_high] must be fully covered\n"<< "  2. Output constraint: ∀x∈[x_low,x_high], Vmin ≤ x·gain ≤ Vmax\n"<< "  3. Gain continuity: Adjacent regions must have overlapping gains\n";exit(1);default:cerr << "Usage: " << argv[0]<< " -i x_low x_high -o Vmin Vmax -s step -j threads\n";exit(1);}}// 打印参数cout << "Vin  [" << x1 << "," << x2 << "]\nVout [" << Vmin << "," << Vmax << "]\nstep=" << step << "\nthreads=" <<threads << endl;auto O1_values = generate_O_values(x1, Vmax, step);auto O2_values = generate_O_values(x1, Vmax, step);vector<pair<int, int> > O1_combs;for (int i = 0; i < O1_values.size(); ++i)for (int j = i + 1; j < O1_values.size(); ++j)O1_combs.emplace_back(i, j);vector<pair<int, int> > O2_combs;for (int i = 0; i < O2_values.size(); ++i)for (int j = i + 1; j < O2_values.size(); ++j)O2_combs.emplace_back(i, j);int total_O1_combs = O1_combs.size();thread display_thread(display_thread_func, total_O1_combs);vector<thread> workers;for (int i = 0; i < threads; ++i)workers.emplace_back(worker, ref(O1_combs), ref(O2_combs), ref(O1_values),ref(O2_values), x1, x2, Vmin, Vmax, total_O1_combs);for (auto &t: workers) t.join();display_thread.join();ofstream out("results.txt");for (int i = 0; i < global_results.size(); ++i){auto &[O1min, O1max, O2min, O2max, gains, split_points] = global_results[i];out << "Result " << i + 1 << ":\n";out << fixed << setprecision(6);out << "O1: [" << O1min << ", " << O1max << "]\n";out << "O2: [" << O2min << ", " << O2max << "]\n";out << "Gains: ";for (auto g: gains) out << g << " ";out << "\nSplit Zone: ";// 子区间double zone[]={x1, split_points[0], split_points[1], split_points[2], x2};for (int n = 0; n < 4; ++n){out << "\n[" << zone[n] << "," << zone[n + 1] << "]\t\t"<<"[" << gains[n] * zone[n] << "," << gains[n]*zone[n + 1] << "]";}out << "\n\n";}return 0;
}

下载小工具

        程序已上传gitcode和github,可通过下面链接进行下载

Release详情 - auto_OPA - GitCode

Release auto_OPA v1.0.0 · ichliebedich-DaCapo/auto_OPA

三级程控放大倍数(无)

        依照相同的原理,三级也能实现,不过计算量实在太大,不建议编程来确定最佳区间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897339.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑总显示串口正在被占用处理方法

1.现象 在嵌入式开发过程中&#xff0c;有很多情况下要使用串口调试&#xff0c;其中485/422/232转usb串口是非常常见的做法。 根据协议&#xff0c;接口芯片不同&#xff0c;需要安装对应的驱动程序&#xff0c;比如ch340&#xff0c;cp2102&#xff0c;CDM212364等驱动。可…

优雅拼接字符串:StringJoiner 的完整指南

在Java开发中&#xff0c;字符串拼接是高频操作。无论是日志格式化、构建CSV数据&#xff0c;还是生成动态SQL&#xff0c;开发者常需处理分隔符、前缀和后缀的组合。传统的StringBuilder虽然灵活&#xff0c;但代码冗余且易出错。Java 8推出的StringJoiner类&#xff0c;以简洁…

LabVIEW闭环控制系统硬件选型与实时性能

在LabVIEW闭环控制系统的开发中&#xff0c;硬件选型直接影响系统的实时性、精度与稳定性。需综合考虑数据采集速度&#xff08;采样率、接口带宽&#xff09;、计算延迟&#xff08;算法复杂度、处理器性能&#xff09;、输出响应时间&#xff08;执行器延迟、控制周期&#x…

Hive的架构

1. 概念 Hive 是建立在 Hadoop 上的数据仓库工具&#xff0c;旨在简化大规模数据集的查询与管理。它通过类 SQL 语言&#xff08;HiveQL&#xff09;将结构化数据映射为 Hadoop 的 MapReduce&#xff0c;适合离线批处理&#xff0c;尤其适用于数据仓库场景。 2. 数据模型 表&a…

深入解析:Linux中KVM虚拟化技术

这篇文章将深入分析Linux中虚拟化技术的实现----KVM技术&#xff0c;从KVM技术的简介、技术架构、以及虚拟机和宿主机交互的重要处理逻辑出发&#xff0c;深入探究KVM技术的实现。 一、KVM简介&#xff1a; 首先&#xff0c;我们先查看一下KVM架构&#xff0c;看看它的整体工…

golang学习笔记——go语言安装及系统环境变量设置

文章目录 go语言安装go envgo getgoproxy测试安装 Go 插件安装 Go 插件依赖工具参考资料用户环境变量和系统环境变量用户环境变量系统环境变量示例设置环境变量的步骤设置用户环境变量设置系统环境变量 验证环境变量总结 2024年最火的5大Go框架1. Gin&#xff1a;高并发接口的“…

3.6c语言

#define _CRT_SECURE_NO_WARNINGS #include <math.h> #include <stdio.h> int main() {int sum 0,i,j;for (j 1; j < 1000; j){sum 0;for (i 1; i < j; i){if (j % i 0){sum i;} }if (sum j){printf("%d是完数\n", j);}}return 0; }#de…

【TI】如何更改 CCS20.1.0 的 WORKSPACE 默认路径

参考链接&#xff1a; 如何更改 CCS Theia 中工作区的默认位置&#xff1f;- Code Composer Studio 论坛 - Code Composer Studio™︎ - TI E2E 支持论坛 --- How to change the default location for the workspace in CCS Theia? - Code Composer Studio forum - Code Comp…

Vue3中动态Ref的魔法:绑定与妙用

前言 在Vue 3的开发过程中,动态绑定Ref是一项非常实用的技术,特别是在处理复杂组件结构和动态数据时。通过动态绑定Ref,我们可以更灵活地访问和操作DOM元素或组件实例,实现更高效的交互和状态管理。本文将详细介绍如何在Vue 3中实现动态Ref的绑定,并通过实例展示其妙用。…

CarPlanner:用于自动驾驶大规模强化学习的一致性自回归轨迹规划

25年2月来自浙大和菜鸟网络的论文“CarPlanner: Consistent Auto-regressive Trajectory Planning for Large-scale Reinforcement Learning in Autonomous Driving”。 轨迹规划对于自动驾驶至关重要&#xff0c;可确保在复杂环境中安全高效地导航。虽然最近基于学习的方法&a…

VS Code连接服务器教程

VS Code是什么 VS Code&#xff08;全称 Visual Studio Code&#xff09;是一款由微软推出的免费、开源、跨平台的代码编辑神器。VS Code 支持 所有主流操作系统&#xff0c;拥有强大的功能和灵活的扩展性。 官网&#xff1a;https://code.visualstudio.com/插件市场&#xff1…

【JavaWeb】Web基础概念

文章目录 1、服务器与客户端2、服务器端应用程序3、请求和响应4、项目的逻辑构成5、架构5.1 概念5.2 发展演变历程单一架构分布式架构 5.3 单一架构技术体系 6、本阶段技术体系 1、服务器与客户端 ①线下的服务器与客户端 ②线上的服务器与客户端 2、服务器端应用程序 我…

安徽省考计算机专业科目2025(持续更新)

目录 第一部分 计算机科学技术基础 第一章 计算机及其应用基础知识 1.1 计算机的特点、分类及其应用 1.2 信息编码与数据表示&#xff1b;数制及其转换方法&#xff1b;算术运算和逻辑运算的过程 第一部分 计算机科学技术基础 第一章 计算机及其应用基础知识 1.1 计算机…

前端知识点---路由模式-实例模式和单例模式(ts)

在 ArkTS&#xff08;Ark UI 框架&#xff09;中&#xff0c;路由实例模式&#xff08;Standard Instance Mode&#xff09;主要用于管理页面跳转。当创建一个新页面时&#xff0c;可以选择标准实例模式&#xff08;Standard Mode&#xff09;或单实例模式&#xff08;Single M…

【leetcode hot 100 73】矩阵置零

解法一&#xff1a;&#xff08;使用两个标记变量&#xff09;用矩阵的第一行和第一列代替方法一中的两个标记数组&#xff08;col、row[ ]&#xff1a;第几列、行出现0&#xff09;&#xff0c;以达到 O(1) 的额外空间。 这样会导致原数组的第一行和第一列被修改&#xff0c;…

【十三】Golang 通道

&#x1f4a2;欢迎来到张胤尘的开源技术站 &#x1f4a5;开源如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 通道通道声明初始化缓冲机制无缓冲通道代码示例 带…

【JAVA架构师成长之路】【电商系统实战】第12集:秒杀系统性能优化实战(CAN + Nginx + Sentinel)

30分钟课程&#xff1a;秒杀系统性能优化实战&#xff08;CDN Nginx Sentinel&#xff09; 课程目标 掌握静态资源 CDN 加速的配置与优化策略。通过 Nginx 实现负载均衡&#xff0c;提升系统横向扩展能力。使用 Sentinel 实现服务降级&#xff0c;保障核心链路稳定性。 课程…

K8S学习之基础十八:k8s的灰度发布和金丝雀部署

灰度发布 逐步扩大新版本的发布范围&#xff0c;从少量用户逐步扩展到全体用户。 特点是分阶段发布、持续监控、逐步扩展 适合需要逐步验证和降低风险的更新 金丝雀部署 将新版本先部署到一小部分用户或服务器&#xff0c;观察其表现&#xff0c;再决定是否全面推广。 特点&…

毕业项目推荐:基于yolov8/yolo11的苹果叶片病害检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

redis有哪几种持久化方式

Redis 提供了两种持久化方式&#xff1a;RDB&#xff08;Redis Database&#xff09; 和 AOF&#xff08;Append-Only File&#xff09;。它们各有优缺点&#xff0c;适用于不同的场景。以下是它们的原理、优缺点以及如何选择的建议&#xff1a; 1. RDB&#xff08;Redis Datab…