NMPC非线性模型预测控制经验分享与代码实例

news2024/9/21 2:14:39

NMPC非线性模型预测控制经验分享与代码实例

原本做完本科毕设之后就应该动笔写这一部分,但是做的过程中慢慢意识到自己懂的只是一点点。最近重新接触一些优化相关的问题,希望能够做出我认知之下比较好的解答。本人知识有限,难免写的有问题,希望大家能够指出。

在开始之前,我先抛出两个问题,希望到最后大家能够得到这个问题的答案:

  • 什么是NMPC问题?
  • 我们如何解决NMPC问题?

1. 线性MPC问题回顾

在上一部分内容中,根据dr_can老师的视频指导,我们了解了模型预测控制的基本理念。模型预测控制在k时刻需要三步:

第一步:估计/测量读取系统的当前状态;

第二步:基于u(k),u(k+1),u(k+2)…u(k+j)进行最优化处理;

第三步:只取u(k)作为系统的控制输入施加到系统上。

在后面的例子中,我们用了一个线性的模型来验证我们的策略。我们将预测区间内的状态写出来,使用二次规划来求解系统的最优值。当时我们定义的系统状态函数是这样的:
X ˙ = A X + B U \dot{X} = AX+BU X˙=AX+BU
我们定义的代价函数是这样的:
min ⁡ u 0 , … , u N − 1 J = x N ⊤ F x N + ∑ k = 0 N − 1 ( u k ⊤ R u k + x k ⊤ Q x k ) \min _{u_0, \ldots,u_{N-1}}J= x_{N}^{\top} Fx_{N} + \sum_{k=0}^{N-1} (u_{k}^{\top}Ru_{k} + x_{k}^{\top}Qx_{k}) u0,,uN1minJ=xNFxN+k=0N1(ukRuk+xkQxk)

2. 问题构建

点进来的朋友可能会要问这样的问题:如果我们的系统模型、我们定义的代价不是上面这样的形式的怎么办?

答:难办。如果我们的形式不是按上面的形式写的话,我们就无法写成上述二次规划(QP)来求解的形式。二次规划的好处在于它可以十分快速地求解出预测控制的全局最优解(即计算得又快又好)。

对于更一般性的情况,系统状态为:
x ˙ = f ( x ( t ) , u ( t ) ) \dot{x} = f(x(t), u(t)) x˙=f(x(t),u(t))
系统的代价为:
min ⁡ u 0 , … , u N − 1 J = l f ( x N ) + ∑ k = 0 N − 1 l ( x k , u k ) \min _{u_0, \ldots,u_{N-1}}J= l_f(x_N) + \sum_{k=0}^{N-1} l(x_k, u_k) u0,,uN1minJ=lf(xN)+k=0N1l(xk,uk)
这里的模型和代价可以是非线性的。对于如何求解这样的问题,让我们通过一个例子来理解。

3. 求解示例:倒立摆控制

本文的求解一部分参考了Dr. Li的视频,如果想要了解如何在simulink中实现类似的控制方法,请移步b站或他的github。

本文的求解采用m文件实现,开源代码可参考这里。同时本文使用非线性求解库casadi,如果使用nmpc的话需要在这里安装。

为了能看到控制的效果,这里我选择了一种经典的非线性模型:倒立摆模型(cart-pole),大概是这个样子:

Cartpole

我们状态变量是小车的位移x,杆的角度 θ \theta θ以及它们的导数 x ˙ , θ ˙ \dot{x}, \dot{\theta} x˙,θ˙,输入为施加在小车上的力 f f f

