games103——作业1

news2024/9/21 10:59:58

实验一主要实现简单的刚体动画模拟(一只兔子),包括 impulse 的碰撞检测与响应,以及 Shape Matching方法。

完整项目已上传至github。

文章目录

  • 简单刚体模拟(不考虑碰撞)
    • 平移运动
    • 旋转运动
  • 粒子碰撞检测与响应
    • 碰撞检测
    • 碰撞响应
      • Penalty Methods
        • Quadratic Penalty Method
        • Quadratic Penalty Method with a Buffer
        • Log-Barrier Penalty Method
      • Impulse Method
  • 刚体碰撞检测与响应
    • 碰撞检测
    • Impulse方法的碰撞响应
  • 作业代码与提高项
    • 作业代码
    • 提高项


简单刚体模拟(不考虑碰撞)

物体在场景中的位置由其平移矩阵,以及旋转矩阵决定(刚体不能形变,所以不需要缩放矩阵)。

平移运动

对于平移运动,状态变量(state variable) 包括位置 x 和 速度 v,其正常更新如下

但这个积分没法在计算机内直接求,可以使用数值积分的方法进行运算,比如显式积分和隐式积分

  • 显式积分
  • 隐式积分
    这里我们采用 Leapfrog Integration 方法,其在速度的更新上采用显式积分,而在位置的更新上采用隐式积分。

    那么在仿真过程,平移运动的更新过程如下,这里的 △ t \triangle t t 为用户指定的变量。

    在 lab1 中的对应代码如下,这里只考虑了重力,linear_decay 表示速度的衰减系数,比如空气阻力等。
// Part I: Update velocities
v += dt * gravity;
v *= linear_decay;
//Update linear status
Vector3 x = x_0 + dt * v;

旋转运动

旋转运动在仿真中一般使用矩阵、欧拉角以及四元数表示,对于矩阵与欧拉角都有一定的缺点,这里主要使用四元数(当然它们能互相转换)。

我们选择四元数 q 表示朝向,即从局部坐标转化为全局坐标。再使用一个3D向量 ω ⃗ \vec{ \omega } ω 表示角速度(angular velocity)。

  • ω ⃗ \vec{ \omega } ω 的方向为旋转轴的方向
  • ω ⃗ \vec{ \omega } ω 的大小为角速度的大小

这里引入力矩,其表示如下。与力的作用会改变平移速度相同,力矩会改变角速度。

我们知道动量表示为 P ⃗ = m v ⃗ \vec {P}=m \vec{v} P =mv ,而角动量表示为 L ⃗ = I × ω ⃗ \vec{L} = I \times \vec{ \omega } L =I×ω 这里的 I I I 为一个3x3的矩阵,称为惯性张量,其描述了物体中的相对于质心的的质量分布,其可以如下表示。我们把刚体看作很多粒子(质点)组成的质点系,这里的 r i r_i ri 表示该粒子(质点)在局部系中的位置, m i m_i mi 表示该质点的质量, R R R 表示旋转矩阵,即将局部系转为全局系的矩阵,其可以由四元数q转换得到。这里先求出一个 I r e f I_{ref} Iref 的目的是其可以在仿真开始前就计算出来,仿真过程中只需要计算 I = R I r e f R T I = RI_{ref}R^T I=RIrefRT 即可,减小计算开销。

P ⃗ \vec {P} P 的微分表示力类似, L ⃗ \vec{L} L 的微分表示力矩 τ \tau τ。且 L ⃗ \vec{L} L 的微分也可表示为 I × β ⃗ I \times \vec{\beta} I×β 的形式,这里的 β ⃗ \vec{\beta} β 表示角加速度。那么仿真过程中角速度的更新如下

由于lab1中只考虑了重力,所以其力矩和角速度是不需要更新的,当然碰撞之后对于的角速度要更新。


粒子碰撞检测与响应

碰撞检测

我们可以使用 ϕ ( x ⃗ ) \phi(\vec{x}) ϕ(x ) 定义位于 x ⃗ \vec{x} x 位置的粒子到一个曲面的有向距离,其符号由粒子位于曲面的内侧还是外侧确定。

