医用眼科前房高精度侵入式压力传感器.电路设计上

原创 云深之无迹 2023-09-23 23:56

医用眼科前房高精度侵入式压力传感器

医用眼科前房高精度侵入式压力传感器.硬件方案

忙到飞起?(可能是我磨蹭吧)

之前写过这个东西,后面又断断续续的写了其他的一些东西,这篇文章是尽量的给出一个详细的电路设计。但是后面又看到不少好玩的东西我就都写进去了,所以文章和以往一样没有那么纯粹,而且一些具体的参数性调节的计算可能不会写很多,不过之后都会补上的。

一个电子设计其实大多数的时候都是围绕着一个关键的器件展开工作的,这次的设计就是以一个压力的传感器展开。

最简单的电桥包括由4条支路(又称桥臂)形成的闭封回路(即桥体)和辅助设备(见图。后者主要有电源和检测仪表各支路由电参数元件组成,它们的4个连接点A、B、C、D称作顶点。电源接在两个相对顶点间,检测仪表接在另两个相对顶点间。桥式测量电路于1833年首先由S,H.克里斯蒂发明。当时电桥一词系指连接两个相对顶点的支路,特别是图中的检测仪表支路,如同在相对顶点间架设的一座小桥。后来,电桥这一名词被用来泛指整个线路。复杂的电桥还包括更多数量的桥臂。

电桥有两种工作方式。

平衡方式。检测仪表支路两端的电位差为零,且该支路中的电流为零,因而检测仪表又称指零仪表。如以被测元件为一个桥臂,而其他各臂由标准元件组成,则当电路平衡时,可读出或计算出被测参数的量值。工作在平衡方式的电桥,特点之一是测量结果不受电源电压高低和变化的影响。这种工作方式多用于对测量准确度要求较高的情况。

不平方式。检测仪表支路两端的电位差不为零,该支路中有电流通过。检测仪表的指示数是被测参数变化的函数。这种工作方式多用于非电量的电测量和生产过程的检测中。电桥主要用于测量电路元件(如直流电阻、交流电感、电容、电阻等)的量值、变化量,也用于测量转换为电参数的非电量

我们使用的这个欧姆龙是第一种模式

在使用以前需要知道引脚怎么连接

正好嘉立创有这样的封装

搬我文章以前的图了,大概测量的图就是这样,要连接4个口

因为电桥的话是差分的电压输出,而且必须要加一个恒压提供激烈,不然传感器本体是出不来什么信号的,即使它再小,也没有。


我个人是用AD620多,这里也放一个测量用的电路


是一个3Kg的压力电桥,后面的AD705是一个缓冲器,就是给REF的。这个AD705是没有见过的。

停产了呀

看这个输入的偏置电流就知道是个FET器件的运放

然后需要一个恒流源的电流,这些是具体的计算

恒流

增益

注意,要加一个恒流源的电路,需要使用一个运放

运放反相和正相输入端电压总是相等
运放反相和正相输入端没有电流输入

只需要在正相输入端提供稳定参考电压,反相连接限流电阻和负载,就可以实现简易的恒流源。恒流源的电流大小只和限流电阻阻值有关系,与负载阻值没关系。调节负载阻值不会改变电流大小,但是会改变运放输出端的电压。

知乎老哥的一个文章不错

参考电压3V,限流电阻10KΩ,意味着恒流源电流为0.3mA(300uA)。负载阻值从100Ω到9.9kΩ可调。

特别的,在100Ω情况下,运放输出端电压为3V(3.02V)。

还有一个搭配三极管的电路也很好~

电阻R2起到对运放的保护作用,这个值不能太大,一般取值10R左右。(一点小经验吧,我们设计的产品中一般都有加);
电阻R3为三极管提供一个基极电流;
电阻R4位采样电阻,RL为负载电阻;
电阻R5起缓冲限流的作用,一般选取1K~100K之间(也有些电路没加这个电阻)。
原理是:实际原理是当采样电阻R4的电压变化时,直接反馈到运放的反相输入端,它与同相输入端电压的差值被运放放大,输出控制三极管的基极电流,改变三极管的内阻,从而改变发射极与集电极间的电压降,从而使采样电阻的电压保持不变,以达到负载电流恒定的目的。
电路的缺点:
虽然,三极管发射极电流与集电极电流近似相等,但实际上,发射极的电流还包含了基极电流。

可以看出,运放输出级使用三极管时,输出电流会产生基极电流分量这一误差。如果此时还不满足电路精度要求,可将三极管改成MOS管。
MOS管属于压控器件,栅极需要的电流很小。IRL和IR4可以非常的接近,相比三极管而言,电流的精度提升了。

升级;一点点

这个也是一个,稳定的电压通过一个R1转换成电流

在这样推荐的电路图中,仪表放大器如德州仪器LMV324具有四个运算放大器,可用于增益调节。R3应采用具有良好温度特性的金属膜电阻器。

此外,还需要根据要使用的增益调整连接在R10上的Ref 电压值。

例如:

如果增益= 21,则Ref= 1.0 [V],

如果增益=201,则需要Ref= 1.5 [V]。


本来是想好好的写到尾的,但是没有办法,文档里面的知识太多了。

20年前的文档,后面是有仿真的结果的,也说明了这里到底是什么设计

现在的,方案还是一样

INA系列具有低失调电压和低失调电压漂移的特点。大多数应用不需要外部偏移调整。显示了用于调整输出失调电压的可选电路。

施加到 Ref 引脚的电压被添加到输出信号中。运算放大器缓冲器在 Ref 引脚处提供低阻抗,以保持良好的共模抑制。

这就是提供一个泄露使用的通道

输入阻抗极高,约为 10^9 Ω。然而,必须为两个输入的输入偏置电流提供一条路径。该输入偏置电流通常为 –10 nA(电流从输入引脚流出)。高输入阻抗意味着该输入偏置电流随着输入电压的变化而变化很小。
输入电路必须为该输入偏置电流提供一条路径才能正常运行。如果没有偏置电流路径,输入会浮动到超过共模范围的电位,并且输入放大器将饱和。如果差分源电阻较低,则可以将偏置电流返回路径连接到一个输入 
我是想着就用欧姆龙的传感器,然后也是使用Ti 的 INA 仪表放大器,然后是LMC6482,里面一个运放做恒流源,一个做同向放大器(或是是REF的偏移器,需要我调试),系统也是喜欢一个稳健的电源以及一个MCU和一个显示器和交互的按键啥的?还需要一个上位机。
还是把以前的图拿来,继续的分析

原理图是这样绘制

先简单的来把电桥鸡儿运放塞一起

使用网络标签让它看起来好看一些,注意电桥上面的电源是下面运放给的

然后核心器件是电桥,所以这里就按照参考设计搭建,但是这个恒流源也不难。

现在的问题是还有一个运放,是做REF还是同相放大器呢?

可以先看一个换算

大概就是这样

按照这个来看,就是0~10mV之间的

模拟信号从压力传感器传递过来的转换为通过Arduino Due板的采集速率为50毫秒(20Hz). 

采样次数 = 采样频率 × 时间(秒)
在这种情况下,采样频率为20Hz,时间为50毫秒,将时间转换为秒:
时间 = 50毫秒 = 0.05秒
现在,使用公式计算采样次数:
采样次数 = 20Hz × 0.05秒 = 1次采样

这个是原版论文里面的采样率也就是20Hz。

内部电路确保外部压力测量范围不会产生大的电压足以损坏Arduino。这是实现通过内置在压力传感器中的惠斯通电桥。然后电压以精确的增益放大。使用仪表放大器,设置灵敏度的的压力测量。

再看看,前面是INA,走线等长差分,下面是运放搞得恒流源。不对,两个顺序换一下,第二个是INA,因为很明显有增益电阻,然后接着是一个LDO,给运放接电,俩电容实在是抢眼。看着是直接他妈的接了个电阻在输出口上,应该是还有一个二极管,小信号的,但是跨在芯片之间。

然后限制输出使用两个限制器电路;一个为上界,另一个为其他为期望压力下界范围内。

上界和下界由由于Arduino的内部ADC,但灵敏度测量值的大小可以通过调整仪表放大器的反馈电阻。这内部Arduino的ADC将模拟信号数字化信号在用户定义的采样率

那就按照是3.6V吧

放大倍数 = 输出电压范围 / 输入电压范围
在这种情况下,输出电压范围是3.6V,输入电压范围是5mV。
放大倍数 = 3.6V / 5mV = 720

需要一个精密的电阻+一个电位器

大概输出的摆幅在-2.5~2.5之间是最好的

看着参考设计最后是给了一个1V DC的参考,这里就最后一个运放就这样用。

OK,大概就是这样,需要缓冲的地方设置成1V

大概就先这样,还有很多的细节我没有绘制上,不过最重要的还是布线

这张图我是觉得有学习的地方,其一级是精密的运放,那么为了整体的性能,下一级就是放一个低噪音的运放了。

刘总出来挨打


我看的设计书里面这个也有大量的论述(就几页而已)

在找芯片的时候,看见了国产的润石,基本上是常见的领域都有替代了。

我朋友圈吐槽的时候被抓到了,怪不得感觉润石的文档一股子TI味

是我的菜

感觉参数还是很好看的,FET的应该是

是经典的三运放结构

封装没有SOIC

这些就不说了,一股子的TI味道(好喜欢)

这个芯片值得好好研究

我仿真了一下,原来是这个电路可以提供一个非常小的电压

pV

还是非常小的pV级别的

上面的电压和这个Vos会加在一起,要校准芯片的直流性能

RS8541是一款低功耗、零漂移、轨对轨输入输出的精密运算放大器,它的工作电压范围在2v到5.5v,失调电压为±7uV,失调电压温漂为±0.08uV/℃的,它具有低至40uA的静态电流,这些特性保证了它的高精度和低功耗。电源抑制比为110db,共模抑制比为120dB,提高了抗电源噪声和共模干扰的能力,130dB的高开环增益也保证了运放的高精度。Rs8541的这些特性也使其广泛应用于各种传感、仪表、医疗器械以及精密计量设备等。

也就是看右上角的这个框图,也就是低阻+运放构成的一个直流校准系统(我瞎说的)

下面是我看的一个拆机的视频,感觉有很多要需要的地方。

首先就是一些芯片可以提起绘制好,为了升级,以及使用GH的小接口

充电和复位安装在一起,看起来也是比较合理的

然后在做展示的时候使用这样的动画效果

这个是接口的细节,DJI的硬件也是喜欢这样的搞

记得留下很多的测试点,在测试点上面写好丝印

这个是一个验尿的机器,LCD的屏幕很小巧

PCB是绘制的真漂亮啊

然后就是欣赏PCB

这个是一个我没有见过的传感器

测试时,首先尿液顺着结构浸润试纸的膜块上,膜块会跟你尿液中的物质发生颜色变化,颜色深浅与尿液中的相应物质的浓度成正比,然后膜块受到 LED 光源照射会产生不同的反射光,不同的反射光也就是对应尿液中不同的生化成分的含量(到这里你其实已经发现了,这部分检测原理本质上就是对光的吸收和反射,也就是光电比射原理),然后彩色传感器接受反射光转化为电信号 --->再通过 MCU 计算处理输出到屏幕上。同时,如果你手机绑定了 HiPee,结果也会通过 WiFi 同时推送到你手机上。

这个是上面的方案图

还有爆炸的装配图

我一直以为是彩色的屏幕最好,没想到普通的屏幕用到恰到好处也很美

和学妹要了张照片来提升一下我文章的质感

https://mp.weixin.qq.com/s/CjqFIVWXb952wJT2ZnjcYA
https://omronfs.omron.com/en_US/ecb/products/pdf/en_a283_e1_01.pdf
https://zhuanlan.zhihu.com/p/144911185

评论 (0)
  • 托马斯微积分第十版中文
    电子书为扫描版本,自己手动添加书签作为目录供参考
  • 首个基于Transformer的分割检测+视觉大模型视频课程(23年新课+源码+课件)
    自动驾驶是高安全型应用,需要高性能和高可靠的深度学习模型,Vision Transformer是理想的选摔。现在主流的自动驾驶感知算法基本都使用了Vision Transformer相关技术,比如分割、2D/3D检测,以及最近大火的大模型 (如SAM),Vision Transformer在自动驾驶领域的落地方面遍地开花。5一方面,在自动驾驶或图像处理相关算法岗位的面试题中,Vision Transformer是必考题,需要对其理论知识有深入理解,并且在项目中真实的使用过相关技术。

    Transformer出自于Google于2017年发表的论文《Attention is all you need》,最开始是用于机器翻译,并且取得了非常好的效果。但是自提出以来,Transformer不仅仅在NLP领域大放异彩,并且在CV、RS等领域也取得了非常不错的表现。尤其是2020年,绝对称得上是Transformer的元年,比如在CV领域,基于Transformer的模型横扫各大榜单,完爆基于CNN的模型。为什么Transformer模型表现如此优异?它的原理是什么?它成功的关键又包含哪些?本文将简要地回答一下这些问题。

    我们知道Transformer模型最初是用于机器翻译的,机器翻译应用的输入是某种语言的一个句子,输出是另外一种语言的句子。
    var i *int = nil
    fmt.Println("i.size:", unsafe.Sizeof(i)) //8

    var i8 *int8 = nil
    fmt.Println("i8.size:", unsafe.Sizeof(i8)) //8

    var s *string = nil
    fmt.Println("s.size:", unsafe.Sizeof(s)) //8

    var ps *struct{} = nil
    fmt.Println("ps.size:", unsafe.Sizeof(ps)) //8

    var si []int = nil
    var si1 []int = nil
    fmt.Println("si.size:", unsafe.Sizeof(si)) //24

    var ii interface{} = nil
    fmt.Println("ii.size:", unsafe.Sizeof(ii)) //16
    我们以生成我,爱,机器,学习,翻译成<bos>,i,love,machine,learning,<eos>这个例子做生成过程来解释。
    训练:

    把“我/爱/机器/学习”embedding后输入到encoder里去,最后一层的encoder最终输出的outputs [10, 512](假设我们采用的embedding长度为512,而且batch size = 1),此outputs 乘以新的参数矩阵,可以作为decoder里每一层用到的K和V;
    将<bos>作为decoder的初始输入,将decoder的最大概率输出词向量A1和‘i’做cross entropy(交叉熵)计算error。
    将<bos>,“i” 作为decoder的输入,将decoder的最大概率输出词 A2 和‘love’做cross entropy计算error。
    将<bos>,“i”,“love” 作为decoder的输入,将decoder的最大概率输出词A3和’machine’ 做cross entropy计算error。
    将<bos>,“i”,"love ",“machine” 作为decoder的输入,将decoder最大概率输出词A4和‘learning’做cross entropy计算error。
    将<bos>,“i”,"love ",“machine”,“learning” 作为decoder的输入,将decoder最大概率输出词A5和终止符做cross entropy计算error。
    那么并行的时候是怎么做的呢,我们会有一个mask矩阵在这叫seq mask,因为他起到的作用是在decoder编码我们的target seq的时候对每一个词的生成遮盖它之后的词的信息。
    func main() {
    s := []string{"a", "b", "c"}
    fmt.Println("s:origin", s)
    changes1(s)
    fmt.Println("s:f1", s)

    changes2(s)
    fmt.Println("s:f2", s)

    changes3(s)
    fmt.Println("s:f3", s)
    }

    func changes1(s []string) {
    var tmp = []string{"x", "y", "z"}
    s = tmp
    }

    func changes2(s []string) {
    // item只是一个副本,不能改变s中元素的值
    for i, item := range s {
    item = "d"
    fmt.Printf("item=%s;s[%d]=%s", item, i, s[i])
    }
    }

    func changes3(s []string) {
    for i := range s {
    s[i] = "d"
    }
    }

    首先我们需要为每个输入向量(也就是词向量)创建3个向量,分别叫做Query、Key、Value。那么如何创建呢?我们可以对输入词向量分别乘上3个矩阵来得到Q、K、V向量,这3个矩阵的参数在训练的过程是可以训练的。注意Q、K、V向量的维度是一样的,但是它们的维度可以比输入词向量小一点,比如设置成64,其实这步也不是必要的,这样设置主要是为了与后面的Mulit-head注意力机制保持一致(当使用8头注意力时,单头所处理的词向量维度为512/8=64,此时Q、K、V向量与输入词向量就一致了)。我们假设输入序列为英文的"Thinking Machines"
    想要深度理解Attention机制,就需要了解一下它产生的背景、在哪类问题下产生,以及最初是为了解决什么问题而产生。

    首先回顾一下机器翻译领域的模型演进历史:

    机器翻译是从RNN开始跨入神经网络机器翻译时代的,几个比较重要的阶段分别是: Simple RNN, Contextualize RNN,Contextualized RNN with attention, Transformer(2017),下面来一一介绍。

    「Simple RNN」 :这个encoder-decoder模型结构中,encoder将整个源端序列(不论长度)压缩成一个向量(encoder output),源端信息和decoder之间唯一的联系只是: encoder output会作为decoder的initial states的输入。这样带来一个显而易见的问题就是,随着decoder长度的增加,encoder output的信息会衰减。
    func main(){
    var c = make(chan int)
    fmt.Printf("c.pointer=%p\n", c) //c.pointer=0xc000022180
    go func() {
    c <- 1
    addChannel(c)
    close(c)
    }()

    for item := range c {
    //item: 1
    //item: 2
    fmt.Println("item:", item)
    }
    }

    func addChannel(done chan int) {
    done <- 2
    fmt.Printf("done.pointer=%p\n", done) //done.pointer=0xc000022180
    }
    在测试模型的时候,Test:decoder没有label,采用自回归一个词一个词的输出,要翻译的中文正常从encoder并行输入(和训练的时候一样)得到每个单词的embedding,然后decoder第一次先输入bos再此表中的id,得到翻译的第一个单词,然后自回归,如此循环直到预测达到eos停止标记
    type visit struct {
    a1  unsafe.Pointer
    a2  unsafe.Pointer
    typ Type
    }

    func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
    if !v1.IsValid() || !v2.IsValid() {
    return v1.IsValid() == v2.IsValid()
    }
    if v1.Type() != v2.Type() {
    return false
    }

    // We want to avoid putting more in the visited map than we need to.
    // For any possible reference cycle that might be encountered,
    // hard(v1, v2) needs to return true for at least one of the types in the cycle,
    // and it's safe and valid to get Value's internal pointer.
    hard := func(v1, v2 Value) bool {
    switch v1.Kind() {
    case Pointer:
    if v1.typ.ptrdata == 0 {
    // not-in-heap pointers can't be cyclic.
    // At least, all of our current uses of runtime/internal/sys.NotInHeap
    // have that property. The runtime ones aren't cyclic (and we don't use
    // DeepEqual on them anyway), and the cgo-generated ones are
    // all empty structs.
    return false
    }
    fallthrough
    case Map, Slice, Interface:
    // Nil pointers cannot be cyclic. Avoid putting them in the visited map.
    return !v1.IsNil() && !v2.IsNil()
    }
    return false
    }

    if hard(v1, v2) {
    // For a Pointer or Map value, we need to check flagIndir,
    // which we do by calling the pointer method.
    // For Slice or Interface, flagIndir is always set,
    // and using v.ptr suffices.
    ptrval := func(v Value) unsafe.Pointer {
    switch v.Kind() {
    case Pointer, Map:
    return v.pointer()
    default:
    return v.ptr
    }
    }
    addr1 := ptrval(v1)
    addr2 := ptrval(v2)
    if uintptr(addr1) > uintptr(addr2) {
    // Canonicalize order to reduce number of entries in visited.
    // Assumes non-moving garbage collector.
    addr1, addr2 = addr2, addr1
    }

    // Short circuit if references are already seen.
    typ := v1.Type()
    v := visit{addr1, addr2, typ}
    if visited[v] {
    return true
    }

    // Remember for later.
    visited[v] = true
    }

    switch v1.Kind() {
    case Array:
    for i := 0; i < v1.Len(); i++ {
    if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
    return false
    }
    }
    return true
    case Slice:
    if v1.IsNil() != v2.IsNil() {
    return false
    }
    if v1.Len() != v2.Len() {
    return false
    }
    if v1.UnsafePointer() == v2.UnsafePointer() {
    return true
    }
    // Special case for []byte, which is common.
    if v1.Type().Elem().Kind() == Uint8 {
    return bytealg.Equal(v1.Bytes(), v2.Bytes())
    }
    for i := 0; i < v1.Len(); i++ {
    if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
    return false
    }
    }
    return true
    case Interface:
    if v1.IsNil() || v2.IsNil() {
    return v1.IsNil() == v2.IsNil()
    }
    return deepValueEqual(v1.Elem(), v2.Elem(), visited)
    case Pointer:
    if v1.UnsafePointer() == v2.UnsafePointer() {
    return true
    }
    return deepValueEqual(v1.Elem(), v2.Elem(), visited)
    case Struct:
    for i, n := 0, v1.NumField(); i < n; i++ {
    if !deepValueEqual(v1.Field(i), v2.Field(i), visited) {
    return false
    }
    }
    return true
    case Map:
    if v1.IsNil() != v2.IsNil() {
    return false
    }
    if v1.Len() != v2.Len() {
    return false
    }
    if v1.UnsafePointer() == v2.UnsafePointer() {
    return true
    }
    for _, k := range v1.MapKeys() {
    val1 := v1.MapIndex(k)
    val2 := v2.MapIndex(k)
    if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) {
    return false
    }
    }
    return true
    case Func:
    if v1.IsNil() && v2.IsNil() {
    return true
    }
    // Can't do better than this:
    return false
    case Int, Int8, Int16, Int32, Int64:
    return v1.Int() == v2.Int()
    case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
    return v1.Uint() == v2.Uint()
    case String:
    return v1.String() == v2.String()
    case Bool:
    return v1.Bool() == v2.Bool()
    case Float32, Float64:
    return v1.Float() == v2.Float()
    case Complex64, Complex128:
    return v1.Complex() == v2.Complex()
    default:
    // Normal equality suffices
    return valueInterface(v1, false) == valueInterface(v2, false)
    }
    }
    这便是encoder的整体计算流程图了,Transformer模型中堆叠了多个这样的encoder,无非就是输出连接输入罢了,常规操作。
    最后再附上一个Transformer的代码实现,读者有兴趣可以跟着自己复现一下Transformer模型的代码。
       package main

       import (
           "log"
           "sync"
       )

       func init() {
           log.SetFlags(log.Lshortfile)
       }
       func main() {
           lock := sync.Mutex{}

           //Go 1.18 新增,是一种非阻塞模式的取锁操作。当调用 TryLock() 时,
           //该函数仅简单地返回 true 或者 false,代表是否加锁成功
           //在某些情况下,如果我们希望在获取锁失败时,并不想停止执行,
           //而是可以进入其他的逻辑就可以使用TryLock()
           log.Println("TryLock:", lock.TryLock())
           //已经通过TryLock()加锁,不能再次加锁
           lock.Lock()

       }

  • RadarSensors_ARS404-21_cn数据手册​
    RadarSensors_ARS404-21_cn数据手册
  • ECG前置电路设计
    TI出的一个经验文档,讲的很不错
  • 15、贴片叠层电感应用测试中不良率高的原因
    15、贴片叠层电感应用测试中不良率高的原因
  • [完结11章]技术大牛成长课,从0到1带你手写一个数据库系统
    大家好,今天我将给大家分享关于如何开发一个数据库系统的知识,将从0到1手把手带着一步步去开发这个项目,希望我的分享对大家的学习和工作有所帮助,如果有不足的地方还请大家多多指正。

    一、什么是数据库系统
    数据库系统一般由数据库、数据库管理系统(及其开发工具)、应用系统、数据库管理员构成 

    二、数据库管理系统的主要功能包括
    数据定义功能:DBMS提供数据定义语言(Data Definition Language,DDL),用户通过它可以方便地对数据库中的对象进行定义
    数据组织、存储和管理:DBMS要分类组织、存储和管理各种数据,包括数据字典、用户数据、数据的存取路径等。
    数据操纵功能:DBMS提-供数据操纵语言(Data Manipulation Language,DML),用户可以使用DML操纵数据,实现对数据库的基本操作,如查询、插入、删除和修改等
    数据库的事务管理和运行管理:数据库在建立、运用和维护时由数据管理系统统一管理、统一控制,以保证数据的安全性、完整性、多用户对数据的并发使用以及发生故障后的系统恢复
    数据库建立和维护功能:数据库初始数据的输入、转换功能,数据库的转储、恢复功能,数据库的重组织功能和性能监视、分析功能等。

    三、数据库系统结构
    1.1模式(概念模式或逻辑模式)
    定义:数据库中全体数据的逻辑结构特征的描述,是所有用户的公用数据库结构。

    特性:

    一个数据库只有一个模式
    模式与应用程序无关,只是数据的一个框架
    1.2子模式(外模式或用户模式)
    定义:数据库用户所见和使用的局部数据的逻辑结构和特征的描述,是用户所用的数据库结构

    特性:

    子模式是模式的子集
    一个数据库有多个子模式,每个用户至少使用一个子模式
    同一个用户可以使用不同的子模式,每个子模式可为不同的用户所用
    1.3内模式(存储模式)
    定义:是数据物理结构和存储方法的描述。它是整个数据库的最低层结构的表示。

    特性:

    一个数据库只有一个内模式,内模式对用户透明
    一个数据库由多种文件组成,如用户数据文件,索引文件及系统文件
    内模式设计直接影响数据库的性能

    以下是开发流程:
    在idea中构建如下几个子模块工程:
    @PostMapping("/doLogin")
    @ApiOperation(value = "一键注册登录接口", notes = "一键注册登录接口", httpMethod = "POST")
    public GraceJSONResult doLogin(HttpServletRequest request,
                                   HttpServletResponse response,
                                   @RequestBody @Valid RegisterLoginBO registerLoginBO,
                                   BindingResult result);
    验证的字段上方可以写一些相关的注解,系统识别后会自动检查
    RegisterLoginBO.java
    public class RegisterLoginBO {

        @NotBlank(message = "手机号不能为空")
        private String mobile;
        @NotBlank(message = "短信验证码不能为空")
        private String smsCode;

        public String getMobile() {
            return mobile;
        }

        public void setMobile(String mobile) {
            this.mobile = mobile;
        }

        public String getSmsCode() {
            return smsCode;
        }

        public void setSmsCode(String smsCode) {
            this.smsCode = smsCode;
        }

        @Override
        public String toString() {
            return "RegisterLoginBO{" +
                    "mobile='" + mobile + '\'' +
                    ", smsCode='" + smsCode + '\'' +
                    '}';
        }
    }

    如果校验有问题,那么可以直接获得并且放回给前端即可。
    BaseController.java
    /**
     * 验证beanBO中的字段错误信息
     * @param result
     * @return
     */
    public Map<String, String> getErrors(BindingResult result) {
        Map<String, String> map = new HashMap<>();
        List<FieldError> errorList = result.getFieldErrors();
        for (FieldError error : errorList) {
            // 发生验证错误所对应的某一个属性
            String errorField = error.getField();
            // 验证错误的信息
            String errorMsg = error.getDefaultMessage();
            map.put(errorField, errorMsg);
        }
        return map;
    }
    一般来说,admin系统不会有主动注册功能,账号都是分配的,那么默认就会存在一个基本账户,这也是预先通过代码生成用户名和密码的。直接手动生成即可:
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    同理,查询操作也是类似JPA的操作,再继承Repository后直接使用其内置api即可:
    FriendLinkMngControllerApi.java
    @PostMapping("getFriendLinkList")
    @ApiOperation(value = "查询友情链接列表", notes = "查询友情链接列表", httpMethod = "POST")
    public GraceJSONResult getFriendLinkList();
    首先可以在数据库通过写sql脚本实现查询
    SELECT
    c.id as commentId,
    c.father_id as fatherId,
    c.article_id as articleId,
    c.comment_user_id as commentUserId,
    c.comment_user_nickname as commentUserNickname,
    c.content as content,
    c.create_time as createTime,
    f.comment_user_nickname as quoteUserNickname,
    f.content as quoteContent
    FROM
    comments c
    LEFT JOIN
    comments f
    on
    c.father_id = f.id
    WHERE
    c.article_id = '2006117B57WRZGHH'
    order by
    c.create_time
    desc
    目前我们所搭建的eureka是单机单实例的注册中心,如果挂了,那么整个微服务体系完全不可以,这是不应该的,所以为了实现eureka的高可用,我们可以搭建集群。
    在进行集群构建之前,大家先参照目前的eureka再去构建一个一模一样的工程,可以取名为 springcloud-eureka-cluster。
    为集群中各个eureka节点配置host
    eureka:
      instance:
        hostname: eureka-cluster-${port:7001}    # 集群中每个eureka的名字都要唯一
      # 自定义eureka集群中另外的两个端口号
      other-node-port2: ${p2:7002}
      other-node-port3: ${p3:7003}
      client:
    #    register-with-eureka: false
    #    fetch-registry: false
        service-url:
          # 集群中的每个eureka单实例,都需要相互注册到其他的节点,在此填入集群中其他eureka的地址进行相互注册
          defaultZone: http://eureka-cluster-${eureka.other-node-port2}:${eureka.other-node-port2}/eureka/,http://eureka-cluster-${eureka.other-node-port3}:${eureka.other-node-port3}/eureka/
    我们自己测试的时候时间可以设置为10秒内有10次,我认定非法请求,直接限制这个ip访问15秒,15秒后释放。(像有的网站会出现二维码让你扫描通过,或者手机验证码或者人机交互判断你当前是否是人还是机器,因为有可能是爬虫请求)
    开发步骤:
    首先在yml中设置基本参数:
    @Override
    public Object run() throws ZuulException {
        System.out.println("执行【IP黑名单】Zuul过滤器...");

        // 获得上下文对象requestContext
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        // 获得ip
        String ip = IPUtil.getRequestIp(request);

        /**
         * 需求:
         * 判断ip在10秒内请求的次数是否超过10次,
         * 如果超过,则限制访问15秒,15秒过后再放行
         */
        final String ipRedisKey = "zuul-ip:" + ip;
        final String ipRedisLimitKey = "zuul-ip-limit:" + ip;

        // 获得剩余的限制时间
        long limitLeftTime = redis.ttl(ipRedisLimitKey);
        // 如果剩余时间还存在,说明这个ip不能访问,继续等待
        if (limitLeftTime > 0) {
            stopRequest(requestContext);
            return null;
        }

        // 在redis中累加ip的请求访问次数
        long requestCounts = redis.increment(ipRedisKey, 1);

        // 从0开始计算请求次数,初期访问为1,则设置过期时间,也就是连续请求的间隔时间
        if (requestCounts == 1) {
            redis.expire(ipRedisKey, timeInterval);
        }

        // 如果还能取得到请求次数,说明用户连续请求的次数落在10秒内
        // 一旦请求次数超过了连续访问的次数,则需要限制这个ip了
        if (requestCounts > continueCounts) {
            // 限制ip访问一段时间
            redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);

            stopRequest(requestContext);
        }

        return null;
    }

    private void stopRequest(RequestContext requestContext){
        // 停止继续向下路由,禁止请求通信
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(200);
        String result = JsonUtils.objectToJson(
                GraceJSONResult.errorCustom(
                        ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP));
        requestContext.setResponseBody(result);
        requestContext.getResponse().setCharacterEncoding("utf-8");
        requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
    }
    上面这些都是通过不同key要执行多次才能得到结果,一般来说我们会使用es的aggs功能做聚合统计,会更好。
    通过一个脚本来统计男女数量:
    POST http://192.168.1.203:9200/fans/_doc/_search
    {
        "size": 0,
        "query":{
            "match":{
                "writerId":"201116760SMSZT2W"
            }
        },
        "aggs": {
            "counts": {
                "terms": {
                    "field": "sex"
                }
            }
        }
    }

    以下就是数据库系统开发的整个流程讲解,感谢大家的阅读

  • 浅谈地下污水厂智能照明控制应用

    结合某地下污水厂项目,从结构、系统组成、系统功能、控制要求、场景模式等方面介绍了地下污水厂智能照明控制系统,探索了一套适用于地下污水厂的智能照明控制策略,以确保地下污水厂正常运行的照明需求。

  • 工业级液晶显示控制芯片RA8889ML3N原理图
    TFT-LCD液晶显示控制芯片RA8889ML3N的优势:
    低功耗及功能强大:这款芯片最大支持分辨率为1366x2048,内置128Mb SDRAM,可为内容显示进行快速刷新,同时内置视频解码单元,支持JPEG/AVI硬解码播放,为普通单片机实现视频播放提供可能。
    支持多种接口:RA8889ML3N支持MCU端的8080/6800 8/16-bit 非同步并列接口和3/4线SPI及IIC串列接口,以及最大驱动1366x800分辨率的TFT LCD。
    显示功能强大:RA8889ML3N提供多段的显示记忆体缓冲区段,支持多图层功能,并提供画中画(PIP)、支持透明度控制与显示旋转镜像等显示功能。
    应用范围广:这款芯片广泛应用于自动化控制设备、电力监测控制、测量检测仪器仪表、电教设备、智能家电、医疗检测设备、车用仪表及工控自动化等领域。
  • 基本_碳化硅功率器件_选型手册_2023Q3.pdf
    基本_碳化硅功率器件_选型手册_2023Q3.pdf
  • 14、小电流贴片共模电感更换需要注意些什么.
    14、小电流贴片共模电感更换需要注意些什么.
  • ​无论当下看不看机会,把握行情肯定是没错。 通过岗位数量,岗位要求(如对学历,技术点的要求)来了解行情是有效途径之一。 可以找我了解更多全国岗位。 【嵌入式软件工程师】 上海,风电行业国资企业,对学历有要求。 岗位职责: 1. 负责产品全周期研发,包括市场调研、客户需求技术转化、产品设计、产品制造、产品安装调试、产品测试验证和产品认证; 2. 负责产品失效根因分析,提供有效解决方案; 3. 负责组织供应商选择和产品质量管控; 4. 组织编制产品开发技
    落子无悔 2023-12-06 13:27 63浏览
  • #这段代码是一个基于C语言的嵌入式程序,用于在HPMicro平台上运行。它的主要功能是初始化一个LED灯,并使其以一定的时间间隔闪烁。#以下是对代码的解析:#```c#include #include "board.h"#include "hpm_debug_console.h"#define LED_FLASH_PERIOD_IN_MS 300int main(void){    int u;    board_init(); // 初始化板子 
    丙丁先生 2023-12-06 14:22 105浏览
  • 近日,英国伦敦的可持续倡议(SMI)公布了2023年《地球宪章》徽章获奖名单。在这个名单中,全球共17家企业入选,中国企业仅占两席。最值得注目的是,海尔智家作为唯一获奖的中国家电企业,荣登榜单。 据了解,《地球宪章》徽章由现任英国国王查尔斯三世于2021年发起,旨在表彰对全球环境的可持续发展做出突出贡献的企业,鼓励企业将自然、人类和地球置于核心位置,通过创新和可持续的商业模式,共同应对全球气候挑战。获奖企业由来自环境、商业、政治和慈善界的全球专家小组经过严苛的评选选出。 海尔
    锦缎研究院 2023-12-06 12:41 53浏览
  • 来源:虹科汽车电子 虹科技术丨BabyLIN产品如何轻松搞定K线协议实现? 原文链接:https://mp.weixin.qq.com/s/LR7VHFQajjyw6s6bVDJmsg 欢迎关注虹科,为您提供最新资讯!   导读 为了实现K线通信,SDF-V3在协议部分中定义了新的协议类型KLine Raw。所有能够运行SDF-V3文件(LinWorks版本在V.2.29.4以上)并使用最新固件(固件版本在V.6.18以上)的BabyLIN设备都可以执行KLine Raw
    虹科电子科技 2023-12-06 14:42 108浏览
  •     今天看到一个麦肯锡的统计数据,2021年中国出口的电子产品占世界34%。越来越多的PCB组件在中国造出来,然后送往全世界。作为电子工程师,除了增加修养,不断实现良好的设计,也要减少电子垃圾,对国际上的主要环保要求有所了解。    ROHS  Restriction of Hazardous Substances  有毒物质限制        这个标准针对 6 类电子产品中常见的的有毒物质,
    电子知识打边炉 2023-12-06 22:21 107浏览
  • 背景   随着汽车行业的不断迭代发展,市场及消费者对汽车提出了更高的要求,智能网联、自动驾驶等新技术的应用推动整车厂对车载芯片、汽车软件等方面投入了更多的精力,SOA(面向服务的架构)逐渐成为大多整车厂顺应市场趋势和技术趋势的首选。SOA架构使服务间的通讯变得更加简单,ECU更新、软件升级等变得更加灵活,使系统的健壮性和拓展性获得了大幅提高。但是在SOA架构开发阶段,由于市面上的IDL(接口描述语言)众多,例如FIDL、Protobuf、vCDL、ARXML、OMG IDL、CAN
    北汇信息 2023-12-06 11:41 73浏览
  • 近日,在传感器专家网的压力传感器专业交流群组中,有相关专家交流了目前我国压力传感器的一些情况。交流中指出,目前国内已有一些企业在做MEMS压力传感器芯片,在该领域国内相关企业总体来说技术差不多,精度等关键技术指标彼此间相差不大,但与国外压力传感器芯片巨头相比,精度等指标却有较大差距。传感器专家网https://www.sensorexpert.com.cn专注于传感器技术领域,致力于对全球前沿市场动态、技术趋势与产品选型进行专业垂直的服务,是国内领先的传感器产品查询与媒体信息服务平台。基于传感器
    传感器专家网 2023-12-06 11:03 52浏览
  • 导语:CINNO Research统计数据表明,Q3'23全球半导体设备厂商市场规模前十大公司合计超250亿美元,同比下降9%,环比增长3%。CINNO Research统计数据表明,Q3'23全球半导体设备厂商市场规模Top10营收合计超250亿美元,同比下降9%,环比增长3%。Q3'23全球半导体设备厂商市场规模排名Top10与1H'23的Top10设备商相比,日立高新(Hitachi High-Tech)排名跌出Top10,泰瑞达(Teradyne)排名回归第十。荷兰公司阿斯麦(ASML)
    CINNOResearch 2023-12-06 14:04 105浏览
  • 光耦合器是一种在现代科技中发挥关键作用的设备,其应用领域横跨通信、医疗、工业等多个行业。光耦合器通过巧妙地将光电子器件结合起来,实现了光与电的高效转换和传输,为光电子领域的发展提供了强大的支持。光耦合器是什么光耦合器是一种用于隔离、耦合和传输光信号的器件。其主要功能是将一个光学系统的光信号转换成电信号,或者将电信号转换为光信号,以实现光与电之间的高效转换。在各类光电子设备中,光耦合器起到了桥梁的作用,实现了不同部分之间的无缝衔接。光耦合器的原理及结构光耦合器的基本原理是通过光电二极管和光电晶体管
    克里雅半导体科技 2023-12-06 10:58 31浏览
  • 摘要:根据CINNO Research产业统计数据,Q3'23国内智能手机屏下指纹识别占比创历史新高达45%,而侧边指纹识别占比较去年同期下降12%,后置指纹识别占比下降至1%,而前置指纹已淡出国内智能手机指纹识别市场。根据CINNO Research产业统计数据,受华为、荣耀热门机型销售的影响,Q3'23国内智能手机指纹识别搭载率上升至84%。图示:中国市场智能手机指纹识别技术别占比趋势来源: CINNO Research月度中国市场智能手机指纹识别应用趋势报告2023年第三季度,国内OLED
    CINNOResearch 2023-12-06 12:53 91浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