无人驾驶控制算法LQR和MPC的仿真实现

1 首先来说说LQR

1.1 Problem Statement

首先我们简单的假设一个物理系统,在光滑的一维地面上有一个质量为 m m m 的滑块,初始位置与初始速度都为 0 0 0,现需要设计控制器,在传感器测得滑块位置 x x x 的基础上,为滑块提供外力 u u u,使其跟随参考点 x r x_r xr

在这里插入图片描述

建立动力学模型:

x ¨ = u m \ddot x = \frac{u}{m} x¨=mu

选取状态向量 x = [ x 1 x 2 ] = [ x x ˙ ] x= \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} =\begin{bmatrix} x \\\dot x \end{bmatrix} x=[x1x2]=[xx˙] ,这里我们将状态变量的x1等于位移x,状态变量x2等于速度,得到系统状态方程为:
x ˙ = A x + B u \dot x= Ax+Bu x˙=Ax+Bu
其中:
A = [ 0 1 0 0 ] , B = [ 0 1 m ] A=\begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix},B=\begin{bmatrix} 0 \\ \frac{1}{m} \end{bmatrix} A=[0010],B=[0m1]
我们需要的输出是 x 1 x_1 x1(也就是我们的位移量),我们希望能通过控制 u u u来达到控制我们物块位移 x 1 x_1 x1

我们看到系统的开环矩阵 A A A 就可以判断如果没有控制器这个系统是不可能稳定的,从物理角度也很好理解你给这个物块一个不变的 u u u,物块在光滑的地面上是不可能自己停下来的。

1.2 控制器

我们上节说到需要引入控制器才能使我们的系统稳定,我们考虑:
u = − k x = − [ k 1 , k 2 , ⋯ ] [ x 1 x 2 ⋮ ] u=-kx=-\begin{bmatrix} k_1,k_2,\cdots \end{bmatrix}\begin{bmatrix} x_1 \\ x_2 \\ \vdots \end{bmatrix} u=kx=[k1,k2,] x1x2
这样我们就可以得到一个新的闭环矩阵:
x ˙ = A x − B k x = ( A − B k ) x x ˙ = A c l x \begin{align*} &\dot x = Ax-Bkx =(A-Bk)x \\ &\dot x=A_{cl}x \end{align*} x˙=AxBkx=(ABk)xx˙=Aclx
也就是说我们通过选择k来改变 A c l A_{cl} Acl 的特征值从而达到来控制系统的表现的目的。

所以问题就是我们如何来选择这个最好的 k k k

我们有很多方法可以让系统达到稳定的状态,但是如何选取这个特征值将决定着我们让这个系统以一个怎么样的方式去靠近这个稳定状态。

所以带着对这句话的理解,我们来看LQR控制器的思想。

1.3 LQR(Linear Quadratic Regulator)

我们引入 Cost Function 的概念
J = ∫ 0 ∞ ( x T Q x + u T R u ) d t J= \int_0^\infty{(x^TQx+u^TRu)}dt J=0(xTQx+uTRu)dt
在这里Q矩阵 [ a 0 0 0 b 0 0 0 c ] \begin{bmatrix} a & 0 & 0 \\ 0 & b & 0 \\ 0 & 0 & c \end{bmatrix} a000b000c 的a,b,c对应的是 x 1 2 , x 2 2 , x 3 2 x_1^2, x_2^2, x_3^2 x12,x22,x32 ,从这里看到我们希望x1对系统的影响大一点的话,我们就可以增加a的值,同理如果我们的R值很大,自然输入u对于系统的影响就会很大。