常见的 ϕ ( x ⃗ ) \phi(\vec{x}) ϕ(x ) 的实例

碰撞响应

Penalty Methods

Quadratic Penalty Method

一个 penalty 方法会在下一次更新中应用一次穿透力,即这次更新穿透还是发生了,我们在下一次更新时进行纠正。

Quadratic Penalty Method with a Buffer

上一个方法的问题在于穿透已经实际产生了,我们可以加一个缓冲区,在穿透还未发生时阻止其产生。需要注意的 k 的大小设置很重要,如果 k 太小穿透仍会发生,而 k 过大会产生 overshooting 问题,即受到的穿透力过大导致物体飞出去。

Log-Barrier Penalty Method

使用log-barrier penalty 能确保力足够大,但是仍存在overshooting问题,且需要保证 ϕ ( x ⃗ ) < 0 \phi(\vec{x})<0 ϕ(x )<0 不会发生,否则会导致发生穿透越陷越深。

Impulse Method

Impulse 方法假设碰撞会立即改变位置与速度


我们可以把当前速度看成与碰撞平面法向量相同的法向速度 v N ⃗ \vec{v_N} vN ,以及相切的切向速度 v T ⃗ \vec{v_T} vT 。对于法向速度,我们要将其反向且乘上一个摩擦系数,因为会有损耗,切向速度也要乘上摩擦系数。

而摩擦系数与新速度和旧速度之间的关系要满足 Coulomb’s law,因此 a 可以如上图求出。


刚体碰撞检测与响应

碰撞检测

与粒子的碰撞检测相同,刚体可以看作很多粒子组成的系统,那么我们就对所有粒子进行粒子的碰撞检测。

Impulse方法的碰撞响应

我们可以按照粒子的 Impulse 方法的碰撞响应来进行,但是我们不能直接对速度进行更新(按照老师的话其只是一个中间变量),因为我们对于速度的更新是分为平移速度和角速度的,我们要想办法转为对这两个量的更新。

向量叉乘可以转化为对应的矩阵乘向量

其整体流程如下

需要注意的是,我们要对所有碰撞的点求平均位置与平均速度来进行后续计算。这里不直接更新位置的原因是,这个问题是非线性的,之后会约束的章节讨论。


作业代码与提高项

Unity 2021.3.21f1c1

作业代码

using UnityEngine;
using System.Collections;
using System;

public class Rigid_Bunny : MonoBehaviour
{
    bool launched = false;
    float dt = 0.015f;
    Vector3 v = new Vector3(0, 0, 0);   // velocity
    Vector3 w = new Vector3(0, 0, 0);   // angular velocity

    float mass;                                 // mass
    Matrix4x4 I_ref;                            // reference inertia

    float linear_decay = 0.999f;                // for velocity decay
    float angular_decay = 0.98f;
    float restitution = 0.5f;                 // for collision
    float friction = 0.2f;                  

    Vector3 gravity = new Vector3(0.0f, -9.8f, 0.0f);


    // Use this for initialization
    void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;

