扩展【从0制作自己的ros导航小车】C++_ROS_QT5联合编译,简单界面为ROS开发增添交互

从0制作自己的ros导航小车

    • 前言
    • 一、环境搭建
    • 二、联合编译
    • 三、测试

前言

前面已经实现了导航功能,对于之后的一些开发,有交互能力是比较重要的,比如小车上连接一块屏幕,通过屏幕来选择模式,可视化等等。QT是不错的选择,但是需要做一些额外的工作,让QT与ROS能够建立联系,实现通信。本文基于linux操作系统搭建C++_ROS_QT5联合编译环境,让ros与qt数据互通起来,为之后的开发打下基础。
环境是直接使用ubuntu20.04安装之后默认的一些比如cmake等等,自己只需要再安装一下完整的QT5、Vscode即可。

一、环境搭建

一般在虚拟机的ubuntu里安装VScode进行开发,因为开发板端性能较差,所以本文都是在虚拟机操作。对于开发板的话,旭日x3派好像是没有预留触摸屏的接口,所以也只能在虚拟机里先玩着了,如果想放到板端运行可以去看正点原子的qt教程,需要交叉编译的。
①安装VScode
ubuntu下安装VScode

②安装QT5
ubuntu下安装QT5

③VScode扩展安装

switcher
include-autocomplete
vscode-switcher
gbktoutf8
vscode-typora
code-runner
todo-tree
cppsnippets
c-cpp-flylint
qt-support
vscode-clang
vscode-language-pack-zh-hans
vscode-dotnet-runtime
vscode-ros
debugpy
python
pylance
cmake-tools
cpptools
azurerm-vscode-tools
devicetree
tabnine-vscode
rainbow-bracket
qt-cpp-pack
qt-official
qtvsctools
cmake
qtconfigure
vscode-icons
material-theme

二、联合编译

1、创建自己的工程项目:
①终端输入:qtcreator
②创建新QT项目:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接下来全点下一步即可。
③新建文件夹qt_ros,将刚刚新建的qt工程中的四个文件复制过去:
在这里插入图片描述
新建文件夹qt_ros中新建build文件夹、CMakeLists.txt、rosNode.cpp、rosNode.h、style.qss,最后目录格式如下:
在这里插入图片描述

2、各部分代码及介绍:
①CMakeList.txt:
这是最重要的,联合编译靠的就是这里面的设置,大致就是添加QT和ROS的库并链接,实现联合编译。

#*************************
# 声明cmake最低的版本号
#************************
cmake_minimum_required(VERSION 3.16)#*************************
# 项目名称
#************************
project(qt_ros)#*************************
# 设置编译类型
#************************
# set(DEFAULT_BUILD_TYPE "Release")
if(NOT CMAKE_BUILD_TYPE)set(CMAKE_BUILD_TYPE "Release")
endif()#*************************
# 指定C++标准 
#************************
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 确保编译器支持所选标准#************************
# 设置优化等级
#************************
set(CMAKE_CXX_FLAGS "-w")# 忽略警告信息
set(CMAKE_CXX_FLAGS_DEBUG "-g -ggdb ${CMAKE_CXX_FLAGS}")# 设置DEBUG模式的编译选项
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -g -ggdb ${CMAKE_CXX_FLAGS}")# 设置RELEASE模式的编译选项#*************************
# 添加头文件
#************************
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})#************************
# 导入QT库
#************************
find_package(Qt5 COMPONENTS Widgets Core REQUIRED)find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgssensor_msgs)
include_directories(${catkin_INCLUDE_DIRS})#************************
# 添加源文件
#************************
file(GLOB QT_FORMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.ui)
file(GLOB QT_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qrc)
file(GLOB_RECURSE QT_MOC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS *.h)
set(QT_SOURCES mainwindow.cpp rosNode.cpp) #手动添加cpp文件
set(QSS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/style.qss) #手动添加qss文件#三个用于处理Qt特定资源和代码生成的命令
QT5_WRAP_UI(QT_FORMS_HPP ${QT_FORMS}) #从 .ui 文件自动生成用户界面类的头文件和源文件
QT5_ADD_RESOURCES(QT_RESOURCES_CPP ${QT_RESOURCES})#将 .qrc 资源文件转换成可编译的C++源文件
QT5_WRAP_CPP(QT_MOC_HPP ${QT_MOC}) #.h 文件(包含Qt对象的头文件)自动生成moc(元对象编译器)文件,这些文件是Qt信号和槽机制所必需的#*************************
# 生成动态库
#************************
add_library(${PROJECT_NAME} SHARED  ${QT_SOURCES} ${QT_RESOURCES_CPP} ${QT_FORMS_HPP} ${QT_MOC_HPP})
target_link_libraries(${PROJECT_NAME} Qt5::Widgets ${catkin_LIBRARIES})#*************************
# 生成可执行文件
#************************
add_executable(${PROJECT_NAME}_test main.cpp)target_link_libraries(${PROJECT_NAME}_test ${PROJECT_NAME} )

