[ROS 系列学习教程] 建模与仿真 - 使用 ros_control 控制差速轮式机器人

在这里插入图片描述

ROS 系列学习教程(总目录)

本文目录

  • 一、差速轮式机器人
  • 二、差速驱动机器人运动学模型
  • 三、对外接口
    • 3.1 输入接口
    • 3.2 输出接口
  • 四、控制器参数
  • 五、配置控制器参数
  • 六、编写硬件抽象接口
  • 七、控制机器人移动
  • 八、源码

ros_control 提供了多种控制器,其中 diff_drive_controller 用于控制差速驱动轮式机器人。

一、差速轮式机器人

差速轮式机器人是一种移动机器人,其运动基于机器人身体两侧的两个独立驱动轮。因此,它可以通过改变轮子的相对旋转速度来改变方向,不需要额外的转向运动。具有这种驱动器的机器人通常有一个或多个脚轮,以防止车辆倾斜。

如果两个轮子以相同的方向和速度驱动,机器人将沿直线行驶。如果两个轮子以相同的速度朝相反的方向转动,如所示图所示,机器人将绕轴的中心点旋转。否则,根据旋转速度和方向,旋转中心可能落在由两个轮胎接触点定义的线上的任何位置。当机器人沿直线行驶时,旋转中心距离机器人无限远。由于机器人的方向取决于两个驱动轮的旋转速度和方向,因此应精确感测和控制这些量。

image-20240623192426694

差速转向机器人与汽车中使用的差速 齿轮类似,两个车轮可以有不同的转速,但与差速齿轮系统不同,差速转向系统将为两个车轮提供动力。差速轮式机器人在机器人技术中得到广泛应用,因为它们的运动易于编程并且可以很好地控制。当今市场上几乎所有的消费机器人都使用差速转向,主要是因为它成本低且简单。

二、差速驱动机器人运动学模型

如下图为轮式机器人的差速驱动运动学模型示意图:

image-20240623192426694

其中,
V − 机器人线速度 ω − 机器人角速度 X O Y − 世界坐标系 X B Y B − 机器人坐标系 φ − 机器人在世界坐标系的角度 r − 车轮半径 b − 轮距 I C R − 瞬时旋转中心 R − 瞬心到机器人中心的距离 v L , v R − 左右轮接地点切向线速度 ω L , ω R − 左右轮角速度 V - 机器人线速度\\ \omega - 机器人角速度\\ XOY - 世界坐标系\\ X_BY_B - 机器人坐标系\\ \varphi - 机器人在世界坐标系的角度\\ r - 车轮半径\\ b - 轮距\\ ICR - 瞬时旋转中心\\ R - 瞬心到机器人中心的距离\\ v_L,v_R - 左右轮接地点切向线速度\\ \omega_L,\omega_R - 左右轮角速度 V机器人线速度ω机器人角速度XOY世界坐标系XBYB机器人坐标系φ机器人在世界坐标系的角度r车轮半径b轮距ICR瞬时旋转中心R瞬心到机器人中心的距离vL,vR左右轮接地点切向线速度ωL,ωR左右轮角速度