        float m = 1;
        mass = 0;
        for (int i = 0; i < vertices.Length; i++)
        {
            mass += m;
            float diag = m * vertices[i].sqrMagnitude;//diag = mv^2
            I_ref[0, 0] += diag;
            I_ref[1, 1] += diag;
            I_ref[2, 2] += diag;
            I_ref[0, 0] -= m * vertices[i][0] * vertices[i][0];
            I_ref[0, 1] -= m * vertices[i][0] * vertices[i][1];
            I_ref[0, 2] -= m * vertices[i][0] * vertices[i][2];
            I_ref[1, 0] -= m * vertices[i][1] * vertices[i][0];
            I_ref[1, 1] -= m * vertices[i][1] * vertices[i][1];
            I_ref[1, 2] -= m * vertices[i][1] * vertices[i][2];
            I_ref[2, 0] -= m * vertices[i][2] * vertices[i][0];
            I_ref[2, 1] -= m * vertices[i][2] * vertices[i][1];
            I_ref[2, 2] -= m * vertices[i][2] * vertices[i][2];
        }
        I_ref[3, 3] = 1;
    }

    Matrix4x4 Get_Cross_Matrix(Vector3 a)//得到向量a的叉乘矩阵
    {
        //Get the cross product matrix of vector a
        Matrix4x4 A = Matrix4x4.zero;
        A[0, 0] = 0;
        A[0, 1] = -a[2];
        A[0, 2] = a[1];
        A[1, 0] = a[2];
        A[1, 1] = 0;
        A[1, 2] = -a[0];
        A[2, 0] = -a[1];
        A[2, 1] = a[0];
        A[2, 2] = 0;
        A[3, 3] = 1;
        return A;
    }

    private Quaternion Add(Quaternion a, Quaternion b)
    {
        a.x += b.x;
        a.y += b.y;
        a.z += b.z;
        a.w += b.w;
        return a;
    }

    private Matrix4x4 Matrix_subtraction(Matrix4x4 a, Matrix4x4 b)
    {
        for (int i = 0; i < 4; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                a[i, j] -= b[i, j];
            }
        }
        return a;
    }


    private Matrix4x4 Matrix_miltiply_float(Matrix4x4 a, float b)
    {
        for (int i = 0; i < 4; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                a[i, j] *= b;
            }
        }
        return a;
    }

    // In this function, update v and w by the impulse due to the collision with
    // a plane <P, N>
    void Collision_Impulse(Vector3 P, Vector3 N)
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;

        Matrix4x4 R = Matrix4x4.Rotate(transform.rotation);  // rotation matrix
        Vector3 T = transform.position;                     // translation vector

        Vector3 sum = new Vector3(0, 0, 0);                 
        int collisionNum = 0;                               // number of collision


        for (int i = 0; i < vertices.Length; i++)
        {
            Vector3 r_i = vertices[i];
            Vector3 Rri = R.MultiplyVector(r_i);
            Vector3 x_i = T + Rri;
            float d = Vector3.Dot(x_i - P, N);
            if (d < 0.0f) // collision occur
            {
                Vector3 v_i = v + Vector3.Cross(w, Rri);
                float v_N_size = Vector3.Dot(v_i, N);
                // check velocity
                if (v_N_size < 0.0f)
                {
                    sum += r_i;
                    collisionNum++;
                }
            }
        }

        if (collisionNum == 0) return;
        Matrix4x4 I_rot = R * I_ref * R.transpose;
        Matrix4x4 I_inverse = I_rot.inverse;      
        Vector3 r_collision = sum / (float)collisionNum;                // virtual collision point(local coordination)
        Vector3 Rr_collision = R.MultiplyVector(r_collision);
        //Vector3 x_collision = T + Rr_collision;							 // virtual collision point(global coordination)
        Vector3 v_collision = v + Vector3.Cross(w, Rr_collision);
        
        // Compute the wanted v_N
        Vector3 v_N = Vector3.Dot(v_collision, N) * N;
        Vector3 v_T = v_collision - v_N;
        Vector3 v_N_new = -1.0f * restitution * v_N;
        float a = Math.Max(1.0f - friction * (1.0f + restitution) * v_N.magnitude / v_T.magnitude, 0.0f);
        Vector3 v_T_new = a * v_T;
        Vector3 v_new = v_N_new + v_T_new;
        
        // Compute the impulse J
        Matrix4x4 Rri_star = Get_Cross_Matrix(Rr_collision);
        Matrix4x4 K = Matrix_subtraction(Matrix_miltiply_float(Matrix4x4.identity, 1.0f / mass),
                                        Rri_star * I_inverse * Rri_star);
        Vector3 J = K.inverse.MultiplyVector(v_new - v_collision);
        
        // Update v and w with impulse J
        v = v + 1.0f / mass * J;
        w = w + I_inverse.MultiplyVector(Vector3.Cross(Rr_collision, J));
    }

    // Update is called once per frame
    void Update()
    {
        //Game Control
        if (Input.GetKey("r"))
        {
            // return initial state
            transform.position = new Vector3(0, 0.6f, 0);
            transform.eulerAngles = new Vector3(80, 0, 0);
            restitution = 0.5f;
            launched = false;
            Debug.Log("return to origin");
        }
        if (Input.GetKey("l"))
        {
            v = new Vector3(5, 2, 0);
            w = new Vector3(0, 1, 0);
            launched = true;
        }

        if (launched)
        {
            // Part I: Update velocities
            v += dt * gravity;
            v *= linear_decay;
            w *= angular_decay;

            // Part II: Collision Impulse
            Collision_Impulse(new Vector3(0, 0.01f, 0), new Vector3(0, 1, 0));
            Collision_Impulse(new Vector3(2, 0, 0), new Vector3(-1, 0, 0));

            // Part III: Update position & orientation
            Vector3 x_0 = transform.position;
            Quaternion q_0 = transform.rotation;
            //Update linear status
            Vector3 x = x_0 + dt * v;
            //Update angular status
            Vector3 dw = 0.5f * dt * w;
            Quaternion qw = new Quaternion(dw.x, dw.y, dw.z, 0.0f);
            Quaternion q = Add(q_0, qw * q_0);

            // Part IV: Assign to the object
            transform.position = x;
            transform.rotation = q;
        }
    }
}