综上就是LQR可以在满足系统稳定性的同时,帮我们寻找到代价函数的最小值。
m i n J = ∫ 0 ∞ ( x T Q x + u T R u ) d t min J= \int_0^\infty{(x^TQx+u^TRu)}dt minJ=0(xTQx+uTRu)dt
现在我们回到刚才的小物块物理系统,我们现在希望小物块按照我们预设的轨迹 x r x_r xr运动,所以我们会有一个误差 e e e,那最终我们希望这个误差可以趋向于0,我们稍微更改一下我们现在的状态方程,我们用误差e来替换我们的x1,也就是
e ˙ = x ˙ r − x ˙ 1 = − x ˙ 1 = − x 2 \dot e=\dot x_r - \dot x_1=-\dot x_1 = -x_2 e˙=x˙rx˙1=x˙1=x2
那么得到新的状态方程
[ e ˙ x ˙ 2 ] = [ 0 − 1 0 0 ] [ e x 2 ] + [ 0 1 m ] u \begin{bmatrix} \dot e \\ \dot x_2 \end{bmatrix} = \begin{bmatrix} 0 & -1 \\ 0 & 0 \end{bmatrix} \begin{bmatrix} e \\ x_2 \end{bmatrix} + \begin{bmatrix} 0 \\ \frac{1}{m} \end{bmatrix}u [e˙x˙2]=[0010][ex2]+[0m1]u
假设在这里我们希望u对代价函数的影响更大一点,我们就分别设置Q和R为:
Q = [ 1 0 0 1 ] R = 10 Q = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} \\ R=10 Q=[1001]R=10
利用Riccati方程计算

在这里插入图片描述

1.4 仿真建模

简单粗略地仿真建模,见谅 >ˍ<

在这里插入图片描述

我们将初始值设在5,目标值设在1,最后得到

在这里插入图片描述

至此一个简单物理系统的LQR控制系统就OK了,接下来我们看一下更复杂一点的MPC。

2.接着说MPC

2.1 模型的离散化

还是之前的控制对象:
x ˙ = A x + B u \dot x= Ax+Bu x˙=Ax+Bu
我们用前向欧拉法将状态方程离散化:
x ˙ = x ( k + 1 ) − x ( k ) T = A x ( k ) + B u ( k ) x ( k + 1 ) = ( I + T A ) x ( k ) + T B u ( k ) x ( k + 1 ) = A ˉ x ( k ) + B ˉ u ( k ) \begin{align*} &\dot x =\frac{x(k+1)-x(k)}{T}=Ax(k)+Bu(k) \\ &x(k+1) =(I+TA)x(k)+TBu(k) \\ &x(k+1) =\bar Ax(k) + \bar B u(k) \end{align*} x˙=Tx(k+1)x(k)=Ax(k)+Bu(k)x(k+1)=(I+TA)x(k)+TBu(k)x(k+1)=Aˉx(k)+Bˉu(k)
其中
A ˉ = [ 1 T 0 1 ] , B ˉ = [ 0 T m ] \bar A=\begin{bmatrix} 1 & T \\ 0 & 1 \end{bmatrix},\bar B=\begin{bmatrix} 0 \\ \frac{T}{m} \end{bmatrix} Aˉ=[10T1],Bˉ=[0mT]
这里的T是控制周期。

2.2 Prediction预测

MPC方法的一个独特之处就是需要对未来系统状态进行预测,在k时刻我们预估未来p个控制周期内预测的系统状态为:
X k = [ x ( k + 1 ∣ k ) T x ( k + 2 ∣ k ) T ⋯ x ( k + p ∣ k ) T ] T X_k= \begin{bmatrix} x(k+1|k)^T & x(k+2|k)^T &\cdots& x(k+p|k)^T \end{bmatrix}^T Xk=[x(k+1∣k)Tx(k+2∣k)Tx(k+pk)T]T
p称为预测时域,括号中k+1|k表示在当前k时刻预测k+1时刻的系统状态,以此类推。

另外,预测动态系统未来状态时,还需要知道预测时域内的控制量Uk:
U k = [ u ( k ∣ k ) T u ( k + 1 ∣ k ) T ⋯ u ( k + p − 1 ∣ k ) T ] T U_k= \begin{bmatrix} u(k|k)^T & u(k+1|k)^T &\cdots& u(k+p-1|k)^T \end{bmatrix}^T Uk=[u(kk)Tu(k+1∣k)Tu(k+p1∣k)T]T
这是我们接下来将要求解的优化问题的独立变量。

