Autoware.universe部署05:实车调试

news2024/11/22 18:10:17

文章目录

  • 一、建图
    • 1.1 点云地图
    • 1.2 高精地图
  • 二、参数配置
  • 三、传感器数据通信接口
    • 3.1 雷达点云
    • 3.2 图像
    • 3.3 IMU
    • 3.4 GNSS RTK
  • 四、实车调试
    • 4.1 编写启动
    • 4.2 修改传感器外参
    • 4.3 修改车身参数
    • 4.4 实车调试


本文介绍了 Autoware.universe 在实车上的部署,本系列其他文章:
Autoware.universe部署01:Ubuntu20.04安装Autoware.universe并与Awsim联调
Autoware.universe部署02:高精Lanelet2地图的绘制
Autoware.universe部署03:与Carla(二进制版)联调
Autoware.universe部署04:universe传感器ROS2驱动

一、建图

1.1 点云地图

不同于AI,官方在universe中并未提供建图功能,官方推荐其他建图算法:Autoware-documentation
录制点云bag,使用其他SLAM算法建图(最好使用能与GNSS组合的SLAM算法,这样才能建出绝对坐标系的点云地图,方便后面使用RTK),例如LIO-SAM

1.2 高精地图

使用Autoware.universe部署02:高精Lanelet2地图的绘制的方法绘制高精地图,由于场地有限,仅仅绘制了一条单向车道线,一个小的停车场和一个小的停车位
在这里插入图片描述

二、参数配置

(1)修改 autoware.launch.xml 文件(这里不改也行,后面可以外部传参)
在src/launcher/autoware_launch/autoware_launch/launch/autoware.launch.xml第17行将launch_sensing_driver中的true改成false,不使用Autoware自带的驱动程序,可以直接复制以下替换第17行。

<arg name="launch_sensing_driver" default="false" description="launch sensing driver"/>

(2)修改 gnss.launch.xml 文件
在src/sensor_kit/sample_sensor_kit_launch/sample_sensor_kit_launch/launch/gnss.launch.xml第4行将coordinate_system中的1改成2,可以直接复制以下替换第4行。

<arg name="coordinate_system" default="2" description="0:UTM, 1:MGRS, 2:PLANE"/>

在第5行添加如下:

<arg name="plane_zone" default="0"/>

(3)如果不使用GNSS,则在/src/universe/autoware.universe/launch/tier4_localization_launch/launch/util/util.launch.xml将GNSS设置为不可用

<arg name="gnss_enabled" value="false"/>
<!-- <arg name="gnss_enabled" value="true"/> -->

(4)预处理参数修改
可能需要修改点云预处理的参数,可以在下面找到autoware_universe/src/universe/autoware.universe/launch/tier4_localization_launch/config,尤其是体素滤波参数:
在这里插入图片描述

三、传感器数据通信接口

根据官方提供的流程图,可以看到各个节点之间的数据通信:https://autowarefoundation.github.io/autoware-documentation/galactic/design/autoware-architecture/node-diagram/
在这里插入图片描述
从Carla仿真中也可以得知,Carla输出以及接收以下消息:
在这里插入图片描述

其中:

  • /control/command/contral_cmd为Autoware发出的用于控制小车底盘的消息,/op_ros2_agent会将其转化为CAN消息,进而控制底盘;
  • /sensing/imu/tamagawa/imu_raw为IMU消息,用于获取里程计,frame_id为tamagawa/imu_link
  • /sensing/gnss/ublox/nav_sat_fix为GNSS消息,用于位姿初始化(可选),frame_id为gnss_link
  • /sensing/camera/traffic_light/image_raw为图像消息,用于识别红绿灯,frame_id为camera4/camera_link
  • /vehicla/status为车反馈的速度等状态消息(具体可以看CAN代码);
  • /Carla_pointcloud为Caral输出的点云,Autoware输入的点云为 /sensing/lidar/top/outlier_filtered/pointcloud/sensing/lidar/concatenated/pointcloud(frame_id均为base_link),下面的/carla_pointcloud_interface节点会将/Carla_pointcloud转化, /sensing/lidar/top/outlier_filtered/pointcloud用于定位,/sensing/lidar/concatenated/pointcloud用于感知;
    在这里插入图片描述

3.1 雷达点云

编写了如下的节点,将/points_raw(frame_id为velodyne)(或者是你的雷达驱动输出)坐标转换到base_link(需要雷达外参),然后发布 /sensing/lidar/top/outlier_filtered/pointcloud/sensing/lidar/concatenated/pointcloud