运行结果

提高项

Shape Matching的思想是将刚体看作粒子系统,针对每个粒子有其自己的位置与速度,分别对每个粒子进行更新。之后对更新后的粒子,使用刚体的约束进行修复。这里的刚体约束就是每个粒子的位置,由质心坐标与其相对于质心的矢量以及旋转矩阵R有关,即 x i ⃗ = c ⃗ + R r i ⃗ \vec{x_i}=\vec{c}+R\vec{r_i} xi =c +Rri 。要让约束后的位置和初始位置 y i y_i yi 尽可能接近。


其更新过程如下

对于粒子的碰撞响应,采用之前impulse的方法,但是直接更新其法向速度和切向速度。

代码如下

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rigid_Bunny_by_Shape_Matching : MonoBehaviour
{
    public bool launched = false;
    Vector3[] X;
    Vector3[] Q;
    Vector3[] V;
    Vector3[] Y;
    Matrix4x4 QQt = Matrix4x4.zero;
    Vector3 gravity = new Vector3(0, -9.8f, 0);
    float linear_decay = 0.999f; // for velocity decay
    float restitution = 5.0f; // for collision
    float friction = 0.5f;

    // Start is called before the first frame update
    void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        V = new Vector3[mesh.vertices.Length];
        Y = mesh.vertices;
        X = mesh.vertices;
        Q = mesh.vertices;

        //Centerizing Q.
        Vector3 c = Vector3.zero;
        for (int i = 0; i < Q.Length; i++)
            c += Q[i];
        c /= Q.Length;
        //Debug.Log(c);
        for (int i = 0; i < Q.Length; i++)
            Q[i] -= c;

        //Get QQ^t ready.
        for (int i = 0; i < Q.Length; i++)
        {
            QQt[0, 0] += Q[i][0] * Q[i][0];
            QQt[0, 1] += Q[i][0] * Q[i][1];
            QQt[0, 2] += Q[i][0] * Q[i][2];
            QQt[1, 0] += Q[i][1] * Q[i][0];
            QQt[1, 1] += Q[i][1] * Q[i][1];
            QQt[1, 2] += Q[i][1] * Q[i][2];
            QQt[2, 0] += Q[i][2] * Q[i][0];
            QQt[2, 1] += Q[i][2] * Q[i][1];
            QQt[2, 2] += Q[i][2] * Q[i][2];
        }
        QQt[3, 3] = 1;

        for (int i = 0; i < X.Length; i++)
            V[i][0] = 4.0f;

        //Debug.Log(transform.position);
        // transform X from local coordination to global coordination
        Update_Mesh(transform.position, Matrix4x4.Rotate(transform.rotation), 0);
        transform.position = Vector3.zero;
        transform.rotation = Quaternion.identity;
        //Debug.Log(transform.position);
    }

    // Polar Decomposition that returns the rotation from F.
    Matrix4x4 Get_Rotation(Matrix4x4 F)
    {
        Matrix4x4 C = Matrix4x4.zero;
        for (int ii = 0; ii < 3; ii++)
            for (int jj = 0; jj < 3; jj++)
                for (int kk = 0; kk < 3; kk++)
                    C[ii, jj] += F[kk, ii] * F[kk, jj];

        Matrix4x4 C2 = Matrix4x4.zero;
        for (int ii = 0; ii < 3; ii++)
            for (int jj = 0; jj < 3; jj++)
                for (int kk = 0; kk < 3; kk++)
                    C2[ii, jj] += C[ii, kk] * C[jj, kk];

        float det = F[0, 0] * F[1, 1] * F[2, 2] +
                        F[0, 1] * F[1, 2] * F[2, 0] +
                        F[1, 0] * F[2, 1] * F[0, 2] -
                        F[0, 2] * F[1, 1] * F[2, 0] -
                        F[0, 1] * F[1, 0] * F[2, 2] -
                        F[0, 0] * F[1, 2] * F[2, 1];

        float I_c = C[0, 0] + C[1, 1] + C[2, 2];
        float I_c2 = I_c * I_c;
        float II_c = 0.5f * (I_c2 - C2[0, 0] - C2[1, 1] - C2[2, 2]);
        float III_c = det * det;
        float k = I_c2 - 3 * II_c;

        Matrix4x4 inv_U = Matrix4x4.zero;
        if (k < 1e-10f)
        {
            float inv_lambda = 1 / Mathf.Sqrt(I_c / 3);
            inv_U[0, 0] = inv_lambda;
            inv_U[1, 1] = inv_lambda;
            inv_U[2, 2] = inv_lambda;
        }
        else
        {
            float l = I_c * (I_c * I_c - 4.5f * II_c) + 13.5f * III_c;
            float k_root = Mathf.Sqrt(k);
            float value = l / (k * k_root);
            if (value < -1.0f) value = -1.0f;
            if (value > 1.0f) value = 1.0f;
            float phi = Mathf.Acos(value);
            float lambda2 = (I_c + 2 * k_root * Mathf.Cos(phi / 3)) / 3.0f;
            float lambda = Mathf.Sqrt(lambda2);

            float III_u = Mathf.Sqrt(III_c);
            if (det < 0) III_u = -III_u;
            float I_u = lambda + Mathf.Sqrt(-lambda2 + I_c + 2 * III_u / lambda);
            float II_u = (I_u * I_u - I_c) * 0.5f;


            float inv_rate, factor;
            inv_rate = 1 / (I_u * II_u - III_u);
            factor = I_u * III_u * inv_rate;

            Matrix4x4 U = Matrix4x4.zero;
            U[0, 0] = factor;
            U[1, 1] = factor;
            U[2, 2] = factor;

            factor = (I_u * I_u - II_u) * inv_rate;
            for (int i = 0; i < 3; i++)
                for (int j = 0; j < 3; j++)
                    U[i, j] += factor * C[i, j] - inv_rate * C2[i, j];

            inv_rate = 1 / III_u;
            factor = II_u * inv_rate;
            inv_U[0, 0] = factor;
            inv_U[1, 1] = factor;
            inv_U[2, 2] = factor;

            factor = -I_u * inv_rate;
            for (int i = 0; i < 3; i++)
                for (int j = 0; j < 3; j++)
                    inv_U[i, j] += factor * U[i, j] + inv_rate * C[i, j];
        }

        Matrix4x4 R = Matrix4x4.zero;
        for (int ii = 0; ii < 3; ii++)
            for (int jj = 0; jj < 3; jj++)
                for (int kk = 0; kk < 3; kk++)
                    R[ii, jj] += F[ii, kk] * inv_U[kk, jj];
        R[3, 3] = 1;
        return R;
    }

    // Update the mesh vertices according to translation c and rotation R.
    // It also updates the velocity.
    void Update_Mesh(Vector3 c, Matrix4x4 R, float inv_dt)
    {
        for (int i = 0; i < Q.Length; i++)
        {
            Vector3 x = (Vector3)(R * Q[i]) + c;

            V[i] = (x - X[i]) * inv_dt;
            X[i] = x;
        }
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        mesh.vertices = X;
    }

    void Collision(float inv_dt)
    {
        Vector3 P = new Vector3(0, 0.01f, 0);
        Vector3 N = new Vector3(0, 1, 0);
        // check collision with ground
        for (int i = 0; i < X.Length; i++)
        {
            float d = Vector3.Dot(X[i] - P, N);
            if (d < 0.0f) // collision occur
            {
                float v_N_size = Vector3.Dot(V[i], N);
                // check velocity
                if (v_N_size < 0.0f)
                {
                    Vector3 v_N = v_N_size * N;
                    Vector3 v_T = V[i] - v_N;
                    Vector3 v_N_new = -1.0f * restitution * v_N;
                    float a = Math.Max(1.0f - friction * (1.0f + restitution) * v_N.magnitude / v_T.magnitude, 0.0f);
                    Vector3 v_T_new = a * v_T;
                    V[i] = v_N_new + v_T_new;
                }
            }
        }

        P = new Vector3(2.01f, 0, 0);
        N = new Vector3(-1, 0, 0);
        // check collsion with wall
        for (int i = 0; i < X.Length; i++)
        {
            float d = Vector3.Dot(X[i] - P, N);
            if (d < 0.0f) // collision occur
            {
                float v_N_size = Vector3.Dot(V[i], N);
                // check velocity
                if (v_N_size < 0.0f)
                {
                    Vector3 v_N = v_N_size * N;
                    Vector3 v_T = V[i] - v_N;
                    Vector3 v_N_new = -1.0f * restitution * v_N;
                    float a = Math.Max(1.0f - friction * (1.0f + restitution) * v_N.magnitude / v_T.magnitude, 0.0f);
                    Vector3 v_T_new = a * v_T;
                    V[i] = v_N_new + v_T_new;
                }
            }
        }
    }

    Matrix4x4 Vector3_mul_Vector3T(Vector3 v1, Vector3 v2)
    {
        Matrix4x4 res = Matrix4x4.zero;
        res[3, 3] = 1.0f;
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                res[i, j] = v1[i] * v2[j];
            }
        return res;
    }

    Matrix4x4 Matrix4x4_add_Matrix4x4(Matrix4x4 m1, Matrix4x4 m2)
    {
        Matrix4x4 res = Matrix4x4.zero;
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                res[i, j] = m1[i, j] + m2[i, j];
            }
        return res;
    }

    // Update is called once per frame
    void Update()
    {
        //Game Control
        if (Input.GetKey("r"))
        {
            // return initial state
            launched = false;
            for (int i = 0; i < V.Length; i++)
            {
                V[i] = new Vector3(4.0f, 0.0f, 0.0f);
            }
            Update_Mesh(new Vector3(0, 0.6f, 0),
                        Matrix4x4.Rotate(Quaternion.Euler(new Vector3(80, 0, 0))), 0);
            Debug.Log("return to origin");
        }
        if (Input.GetKey("l"))
        {
            launched = true;
            for (int i = 0; i < V.Length; i++)
            {
                V[i] = new Vector3(5.0f, 2.0f, 0.0f);
            }
        }

        if (!launched)
            return;

        float dt = 0.015f;

        //Step 1: run a simple particle system.
        for (int i = 0; i < V.Length; i++)
        {
            V[i] = V[i] + dt * gravity;
            V[i] = V[i] * linear_decay;
        }

        //Step 2: Perform simple particle collision.
        Collision(1 / dt);

        // Step 3: Use shape matching to get new translation c and 
        // new rotation R. Update the mesh by c and R.
        //Shape Matching (translation)
        for (int i = 0; i < V.Length; i++)
        {
            Y[i] = X[i] + V[i] * dt;
        }
        Vector3 c = Vector3.zero;
        for (int i = 0; i < Y.Length; i++)
        {
            c = c + Y[i];
        }
        c = c / Y.Length;
        //Shape Matching (rotation)
        Matrix4x4 A = Matrix4x4.zero;
        for (int i = 0; i < Y.Length; i++)
        {
            A = Matrix4x4_add_Matrix4x4(A, Vector3_mul_Vector3T(Y[i] - c, Q[i]));
        }
        A[3, 3] = 1.0f;
        A = A * QQt.inverse;
        Matrix4x4 R = Get_Rotation(A);
        //Update_Mesh(c, R, 1/dt);
        Update_Mesh(c, R, 1 / dt);
    }
}