有如下关系:
ω ⋅ ( R + b / 2 ) = v R ω ⋅ ( R − b / 2 ) = v L \omega \cdot (R + b/2) = v_R\\ \omega \cdot (R - b/2) = v_L ω(R+b/2)=vRω(Rb/2)=vL
解这两个方程可得 ω \omega ω R R R
ω = ( v R − v L ) / b R = b / 2 ⋅ ( v R + v L ) / ( v R − v L ) \omega = (v_R-v_L)/b\\ R = b/2 \cdot(v_R+v_L)/(v_R-v_L) ω=(vRvL)/bR=b/2(vR+vL)/(vRvL)
利用角速度方程可得机器人瞬时速度 V V V
V = ω ⋅ R = ( v R + v L ) / 2 V = \omega \cdot R = (v_R+v_L)/2 V=ωR=(vR+vL)/2
车轮切向速度也可以写成:
v R = r ⋅ ω R v L = r ⋅ ω L v_R = r \cdot \omega_R\\ v_L = r \cdot \omega_L vR=rωRvL=rωL
则机器人在本体坐标系中的运动学模型为:
[ x ˙ B y ˙ B φ ˙ ] = [ v ⋅ x B v ⋅ y B ω ] = ⏞ v = r ω [ r 2 r 2 0 0 − r b r b ] [ ω L ω R ] \begin{bmatrix} \dot{x}_B \\ \dot{y}_B \\ \dot{\varphi} \end{bmatrix} = \begin{bmatrix} v \cdot x_B \\ v \cdot y_B \\ \omega \end{bmatrix} \overbrace{=}^{v=r\omega} \begin{bmatrix} \frac r2 & \frac r2 \\ 0 & 0 \\ -\frac rb & \frac rb \end{bmatrix} \begin{bmatrix} \omega_L \\ \omega_R \end{bmatrix} x˙By˙Bφ˙ = vxBvyBω = v=rω 2r0br2r0br [ωLωR]
再通过坐标变换,最终可以得到机器人在世界坐标中的运动学模型:
[ x ˙ y ˙ φ ˙ ] = [ cos ⁡ φ 0 sin ⁡ φ 0 0 1 ] [ V ω ] \begin{bmatrix} \dot{x} \\ \dot{y} \\ \dot{\varphi} \end{bmatrix} = \begin{bmatrix} \cos\varphi & 0 \\ \sin\varphi & 0 \\ 0 & 1 \end{bmatrix} \begin{bmatrix} V \\ \omega \end{bmatrix} x˙y˙φ˙ = cosφsinφ0001 [Vω]
其中, V V V ω \omega ω 为控制变量。

通常我们需要通过机器人的速度和结构参数逆解出左右轮的转速,用于控制电机。在这种情况下,可以很容易地重新表述前面提到的方程。使用如下方程:
R = V / ω ω R = v R / r ω L = v L / r R = V/\omega\\ \omega_R = v_R/r\\ \omega_L = v_L/r R=V/ωωR=vR/rωL=vL/r
可得左右轮角速度方程:
ω R = V + ω ⋅ b / 2 r ω L = V − ω ⋅ b / 2 r \omega_R = \frac{V+\omega \cdot b/2}{r} \\ \omega_L = \frac{V-\omega \cdot b/2}{r} ωR=rV+ωb/2ωL=rVωb/2

三、对外接口

diff_drive_controller 主要通过订阅速度命令作为模块的输入,然后解析运动学模型控制电机,达到控制机器人的目的。

3.1 输入接口

  • cmd_vel(geometry_msgs/Twist)

    位于控制器的命名空间下,给机器人发布速度

3.2 输出接口

  • odom(nav_msgs/Odometry)

    位于控制器的命名空间下,根据硬件反馈计算的里程计信息

  • /tf(tf/tfMessage)

    从 odom 转换为 base_link

  • cmd_vel_out(geometry_msgs/TwistStamped)

    publish_cmd 参数设置为 True 时可用。在控制器的输入上应用限制器后的 Twist。

四、控制器参数

diff_drive_controller 提供了一些参数,用于配置机器人控制。