现在,我们可以通过离散化状态方程依次对未来p个控制周期的系统状态进行预测:
x ( k + 1 ∣ k ) = A ˉ x ( k ) + B ˉ u ( k ∣ k ) x ( k + 2 ∣ k ) = A ˉ 2 x ( k ) + A ˉ B ˉ u ( k ∣ k ) + B ˉ u ( k + 1 ∣ k ) x ( k + 3 ∣ k ) = A ˉ 3 x ( k ) + A ˉ 2 B ˉ u ( k ∣ k ) + A ˉ B ˉ u ( k + 1 ∣ k ) + B ˉ u ( k + 2 ∣ k ) ⋮ x ( k + p ∣ k ) = A ˉ p x ( k ) + ∑ i = 0 p − 1 A ˉ p − 1 − i B ˉ u ( k + i ∣ k ) \begin{align*} & x(k+1|k) = \bar Ax(k) + \bar B u(k|k) \\ & x(k+2|k) = \bar A^2x(k) + \bar A \bar B u(k|k) + \bar B u(k+1|k) \\ & x(k+3|k) = \bar A^3x(k) + \bar A^2 \bar B u(k|k) + \bar A \bar B u(k+1|k) +\bar B u(k+2|k)\\ & \vdots \\ & x(k+p|k) = \bar A^p x(k) + \sum_{i=0}^{p-1} \bar A^{p-1-i} \bar B u(k+i|k) \end{align*} x(k+1∣k)=Aˉx(k)+Bˉu(kk)x(k+2∣k)=Aˉ2x(k)+AˉBˉu(kk)+Bˉu(k+1∣k)x(k+3∣k)=Aˉ3x(k)+Aˉ2Bˉu(kk)+AˉBˉu(k+1∣k)+Bˉu(k+2∣k)x(k+pk)=Aˉpx(k)+i=0p1Aˉp1iBˉu(k+ik)

整合成矩阵形式:
X k = Ψ x ( k ) + Θ U k X_k=\Psi x(k)+ \Theta U_k Xk=Ψx(k)+ΘUk
其中
Ψ = [ A ˉ 1 A ˉ 2 ⋮ A ˉ p ] , Θ = [ A ˉ 1 − 1 B ˉ ⋯ 0 0 A ˉ 2 − 1 B ˉ A ˉ 2 − 2 B ˉ ⋯ 0 ⋮ ⋮ ⋱ ⋮ A ˉ p − 1 B ˉ A ˉ p − 2 B ˉ ⋯ A ˉ p − p B ˉ ] \Psi =\begin{bmatrix} \bar A^1 \\ \bar A^2 \\ \vdots \\ \bar A^p \end{bmatrix}, \Theta =\begin{bmatrix} \bar A ^{1-1} \bar B & \cdots & 0 & 0 \\ \bar A ^{2-1} \bar B & \bar A ^{2-2} \bar B & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ \bar A ^{p-1} \bar B & \bar A ^{p-2} \bar B & \cdots & \bar A ^{p-p} \bar B \end{bmatrix} Ψ= Aˉ1Aˉ2Aˉp ,Θ= Aˉ11BˉAˉ21BˉAˉp1BˉAˉ22BˉAˉp2Bˉ000AˉppBˉ

上式中的下三角形式,直接反映了系统在时间上的因果关系,即k+1时刻的输入对k时刻的输出没有影响,k+2时刻的输入对k和k+1时刻没有影响。

2.3 优化

这一节我们将求解预测时域内的控制输出 U k U_k Uk,在求解优化问题之前,我们首先明确优化问题的数学描述。

我们的控制目标是使系统的状态跟踪期望的一条轨迹,通常称为参考值,定义预测时域内的参考值序列:
R k = [ r ( k + 1 ) T r ( k + 2 ) T ⋯ x ( k + p ) T ] T R_k= \begin{bmatrix} r(k+1)^T & r(k+2)^T & \cdots & x(k+p)^T \end{bmatrix}^T Rk=[r(k+1)Tr(k+2)Tx(k+p)T]T
注意,在k时刻进行控制的时候,控制器就必须已经得到了k时刻到k+p时刻的参考值,而PID就不需要这么多信息,这是MPC的一个缺点。

我们希望寻找最佳的控制量 U k U_k Uk,使得预测时域内的状态向量与参考值越接近越好,这是一个开环最优控制问题。为此,我们用预测状态向量与参考值之间的累计误差定义一个简单的优化目标函数:
J ( U k ) = ( X k − R k ) T Q ( X k − R k ) J(U_k)=(X_k-R_k)^TQ(X_k-R_k) J(Uk)=(XkRk)TQ(XkRk)