运行结果

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

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

相关文章

如何安全的从硬盘恢复文件?

可以从硬盘中恢复永久删除的文件吗&#xff1f; “我删除了一些看起来不重要的文件夹&#xff0c;并清空了回收站。但在几天后&#xff0c;我意识到删除的文件夹里有些重要的数据。如何恢复永久删除的文件&#xff1f;谢谢&#xff01;” 随着科技的进步与发展&#xff0c…

LeetCode 2404. 出现最频繁的偶数元素

原题链接&#xff1a;2404. 出现最频繁的偶数元素 给你一个整数数组 nums &#xff0c;返回出现最频繁的偶数元素。 如果存在多个满足条件的元素&#xff0c;只需要返回 最小 的一个。如果不存在这样的元素&#xff0c;返回 -1 。 示例 1&#xff1a; 输入&#xff1a;nums …

OpenAI团队抢着用的编程语言?

作为一名合格的&#xff08;准&#xff09;程序员&#xff0c;必做的一件事是关注编程语言的热度&#xff0c;编程榜代表了编程语言的市场占比变化&#xff0c;它的变化更预示着未来的科技风向和机会&#xff01; 快一起看看本月排行有何看点&#xff1a; 4月Tiobe排行榜前15…

如何学习智能交通?

AI的专业领域知识是指AI与具体应用领域相结合时所需要的该应用领域的知识。AI的应用领域非常广泛&#xff0c;例如计算机视觉、智能交通、智能制造、智慧金融、智慧教育、智慧农业、智慧能源、智能通信、智能芯片等。本文主要介绍智能交通的基本概念、发展历程、主要研究内容、…