参数数据类型说明
left_wheelstring /string[…]左轮关节名称或关节名称列表
right_wheelstring /string[…]右轮关节名称或关节名称列表
pose_covariance_diagonaldouble[6]用于里程计位姿发布的协方差矩阵的对角线
twist_covariance_diagonaldouble[6]用于里程计 twist 发布的协方差矩阵的对角线
publish_ratedouble发布里程计的频率,用于 tf 和 odom(单位:Hz,默认值: 50.0)
wheel_separationdouble轮距,左轮和右轮之间的距离。如果未指定此参数,diff_drive_controller 将尝试从 URDF 读取值
wheel_separation_multiplierdouble轮距参数的系数。用于解释机器人模型和真实机器人之间的差异。(默认值:1.0)
wheel_radiusdouble车轮半径。默认两侧车轮都具有相同的尺寸。如果未指定此参数,diff_drive_controller 将尝试从 URDF 读取值。
wheel_radius_multiplierdouble车轮半径参数的系数。用于解释机器人模型和真实机器人之间的差异。(默认值:1.0)
cmd_vel_timeoutdouble两个连续速度命令之间允许的时间间隔。此延迟后,将向车轮发送零速命令。(单位:s,默认值:0.5)
base_frame_idstring用于填充Odometry消息和TF的child_frame_id(默认值:“base_link”)
linearobject线性速度配置参数
+ xobjectx轴,两轮差速机器人线速度只有x轴
++ has_velocity_limitsbool控制器是否限制线速度。(默认值: false)
++ max_velocitydouble最大线速度(单位:m/s)
++ min_velocitydouble最小线速度(单位:m/s)。未指定时,使用max_velocity
++ has_acceleration_limitsbool控制器是否限制线加速度。(默认值: false)
++ max_accelerationdouble最大线加速度(单位:m/s^2)
++ min_accelerationdouble最小线加速度(单位:m/s^2)。未指定时,使用max_acceleration
++ has_jerk_limitsbool控制器是否限制线加速度的变化快慢(默认值: false)
++ max_jerkdouble最大 jerk(单位:m/s^3)
angularobject角速度配置参数
+ zobjectz轴,两轮差速机器人角速度只有z轴
++ has_velocity_limitsbool控制器是否应该限制角速度(默认值: false)
++ max_velocitydouble最大角速度(单位:rad/s)
++ min_velocitydouble最小角速度(单位:rad/s)。将其设置为 0.0 将禁用逆时针旋转。未指定时,将使用max_velocity
++ has_acceleration_limitsbool控制器是否应该限制角加速度(默认值: false)
++ max_accelerationdouble最大角加速度(单位:rad/s^2)
++ min_accelerationdouble最小角加速度(单位为 rad/s^2)。未指定时,使用max_acceleration。
++ has_jerk_limitsbool控制器是否限制角加速度的变化快慢(默认值: false)
++ max_jerkdouble最大 jerk(单位:rad/s^3)
enable_odom_tfbool是否直接发布到 TF(默认值: true )
odom_frame_idstring里程计的frame_id(默认值:“/odom”)
publish_cmdbool发布要执行的速度命令。用于监控限制器对控制器输入的影响。(默认值: False)
allow_multiple_cmd_vel_publishersbool将其设置为 true 将允许输入接口 ~/cmd_vel 有多个发布者。如果将其设置为 false,则如果 ~/cmd_vel 有多个发布者,则会导致控制器停止运行。(默认值: False)
velocity_rolling_window_sizeint用于计算里程计 twist.linear.x 和 twist.angular.z 速度的平均速度样本数量(默认值: 10)

五、配置控制器参数

最小配置示例(即必要配置项):

diff_drive_controller:
    type: "diff_drive_controller/DiffDriveController"
    left_wheel: "left_wheel_joint"
    right_wheel: "right_wheel_joint"
    pose_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]
    twist_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]

该差速轮式机器人完整配置:

# 用于控制器硬件接口配置
hardware_interface:
  joints:
    - left_wheel_joint
    - right_wheel_joint
    - front_caster_joint
    - back_caster_joint

# joint_state_controller 控制器,用于发布各关节状态
joint_state_controller:
  type: "joint_state_controller/JointStateController"
  publish_rate: 50

# diff_drive_controller 控制器
diff_drive_controller:
  type: "diff_drive_controller/DiffDriveController"
  left_wheel: "left_wheel_joint"
  right_wheel: "right_wheel_joint"
  publish_rate: 50
  pose_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]
  twist_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]
  cmd_vel_timeout: 100
  velocity_rolling_window_size: 1

  publish_cmd: true
  base_frame_id: base_link
  enable_odom_tf: true
  odom_frame_id: odom

  # 轮间距和轮半径
  wheel_separation: 0.38
  wheel_radius: 0.06
  wheel_separation_multiplier: 1.0
  wheel_radius_multiplier: 1.0

  # 速度和加速度限制
  linear:
    x:
      has_velocity_limits: true
      max_velocity: 1.0 # m/s
      has_acceleration_limits: true
      max_acceleration: 3.0 # m/s^2
  angular:
    z:
      has_velocity_limits: true
      max_velocity: 2.0 # rad/s
      has_acceleration_limits: true
      max_acceleration: 6.0 # rad/s^2

六、编写硬件抽象接口

下面写一个两轮差速硬件接口,使用速度控制接口 VelocityJointInterface 控制 joint 的速度,使用 JointStateInterface 获取 joint 的位置、速度、力等信息。