经常地,我们不希望控制动作太大,优化目标函数再添加一项对控制量的约束:
J ( U k ) = ( X k − R k ) T Q ( X k − R k ) + U k T W U k J(U_k)=(X_k-R_k)^TQ(X_k-R_k)+U_k^TWU_k J(Uk)=(XkRk)TQ(XkRk)+UkTWUk

因此,该优化问题可以描述如下:
m i n J ( U k ) min J(U_k) minJ(Uk)

我们将优化函数J(Uk)展开后合并同类项:
J ( U k ) = ( X k − R k ) T Q ( X k − R k ) + U k T W U k = ( E + Θ U k ) T Q ( E + Θ U ) + U k T W U k = E T Q E + ( Θ U k ) T Q ( Θ U k ) + 2 E T Q ( Θ U k ) + U k T W U k = U k T Θ T Q Θ U k + U k T W U k + 2 E T Q ( Θ U k ) + E T Q E = U k T ( Θ T Q Θ + W ) U k + 2 ( E T Q Θ ) U k + E T Q E \begin{align*} J(U_k) &= (X_k-R_k)^TQ(X_k-R_k)+U_k^TWU_k \\ &= (E+\Theta U_k)^TQ(E+\Theta U)+U_k^TWU_k \\ &= E^TQE + (\Theta U_k)^TQ(\Theta U_k)+2E^TQ(\Theta U_k)+U_k^TWU_k \\ &= U_k^T \Theta^T Q \Theta U_k+U_k^TWU_k +2E^TQ(\Theta U_k)+E^TQE \\ &= U_k^T (\Theta^T Q \Theta + W)U_k +2(E^TQ\Theta) U_k +E^TQE \end{align*} J(Uk)=(XkRk)TQ(XkRk)+UkTWUk=(E+ΘUk)TQ(E+ΘU)+UkTWUk=ETQE+(ΘUk)TQ(ΘUk)+2ETQ(ΘUk)+UkTWUk=UkTΘTQΘUk+UkTWUk+2ETQ(ΘUk)+ETQE=UkT(ΘTQΘ+W)Uk+2(ETQΘ)Uk+ETQE

上式中 是常数项,对“ U k U_k Uk为何值时 J J J取得最小值”这一问题没有影响,因此直接舍去。