DAMA数据治理认证,一定要先考CDGA才能考CDGP吗?

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

react4:配置网络请求axios --- 配置@路径别名

1. axios 请求安装&#xff1a;npm i axios axios 二次封装&#xff1a;配置,拦截器等应用 在&#xff1a;utils文件夹下request.js中配置 //utils文件夹下request.js中配置//导入 axios import axios from "axios";//实例化axios var service axios.create({time…

QT5学习教程一——Hello World例子

一、打开 Qt Creator -文件-新建文件或项目-Application-Qt Widgets Application-选择&#xff0c;如图 1.1 图 1.2 将项目放到指定目录&#xff0c;便于管理。单击[下一步] 选择构建的环境&#xff0c;可以根据自己的配置来选择&#xff0c;本例子选择MSVC2015。MSVC是指微软的…

JAVA阶段考内容知识点的梳理

前言&#xff1a;这些都是很基本的&#xff0c;还有很多没有写出来&#xff0c;重点在于编程序理解。 目录 第一章概述 课堂总结 相关习题 第二章&#xff1a;语言基础 课堂总结 相关习题 第三章&#xff1a;类和对象 内容总结 相关习题 第四章&#xff1a;类的派生与…

【Linux】网络编程套接字(下)

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

【三十天精通 Vue 3】 第十天 Vue 状态管理详解

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录引言一、Vue 3 状态管理概述1.1 状态管理的概念1.2 状态管理的作用…

