MPC(模型预测控制)控制小车沿轨迹移动——C++实现

article/2025/11/9 10:46:14

任务说明

要求如下图所示,给定一条轨迹,要求控制小车沿这条轨迹移动,同时可以适用于系统带有延时的情况。注意,本篇文章只给出部分C++代码参考。

主要流程

首先用运动学自行车模型(Kinematic Bicycle Model)对小车建模,设计相应的成本函数(cost function)和约束,之后利用OSQP求解二次规划问题,实现线性时变模型预测控制(Linear Time-Varying MPC)器。

运动学自行车模型(Kinematic Bicycle Model)

首先我们来讲一下运动学自行车模型,如下图所示,小车的运动学模型可以当作是一个自行车,前轮控制拐的角度 \delta ,后轮控制加速度 a ,所以这两个量就是我们就控制量。系统的状态为惯性系中小车后轮的 x 和 y 的坐标, 惯性系速度 v 以及小车相对于惯性系的姿态角 \phi ,其微分的形式如下式所示:

线性时变模型预测控制(Linear Time-Varying MPC)

动力学系统模型

一个普通动力学系统的模型如下图所示,首先其状态量可以微分,其微分形式是关于系统状态和控制量的一个函数。其次系统的下一个状态也是相当于当前状态与当前状态采取的控制量相关的一个函数。

线性模型

如果一个动力学系统是线性的,那么就可以写成以下的形式,其中 A 和 B 是系数,如果不是时变模型的话,其值不会改变:

那么我们就可以将其推导成与初始状态 x_0 相关的形式:

那么其矩阵形式就是这样的:

线性时变模型

线性时变模型就是系统的系数 A 和 B会随时间 t 改变:

我们重看回运动学自行车模型,发现其系统模型并不是线性的(因为系统模型中存在三角函数),那么我们可以将其线性化,通过以下的形式,我们就可以将非线性模型转换成线性时变模型,其中上面带横线的变量是参考系统的状态和输出,也就是我们希望的状态量和输出量,比如说,我们希望系统的位置处于轨迹点上,并且速度为期望值。

我们会发现,相比于线性模型,会多出一个系数 g ,下面我们来通过线性化运动学自行车模型来看是不是会多出这一项。

线性化模型(Model linearization)

我们利用上面线性化的公式对运动学自行车模型进行线性化:

 那么就得到:

我们将其离散化,方便求解和控制:

其线性化模型的C++的代码如下, 其中 ll_就是公式中的 L,dt_ 就是时间 T_s

static constexpr int n = 4;  // state x y phi v
static constexpr int m = 2;  // input a delta
typedef Eigen::Matrix<double, n, n> MatrixA;
typedef Eigen::Matrix<double, n, m> MatrixB;
typedef Eigen::Vector4d VectorG;
typedef Eigen::Vector4d VectorX;
typedef Eigen::Vector2d VectorU;
void linearization(const double &phi, const double &v, const double &delta) {Ad_ <<  0, 0, -v*sin(phi), cos(phi),0, 0,  v*cos(phi), sin(phi),0, 0, 0, tan(delta) / ll_,0, 0, 0, 0;Bd_ <<  0, 0,0, 0,0, v/(ll_*pow(cos(delta),2)),1, 0;gd_ <<  v*phi*sin(phi), -v*phi*cos(phi), -v*delta/(ll_*pow(cos(delta),2)), 0;Ad_ = MatrixA::Identity() + dt_ * Ad_;Bd_ = dt_ * Bd_;gd_ = dt_ * gd_;return;}

设计成本函数(cost function)和约束

设计成本函数

我们要得到一个最优的控制量,那么就要有一个目标,这个目标要求整个系统从起始状态到终止状态的总成本最小,其可以分成两个部分,其中一个是表示系统状态转移时的成本,由当前状态和当前控制共同决定,另一个是系统到终止状态时花费的成本,两者相加就是总成本:

同时系统要满足系统状态转换的约束和一些等式约束和不等式约束:

在本次小车跟踪轨迹的任务中我们设计的成本函数是这样的,要求小车在当前位置与轨迹的位置误差和偏航角误差尽量小:

\large J=(x-x_{ref})^2+(y-y_{ref})^2+\rho (\phi-\phi_{ref})^2

那么我们就可以设计一个 Q 矩阵,其中每一个小方括号代表每一个系统状态,每一行对应 x 和 y 的坐标, 小车姿态角 \phi 以及速度 v ,如果我们设置预测步长为40个,那么就有40个小方括号,其中 \rho _N 表示最终状态的系数,由于系统的stage cost和terminal cost不一样所以设置得不一样,一般我们认为最终状态速度要为 0,所以最后一个元素就设置成 1:

那么系统的成本函数就可以写成如下形式,注意这里的 \large \textbf{x} 是一个向量,表示系统的状态:

\large J=(\textbf{x}-\textbf{x}_{ref})^T\textbf{Q}(\textbf{x}-\textbf{x}_{ref})

约束

这里我们设置的约束比较简单,就只是一些不等式约束:

\large -v_{max}\leqslant v\leqslant v_{max}

\large -a_{max}\leqslant a\leqslant a_{max}

\large -\delta_{max}\leqslant \delta\leqslant \delta _{max}

\large -\dot{\delta}_{max} * \Delta t\leqslant \dot{\delta}\leqslant \dot{\delta}_{max} * \Delta t

由于我们要输入到OSQP上,所以要对这些变量作一些处理,其C++代码如下,其中N_代表MPC的预测步长:

/* **               /  x1  \*               |  x2  |*  lx_ <=  Cx_  |  x3  |  <= ux_*               | ...  |*               \  xN  /* */Eigen::SparseMatrix<double> Cx_, lx_, ux_;  // p, v constrains/* **               /  u0  \*               |  u1  |*  lu_ <=  Cu_  |  u2  |  <= uu_*               | ...  |*               \ uN-1 /* */Eigen::SparseMatrix<double> Cu_, lu_, uu_;  // a delta vs constrainsEigen::SparseMatrix<double> Qx_;// set size of sparse matricesP_.resize(m * N_, m * N_);q_.resize(m * N_, 1);Qx_.resize(n * N_, n * N_);// stage costQx_.setIdentity();for (int i = 1; i < N_; ++i) {Qx_.coeffRef(i * n - 2, i * n - 2) = rho_;Qx_.coeffRef(i * n - 1, i * n - 1) = 0;}Qx_.coeffRef(N_ * n - 4, N_ * n - 4) = rhoN_;Qx_.coeffRef(N_ * n - 3, N_ * n - 3) = rhoN_;Qx_.coeffRef(N_ * n - 2, N_ * n - 2) = rhoN_ * rho_;int n_cons = 4;  // v a delta ddeltaA_.resize(n_cons * N_, m * N_);l_.resize(n_cons * N_, 1);u_.resize(n_cons * N_, 1);// v constrainsCx_.resize(1 * N_, n * N_);lx_.resize(1 * N_, 1);ux_.resize(1 * N_, 1);// a delta constrainsCu_.resize(3 * N_, m * N_);lu_.resize(3 * N_, 1);uu_.resize(3 * N_, 1);// set lower and upper boundariesfor (int i = 0; i < N_; ++i) {// -a_max <= a <= a_max for instance:Cu_.coeffRef(i * 3 + 0, i * m + 0) = 1;lu_.coeffRef(i * 3 + 0, 0) = -a_max_;uu_.coeffRef(i * 3 + 0, 0) = a_max_;// ...Cu_.coeffRef(i * 3 + 1, i * m + 1) = 1;lu_.coeffRef(i * 3 + 1, 0) = -delta_max_;uu_.coeffRef(i * 3 + 1, 0) = delta_max_;Cu_.coeffRef(i * 3 + 2, i * m + 1) = 1;if (i > 0){Cu_.coeffRef(i * 3 + 2, (i - 1) * m + 1) = -1;}lu_.coeffRef(i * 3 + 2, 0) = -ddelta_max_ * dt_;uu_.coeffRef(i * 3 + 2, 0) = ddelta_max_ * dt_;// -v_max <= v <= v_maxCx_.coeffRef(i, i * n + 3) = 1;lx_.coeffRef(i, 0) = -v_max_;ux_.coeffRef(i, 0) = v_max_;}