#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <pcl_ros/transforms.hpp>

class LidarTransformNode : public rclcpp::Node
{
public:
    // 构造函数
    LidarTransformNode() : Node("points_raw_transform_node")
    {
        // 初始化坐标转换参数
        // this->declare_parameter<double>("transform_x", 0.0);
        // this->get_parameter("transform_x", transform_x);
        transform_x = this->declare_parameter("transform_x", 0.0);
        transform_y = this->declare_parameter("transform_y", 0.0);
        transform_z = this->declare_parameter("transform_z", 0.0);
        transform_roll = this->declare_parameter("transform_roll", 0.0);
        transform_pitch = this->declare_parameter("transform_pitch", 0.0);
        transform_yaw = this->declare_parameter("transform_yaw", 0.0);

        std::cout << "velodyne to base_link:" << std::endl
                  << "  transform_x:    " << transform_x << std::endl
                  << "  transform_y:    " << transform_y << std::endl
                  << "  transform_z:    " << transform_z << std::endl
                  << "  transform_roll: " << transform_roll << std::endl
                  << "  transform_pitch:" << transform_pitch << std::endl
                  << "  transform_yaw:  " << transform_yaw << std::endl;

        // Initialize translation
        transform_stamp.transform.translation.x = transform_x;
        transform_stamp.transform.translation.y = transform_y;
        transform_stamp.transform.translation.z = transform_z;
        // Initialize rotation (quaternion)
        tf2::Quaternion quaternion;
        quaternion.setRPY(transform_roll, transform_pitch, transform_yaw);
        transform_stamp.transform.rotation.x = quaternion.x();
        transform_stamp.transform.rotation.y = quaternion.y();
        transform_stamp.transform.rotation.z = quaternion.z();
        transform_stamp.transform.rotation.w = quaternion.w();

        // 创建发布者
        publisher_1 = this->create_publisher<sensor_msgs::msg::PointCloud2>(
            "/sensing/lidar/top/outlier_filtered/pointcloud",
            10);
        publisher_2 = this->create_publisher<sensor_msgs::msg::PointCloud2>(
            "/sensing/lidar/concatenated/pointcloud",
            10);

        // 订阅原始点云消息
        subscription_ = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/points_raw",
            10,
            std::bind(&LidarTransformNode::pointCloudCallback, this, std::placeholders::_1));
    }

private:
    void pointCloudCallback(const sensor_msgs::msg::PointCloud2::SharedPtr msg)
    {
        // 过滤掉距离传感器较近的点
        pcl::PointCloud<pcl::PointXYZI>::Ptr xyz_cloud(new pcl::PointCloud<pcl::PointXYZI>);
        pcl::PointCloud<pcl::PointXYZI>::Ptr pcl_output(new pcl::PointCloud<pcl::PointXYZI>);
        pcl::fromROSMsg(*msg, *xyz_cloud);
        for (size_t i = 0; i < xyz_cloud->points.size(); ++i)
        {
          if (sqrt(xyz_cloud->points[i].x * xyz_cloud->points[i].x + xyz_cloud->points[i].y * xyz_cloud->points[i].y +
                   xyz_cloud->points[i].z * xyz_cloud->points[i].z) >= RadiusOutlierFilter && !isnan(xyz_cloud->points[i].z))
          {
            pcl_output->points.push_back(xyz_cloud->points.at(i));
          }
        }
        sensor_msgs::msg::PointCloud2 output;
        pcl::toROSMsg(*pcl_output, output);
        output.header = msg->header;
    
        // 执行坐标转换
        sensor_msgs::msg::PointCloud2 transformed_cloud;
        pcl_ros::transformPointCloud("base_link", transform_stamp, output, transformed_cloud);
    
        // 发布转换后的点云消息
        publisher_1->publish(transformed_cloud);
        publisher_2->publish(transformed_cloud);
    }
  
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subscription_;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr publisher_1, publisher_2;

    double transform_x, transform_y, transform_z, transform_roll, transform_pitch, transform_yaw;
    geometry_msgs::msg::TransformStamped transform_stamp;
};

int main(int argc, char **argv)
{
    // 初始化节点
    rclcpp::init(argc, argv);

    // 创建实例
    auto node = std::make_shared<LidarTransformNode>();

    rclcpp::spin(node);
    rclcpp::shutdown();

    return 0;
}