倒立摆的动力学模型为:
[ m c + m p m p L cos ⁡ θ m p L cos ⁡ θ m p L 2 ] ⏟ M ( q ) [ x ¨ θ ¨ ] ⏟ q ¨ + [ 0 − m p L sin ⁡ θ θ ˙ 0 0 ] ⏟ C ( q , q ˙ ) [ x ˙ θ ˙ ] ⏟ q ˙ + [ 0 − m p g L sin ⁡ θ ] ⏟ g ( q ) = [ f 0 ] ⏟ τ \underbrace {\begin{bmatrix} m_c + m_p & m_pL\cos\theta \\ m_pL\cos \theta & m_pL^2\end{bmatrix}}_{\mathbf{M(q)}} \underbrace{\begin{bmatrix} \ddot x \\ \ddot \theta\end{bmatrix}}_{\mathbf{\ddot{q}}} + \underbrace{\begin{bmatrix} 0 & -m_pL\sin\theta \dot \theta \\ 0 & 0\end{bmatrix}}_{\mathbf{C(q,\dot{q})}} \underbrace{\begin{bmatrix} \dot x \\ \dot \theta\end{bmatrix}}_{\mathbf{\dot{q}}} + \underbrace{\begin{bmatrix} 0 \\ -m_pgL\sin \theta\end{bmatrix}} _{\mathbf{g(q)}} = \underbrace{\begin{bmatrix} f \\ 0\end{bmatrix}}_{\boldsymbol \tau} M(q) [mc+mpmpLcosθmpLcosθmpL2]q¨ [x¨θ¨]+C(q,q˙) [00mpLsinθθ˙0]q˙ [x˙θ˙]+g(q) [0mpgLsinθ]=τ [f0]
其中杆的质量为 m p m_p mp(集中在末端的小球上),车的质量为 m c m_c mc,杆的长度为 L L L。注意这是一个非线性的模型,因为M矩阵C矩阵,g矩阵均和系统的状态变量相关。

我们的控制目标是让杆始终保持竖直向上,即 X d = [ x d , θ d , x ˙ d , θ ˙ d ] = 0 X_d = [x_d, \theta_d, \dot{x}_d, \dot{\theta}_d] = \mathbf{0} Xd=[xd,θd,x˙d,θ˙d]=0

代价函数为:
min ⁡ u 0 , … , u N − 1 J = x N ⊤ F x N + ∑ k = 0 N − 1 ( u k ⊤ R u k + x k ⊤ Q x k ) \min _{u_0, \ldots,u_{N-1}}J= x_{N}^{\top} Fx_{N} + \sum_{k=0}^{N-1} (u_{k}^{\top}Ru_{k} + x_{k}^{\top}Qx_{k}) u0,,uN1minJ=xNFxN+k=0N1(ukRuk+xkQxk)
其中 x k = X k − X d , k , u k = τ x_{k} = X_k-X_{d,k}, u_k = \tau xk=XkXd,k,uk=τ,接下来,我们来求解这个问题。

4. 求解思路:工作点线性化(LQR,LMPC)

观察上述的问题构建,我们发现系统的动力学是非线性的,但系统的代价函数是线性的,那我们可不可以使用某种方法将系统转化为一个线性化模型,然后用控制线性化模型的方法来处理?

让我们来尝试一下将这个系统在期望位置处进行线性化。我们知道,当 θ \theta θ 趋近于0时,有 sin ⁡ θ ≈ θ , cos ⁡ θ ≈ 1 \sin \theta \approx \theta, \cos\theta \approx 1 sinθθ,cosθ1,我们将其代入到状态方程中,并忽略项 m p L sin ⁡ θ θ ˙ 2 m_p L \sin \theta \dot{\theta} ^2 mpLsinθθ˙2,我们得到系统在工作点 X = [ 0 , 0 , 0 , 0 ] ⊤ X = [0 ,0,0,0]^\top X=[0,0,0,0]线性化模型为:
[ x ˙ θ ˙ x ¨ θ ¨ ] = [ 0 0 1 0 0 0 0 1 0 − m p g / m c 0 0 0 ( m p g + m c g ) / ( m C L ) 0 0 ] [ x θ x ˙ θ ˙ ] + [ 0 0 1 / m c − 1 / m c L ] f \begin{bmatrix} \dot x\\ \dot \theta \\ \ddot x \\ \ddot \theta\end{bmatrix} = \begin{bmatrix} 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \\ 0 & -m_pg/m_c &0 &0 \\0& (m_pg + m_cg)/(m_CL) &0&0 \end{bmatrix} \begin{bmatrix} x\\ \theta \\ \dot x \\ \dot \theta\end{bmatrix} + \begin{bmatrix} 0\\ 0 \\ 1/m_c \\ -1/m_cL\end{bmatrix} f x˙θ˙x¨θ¨ = 000000mpg/mc(mpg+mcg)/(mCL)10000100 xθx˙θ˙ + 001/mc1/mcL f
对倒立摆进行LQR控制,计算LQR增益(LQR控制这里只计算了一次增益):