求解二次规划问题

这样,我们就可以通过得到系统的当前状态 \textbf{x}_0,然后通过下式得到未来预测 N 步的状态,注意下式中的 A 和 B 此时已经不再是一样的,都是随时间而不同(也就是说其实是有下标的),因为现在是线性时变模型:

这个才是正确的,在下面的代码中就是AA BB gg矩阵的构建: 

之后我们就可以将我们要求解的问题根据OSQP的需要一步步处理完输进去。

具体怎么用OSQP求解可以参考下面这个链接,输入到OSQP的形式是什么样子的都可以看到:

使用OSQP解决二次凸优化(QP)问题

首先是cost function,我们先推导出其形式:

\large J=(\textbf{x}-\textbf{x}_{ref})^T\textbf{Q}(\textbf{x}-\textbf{x}_{ref})

这里为了表达方便,我们就作如下简化,分别用这几个变量代替: 

将下式代入到上式: 

\large \textbf{x}=\textbf{A}\textbf{x}_0 + \textbf{B}\textbf{u}+\textbf{g}=\bar{\textbf{T}}\textbf{x}_0 + \bar{\textbf{S}}\textbf{Z}+\textbf{g}

由于自变量是控制量 \textbf{Z} ,我们可以在推导的过程把没有 \textbf{Z} 的项直接去掉,推导的过程我就不一一写出来了,最后我们就可以得到:

\large J=\frac{1}{2}\textbf{Z}^T\bar{\textbf{S}}^T\textbf{Q}\bar{\textbf{S}}\textbf{Z}+[(\bar{\textbf{T}}\textbf{x}_0+\textbf{g})^T\textbf{Q}\bar{\textbf{S}}-{\textbf{x}_{ref}}^T\textbf{Q}\bar{\textbf{S}}]\textbf{Z}

那么输入到OSQP的前两个矩阵就是: 

\large H = \bar{\textbf{S}}^T\textbf{Q}\textbf{S} \ \ \ \ f= \textbf{S}\bar{\textbf{Q}}^T (\bar{\textbf{T}}\textbf{x}_0+\textbf{g})+\textbf{S}\textbf{q}_x

 其中:

\large \textbf{q}_x = -\textbf{Q}^T\textbf{x}_{ref}

之后我们把对应的上界约束和下界约束转换成与控制量有关的形式,输入进OSQP中,就能得出我们的最优控制量。下面就是相关的代码:

int solveQP(const VectorX& x0_observe) {x0_observe_ = x0_observe;historyInput_.pop_front();historyInput_.push_back(predictInput_.front());lu_.coeffRef(2, 0) = predictInput_.front()(1) - ddelta_max_ * dt_;uu_.coeffRef(2, 0) = predictInput_.front()(1) + ddelta_max_ * dt_;VectorX x0 = compensateDelay(x0_observe_);// set BB, AA, ggEigen::MatrixXd BB, AA, gg;BB.setZero(n * N_, m * N_);AA.setZero(n * N_, n);gg.setZero(n * N_, 1);double s0 = s_.findS(x0.head(2));double phi, v, delta;double last_phi = x0(2);Eigen::SparseMatrix<double> qx;qx.resize(n * N_, 1);for (int i = 0; i < N_; ++i) {calLinPoint(s0, phi, v, delta);if (phi - last_phi > M_PI) {phi -= 2 * M_PI;} else if (phi - last_phi < -M_PI) {phi += 2 * M_PI;}last_phi = phi;linearization(phi, v, delta);// calculate big state-space matrices/* *                BB                AA* x1    /       B    0  ... 0 \    /   A \* x2    |      AB    B  ... 0 |    |  A2 |* x3  = |    A^2B   AB  ... 0 |u + | ... |x0 + gg* ...   |     ...  ...  ... 0 |    | ... |* xN    \A^(n-1)B  ...  ... B /    \ A^N /**     X = BB * U + AA * x0 + gg* */if (i == 0) {BB.block(0, 0, n, m) = Bd_;AA.block(0, 0, n, n) = Ad_;gg.block(0, 0, n, 1) = gd_;} else {BB.block(i * n, i * m, n, m) = Bd_;for (int j = i - 1; j >= 0; --j){BB.block(i * n, j * m, n, m) = Ad_ * BB.block((i - 1) * n, j * m, n, m);}AA.block(i * n, 0, n, n) = Ad_ * AA.block((i - 1) * n, 0, n, n);gg.block(i * n, 0, n, 1) = Ad_ * gg.block((i - 1) * n, 0, n, 1) + gd_;}Eigen::Vector2d xy = s_(s0);  // reference (x_r, y_r)// cost function should be represented as follows:/* **           /  x1  \T       /  x1  \         /  x1  \*           |  x2  |        |  x2  |         |  x2  |*  J =  0.5 |  x3  |   Qx_  |  x3  | + qx^T  |  x3  | + const.*           | ...  |        | ...  |         | ...  |*           \  xN  /        \  xN  /         \  xN  /* */qx.coeffRef(i * n + 0, 0) = -Qx_.coeffRef(i * n + 0, i * n + 0) * xy(0);qx.coeffRef(i * n + 1, 0) = -Qx_.coeffRef(i * n + 1, i * n + 1) * xy(1);qx.coeffRef(i * n + 2, 0) = -Qx_.coeffRef(i * n + 2, i * n + 2) * phi;qx.coeffRef(i * n + 3, 0) = -Qx_.coeffRef(i * n + 3, i * n + 3) * v;s0 += desired_v_ * dt_;s0 = s0 < s_.arcL() ? s0 : s_.arcL();}Eigen::SparseMatrix<double> BB_sparse = BB.sparseView();Eigen::SparseMatrix<double> AA_sparse = AA.sparseView();Eigen::SparseMatrix<double> gg_sparse = gg.sparseView();Eigen::SparseMatrix<double> x0_sparse = x0.sparseView();// state constrants propogate to input constraints using "X = BB * U + AA * x0 + gg"/* **               /  x1  \                              /  u0  \*               |  x2  |                              |  u1  |*  lx_ <=  Cx_  |  x3  |  <= ux_    ==>    lx <=  Cx  |  u2  |  <= ux*               | ...  |                              | ...  |*               \  xN  /                              \ uN-1 /* */Eigen::SparseMatrix<double> Cx = Cx_ * BB_sparse;Eigen::SparseMatrix<double> lx = lx_ - Cx_ * AA_sparse * x0_sparse - Cx_ * gg_sparse;Eigen::SparseMatrix<double> ux = ux_ - Cx_ * AA_sparse * x0_sparse - Cx_ * gg_sparse;/* *      / Cx  \       / lx  \       / ux  \*   A_ = \ Cu_ /, l_ = \ lu_ /, u_ = \ uu_ /* */Eigen::SparseMatrix<double> A_T = A_.transpose();A_T.middleCols(0, Cx.rows()) = Cx.transpose();A_T.middleCols(Cx.rows(), Cu_.rows()) = Cu_.transpose();A_ = A_T.transpose();for (int i = 0; i < lx.rows(); ++i) {l_.coeffRef(i, 0) = lx.coeff(i, 0);u_.coeffRef(i, 0) = ux.coeff(i, 0);}for (int i = 0; i < lu_.rows(); ++i) {l_.coeffRef(i + lx.rows(), 0) = lu_.coeff(i, 0);u_.coeffRef(i + lx.rows(), 0) = uu_.coeff(i, 0);}Eigen::SparseMatrix<double> BBT_sparse = BB_sparse.transpose();P_ = BBT_sparse * Qx_ * BB_sparse;q_ = BBT_sparse * Qx_.transpose() * (AA_sparse * x0_sparse + gg_sparse) + BBT_sparse * qx;// osqpEigen::VectorXd q_d = q_.toDense();Eigen::VectorXd l_d = l_.toDense();Eigen::VectorXd u_d = u_.toDense();qpSolver_.setMats(P_, q_d, A_, l_d, u_d);qpSolver_.solve();int ret = qpSolver_.getStatus();if (ret != 1) {ROS_ERROR("fail to solve QP!");return ret;}solve_flag = true;Eigen::VectorXd sol = qpSolver_.getPrimalSol();Eigen::MatrixXd solMat = Eigen::Map<const Eigen::MatrixXd>(sol.data(), m, N_);Eigen::VectorXd solState = BB * sol + AA * x0 + gg;Eigen::MatrixXd predictMat = Eigen::Map<const Eigen::MatrixXd>(solState.data(), n, N_);for (int i = 0; i < N_; ++i) {predictInput_[i] = solMat.col(i);predictState_[i] = predictMat.col(i);}return ret;}

延时模型

注意到上面的代码中有:

VectorX x0 = compensateDelay(x0_observe_);

这个函数就是要考虑延时的情况,当系统是无延时的时候状态就是它本身。 

延时模型的意思就是系统当前的控制量并不会马上作用于当前的系统,而是会经过一段时间 \tau 后才作用,也就是说系统的控制是有延时的,如下所示:

那么我们就可以把系统的初始状态  \textbf{x}_0 定义成当前时刻 \tau 之后的状态量,由于我们用的是MPC,可以预测当前时刻  \tau 之后的状态,那么我们就可以利用类似之前AA BB gg矩阵的形式来得到一个delay后的状态,然后我们就可以利用这个状态得到相应的延时控制量,如下所示:

其代码如下: 

  VectorX compensateDelay(const VectorX& x0) {VectorX x0_delay = x0;if (delay_ == 0){return x0_delay;}Eigen::MatrixXd BB, AA, gg, x0_pred;int tau = std::ceil(delay_ / dt_);BB.setZero(n * tau, m * tau);AA.setZero(n * tau, n);gg.setZero(n * tau, 1);x0_pred.setZero(n * tau, 1);double s0 = s_.findS(x0.head(2));double phi, v, delta;double last_phi = x0(2);for (int i = 0; i < tau; ++i) {calLinPoint(s0, phi, v, delta);if (phi - last_phi > M_PI) {phi -= 2 * M_PI;} else if (phi - last_phi < -M_PI) {phi += 2 * M_PI;}last_phi = phi;linearization(phi, v, delta);if (i == 0) {BB.block(0, 0, n, m) = Bd_;AA.block(0, 0, n, n) = Ad_;gg.block(0, 0, n, 1) = gd_;} else {BB.block(i * n, i * m, n, m) = Bd_;for (int j = i - 1; j >= 0; --j){BB.block(i * n, j * m, n, m) = Ad_ * BB.block((i - 1) * n, j * m, n, m);}AA.block(i * n, 0, n, n) = Ad_ * AA.block((i - 1) * n, 0, n, n);gg.block(i * n, 0, n, 1) = Ad_ * gg.block((i - 1) * n, 0, n, 1) + gd_;}}Eigen::VectorXd uu(m * tau, 1);for (double t = delay_; t > 0; t -= dt_) {int i = std::ceil(t / dt_);uu.coeffRef((tau - i) * m + 0, 0) = historyInput_[i - 1][0];uu.coeffRef((tau - i) * m + 1, 0) = historyInput_[i - 1][1];}x0_pred = BB * uu + AA * x0 + gg;x0_delay = x0_pred.block((tau - 1) * n, 0, n, 1);return x0_delay;}

 效果图(注意看小车末尾的紫色线条表示延时):

至此,任务完成,小车可以跟踪给定的轨迹。


http://chatgpt.dhexx.cn/article/QADx5j8b.shtml

相关文章

mpc模型预测控制原理详解

mpc模型预测控制原理详解 前言mpc算法步骤mpc算法推导 前言 本文是对mpc模型预测控制学习的记录&#xff0c;主要参照了DR_CAN老师的视频进行学习。视频专栏链接&#xff1a;DR_CAN老师mpc视频专栏。在这篇博客中博主也针对DR_CAN老师的讲解做了详尽的笔记和代码实现。读者可以…

模型预测控制(MPC)解析(一):模型

一、MPC简介 1.1 预测控制的日常应用 模型预测控制的设计目标是计算未来控制变量u的轨迹&#xff0c;以优化未来的系统输出y。优化过程在一个有限的时间窗口进行&#xff0c;并且利用优化时间窗口开始时的系统信息进行优化。为了理解预测控制的基本思想&#xff0c;以一个日常…

MPC控制

基于状态空间模型的控制 模型预测控制&#xff08;MPC&#xff09;简介 对基于状态空间模型的控制理解得很到位 在这里我重点讲解一下状态空间模型。那么什么是状态&#xff1f;输出是不是也是状态的一种&#xff1f;对的&#xff0c;输出也是一种状态&#…

MPC模型预测控制

这篇主要讲一下模型预测控制&#xff0c;如果对PID控制了解的同学&#xff0c;那效果更好。如果不了解PID控制&#xff0c;还是熟悉下比较好。 模型预测控制&#xff0c;顾名思义&#xff0c;基于模型&#xff0c;预测未来&#xff0c;进行控制。这个控制是基于模型的&#xf…

模型预测控制(MPC)简介

1.引言 在当今过程控制中&#xff0c;PID当然是用的最多的控制方法&#xff0c;但MPC也超过了10%的占有率。MPC是一个总称&#xff0c;有着各种各样的算法。其动态矩阵控制&#xff08;DMC&#xff09;是代表作。DMC采用的是系统的阶跃响应曲线&#xff0c;其突出的特点是解决…

模型预测控制算法(MPC算法)底层逻辑

目录 MPC算法的基本原理 详细解析 预测模型中需要注意的点 滚动优化需要注意的点 构造目标函数约束部分 约束部分 举例说明 复盘总结 MPC算法的基本原理 MPC 的基本原理可以分为三个步骤&#xff1a;预测模型、滚动优化、反馈校正 &#xff08;1&#xff09;预测模型…

模型预测控制(MPC)算法原理

模型预测算法是在欧美等国家兴起的应用于工业领域的一种优化控制算法。目前经过多年的发展&#xff0c;在工业领域、智能控制领域等都有应用。随着算法的理论的完善&#xff0c;其已经成为工业领域内经常使用的一种经典算法。虽然在各个领域算法的应用存在差异。但他们都遵循预…

到底什么是模型预测控制MPC(一)

1. 为什么使用MPC控制 在浏览文章的时候&#xff0c;很多文章都是基于MPC来做的。那么究竟什么是模型预测呢&#xff1f; 模型预测也可以说是一种我们熟悉的反馈控制算法&#xff0c;其目的就是预测出未来的输出。以一个生活中的例子引入&#xff1a; 在我们驾驶汽车的时候&am…

MPC控制笔记(一)

转自 我的博客 笔记参考1&#xff1a;Understanding Model Predictive Control(Youtube 带自动生成字幕) 笔记参考2&#xff1a;Understanding Model Predictive Control(B站 生肉) 一、什么是MPC模型预测控制 MPC(Model Predict Control)是一种反馈控制(feedback control)算…

PID与MPC控制方法

记录udacity–无人驾驶工程师课程中控制部分。 MPC代码和实践链接https://github.com/udacity/CarND-MPC-Quizzes 本文按照对udacity课程的理解和翻译而来 1、PID P&#xff1a;Proportional 比例项&#xff0c; 用比例项乘以误差&#xff1b;快速缩小误差&#xff1b; I&…

了解模型预测控制2--什么是模型预测控制(MPC)

本节&#xff0c;我们将讨论模型预测控制器的工作原理。 在控制问题中&#xff0c;控制器的目标是计算被控对象的输入&#xff0c;使得被控对象输出遵循期望的参考信号。模型预测控制器计算此输入的策略是预测未来。 这听起来像算命&#xff0c;但让我们看看它究竟是什么。MPC使…

模型预测控制(MPC,Model Predictive Control)

发展历史 20世纪70年代后期&#xff0c;一类新型的计算机控制算法出现在美法等国的工业过程领域&#xff0c;如动态矩阵控制(DMC,Dynamic Matrix Control)、模型算法控制(MAC,Model Algorithm Control)。1987年&#xff0c;首次有学者阐述了该系列算法的动因、机理及其在控制工…

MPC学习笔记(1)——原理

最近在学习M. W. Mehrez的MPC时发现了很多不了解的细节&#xff0c;分享一下对该算法的梳理与理解。 在自动驾驶或机器人领域中&#xff0c;模型预测控制(Model Predictive Control, MPC)解决的是轨迹规划的问题。其前提条件是环境地图、载体位姿已知&#xff0c;根据MPC算法&…

MPC 控制原理

MPC 控制原理 1 生活中的启示2 实际控制的例子参考文献 Yin 机械工程师 本文引自 一个模型预测控制&#xff08;MPC&#xff09;的简单实现. 1 生活中的启示 情景如下&#xff1a;你们团队每天早晨开一次例会&#xff0c;主要会议内容是你汇报工作进度&#xff0c;领导根据工作…

EA建模工具,介绍常用的UML图

[导读] 作为程序猿都最好掌握的一门语言&#xff0c;那就是UML&#xff08;Unified Modeling Language&#xff09;&#xff0c;统一建模语言(UML)是软件工程领域中一种通用的开发建模语言&#xff0c;旨在提供一种可视化系统设计的标准方法。是开发人员、系统设计人员交流的有…

uml c语言函数流程图,UML流程图模板分享

原标题&#xff1a;UML流程图模板分享 UML是统一建模语言&#xff0c;又称标准建模语言是用来对软件密集系统进行可视化建模的一种语言。UML的定义包括UML语义和UML表示法两个元素。在流程图中也会经常使用到&#xff0c;但是网上关于该主题的模板不是很多&#xff0c;下面是分…

软技能之UML图

软技能之UML图 工欲善其事必先利其器&#xff0c;程序员建模过程中需要用到的建模工具UML。 UML&#xff1a;Unified Modeling Language 统一建模语言。目标是以对象图的方式来描述任何类型的系统。 UML可分为两类&#xff1a;结构型&#xff08;描述某种结构&#xff09;、行…

生成C++工程的UML类图和类继承关系图

简介 在进行软件开发时&#xff0c;了解代码结构和关系、类之间的继承关系以及类内部的成员函数和变量定义是非常重要的。为此&#xff0c;我们可以使用Doxygen和Graphviz工具来生成UML类图和类集成关系图。 Doxygen是一个用于从注释的C源代码中生成文档的工具&#xff0c;支…

Ubuntu系统画大型UML图

一、 环境配置 1. 配置java环境&#xff08;请自行查找教程&#xff09; 2. 安装graphviz sudo apt install graphviz 3. 下载plantuml.jar 下载页面 下载好以后&#xff0c;我将其放在了~/bin/目录下面 4. 配置alias export PLANTUML_JAR_PATH~/bin/ export PLANTUML_LIM…

UML图有哪些类型?

UML&#xff08;Unified Modeling Language &#xff09;是用来对软件密集系统进行可视化建模的一种语言&#xff0c;是在开发阶段、说明、可视化、构建和书写一个面向对象软件密集系统的制品的开放方法。 UML图分为两种类型&#xff1a;结构图和行为图。结构图是可视化组件如…