Spark基础 - 名词汇总及集群模式概述

原文地址&#xff1a; Spark基础 - 名词汇总及集群模式概述 本文档内容参考Spark官方文档&#xff1a;Cluster Mode Overview 一. Glossary(术语) TermMeaningcommentApplicationUser program built on Spark. Consists of a driver program and executors on the cluster.…

Springboot应用整合Sentinel实现限流、熔断、降级笔记

可以使用官方的sentinel 也可使用进行持久化改造后的Sentinel &#xff0c;本文基于此进行记录。Sentinel持久化到Nacoshttps://blog.csdn.net/chenjian723122704/article/details/130098896 Sentinel版本为 1.8.6 Nacos版本为 2.2.0 关于网关限流、限流和熔断&#xff0c;我…

【Step1】Linux Kernel -基础

《Linux内核设计与实现.第3版》1-2章学习笔记 1. 获取内核源码 http://www.kernel.org 2. 内核版本命名规则 内核的版本号主要有四个数字组成。 从版本号&#xff1a; 偶数表示稳定版&#xff08;可用于企业级环境&#xff09;&#xff0c;奇数表示开发版&#xff1b; 修订…

ack应答机制

ACK在 Kafka 中&#xff0c;ack&#xff08;Acknowledgment&#xff09;机制是指用于确认生产者发送的消息已经被成功写入到 Kafka 分区中的一种机制。生产者可以通过 ack 参数来控制这个机制&#xff0c;以便根据自己的需求进行设置。 ACK应答级别0&#xff1a;生产者发送过来…