硬件抽象接口头文件:diff_drive_hardware_interface.h

#ifndef DIFF_DRIVE_HARDWARE_INTERFACE_H
#define DIFF_DRIVE_HARDWARE_INTERFACE_H

#include <ros/ros.h>
#include <hardware_interface/joint_command_interface.h>
#include <hardware_interface/joint_state_interface.h>
#include <hardware_interface/robot_hw.h>
#include <controller_manager/controller_manager.h>

class DiffDriveHWInterface : public hardware_interface::RobotHW
{
public:
    struct JointInfo
    {
        std::string name;
        double cmd;
        double pos;
        double vel;
        double eff;

        JointInfo() : name(""), cmd(0.0), pos(0.0), vel(0.0), eff(0.0)
        {}

        JointInfo(std::string name_) 
            : name(name_), cmd(0.0), pos(0.0), vel(0.0), eff(0.0)
        {}

        JointInfo(std::string name_, double cmd_, double pos_, double vel_, double dff_) 
            : name(name_), cmd(cmd_), pos(pos_), vel(vel_), eff(dff_)
        {}

    };
    
public:
    DiffDriveHWInterface(ros::NodeHandle &nh);
    void init();
    void read(const ros::Duration &period);
    void write(const ros::Duration &period);

private:
    ros::NodeHandle m_nh;
    hardware_interface::JointStateInterface m_jnt_state_interface;
    hardware_interface::VelocityJointInterface m_jnt_vel_interface;
    std::vector<JointInfo> m_joints;
};

#endif // DIFF_DRIVE_HARDWARE_INTERFACE_H

源文件:diff_drive_hardware_interface.cpp

#include "diff_drive_control/diff_drive_hardware_interface.h"

DiffDriveHWInterface::DiffDriveHWInterface(ros::NodeHandle &nh) : m_nh(nh)
{
}

/**
 * @brief 初始化关节信息
 *        注册抽象硬件接口
 * 
 */
void DiffDriveHWInterface::init()
{
    std::vector<std::string> joint_names;
    m_nh.getParam("/hardware_interface/joints", joint_names);
    for (std::string name : joint_names)
    {
        m_joints.push_back(JointInfo(name));
    }

    for (auto &joint : m_joints)
    {
        ROS_INFO("get joint: %s", joint.name.c_str());

        // Initialize hardware interface
        hardware_interface::JointStateHandle state_handle(joint.name, &joint.pos, &joint.vel, &joint.eff);
        m_jnt_state_interface.registerHandle(state_handle);

        hardware_interface::JointHandle vel_handle(m_jnt_state_interface.getHandle(joint.name), &joint.cmd);
        m_jnt_vel_interface.registerHandle(vel_handle);
    }

    registerInterface(&m_jnt_state_interface);
    registerInterface(&m_jnt_vel_interface);
}

void DiffDriveHWInterface::read(const ros::Duration &period)
{
    // Read the state of the hardware (e.g., from sensors)
}

void DiffDriveHWInterface::write(const ros::Duration &period)
{
    // Send the command to the hardware (e.g., to actuators)
    for (auto &joint : m_joints)
    {
        joint.pos += joint.vel * period.toSec();
        // if (joint.vel != joint.cmd)
        // {
        //     ROS_INFO("write, joint: %s, cmd: %lf", joint.name.c_str(), joint.cmd);
        // }
        joint.vel = joint.cmd;
    }
}

控制节点:diff_drive_control_node.cpp

#include <ros/ros.h>
#include <controller_manager/controller_manager.h>
#include "diff_drive_control/diff_drive_hardware_interface.h"

int main(int argc, char **argv)
{
    ros::init(argc, argv, "diff_drive_control_node");
    ros::NodeHandle nh;

    DiffDriveHWInterface diff_drive(nh);
    diff_drive.init();

    controller_manager::ControllerManager cm(&diff_drive, nh);

    ros::Rate rate(50.0);
    ros::AsyncSpinner spinner(1);
    spinner.start();

    while (ros::ok())
    {
        ros::Duration period = rate.expectedCycleTime();
        diff_drive.write(period);
        cm.update(ros::Time::now(), period);
        rate.sleep();
    }

    return 0;
}

七、控制机器人移动