②main.cpp:
删除掉自带的构造函数,然后主函数里添加打开qss文件的代码。

#include "mainwindow.h"#include <QApplication>
#include <QFile>int main(int argc, char *argv[])
{QApplication a(argc, argv);QFile file("style.qss");//因为后续会用到qss样式表,所以这里要打开文件if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {QString style = QLatin1String(file.readAll());a.setStyleSheet(style);file.close();}MainWindow w(argc, argv);w.show();return a.exec();
}

③mainwindow.cpp:
在写这个cpp之前需要去ui界面添加几个pushbutton,vscode中打开.ui文件,在点击.ui文件时会显示使用qt designer打开,这时候右下角有东西弹出来,点进去,将qtcreator的路径填进去即可,路径查找使用此命令:which qtcreator
接下来拖5个pushbutton到ui界面中:
在这里插入图片描述
选中五个按钮,右键选择指定到按钮组,然后选择新建按钮组即可。
在这里插入图片描述
单独勾选每一个按钮的checkable:
在这里插入图片描述

#include "mainwindow.h"
#include "./ui_mainwindow.h"MainWindow::MainWindow(int argc,char **argv,QWidget *parent): QMainWindow(parent), ui_(new Ui::MainWindow), rosNode_(argc,argv)
{ui_->setupUi(this);//仪表盘,显示当前左右轮速、偏航角dial_left = new QDial(this);//堆区存储实例化的左轮仪表盘对象dial_left->setGeometry(330, 200, 200, 200);//设置位置与大小dial_left->setPageStep(10);dial_left->setNotchesVisible(true);dial_left->setNotchTarget(1.00);//设置步长dial_left->setRange(0,100);//设置范围dial_right = new QDial(this);dial_right->setGeometry(580, 200, 200, 200);dial_right->setPageStep(10);dial_right->setNotchesVisible(true);dial_right->setNotchTarget(1.00);dial_right->setRange(0,100);dial_imu = new QDial(this);dial_imu->setGeometry(500, 70, 100, 100);dial_imu->setPageStep(10);dial_imu->setNotchesVisible(true);dial_imu->setNotchTarget(1.00);dial_imu->setRange(0,360);//各仪表盘下的label,用于显示文字label_left = new QLabel(this);label_left->setGeometry(380, 400, 200, 50);label_left->setText("左轮速0cm/s");label_right = new QLabel(this);label_right->setGeometry(630, 400, 200, 50);label_right->setText("右轮速0cm/s");label_imu = new QLabel(this);label_imu->setGeometry(520, 170, 100, 30);label_imu->setText("yaw:无数据");connect(dial_left, SIGNAL(valueChanged(int)), this, SLOT(dialValueChanged_left(int)));//将仪表盘数据变换信号与槽函数进行绑定,来刷新label显示。connect(dial_right, SIGNAL(valueChanged(int)), this, SLOT(dialValueChanged_right(int)));connect(dial_imu, SIGNAL(valueChanged(int)), this, SLOT(dialValueChanged_imu(int)));QObject::connect(&rosNode_,&RosNode::signal_wheeldata,this,&MainWindow::updateSpeedDisplay);//ros中订阅的速度信息刷新之后会发布signal_wheeldata信号,这里与updateSpeedDisplay槽函数进行绑定,来刷新仪表盘的显示,这里仪表盘显示变换之后就会触发上面的label槽函数。QObject::connect(&rosNode_,&RosNode::signal_imu,this,&MainWindow::updateImuDisplay);//遥控,前后左右停止rosNode_.init();ros::NodeHandle nh_;pub_ctrl_car = nh_.advertise<geometry_msgs::Twist>("/car_keyboard/cmd_vel", 10);//发布小车控制指令的话题//下面是将按键翻转时产生的信号与槽函数绑定起来,这里多个信号绑定同一个槽函数,根据objectName方法,来判断此时是哪个按键翻转了。QObject::connect(ui_->pushButton,&QPushButton::toggled,this,&MainWindow::slot_ros_ctrl_car_goforward);QObject::connect(ui_->pushButton_2,&QPushButton::toggled,this,&MainWindow::slot_ros_ctrl_car_goforward);QObject::connect(ui_->pushButton_3,&QPushButton::toggled,this,&MainWindow::slot_ros_ctrl_car_goforward);QObject::connect(ui_->pushButton_4,&QPushButton::toggled,this,&MainWindow::slot_ros_ctrl_car_goforward);QObject::connect(ui_->pushButton_5,&QPushButton::toggled,this,&MainWindow::slot_ros_ctrl_car_goforward);
}MainWindow::~MainWindow()
{delete ui_;
}
//下面三个是仪表盘下面label的槽函数,当仪表盘数据变换时触发
void MainWindow::dialValueChanged_left(int val)
{/* QString::number()转换成字符串 */label_left->setText("左轮速" + QString::number(val) + "cm/s");
}
void MainWindow::dialValueChanged_right(int val)
{/* QString::number()转换成字符串 */label_right->setText("右轮速" + QString::number(val) + "cm/s");
}
void MainWindow::dialValueChanged_imu(int val)
{/* QString::number()转换成字符串 */label_imu->setText("yaw:" + QString::number(val) + " 度");
}
//下面两个是轮速、偏航角对应仪表盘的槽函数,触发条件上面初始化时说了
void MainWindow::updateSpeedDisplay(const QByteArray &data)
{// 确保数据长度至少为1字节int firstByte,secondByte,thirdByte;if (data.size() >= 1) {// 获取第一个字节,并将其转换为int类型if(data[0] <= 0){firstByte = static_cast<int>(static_cast<unsigned char>(0.0));}else{firstByte = static_cast<int>(static_cast<unsigned char>(data[0]));}if(data[1] <= 0){secondByte = static_cast<int>(static_cast<unsigned char>(0.0));}else{secondByte = static_cast<int>(static_cast<unsigned char>(data[1]));}// 这里可以添加逻辑来确保firstByte在期望的范围内,例如0到100//firstByte = qBound(0, firstByte, 100);//secondByte = qBound(0, secondByte, 100);// QDial控件来显示速度和角度dial_left->setValue(firstByte);dial_right->setValue(secondByte);} else {// 数据长度不足,无法获取第一个字节qDebug() << "数据长度不足,无法获取第一个字节";}
}void MainWindow::updateImuDisplay(const QString & msg)
{int value = msg.toInt();dial_imu->setValue(value);
}//这个是按键翻转时触发的槽函数
void MainWindow::slot_ros_ctrl_car_goforward(bool checked)
{// 获取触发信号的对象,向下转型为QPushButtongeometry_msgs::Twist pubmsg;QPushButton *button = qobject_cast<QPushButton *>(sender());if (button) {if(button->objectName() == "pushButton" && checked)pubmsg.linear.x = 10;if(button->objectName() == "pushButton_2" && checked)pubmsg.linear.x = 20;//左转if(button->objectName() == "pushButton_3" && checked)pubmsg.linear.x = 30;//右转if(button->objectName() == "pushButton_4" && checked)pubmsg.linear.x = 40;//后退if(button->objectName() == "pushButton_5" && checked)pubmsg.linear.x = 50;//停车// 打印按钮的toggle状态和objectNameqDebug() << "Button toggled:" << (checked ? "on" : "off");qDebug() << "Button objectName:" << button->objectName();pub_ctrl_car.publish(pubmsg);}
}

④mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QString>
#include <QDebug>
#include <QLabel>
#include <QRadioButton>
#include <QPainter>
#include <QPixmap>
#include <QComboBox>
#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QDateTimeEdit>
#include <QTimeEdit>
#include <QDateEdit>
#include <QDial>
#include <QLabel>
#include <QDataStream>
#include <QTimer>#include <ros/ros.h>
#include "rosNode.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(int argc,char **argv,QWidget *parent = nullptr);~MainWindow();ros::Publisher pub_ctrl_car;public slots://九的void dialValueChanged_left(int);void dialValueChanged_right(int);void dialValueChanged_imu(int);void updateSpeedDisplay(const QByteArray &data);void updateImuDisplay(const QString & msg);void slot_ros_ctrl_car_goforward(bool checked);private:Ui::MainWindow *ui_;RosNode rosNode_;QDial *dial_left;QDial *dial_right;QDial *dial_imu;QLabel *label_left;QLabel *label_right;QLabel *label_imu;
};#endif // MAINWINDOW_H

⑤rosNode.cpp:

#include "rosNode.h"
#include <QCoreApplication>RosNode::RosNode(int argc,char **argv):init_argc_(argc),init_argv_(argv)
{}RosNode::~RosNode()
{if(ros::isStarted()){ros::shutdown();ros::waitForShutdown();}
}
//ros的初始化函数,这是必要的
bool RosNode::init()
{ros::init(init_argc_, init_argv_, "qt_ui");if(!ros::master::check()){return false;}ros::start();ros::NodeHandle nh;// 订阅轮速话题sub_wheelspeed_msg = nh.subscribe("/wheel_speed", 10, &RosNode::callback_wheelspeed, this);start();return true;
}void RosNode::run()
{ros::start();ros::NodeHandle nh;ros::Rate rate(10);while(ros::ok()){QCoreApplication::processEvents();ros::spinOnce();rate.sleep();}Q_EMIT signal_rosShutdown();
}
//轮速、imu话题的回调函数,会发射signal_wheeldata、signal_imu信号,触发mainwindow.cpp的updateSpeedDisplay和updateImuDisplay
void RosNode::callback_wheelspeed(const geometry_msgs::TwistConstPtr& msg)
{//ROS_INFO("Received: [%.2f]", msg->angular.x);QByteArray data;data.resize(2);data[0] = static_cast<char>(msg->linear.x);data[1] = static_cast<char>(msg->linear.y); // 存储第二个字节double angular_x = msg->angular.x;QString msgStr = QString::number(angular_x); // 将 double 转换为 QStringQ_EMIT signal_wheeldata(data);Q_EMIT signal_imu(msgStr);
}