function K = cartPoleLQR(Q, R)
global mc mp l g

A = [0 0 1 0;
    0 0 0 1;
    0 -mp*g/mc 0 0;
    0 (mp*g + mc*g)/(mc*l) 0 0];
B = [0;0;1/mc;-1/(mc*l)];
C = eye(4);
D = 0;

cartpole = ss(A,B,C,D);
K = lqr(cartpole, Q, R);
end

设定初始状态为 [ 0 , π / 4 , 0 , 0 ] ⊤ [0,\pi/4,0,0]^\top [0,π/4,0,0],调整Q,R 矩阵参数,得到的结果为:

在这里插入图片描述

在这里插入图片描述

LQR的优点在于它计算的非常快,每一步只需要乘一次预先计算好的增益即可。

接下来我们用线性MPC的方式进行控制(这样的线性化MPC控制方法也被称为Adaptive MPC,如果为多个工作点线性化并设定不同的QR矩阵,则为Gain-scheduled MPC),线性MPC的部分的代码部分和之前的工作大致相同,主要值得注意的是我添加了输入绝对值小于40的约束,并且MPC跟踪的轨迹用了简单的线性插值。完整代码这里就不贴了,可以移步这里参考

设定同样的初始状态,调整Q,R 矩阵参数,得到的结果为:


在这里插入图片描述

在这里插入图片描述

看起来似乎MPC控制器的性能并没有比LQR控制器要好,在我个人的认知中,MPC控制器相比于LQR控制器的主要优点在于它可以很好地处理系统约束,比如输入的上下界,限制 x , θ x, \theta x,θ的速度等。

线性化模型问题在于我们的线性化模型只能在工作点周围非线性程度不高的区间内近似实际模型。当我们给一些离工作点比较远的初值时,得到的结果是不收敛的。比如我们令系统的初始状态为 [ 0 , π / 2 , 0 , 0 ] ⊤ [0,\pi/2,0,0]^\top [0,π/2,0,0],则系统状态不收敛。下图为LMPC得到的结果,系统直接发散了。

在这里插入图片描述

5. 求解思路:完全非线性模型 (NMPC)

另一种思路就是不对模型线性化处理,直接用系统的非线性模型来预测系统变化,求解一个非线性优化问题。这样的话求解会比较慢,并且求解出来的值可能不是全局最优的。这里我们调用cassadi来求解这个问题:

function f = cartPoleNMPC(Q, R, q0, qd, p_h, h)

   nmpc = casadi.Opti();
   opts = struct('ipopt', struct('print_level', 0, 'warm_start_init_point', 'yes', 'max_iter', 100));
   nmpc.solver('ipopt', opts);
   X_ref = generateReference(q0,qd',p_h);
   X = nmpc.variable(4,p_h); 
   F = nmpc.variable(1,p_h); 
   nmpc.subject_to ( X(:,1) == q0 );
   for k = 1 : p_h-1
        % dynamics:
        dX = cartPoleDynamics(F(:,k), X(:,k));

        nmpc.subject_to( X(:, k+1) == X(:, k) + h*dX );
        nmpc.subject_to( -40 <= F(:,k) <= 40);
   end
   
    J = 0;
    % cost function
    for k = 1 : p_h
        J = J + (X_ref(:,k) - X(:,k))' * Q ... 
            * (X_ref(:,k) - X(:,k)) + R * F(:,k)^2;
    end
    nmpc.minimize(J);
    solution = nmpc.solve();
    f_all = solution.value(F);
    f = f_all(1);
end

我的电脑每个循环要跑个100ms左右,而我设置的步长是40ms,这算个Bug,也侧面说明了这个算法是真的比较慢。

为了凸显NMPC算法的优势,我们将倒立摆的初值设置为 [ 0 , π , 0 , 0 ] ⊤ [0,\pi,0,0]^\top [0,π,0,0],期望位置不变,得到的结果为:



在这里插入图片描述
可以把初始向下的倒立摆摆回来。

6. 完整仿真代码

补充我的完整代码如下:为了显示的好看,我为倒立摆添加了动画效果,并保存为视频。这里无需安装任何依赖。

%% Cartpole nmpc,lmpc,LQR
% dependencies : casadi
% model : cartpole (no friction)
import casadi.*
clear;clc;
format long
%------------------------------config-----------------------------------%
h = 0.04; % step
n = 8; % whole time
N = n/h;  % whole step
t = zeros(1,N); % time vec

% state 
dq = zeros(4,N);
q = zeros(4,N);
% input
u = zeros(1,N);
% initial state
q0 = [0, pi,0,0];
q(:,1) = q0;
% desired state
qd = [0,0,0,0];

% cart-pole dynamics
global mc mp l g
mc = 1; mp = 1; % cart mass, pole mass
l = 1; % pole length
g = 9.8; % gravity

% Linearization of the operating point for LMPC and LQR (> pi/4 will result in failure)
% LQR
% Q = diag([100,10,1, 1 ]); % x q dx dq
% R = 1; % fx
% K = cartPoleLQR(Q, R); % the LQR controller computes the gain only once.

% LMPC
% p_h = 80;
% Q = diag([100,10,1, 1 ]); % x q dx dq
% R = 1; % fx

% NMPC
p_h = 20;
Q = diag([600,1000,100, 40 ]); % x q dx dq
R = 1e-3; % fx
%% loop begin
for i = 1:N-1
%     u(i) = K*(qd'-q(:,i)); % LQR
%     u(i) = cartPoleLMPC(Q, R, q(:,i), qd, p_h, h);
    u(i) = cartPoleNMPC(Q, R, q(:,i), qd, p_h, h); 

    % dynamics (rk4)
    k1 = cartPoleDynamics(u(i), q(:, i));
    k2 = cartPoleDynamics(u(i), q(:, i) + 0.5 * h * k1);
    k3 = cartPoleDynamics(u(i), q(:, i) + 0.5 * h * k2);
    k4 = cartPoleDynamics(u(i), q(:, i) + h * k3);
    
    q(:, i+1) = q(:, i) + (h/6) * (k1 + 2 * k2 + 2 * k3 + k4);
    % update time
    t(i+1) = t(i)+h;
end
%% draw figure
% state fig
figure(1)
plot(t,q(1,:),'-','linewidth',2);hold on;
plot(t,q(2,:),'-','linewidth',2);hold on;title('joint state');grid on;
legend('x','\theta');
% input fig
figure(2)
plot(t,u,'-','linewidth',2);title('input');grid on;
legend('F');
% anime
% video
video = VideoWriter('cartpole.mp4', 'MPEG-4');
video.FrameRate = 1/h; 
open(video);

figure(3)
for i = 1:N
    % pole
    plot([q(1,i) q(1,i)+l*sin(q(2,i))],[0 l*cos(q(2,i))],'LineWidth',4);
    % cart
    width = 0.6;height = 0.3;
    rectangle('Position', [q(1,i)-width/2,-height/2, width, height], 'FaceColor', 'none', 'EdgeColor', 'k','LineWidth',2);
    hold on;
    % ball
    radius = 60;
    scatter(q(1,i)+l*sin(q(2,i)), l*cos(q(2,i)), radius, 'filled', 'r');
    hold off;
    xlim([-2 2]); ylim([-2 2]);  
    xlabel('X'); ylabel('Y');  
    grid on; 
    frame = getframe(gcf);
    writeVideo(video, frame);
end
close(video);

7. 总结

最后,我们来简单回顾一下在倒立摆问题中我们的处理方法。当能够线性化模型、约束、代价函数并且模型非线性程度不强,离工作点比较近时,我们可以采用在工作点线性化的方法来求解。但在远离工作点的位置表现不佳。

直接采用非线性模型去求解的主要难度在于如何保证实时性和解的准确性。具体求解方法仍然是研究热点。在我的领域中,我看到的更多是用iLQR或者SQP方法来求解的。不过这就是题外话了。

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

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

相关文章

ClickHousez中如何定时清理过期数据库?

一、脚本清理 要在ClickHouse中自动删除过期的数据库&#xff0c;你可以使用ClickHouse的SQL命令结合外部脚本&#xff08;如Shell脚本&#xff09;和计划任务&#xff08;如cron&#xff09;来实现。下面是一个示例&#xff0c;展示如何创建一个Shell脚本来检查数据库的创建时…

1、.Net UI框架:Avalonia UI - .Net宣传系列文章

Avalonia UI是一个开源的跨平台UI框架&#xff0c;它允许开发者使用C#和XAML来创建应用程序&#xff0c;这些应用程序可以在多个平台上运行&#xff0c;包括Windows、macOS、Linux、Android和iOS。Avalonia UI的设计目标是提供一个现代化、可移植的UI框架&#xff0c;它具有类似…

C++之搜索二叉树(上)

目录 搜索二叉树的概念 搜索二叉树的操作 递归版本 二叉树的插入 二叉树的查找 二叉树的删除 非递归版本 二叉树的递归插入 二叉树的递归查找 二叉树的递归删除 在之前我们已经学习过了二叉树这一数据结构&#xff0c;本期我们将学习一种新的数据结构------搜索二…

Ubuntu服务器时间和本地时间不一致怎么解决——Linux的Local Time和RTC time

最近一直在搞大模型的相关工作&#xff0c;所以一直在用Linux服务器&#xff0c;前面的文章里也提到了&#xff0c;我用的是一台Dell PowerEdge R730xd。 但在使用中发现&#xff0c;IDRAC中的日志时间和本地时间存在时差&#xff0c;大概相关8小时。 对于技术人员&#xff0c…

UE5学习笔记19-服务器的更新频率,根骨骼旋转节点

一、服务器向客户端发送数据的频率 在Config中的DefaultEngine.ini文件添加 [/Script/OnlineSubsystemUtils.IpNetDriver] NetServerMaxTickRate60; 二、角色类中&#xff0c;角色蓝图类中在细节面板收缩net可以在界面中找到下面两个变量 NetUpdateFrequency 66.f; //净更…

代码随想录Day 32|leetcode题目:501.斐波那契数、70.爬楼梯、746.使用最小花费爬楼梯

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 动态规划理论基础一、理论基础1.1 什么是动态规划1.2 动态规划的解题步骤1.3 动态规划应该如何debug 二、题目题目一&#xff1a; 509. 斐波那契数解题思路&#xff1a;动态规划递归解法 题目二&a…

spring boot 项目 prometheus 自定义指标收集区分应用环境集群实例ip,使用 grafana 查询--方法耗时分位数指标

spring boot 项目 prometheus 自定义指标收集 auth author JellyfishMIX - github / blog.jellyfishmix.comLICENSE LICENSE-2.0 说明 网上有很多 promehteus 和 grafana 配置&#xff0c;本文不再重复&#xff0c;只介绍自定义部分。目前只介绍了分位数指标的收集和查询&a…

基于nodejs+vue+uniapp的摄影竞赛小程序

开发语言&#xff1a;Nodejs框架&#xff1a;expressuniapp数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;VS Code 系统展示 管理员登录 管理员主界面 用户管理 书籍分类管理 书籍信息管理 系统管理…

五种常见的人工智能错误以及如何避免它们?

目录 常见错误一&#xff1a;忘乎所以 常见错误二&#xff1a; 未能整合 常见错误三&#xff1a; 过于“以技术为中心”的方法 常见错误四&#xff1a;事后才考虑治理 常见错误五&#xff1a; 没有规模规划 对于肩负着为股东带来收益的重大赌注的高管来说&#xff0c;将人…

使用模块化流简化 RHEL 8 上的 NVIDIA 驱动程序部署

目录 DNF 模块化 使用预编译驱动程序 使用包管理器安装 选择模块化流 切换流 使用模块化配置文件 RHEL 的支持矩阵 概括 相关资源 NVIDIA GPU 已成为加速机器学习、高性能计算 (HPC)、内容创建工作流程和数据中心应用程序等各种工作负载的主流。对于这些企业用例&#xff0c;NV…

【个人笔记】VCS工具与命令

Title&#xff1a;VCS工具学习 一 介绍 是什么&#xff1f; VCS (Verilog Compiler Simulator) 是synopsys的verilog 仿真软件&#xff0c;竞品有Mentor公司的Modelsim、Cadence公司的NC-Verilog、Verilog—XL. VCS能够 分析、编译 HDL的design code&#xff0c;同时内置了 仿…

ubuntu环境下实现ROS 2 与 Arduino 通信

本教程为https://blog.csdn.net/2301_81924597/article/details/141757091?spm1001.2014.3001.5501的进一步拓展 ROS 2 与 Arduino 通信指南 准备工作 确保已安装 ROS 2&#xff08;本指南基于 ROS 2 Humble&#xff09;确保已安装 Arduino IDE 并能正常使用安装必要的 ROS…

系统架构师考试学习笔记第三篇——架构设计高级知识(10)系统质量属性与架构评估

本章知识点&#xff1a; 第10课时主要学习软件系统质量属性、系统架构评估以及ATAM方法评估实践等内容。 本课时内容侧重于概念知识&#xff0c;根据以往全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;。考试的出题规律&#xff0c;考查的知识点多来源于教材&a…

在Web服务应用中,如何编程使用Redis的缓存功能?包括缓存页面内容、缓存数据库查询结果、用户会话信息等代码分享

目录 一、概述 二、redis介绍 1、简介 2、Redis作为缓存的原理 &#xff08;1&#xff09;内存存储 &#xff08;2&#xff09;数据结构 &#xff08;3&#xff09;工作原理 3、Redis作为缓存的作用 三、redis缓存页面内容 1、作用 2、实现方法 3、示例代码&#x…

python07-单元测试框架unittest1-2

5 fixture 可以看作case的前置条件、后置条件 5.1 fixture的用例执行顺序 fixture分为 方法级别类级别模块级别 5.1.1方法级fixture 每个测试用例之前要调用setUp每个测试用例执行后要调用tearDowntestCase中有多少测试用例,那么setUp和tearDown就被调用多少次 def add(…

【Java】Spring-AOP与拦截器简洁实操 (上手图解)

Java系列文章目录 补充内容 Windows通过SSH连接Linux 第一章 Linux基本命令的学习与Linux历史 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;三、问题描述四、解决方案&#xff1a;4.1 认识依赖4.2 使用AOP与拦截器4.2.1 使用AOP4.2.1.1 设置DemoAop类4.2.2.2 设…

【SpringBoot】电脑商城-11-显示购物车功能

加入购物车 1 购物车-创建数据表 1.使用use命令先选中store数据库。 USE store;2.在store数据库中创建t_cart用户数据表。 CREATE TABLE t_cart (cid INT AUTO_INCREMENT COMMENT 购物车数据id,uid INT NOT NULL COMMENT 用户id,pid INT NOT NULL COMMENT 商品id,price BIG…

java fastxml json 科学计数法转换处理

背景&#xff1a; 由于 canal 切换为 tx dbbridge后&#xff0c;发现dbbridge对于canal的兼容性存在较大问题&#xff0c;从而引发 该文档的实践。 就目前发现 dbbrige 的字段 大小写 和 数据类型格式 从binlog 写入kafka 同canal 都会存在差异。 canal之前导出都是小写&…

编程要由 “手动挡” 变 “自动挡” 了?Cursor+Claude-3.5-Sonnet,Karpathy 大神点赞的 AI 代码神器!如何使用详细教程

Cursor 情况简介 AI 大神 Andrej Karpathy 都被震惊了&#xff01;他最近在试用 VS Code Cursor Claude Sonnet 3.5&#xff0c;结果发现这玩意儿比 GitHub Copilot 还好用&#xff01; Cursor 在短短时间内迅速成为程序员群体的顶流神器&#xff0c;其背后的原因在于其默认使…

[VirtualBox+ubuntu24]设置linux学习环境

1)设置网络为桥接网卡&#xff0c;不然发现ifconfig出不来ip地址 依然设置为经典的: 2核4G内存 50G硬盘 2)设置默认root账户登录 // 不然每次都得输入sudo -s // step1: 打开配置文件 sudo vim/etc/gdm3/custom.conf// step2: 默认以root登录 [daemon] AutomaticLoginEnableT…