机器人模型与硬件接口都准备好了,接下来开始编写业务代码控制机器人。

我们仅给机器人发送速度指令,模拟机器人移动任务,如下:

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>

geometry_msgs::Twist moveRobot(const double& linear, const double& angular)
{
    geometry_msgs::Twist msg;
    msg.linear.x = linear; // 线速度
    msg.linear.y = 0.0;
    msg.linear.z = 0.0;
    msg.angular.x = 0.0;
    msg.angular.y = 0.0;
    msg.angular.z = angular; // 角速度
    ROS_INFO("moveRobot, linear: %.3lf, angular: %.1lf", linear, angular*180/M_PI);

    return msg;
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "diff_drive_business");
    ros::NodeHandle nh;
    ros::Publisher velPub = nh.advertise<geometry_msgs::Twist>("/diff_drive_controller/cmd_vel", 10);

    ros::Rate rate(10);

    while (ros::ok())
    {
        velPub.publish(moveRobot(0.5, 0));
        ros::Duration(3.0).sleep();
        velPub.publish(moveRobot(0, 1.57));
        ros::Duration(1.0).sleep();

        rate.sleep();
    }

    return 0;
}

编译执行该节点,在rviz中的可视化结果如下:

在这里插入图片描述

八、源码

项目源码

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

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

相关文章

揭示隐藏的模式:秩和检验和单因素方差分析的实战指南【考题】

1.研究一种新方法对于某实验结果准确性提高的效果&#xff0c;并将其与原有方法进行比较&#xff0c;结果见下表&#xff0c;请评价两者是否有不同? (行无序&#xff0c;列有序)-->单方向有序-->两独立样本的秩和检验) 如下图所示&#xff0c;先将相关数据导入spss。 图…

springboot注解@ComponentScan注解作用

一 ComponentScan作用 1.1 注解作用 项目会默认扫描SpringBootApplication注解所在路径的同级和下级的所有子包&#xff0c;使用ComponentScan后他会取代掉默认扫描。 ComponentScan 是Spring框架的注解&#xff0c;它的作用是扫描指定的包路径下的标有 Component、Service、…

docker-mysql主从复制

MySQL主从复制 安装docker和拉取镜像不再赘述 一.主服务器 1.新建主服务器容器-3307 &#xff08;这里设置的密码可能不生效&#xff0c;若未生效请看问题中的2&#xff09; docker run -p 3307:3306 --name mysql-master \ -v /mydata/mysql-master/log:/var/log/mysql \…

mindspore打卡第9天 transformer的encoder和decoder部分