如图,matlab输入 “help quadprog”查看二次型优化函数quadprog的说明文档,令
{ H = 2 ( Θ T Q Θ + W ) f T = 2 E T Q Θ \left\{ \begin{align*} & H=2(\Theta^TQ\Theta+W) \\ & f^T=2E^TQ\Theta \end{align*} \right. {H=2(ΘTQΘ+W)fT=2ETQΘ

可得最终优化目标函数,至此可直接调用matlab quadprog函数求解 U k U_k Uk,将 U k U_k Uk的第一个元素提取出来,作为本控制周期的控制量。
J ( U k ) = 1 2 U k T H U k + f T x J(U_k)=\frac{1}{2}U_k^THU_k+f^Tx J(Uk)=21UkTHUk+fTx
在这里插入图片描述

2.4 仿真

我们对动力学方程两边拉普拉斯变换:
s 2 X ( s ) = 1 m F ( s ) s^2X(s)=\frac{1}{m}F(s) s2X(s)=m1F(s)
得到传递函数为:
G ( s ) = X ( s ) F ( s ) = 1 m s 2 G(s)=\frac{X(s)}{F(s)}=\frac{1}{ms^2} G(s)=F(s)X(s)=ms21
建立仿真:

在这里插入图片描述

我们得到在固定值和sinwave的情况下基本都可以跟踪的比较好(参数还可继续优化)

在这里插入图片描述

在这里插入图片描述

其中MPC代码为:

function u = Controller(pos_ref, pos, vel)
%参数设置
m    = 1.05;                %滑块质量,增加了5%作为建模误差
T    = 0.01;                %控制周期10ms
p    = 40;                  %控制时域(预测时域)
Q    = 10*eye(2*p);         %累计误差权重
W    = 0.0001*eye(p);       %控制输出权重
umax = 100;                 %控制量限制,即最大的力
Rk   = zeros(2*p,1);        %参考值序列
Rk(1:2:end) = pos_ref;    
Rk(2:2:end) = vel;          %参考速度跟随实际速度
%构建中间变量
xk    = [pos;vel];          %xk
A_    = [1 T;0 1];          %离散化预测模型参数A
B_    = [0;T/m];            %离散化预测模型参数B
psi   = zeros(2*p,2);       %psi
for i=1:1:p
    psi(i*2-1:i*2,1:2)=A_^i;
end
theta = zeros(2*p,p);       %theta
for i=1:1:p
    for j=1:1:i
        theta(i*2-1:i*2,j)=A_^(i-j)*B_;
    end
end
E = psi*xk-Rk;              %E
H = 2*(theta'*Q*theta+W);   %H
f = (2*E'*Q*theta)';        %f
%优化求解
coder.extrinsic('quadprog');
Uk=quadprog(H,f,[],[],[],[],-umax,umax);
%返回控制量序列第一个值
u = 0.0;                    %显示指定u的类型
u = Uk(1);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flutter 3.19 发布,快来看看有什么更新吧?

参考链接:https://medium.com/flutter/whats-new-in-flutter-3-19-58b1aae242d2 新年假期的尾巴,Flutter 迎来了 3.19 更新,该版本带来了 Gemini(Google’s most capable AI ) 的 Dart SDK,更好控制动画颗粒…

扶贫|精准扶贫管理系统|基于Springboot的精准扶贫管理系统设计与实现(源码+数据库+文档)

精准扶贫管理系统目录 目录 基于Springboot的精准扶贫管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员模块的实现 (1)用户信息管理 (2)贫困户信息管理 (3)新闻类型管理 &a…

【论文精读】GPT2

摘要 在单一领域数据集上训练单一任务的模型是当前系统普遍缺乏泛化能力的主要原因,要想使用当前的架构构建出稳健的系统,可能需要多任务学习。但多任务需要多数据集,而继续扩大数据集和目标设计的规模是个难以处理的问题,所以只能…

【MySQL】:DQL查询

🎥 屿小夏 : 个人主页 🔥个人专栏 : MySQL从入门到进阶 🌄 莫道桑榆晚,为霞尚满天! 文章目录 📑前言一. DQL1.1 基本语法1.2 基础查询1.3 条件查询1.3 聚合函数 🌤️ 全篇…

【AI绘画】硬核解读Stable Diffusion(完整版) 小白必收藏!!!

手把手教你入门绘图超强的AI绘画,用户只需要输入一段图片的文字描述,即可生成精美的绘画。给大家带来了全新保姆级教程资料包 (文末可获取) 2022年可谓是AIGC(AI Generated Content)元年,上半年…

【Linux网络编程五】Tcp套接字编程(四个版本服务器编写)

【Linux网络编程五】Tcp套接字编程(四个版本服务器编写) [Tcp套接字编程]一.服务器端进程:1.创建套接字2.绑定网络信息3.设置监听状态4.获取新连接5.根据新连接进行通信 二.客户端进程:1.创建套接字2.连接服务器套接字3.连接成功后进行通信 三…

JavaScript中的变量监听:实时捕捉变化的利器

JavaScript中的变量监听:实时捕捉变化的利器 在JavaScript开发中,经常需要监听变量的改变,并在变量值发生变化时执行相应的操作。这种实时捕捉变化的功能对于构建交互性强的应用程序至关重要。本文将介绍如何在JavaScript中监听变量的改变&am…

模型 4C(消费者、成本、编译、沟通)理论

系列文章 分享 模型,了解更多👉 模型_总纲目录。重在提升认知。以消费者为中心。 1 4C(消费者、成本、编译、沟通)理论的应用 1.1 4C理论在电子商务中的应用 亚马逊是4C营销战略的成功案例。以下是对亚马逊应用4C理论的详细解读: 消费者&a…

【JavaEE Spring 项目】博客系统

博客系统 前⾔项⽬介绍1. 准备⼯作1.1 数据准备1.2 创建项⽬1.3 准备前端⻚⾯1.4 配置配置⽂件1.5 测试 2. 项⽬公共模块2.1 实体类的编写2.2 公共层 3. 业务代码3.1 持久层3.2 实现博客列表3.3 实现博客详情3.4 实现登陆令牌技术JWT令牌介绍JWT令牌⽣成和校验 3.5 实现强制要求…

Python入门知识点分享——(二十一)多继承和运算符重载

在介绍新的知识之前,我们先对之前的内容做一点补充,在面向对象编程的过程中,一个对象常常被要求具备多方面的功能,从而和多个类产生联系,而这一步的实现就用到了“多继承”。多继承是指一个子类可以继承自多个父类&…

【Linux】Linux编译器-gcc/g++ Linux项目自动化构建工具-make/Makefile

目录 Linux编译器-gcc/g使用 1.背景知识 Linux中头文件的目录在 Linux 库 条件编译的典型应用 2.gcc如何完成 动态库 vs 静态库 debug && release Linux项目自动化构建工具-make/Makefile 背景 用法 特殊符号 Linux编译器-gcc/g使用 1.背景知识 预处理&am…

LabVIEW高效电磁阀性能测试

LabVIEW高效电磁阀性能测试 在核电站的安全运营中,电磁阀作为关键组件,其性能的可靠性至关重要。设计一套基于LabVIEW的电磁阀测试平台,既能精准测试电磁阀的多项性能指标,又能提高检修效率与准确性,进而保障核电站的…

Mock.js

在开发后端的应用中,我们使用postman来测试接口,观察和验证前后端之间的数据传递是否正常。 在开发前端的应用中,我们使用Mock.js来模拟后端服务,以便进行前端业务逻辑的开发和测试。 一般情况下,个人开发或者小团队开…

Codeforces Round 919 (Div. 2)题解(A-E)

https://codeforces.com/contest/1920 A Satisfying Constraints 链接&#xff1a;A - Satisfying Constraints 代码 #include <bits/stdc.h> using namespace std; int main() {int T;cin >> T;while(T--){int n;scanf("%d", &n);vector<int&…

算法沉淀——BFS 解决 FloodFill 算法(leetcode真题剖析)

算法沉淀——BFS 解决 FloodFill 算法 01.图像渲染02.岛屿数量03.岛屿的最大面积04.被围绕的区域 BFS&#xff08;广度优先搜索&#xff09;解决 Flood Fill 算法的基本思想是通过从起始点开始&#xff0c;逐层向外扩展&#xff0c;访问所有与起始点相连且具有相同特性&#xf…

docker (三)-开箱即用常用命令

一 docker架构 拉取镜像仓库中的镜像到本地&#xff0c;镜像运行产生一个容器 registry 镜像仓库 registry可以理解为镜像仓库&#xff0c;用于保存docker image。 Docker Hub 是docker官方的镜像仓库&#xff0c;docker命令默认从docker hub中拉取镜像。我们也可以搭建自己…

【Windows】删除 VHD 虚拟磁盘时提示“文件已在 System 中打开”的解决方法

一、原因 正如显示的那样&#xff0c;虚拟磁盘仍在被系统占用。因此我们需要断开磁盘与系统的连接。 二、解决方法 1. 在“开始”菜单中搜索“磁盘管理”&#xff0c;选择“创建并格式化硬盘分区”。 2. 右键点击需要删除的虚拟磁盘&#xff0c;选择“分离 VHD”。 3. 点击“…

laravel_stub type-hints的简单介绍

开发者介绍 存根文件的类型提示的功能是由Nuno Maduro&#xff08;右边的人&#xff09;提供的。 stub type-hints 指的是在 stub 文件中使用的类型提示&#xff08;type hints&#xff09;。 类型提示是 PHP 7 引入的一个特性&#xff0c;允许你在函数、方法和类的上下文中指…

day14笔记(多态)

多态 自己写多态演示遇见两个问题 1.在类里写show方法时犹豫要不要写参数,其实不用写参数也可以获取到类的信息 public void show(){//括号里面写参数吗System.out.println(getName()", "getAge());} 2.在测试类里面写方法时,写了一个show方法带参数,其实这里应该是…

Spring Boot 笔记 017 创建接口_新增文章

1.1实体类增加校验注释 1.1.1 自定义校验 1.1.1.1 自定义注解 package com.geji.anno;import com.geji.validation.StateValidation; import jakarta.validation.Constraint; import jakarta.validation.Payload; import jakarta.validation.constraints.NotEmpty;import jav…