⑥rosNode.h:

#pragma once#ifndef Q_MOC_RUN
#include <ros/ros.h>
#endif#include <QThread>
#include <QString>#include <std_msgs/String.h>
#include "geometry_msgs/Twist.h"class RosNode:public QThread 
{Q_OBJECTpublic:RosNode(int argc,char **argv);virtual ~RosNode();bool init();void run() override;void callback_wheelspeed(const geometry_msgs::TwistConstPtr& msg);ros::Subscriber sub_wheelspeed_msg;Q_SIGNALS:void signal_rosShutdown();void signal_imu(const QString &);//显示左、右轮速度、imuvoid signal_wheeldata(const QByteArray &data);private:int init_argc_;char **init_argv_;
};

⑦style.qss:
这里用于设置五个按钮的样式。

QPushButton { background-color: #8f8888;border-radius: 30px }
QPushButton::checked { background-color: green; color:white }

上述代码全部填好之后,进入build目录:

cmake ..
make

然后就可以运行观察ui是否正确了:./qt_ros_test
没问题的话,ui界面如下,比较丑陋,对于排版和其它样式的需求,可以自己看QT文档:
在这里插入图片描述


三、测试

修改小车的uart.cpp,添加发布小车轮速和偏航角的话题;添加订阅qt发布的控制小车的话题:

pub = nh_.advertise<geometry_msgs::Twist>("/wheel_speed", 10);
subscription_ = nh_.subscribe("car_keyboard/cmd_vel", 10, &MyNode::cmdVelCallback, this);ros::Publisher pub;
ros::Subscriber subscription_;void cmdVelCallback(const geometry_msgs::Twist::ConstPtr& msg) {/*void cmdVelCallback(const geometry_msgs::msg::Twist::SharedPtr msg) {*/double linear_vel = msg->linear.x;double angular_vel = msg->angular.z;if (linear_vel == 10.0) {writeSpeed(15, 15, 0x01);writeSpeed(15, 15, 0x01);} else if (linear_vel == 20.0) {writeSpeed(10, 20, 0x01);writeSpeed(10, 20, 0x01);}else if (linear_vel == 30.0) {writeSpeed(20, 10, 0x01);writeSpeed(20, 10, 0x01);}else if (linear_vel == 40.0) {writeSpeed(-15,-15, 0x01);writeSpeed(-15,-15, 0x01);}else if (linear_vel == 50.0) {writeSpeed(0, 0, 0x01);writeSpeed(0, 0, 0x01);}ROS_INFO("keyboard_ctrl: linear=%.2f", linear_vel);}//在原有的基础上添加几行发布的即可
void MyNode::controlLoop() {double left_speed_now = 0.0;double right_speed_now = 0.0;double angle = 0.0;unsigned char testRece4 = 0x00;readSpeed(left_speed_now, right_speed_now, angle, testRece4);twist.linear.x = left_speed_now;twist.linear.y = right_speed_now;twist.angular.x = angle;pub.publish(twist);}

上面就是简单的固定速度,有兴趣的可以改一改,发布线速度和角速度然后进行解算,做更好看的界面等等。

最终效果如下视频所示,界面中还有一些东西是我自己加的练习,不用在意:


本文参考:
1、正点原子QT开发指导书
2、b站大佬的联合编译

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LVS是什么?以及LVS-NAT以及DR模式实验

目录 NAT LVS LVS集群的类型&#xff1a; LVS-NAT模式实验 环境准备&#xff1a; 实验步骤&#xff1a; LVS-DR模式实验 题目&#xff1a; 环境准备&#xff1a; 实验步骤&#xff1a; LVS-防火墙标签解决轮询调度问题 环境准备&#xff1a; 实验步骤&#xff1…

智界S7 小鹏P7 G3 G3i P5 G9 P7i G6 X9维修手册和电路图线路图接线资料更新

汽修帮手资料库提供各大厂家车型维修手册、电路图、新车特征、车身钣金维修数据、全车拆装、扭力、发动机大修、发动机正时、保养、电路图、针脚定义、模块传感器、保险丝盒图解对照表位置等&#xff0c;并长期保持高频率资料更新&#xff01; 覆盖车型2020-2024年智界S7 小鹏…

在VScode中导入conda环境的记录【原创】

今天在vscode编辑器中运行一个python代码&#xff0c;发现终端可以运行&#xff0c;但是编辑器中点击Run会显示缺包&#xff0c;但是python包明明是有的&#xff0c;在自己的conda环境中。后来发现&#xff0c;是vscode没有发现我自己创建的conda环境&#xff0c;在vscode中导入…

51单片机个人学习笔记16(红外遥控)

前言 本篇文章属于STC89C52单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 [1-1] 课程简介_哔哩…

Java封装原生ES

文章目录 &#x1f31e; Sun Frame&#xff1a;SpringBoot 的轻量级开发框架&#xff08;个人开源项目推荐&#xff09;&#x1f31f; 亮点功能&#x1f4e6; spring cloud模块概览常用工具 &#x1f517; 更多信息1.spring-data-es操作ES1.引入依赖2.application.yml配置uris3…

高频焊接设备配电系统无源滤波系统的设计

1、高频焊机系统谐波状况简介 变压器容量&#xff1a;S11-M-1600/10KVA&#xff08;105%&#xff09;/0.4KV 短路阻抗&#xff1a;3.9% 谐波负载情况&#xff1a;一台600KW高频焊接设备 型号&#xff1a;GGP600-0.3-HC 输入电压&#xff1a;380V 输出电压&#xff1a;0…

【Python机器学习】回归——示例:预测乐高玩具套装的价格

用回归法预测乐高套装价格的基本步骤&#xff1a; 1、收集数据&#xff1a;用Google Shopping的API收集到的数据 2、准备数据&#xff1a;从返回的JSON数据中抽取价格 3、分析算法&#xff1a;可视化并观察数据 4、训练算法&#xff1a;构建不同的模型&#xff0c;采用逐步线性…

操作ArkTS页面跳转及路由相关心得

本文为JS老狗原创。 当前端不得不关注的点&#xff1a;路由&#xff0c;今天聊一聊鸿蒙相关的一点心得。 总体上套路不意外&#xff0c;基本就是&#xff08;尤其是Web&#xff09;前端那些事&#xff1a;维护路由表、跳转带参数、历史堆栈操作&#xff0c;等等。 历史原因&…

设计模式20-备忘录模式

设计模式20-备忘录 动机定义与结构定义结构 C代码推导优缺点应用场景总结备忘录模式和序列化备忘录模式1. **动机**2. **实现方式**3. **应用场景**4. **优点**5. **缺点** 序列化1. **动机**2. **实现方式**3. **应用场景**4. **优点**5. **缺点** 对比总结 动机 在软件构建过…

云服务器和物理服务器的优缺点对比

云服务器优点在于灵活性强、成本效益高、易于扩展且支持全球化部署&#xff1b;缺点则包括安全性与可控性相对较弱&#xff0c;性能可能受限&#xff0c;以及存在服务中断风险。物理服务器则以其高性能、高稳定性、强安全性和完全可控性著称&#xff0c;但成本较高、扩展性受限…

鸿蒙OS ArkTS 省市县级联选择框,封装组件

背景&#xff1a; 公司现在要开发纯血鸿蒙版本APP&#xff0c;我被抽调过来做点功能。现在要做一个省市县级联选择框&#xff0c;并且要封装为组件&#xff0c;供其他页面模块使用。 效果图&#xff1a; 难点&#xff1a; 1. 现在官方文档上只是查到了TextPicker组件是可以做…

Vue3+setup使用vuemap/vue-amap实现地图相关操作

首先要下载依赖并且引入 npm安装 // 安装核心库 npm install vuemap/vue-amap --save// 安装loca库 npm install vuemap/vue-amap-loca --save// 安装扩展库 npm install vuemap/vue-amap-extra --save cdn <script src"https://cdn.jsdelivr.net/npm/vuemap/vue-a…

软件测试需要具备的基础知识【功能测试】---前端知识(三)

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 为了更好的学习软件测试的相关技能&#xff0c;需要具备一定的基础知识。需要学习的基础知识包括&#xff1a; 1、计算机基础 2、前端知识 3、后端知识 4、软件测试理论 后期分四篇文章进行编写&#xff0c;这是第二篇 …

使用es-hadoop同步hive和es之间数据

&#x1f4bb;近期在华为云连接es时的时候发现不能输入账号密码&#xff0c;后面联系华为工程师了解到&#xff0c;华为云默认是非安全模式&#xff0c;即不需要输入账号密码。 如果对你有所帮助&#xff0c;欢迎点赞收藏关注不迷路哦&#x1f493; 目录 使用es-hadoop同步h…

AI时代,我们还可以做什么?

最近看了本书&#xff0c;书名叫做《拐点&#xff1a;站在 AI 颠覆世界的前夜》&#xff0c;作者是万维钢。 本想着看完后&#xff0c;就能掌握一整套 AI 技巧&#xff0c;结果——竟然学了很多道理。 这本书讨论了以下话题&#xff1a; 我们该怎么理解这个 AI 大时代的哲学&am…

国产数据库备份恢复实现

数据库备份恢复是数据库高可用的基本能力&#xff0c;如何通过备份数据快速高效的恢复业务并且满足不同场景下的恢复需求&#xff0c;是各数据库厂商需要关注的要点。本文将介绍几种国产数据库的备份恢复功能&#xff0c;以加深了解。 1、数据库备份恢复方案 数据库备份是生产…

函数实例讲解(七)

文章目录 清洗数据的函数&#xff08;TRIM、CLEAN&#xff09;1、TRIM2、CLEAN3、CONCATENATE4、TEXTJOIN 函数综合练习COUNTIF Excel函数总结1、判断类2、求和类3、计数类4、求平均5、查找引用类6、求数据极值类7、四舍五入类8、提取类9、日期类10、文本处理类11、随机数12、排…

基于SpringBoot+Vue的校园失物招领系统(带1w+文档)

基于SpringBootVue的校园失物招领系统(带1w文档) 基于SpringBootVue的校园失物招领系统(带1w文档) 本课题研发的校园失物招领系统管理系统&#xff0c;就是提供校园失物招领系统信息处理的解决方案&#xff0c;它可以短时间处理完信息&#xff0c;并且这些信息都有专门的存储设…

MyBatis 基本操作 - 注解版

目录 一&#xff0c;查询 - select 1.1 全列查询 1.2 指定列查询 1.3 赋值问题 方法一&#xff1a;起别名 方法二&#xff1a;结果映射 方法三&#xff1a;添加配置 二&#xff0c;新增 - Insert 2.1 使用对象插入 2.2 获取主键 三&#xff0c;删除 - Delete 四&am…

nestjs 全栈进阶--文件上传

nest new upload -p pnpm pnpm i multer pnpm i -D types/multer 允许跨域 1. 单文件上传 我们去新增一个用于上传的handler Post(upload) UseInterceptors(FileInterceptor(file, {dest: uploads })) uploadFile(UploadedFile() file: Express.Multer.File, Body() body) {…