mindspore打卡第9天 transformer的encoder和decoder部分 import mindspore from mindspore import nn from mindspore import ops from mindspore import Tensor from mindspore import dtype as mstypeclass ScaledDotProductAttention(nn.Cell):def __init__(self, dropout_…

13017.win10安装WSL2及CUDA开发环境

文章目录 1 win10版本1.1 关键项不能忽略 2 安装WSL2 ubuntu20.042.1 打开控制面板&#xff0c;开启虚拟子系统功能2.2 离线安装ubuntu2.2 WSL2 启动 ubuntu2.3 修改默认启动用户 3 ubuntu中安装vscode-server3.1 win10 中安装vscode3.2 ubuntu中安装vscode-server3.3 启动WSL2…

嵌入式学习(Day 51:ARM指令/汇编与c语言函数相互调用)

1.Supervisor模式与SVC模式 Supervisor模式是ARM处理器的一个特权工作模式&#xff0c;允许执行特权指令和访问特权资源。SVC模式&#xff08;Supervisor Call&#xff09;是与Supervisor模式相关的一个功能或指令&#xff0c;用于从用户模式切换到Supervisor模式&#xff0c;…

【C++ | 类型转换】转换构造函数、类型转换运算符 详解及例子源码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a; 本文未经允许…

认识100种电路之稳压电路

在电子电路中&#xff0c;稳压电路扮演着至关重要的角色。那么&#xff0c;为什么电路需要稳压&#xff1f;稳压的原理又是什么&#xff1f;以及稳压需要用到哪些元器件&#xff0c;数量又有多少呢&#xff1f;今天&#xff0c;就让我们一同揭开稳压电路的神秘面纱。 【电路为什…

【原创图解 算法leetcode 146】实现一个LRU缓存淘汰策略策略的数据结构

1 概念 LRU是Least Recently Used的缩写&#xff0c;即最近最少使用&#xff0c;是一种常见的缓存淘汰算法。 其核心思想为&#xff1a;当内存达到上限时&#xff0c;淘汰最久未被访问的缓存。 2 LeetCode LeetCode: 146. LRU缓存 3 实现 通过上面LRU的淘汰策略可知&#…

Zookeeper节点ACL权限设置—digest模式

ACL全称为Access Control List&#xff08;访问控制列表&#xff09;&#xff0c;用于控制资源的访问权限。ZooKeeper使用ACL来控制对其znode&#xff08;ZooKeeper数据树的数据节点&#xff09;的访问。 zk利用ACL策略控制节点的访问权限: CREATE c 可以创建子节点 DELETE …

Java中System的用法

System指的是当前进程运行的操作系统&#xff0c;属于java.lang包下面的类 常见的用法有以下几种&#xff1a; 第一种简单,我们直接上第二种方法吧 currentTimeMills()用法 // 演示currentTimeMillis方法public static void main(String[] args) {// 获取当前时间所对应的毫秒…

《红黑树大能量——set,map封装模拟》

前言 书接上篇博客《手撕红黑树》&#xff0c;我们学习到了红黑树是如何通过其内部方式来管理数据的&#xff0c;本篇文章将基于上篇文章继续探讨红黑树的更多应用。set&#xff0c;map是STL库中很重要的容器&#xff0c;他们就是利用红黑树作为底层来实现的&#xff0c;今天让…

入门Java爬虫:认识其基本概念和应用方法

Java爬虫初探&#xff1a;了解它的基本概念与用途&#xff0c;需要具体代码示例 随着互联网的快速发展&#xff0c;获取并处理大量的数据成为企业和个人不可或缺的一项任务。而爬虫&#xff08;Web Scraping&#xff09;作为一种自动化的数据获取方法&#xff0c;不仅能够快速…

数据结构--堆(图文)

在开始学习堆之前&#xff0c;我们要先简单了解二叉树 二叉树 一棵二叉树是结点的一个有限集合&#xff0c;该集合: 为空由一个根结点加上两棵子树&#xff08;左子树和右子树&#xff09; 特殊的二叉树&#xff1a; 满二叉树&#xff1a;一个二叉树&#xff0c;如果每一…

【高考】人生规划指南

作为一个正处在这个选择的十字路口的高考考生&#xff0c;我认为在选择专业和学校时&#xff0c;要根据自己的具体情况和个人目标来权衡。首先&#xff0c;我认为专业是首要考虑因素。因为专业是直接决定未来职业发展方向的&#xff0c;如果不喜欢或者不适合的专业选择&#xf…

1976 ssm 营地管理系统开发mysql数据库web结构java编程计算机网页源码Myeclipse项目

一、源码特点 ssm 营地管理系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开…

C++【引用】

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;LiUEEEEE                        …

2024年Stable Diffusion应用入门,AI绘画超详细兼职攻略,从零开始!

. AI绘画&#xff1a; 利用AI工具在AI绘画上的应用非常广泛&#xff0c;涵盖了从艺术创作到工业设计等多个领域。 目前市面上有许多AI绘画软件和工具&#xff0c;适合不同需求的用户。 例如&#xff0c;Midjourney、DALL-E 2、Stable Diffusion、DreamStudio等都是较为知名的…

docker配置容器——环境变量

很多时候&#xff0c;我们需要为运行在容器中的应用程序提供一些配置。配置通常用于允许同一个容器在完全不同的环境中运行&#xff0c;例如开发、测试或生产环境。在 Linux 中&#xff0c;配置值通常通过环境变量提供。我们已经了解到&#xff0c;在容器内运行的应用程序与其主…

快手正式推出Vision Pro版本,引领虚拟现实社交新潮流

6月28日&#xff0c;快手正式推出其专为Apple Vision Pro打造的版本——快手vp版app&#xff0c;成为国内首批登陆Apple Vision Pro的短视频平台。 借助先进的虚拟现实技术&#xff0c;用户可以在快手上体验更真实生动的视频内容&#xff0c;无论是观看趣味短视频内容&#xf…