手机号码归属地 API 实现个性化推荐的思路分析

前言 随着移动互联网和智能手机的普及&#xff0c;越来越多的人使用手机上网和购物&#xff0c;移动营销已成为企业获取用户和提升品牌知名度的重要手段。手机号码归属地 API 作为移动营销的关键工具&#xff0c;具有广阔的应用前景。 本文将探讨如何利用手机号码归属地 API …

IDEA配置MAVEN_OPTS

IDEA配置MAVEN_OPTS​ 解决问题 maven MAVEN_OPTS设置 maven编译优化 maven编译速度慢 maven打包编译很慢 maven多线程编译打包 IDEA Maven配置教程​​测试环境:Win10(64位) i7-7700HQ 16GB​​ 参考文章: ​​ ​JVM参数MetaspaceSize的误解​​ Java HotSpot™ 64-Bit Ser…

【AI浪潮下的挑战和机遇】许多职业即将消失,AI 即将战胜人类了吗?

文章目录前言一、人类科技发展史二、 AI浪潮下的挑战1. 数据安全和隐私保护问题2. 带来新的伦理和道德问题3. 版权和知识产权问题三、对传统行业和就业的冲击1.传统文本编辑行业受到冲击2.就业岗位的变化3.工作流程的变化4.创意版权问题四、AI浪潮下的机遇1.提高效率和创意性2.…

【软考备战·希赛网每日一练】2023年4月12日

文章目录一、今日成绩二、错题总结第一题三、知识查缺题目及解析来源&#xff1a;2023年04月12日软件设计师每日一练 一、今日成绩 二、错题总结 第一题 解析&#xff1a; 依据题目画出PERT图如下&#xff1a; 关键路径长度&#xff08;从起点到终点的路径中最长的一条&#x…

使用MybatisPlus时注意点的记录

在 MyBatis-Plus 中&#xff0c;默认情况下&#xff0c;实体类的主键属性名必须为 "id"&#xff0c;如果实体类主键属性名为 其他比如"nbid"&#xff0c;则需要进行显式的用FileId配置或者在.XML中配置。 在使用MybatisPlus时,如果主键名是id 实体类属性…

Django REST Framework(DRF)框架之视图类(视图基类、Mixin扩展类、子类视图)

DRF框架之视图类的使用视图类概述视图基类Mixin扩展类子类视图类视图基类APIViewGenericAPIViewMixin扩展类ListModelMixinCreateModelMixinRetrieveModelMixinUpdateModelMixinDestroyModelMixin子类视图ListAPIViewCreateAPIViewRetrieveAPIViewDestoryAPIViewUpdateAPIViewL…