3.2 图像

由于Autoware接收/sensing/camera/traffic_light/image_raw图像消息,用于交通灯识别(融合感知可以使用其他7个相机,但是是可选,因此图像不用于融合感知),frame_id为camera4/camera_link,因此再参数文件中修改frame_id:

/**:
    ros__parameters:
      # 设备挂载点
      video_device: "/dev/video2"
      # 帧率
      framerate: 30.0
      io_method: "mmap"
      # 坐标系
      frame_id: "camera4/camera_link"
      # 像素格式
      pixel_format: "mjpeg2rgb"  # see usb_cam/supported_formats for list of supported formats
      # 像素尺寸
      image_width: 1920
      image_height: 1080
      # 相机名称
      camera_name: "JR_HF868"
      # 标定参数
      camera_info_url: "package://usb_cam/config/camera_info.yaml"
      # 亮度
      brightness: 50
      # 对比度
      contrast: 50
      # 饱和度
      saturation: 40
      # 清晰度
      sharpness: 70
      # 增益
      gain: -1
      # 白平衡
      auto_white_balance: true
      white_balance: 4000
      # 曝光
      autoexposure: true
      exposure: 100
      # 聚焦
      autofocus: false
      focus: -1

在节点代码中修改发布话题:

  UsbCamNode::UsbCamNode(const rclcpp::NodeOptions &node_options)
      : Node("usb_cam", node_options),
        m_camera(new usb_cam::UsbCam()),
        m_image_msg(new sensor_msgs::msg::Image()),
        m_image_publisher(std::make_shared<image_transport::CameraPublisher>(
            image_transport::create_camera_publisher(this, "/sensing/camera/traffic_light/image_raw",
                                                     rclcpp::QoS{100}.get_rmw_qos_profile()))),
        m_parameters(),
        m_camera_info_msg(new sensor_msgs::msg::CameraInfo()),
        m_service_capture(
            this->create_service<std_srvs::srv::SetBool>(
                "set_capture",
                std::bind(
                    &UsbCamNode::service_capture,
                    this,
                    std::placeholders::_1,
                    std::placeholders::_2,
                    std::placeholders::_3)))

3.3 IMU

修改发布话题名称为/sensing/imu/tamagawa/imu_raw以及坐标系为tamagawa/imu_link
在这里插入图片描述
launch也修改一下:
在这里插入图片描述

3.4 GNSS RTK

四、实车调试

4.1 编写启动

将之前写的各个传感器驱动放入Autoware的工作空间,为他们写一个统一的启动:
在这里插入图片描述

<launch>
    <!-- 雷达驱动 -->
    <include file="$(find-pkg-share rslidar_sdk)/launch/start.py" />
    <!-- 转换为Autoware需要的 -->
    <include file="$(find-pkg-share points_raw_transform)/launch/points_raw_transform.launch.xml" >
      <!-- LiDAR to base link -->
      <arg name="transform_x" value="0.75"/>
      <arg name="transform_y" value="0.35"/>
      <arg name="transform_z" value="0.6"/>
      <arg name="transform_roll" value="0.0"/>
      <arg name="transform_pitch" value="0.0"/>
      <arg name="transform_yaw" value="0.0"/>
      <arg name="RadiusOutlierFilter" value="2.5"/>
    </include>

    <!-- 相机驱动 -->
    <include file="$(find-pkg-share usb_cam)/launch/JR_HF868.launch.py" />

    <!-- 生成假的GNSS和IMU数据,用于调试 -->
    <!-- <include file="$(find-pkg-share gnss_imu_sim)/launch/both.launch.py" /> -->

    <!-- GNSS模块驱动(可选,这里未使用) -->
    <!-- <include file="$(find-pkg-share gnss_imu_sim)/launch/M620.launch.py" /> -->
    <!-- IMU模块驱动 -->
    <include file="$(find-pkg-share wit_ros2_imu)/rviz_and_imu.launch.py" />

    <!-- CAN驱动 -->
    <include file="$(find-pkg-share can_ros2_bridge)/can.launch.py" />

    <!-- 启动Autoware -->
    <include file="$(find-pkg-share autoware_launch)/launch/autoware.launch.xml">
      <!-- 地图路径 -->
      <arg name="map_path" value="/home/car/Autoware/QinHang"/>
      <!-- 车辆和传感器模型 -->
      <arg name="vehicle_model" value="sample_vehicle"/>
      <arg name="sensor_model" value="sample_sensor_kit"/>
      <!-- 不使用仿真时间 -->
      <arg name="use_sim_time" value="false"/>
      <!-- 不使用Autoware自带的传感器驱动 -->
      <arg name="launch_sensing_driver" value="false"/>
    </include>
</launch>

以上各个传感器调试好之后,简单测试,可以定位,可以进行红绿灯识别:

# 编译添加的几个功能包
# colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-select colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-select rslidar_msg rslidar_sdk points_raw_transform usb_cam wit_ros2_imu can_ros2_bridge gnss_imu_sim sensor_driver
source install/setup.bash
ros2 launch sensor_driver senser_driver.launch.xml

在这里插入图片描述

4.2 修改传感器外参

安装几个使用的传感器,然后修改其外参(主要是修改IMU和用于识别红绿灯的相机,雷达我们已经在转换节点将其转换到base_link坐标系),外参需要标定(也可以直接拿尺子测量,但不准确),标定方法参考我的文章:SLAM各传感器的标定总结:Camera/IMU/LiDAR
src/sensor_kit/sample_sensor_kit_launch/sample_sensor_kit_description/config/sensor_kit_calibration.yaml:

sensor_kit_base_link:
  camera0/camera_link:
    x: 0.10731
    y: 0.56343
    z: -0.27697
    roll: -0.025
    pitch: 0.315
    yaw: 1.035
  camera1/camera_link:
    x: -0.10731
    y: -0.56343
    z: -0.27697
    roll: -0.025
    pitch: 0.32
    yaw: -2.12
  camera2/camera_link:
    x: 0.10731
    y: -0.56343
    z: -0.27697
    roll: -0.00
    pitch: 0.335
    yaw: -1.04
  camera3/camera_link:
    x: -0.10731
    y: 0.56343
    z: -0.27697
    roll: 0.0
    pitch: 0.325
    yaw: 2.0943951
  camera4/camera_link:
    x: 0.07356
    y: 0.0
    z: -0.0525
    roll: 0.0
    pitch: -0.03
    yaw: -0.005
  camera5/camera_link:
    x: -0.07356
    y: 0.0
    z: -0.0525
    roll: 0.0
    pitch: -0.01
    yaw: 3.125
  traffic_light_right_camera/camera_link:
    x: 0.75
    y: 0.13
    z: 0.60
    roll: 1.57
    pitch: 0.0
    yaw: 1.57
  traffic_light_left_camera/camera_link:
    x: 0.57
    y: 0.56
    z: 0.60
    roll: 1.57
    pitch: 0.0
    yaw: 1.57
  velodyne_top_base_link:
    x: 0.0
    y: 0.0
    z: 0.0
    roll: 0.0
    pitch: 0.0
    yaw: 1.575
  velodyne_left_base_link:
    x: 0.0
    y: 0.56362
    z: -0.30555
    roll: -0.02
    pitch: 0.71
    yaw: 1.575
  velodyne_right_base_link:
    x: 0.0
    y: -0.56362
    z: -0.30555
    roll: -0.01
    pitch: 0.71
    yaw: -1.580
  gnss_link:
    x: -0.1
    y: 0.0
    z: -0.2
    roll: 0.0
    pitch: 0.0
    yaw: 0.0
  tamagawa/imu_link:
    x: 0.75
    y: 0.35
    z: 0.60
    roll: 0.0
    pitch: 0.0
    yaw: 0.0

4.3 修改车身参数

为了适配我们的车辆,需要修改车身参数(下面这些参数可以自己测量一下你的车子)src/vehicle/sample_vehicle_launch/sample_vehicle_description/config/vehicle_info.param.yaml:

/**:
  ros__parameters:
    wheel_radius: 0.18 # The radius of the wheel, primarily used for dead reckoning.车轮半径,用于航位推算
    wheel_width: 0.12 # The lateral width of a wheel tire, primarily used for dead reckoning.车轮横向宽度,用于航位推算
    wheel_base: 1.03 # between front wheel center and rear wheel center 轮距,前后轮中心距离
    wheel_tread: 0.7 # between left wheel center and right wheel center 轴距,左右轮中心距离
    front_overhang: 0.18 # between front wheel center and vehicle front,前悬架,前轮中心与车头距离
    rear_overhang: 0.22 # between rear wheel center and vehicle rear 后悬架
    left_overhang: 0.0 # between left wheel center and vehicle left 左悬伸,左轮中心与车左侧距离
    right_overhang: 0.0 # between right wheel center and vehicle right 又悬伸
    vehicle_height: 1.0  # 车高
    max_steer_angle: 0.45 # [rad] 最大转弯半径

以上内容修改之后重新编译:

# 更新传感器外参,更新汽车车身参数(轮距等)
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-select sample_sensor_kit_description sample_vehicle_description vehicle_info_util

可以通过下列命令查看根据上述参数计算的车子最小转弯半径:

ros2 run vehicle_info_util min_turning_radius_calculator.py

在这里插入图片描述

4.4 实车调试

启动:

source install/setup.bash
ros2 launch sensor_driver senser_driver.launch.xml

在这里插入图片描述
电脑不插电性能太差,导致跑不动…未完待续

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

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

相关文章

《Web安全基础》04. 文件上传漏洞

web 1&#xff1a;文件上传漏洞2&#xff1a;WAF 绕过2.1&#xff1a;数据溢出2.2&#xff1a;符号变异2.3&#xff1a;数据截断2.4&#xff1a;重复数据 本系列侧重方法论&#xff0c;各工具只是实现目标的载体。 命令与工具只做简单介绍&#xff0c;其使用另见《安全工具录》…

2023京东口腔护理赛道行业数据分析(京东销售数据分析)

近年来&#xff0c;口腔护理逐渐成为年轻人重视的健康领域&#xff0c;从口腔护理整体市场来看&#xff0c;牙膏和牙刷等基础口腔护理产品仍占据主导地位。不过&#xff0c;随着口腔护理市场逐步朝向精致化、专业化、多元化等方向发展&#xff0c;不少新兴口腔护理产品受到消费…

C++学习|CUDA内存管理代码实例

前言&#xff1a;之前介绍了CUDA入门知识&#xff0c;对CUDA编程有了一个基本了解&#xff0c;但是实际写起来还是遇到很多问题&#xff0c;例如cpp文件该怎么调用cuda文件、cpu和gpu之间内存数据怎么交换、如何编写.cu和.cuh文件之类的。本篇文章将会以一个实现向量相加的代码…

【数据结构】二叉数的存储与基本操作的实现

文章目录 &#x1f340;二叉树的存储&#x1f333;二叉树的基本操作&#x1f431;‍&#x1f464;二叉树的创建&#x1f431;‍&#x1f453;二叉树的遍历&#x1f3a1;前中后序遍历&#x1f4cc;前序遍历&#x1f4cc;中序遍历&#x1f4cc;后续遍历 &#x1f6eb;层序遍历&am…

什么是Python爬虫分布式架构,可能遇到哪些问题,如何解决

目录 什么是Python爬虫分布式架构 1. 调度中心&#xff08;Scheduler&#xff09;&#xff1a; 2. 爬虫节点&#xff08;Crawler Node&#xff09;&#xff1a; 3. 数据存储&#xff08;Data Storage&#xff09;&#xff1a; 4. 反爬虫处理&#xff08;Anti-Scraping&…

2023-08-30力扣每日一题

链接&#xff1a; 1654. 到家的最少跳跃次数 题意&#xff1a; 从0出发&#xff0c;到X的最少步数 它可以 往前 跳恰好 a 个位置&#xff08;即往右跳&#xff09;。它可以 往后 跳恰好 b 个位置&#xff08;即往左跳&#xff09;。它不能 连续 往后跳 2 次。它不能跳到任何…

2023新版医保目录明细(药品查询)

查询医保目录的主要目的是为了了解医保政策对于特定医疗服务、药品和医疗器械的覆盖范围和支付标准。大众可以通过查看医保目录可以确定哪些药品可以被医保支付以及报销的比例和限额&#xff1b;医药从业者可通过查看医保目录可以即使了解医保政策的变化&#xff0c;便于做出相…

Window10 安装 Lua

1、下载地址&#xff1a;https://luabinaries.sourceforge.net/download.html 2、下载 3、解压后共有4个文件&#xff0c;这里我把这几个文件放到如下目录 D:\Program Files\lua-5.4.2\bin 4、定义环境变量 5、打开 powershell&#xff0c;运行 lua54 -v PS C:\Windows\syste…

qt设计界面

widget.h #ifndef WIDGET_H #define WIDGET_H //防止文件重复包含#include <QWidget> //QWidget类所在的头文件&#xff0c;父类头文件 #include<QIcon> #include<QPushButton> …

Facebook Shops免费面世 Facebook与Instagram携手并肩

图片来源&#xff1a;SaleSmartly官网 近年来网上购物剧增&#xff0c;电子商务越趋重要&#xff0c;Facebook 和Instragram乘势推出Facebook Shops&#xff0c;免费让零售商户在全球最多使用者的两个社交平台创建线上商户&#xff0c;展示产品和进行交易&#xff0c;助零售业走…

DataTable扩展 列转行方法(2*2矩阵转换)

源数据 如图所示 // <summary>/// DataTable扩展 列转行方法&#xff08;2*2矩阵转换&#xff09;/// </summary>/// <param name"dtSource">数据源</param>/// <param name"columnFilter">逗号分隔 如SDateTime,PM25,PM10…

SmokePing网络延迟和丢包监测工具

SmokePing是一种网络延迟和丢包监测工具&#xff0c;其监控原理如下&#xff1a; 监测目标选择&#xff1a;SmokePing通过配置文件&#xff08;Targets&#xff09;定义了要监测的目标&#xff0c;可以是主机、路由器、服务器或其他网络设备。每个目标都有一个唯一的名称和IP地…

9个实用的交互设计软件,Get更简单的原型制作方式!

好用的原型图软件不仅可以快速可视化产品经理的想法&#xff0c;提高沟通效率&#xff0c;还可以加快测试进度&#xff0c;打造更真实的用户体验。今天本文为大家整理了9个好用的原型图工具&#xff0c;一起来看看吧&#xff01; 1、即时设计 在设计场景中&#xff0c;即时设…

基于MyBatis注解的学生管理程序--mybatis注解开发的练手项目

基于MyBatis注解的学生管理程序 需求&#xff1a;完成基于MyBatis注解的学生管理程序&#xff0c;能够用MyBatis注解实现查询操作、实现修改操作、实现一对多查询 &#xff08;1&#xff09;MyBatis注解开发实现查询操作。根据表1和表2在数据库分别创建一个学生表tb_student和…

【CicadaPlayer】getPlayerBufferDuration分析

https://github.com/alibaba/CicadaPlayer/blob/release/0.4.4/mediaPlayer/SuperMediaPlayer.cpp核心关键函数int64_t SuperMediaPlayer::getPlayerBufferDuration(bool gotMax, bool internal)17个地方出现: getPlayerBufferDuration的durations 数组 分别 对音频、视频、字…

登录页面设计的7个小细节,帮你提升用户体验

移动 APP 登录页面的设计直接影响到用户体验&#xff0c;从而决定 APP 的成败。我们应该设计出令用户兴奋而不是沮丧的登录界面。下面就让我和你分享几个提升登录页面 UX 设计的技巧: 如果用户必须登录才能使用服务&#xff0c;那么需要仔细考虑登录表单。 在构建登录页面设计…

简单数学题:找出最大的可达成数字

来看一道简单的数学题&#xff1a;力扣2769. 找出最大的可达成数字 题目描述的花里胡哨&#xff0c;天花乱坠&#xff0c;但这道题目非常简单。我们最多执行t次操作&#xff0c;只需每次操作都让x-1&#xff0c;让num1&#xff0c;执行t次操作后&#xff0c;x就变为xt&#xff…

YAML基本介绍和使用语法

YAML详解及使用方法 一、基本介绍二、数据类型2.1 纯量(scalars)/标量2.1.1 字符串2.1.2 保留换行(Newlines preserved)2.1.3 布尔值&#xff08;Boolean)2.1.4 整数&#xff08;Integer&#xff09;2.1.5 浮点数&#xff08;Floating Point&#xff09;2.1.6 空&#xff08;Nu…

找到9个可以编辑的线框图模板资源,自取

大家好&#xff0c;我是设计师l1m0&#xff0c;最近找到了的9个还比较不错的线框图模板&#xff0c;想要分享给大家。顺便提一下这个免费的资源下载网站&#xff1a;Pixso资源社区。 相比成熟的设计师都知道线框图对产品设计来说扮演着什么角色&#xff0c;在这里不做过多赘述…

搬家小程序开发攻略

随着互联网的快速发展&#xff0c;小程序已成为各行各业实现线上服务的重要工具。特别是在搬家行业&#xff0c;小程序的应用能够让用户更加便捷地获取服务。本文将为您详细介绍如何使用第三方制作平台&#xff0c;如乔拓云网&#xff0c;轻松开发一款搬家小程序&#xff0c;实…