当前位置: 首页 > news >正文

SLAM从入门到精通(构建自己的slam包)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        我们学习了很多的开源包,比如hector、gmapping。但其实我们也可以自己编写一个slam包。这么做最大的好处,主要还是可以帮助自己更好地去了解slam、掌握slam以及用好slam。就像学习rtos一样,使用好别人提供的api是一回事,自己会写rtos又是另外一回事。一旦我们自己会写rtos之后,那么其他所有的实时操作系统都是很容易掌握的。slam也是一样。

        前面我们也知道,怎么构建一个slam包了?一般来说,它就是画图-》定位-》画图循环迭代的过程。今天可以做的简单一点。直接从cmd_vel-》laser-》画图,虽然内容简单了一点,但是效果出来的时候,还是很有成就感的。另外本文代码参考了现有的ros书籍,再次表示感谢。

1、编写slam_tfpub文件

        代码的主要功能就是接收到cmd_vel消息之后,将自己的tf信息发送出去。头文件slam_tfpub.h如下所示,

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <tf/transform_broadcaster.h>
#include <tf/tf.h>#define pi 3.1415926class TfMove
{public:TfMove(ros::NodeHandle& nh, ros::Rate& r);void VelCallback(const geometry_msgs::TwistPtr& vel);void init_sub();private:ros::NodeHandle& nh_;ros::Subscriber sub_;tf::TransformBroadcaster tfbrd_;ros::Rate rate;double x,y,z,roll,pit,yaw;
};

        而源文件slam_tfpub.cpp如下所示,

#include "slam_tfpub.h"TfMove::TfMove(ros::NodeHandle& nh, ros::Rate& r):nh_(nh), rate(r)
{x = 0;y = 0;z = 0;roll = 0;pit = 0;yaw = 0;init_sub();
}void TfMove::VelCallback(const geometry_msgs::TwistPtr& vel)
{x += vel->linear.x;y += vel->linear.y;z += vel->linear.z;roll += vel->angular.x/pi * 180;pit += vel->angular.y/pi * 180;yaw += vel->angular.z/pi * 180;tf::Transform trans;trans.setOrigin(tf::Vector3(x,y,z));tf::Quaternion q;q.setRPY(roll, pit, yaw);trans.setRotation(q);tfbrd_.sendTransform(tf::StampedTransform(trans, ros::Time::now(), "map", "base_link"));rate.sleep();
}void TfMove::init_sub()
{sub_ = nh_.subscribe("cmd_vel", 1, &TfMove::VelCallback, this);ros::spin();
}int main(int argc, char* argv[])
{ros::init(argc, argv, "myslam_tfpub");ros::NodeHandle nh;ros::Rate rate(10);TfMove tfmove(nh, rate);return 0;}

2、编写slam_laser文件

        slam_laser主要是模拟lidar的传感器数据。它的头文件slam_laser.h是这样的,

#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>class LaserScanPub
{public:LaserScanPub(ros::NodeHandle& nh, double minAngle, double maxAngle, double scanTime,double minRange, double maxRange, double scanNums);~LaserScanPub();void scanpub_init();void laserdata_init();private:ros::NodeHandle nh_;ros::Publisher scanpub_;sensor_msgs::LaserScan laserdata_;double minAngle;double maxAngle;double minRange;double maxRange;double scanTime;double scanNums;
};

        而源文件slam_laser.cpp是这样的,

#include "slam_laser.h"LaserScanPub::LaserScanPub(ros::NodeHandle& nh, double min_angle, double maxAngle,double scanTime, double minRange, double maxRange, double scanNums):nh_(nh),minAngle(minAngle), maxAngle(maxAngle),minRange(minRange),maxRange(maxRange), scanNums(scanNums), scanTime(scanTime)
{scanpub_init();
}LaserScanPub::~LaserScanPub()
{
}void LaserScanPub::laserdata_init()
{ros::Time scantime = ros::Time::now();laserdata_.header.stamp = scantime;laserdata_.header.frame_id = "base_link";laserdata_.range_min = minRange;laserdata_.range_max = maxRange;laserdata_.scan_time = scanTime;laserdata_.angle_increment = (maxAngle - minAngle)/scanNums; // angle resolutionlaserdata_.time_increment = scanTime/scanNums; // time resolutionlaserdata_.ranges.resize(scanNums);laserdata_.intensities.resize(scanNums);for(int i = 0; i < scanNums; i++){laserdata_.ranges[i] = 5;laserdata_.intensities[i] = 100;}
}void LaserScanPub::scanpub_init()
{scanpub_ = nh_.advertise<sensor_msgs::LaserScan>("scan", 100);ros::Rate rate(10);while(nh_.ok()){laserdata_init();scanpub_.publish(laserdata_);rate.sleep();}
}int main(int argc, char* argv[])
{ros::init(argc, argv, "myslam_laser");ros::NodeHandle nh;LaserScanPub scanpub(nh, 0, 1.57, 0.01, 0, 10, 100);return 0;
}

3、编写book_myslam文件

        前面准备好了tf和laser,接下来就是最终要的制图工作的。它的基本原理就是在laser触发回调的时候,利用tf信息,计算出lidar坐标在地图上的实际位置。中间绘制的方法使用到了bresenham算法,这个前面也提及过。book_myslam.h头文件是这样的,

#include <ros/ros.h>
#include <nav_msgs/OccupancyGrid.h>
#include <nav_msgs/MapMetaData.h>
#include <sensor_msgs/LaserScan.h>
#include <tf/transform_listener.h>#include <tf/tf.h>
#include <vector>
#include <fstream>
#include <math.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>using namespace std;struct MapPoint
{int x;int y;MapPoint(){x = 0;y = 0;}MapPoint(int x0, int y0){x = x0;y = y0;}
};class MySlam
{public:MySlam(ros::NodeHandle& nh, double mapreso, double mposx, double mposy,double mposz, double morientx, double morienty, double orientz,double morientw, int mwidth, int mheight);~MySlam();void mappub_init();void lasersub_init();void lasercallback(const sensor_msgs::LaserScanConstPtr& laserdata);void mapdata_init();vector<MapPoint> bresenham(int x0, int y0, int x1, int y1);private:ros::NodeHandle nh_;ros::Subscriber lasersub_;ros::Publisher mappub_;tf::TransformListener tflistener_;nav_msgs::OccupancyGrid mapdata_;double mapreso;double mposx;double mposy;double mposz;double morientx;double morienty;double morientz;double morientw;int mwidth;int mheight;vector<MapPoint> endpoints;MapPoint endpoint;vector<MapPoint> mappoints;tf::StampedTransform base2map;tf::Quaternion quat;double theta;tf::Vector3 trans_base2map;double tx, ty;int basex0, basey0;double basex, basey;double mapx, mapy;double beamsAngle;int mapxn, mapyn;int laserNum;int nx,ny;int idx;ofstream fopen;int scan_count;int scan_reso;boost::mutex map_mutex;	
};

        它的实现文件book_myslam.cpp是这样的,

#include "book_myslam.h"MySlam::MySlam(ros::NodeHandle& nh, double mapreso, double mposx, double mposy,double mposz, double morientx, double morienty, double morientz,double morientw, int mwidth, int mheight):nh_(nh), mapreso(mapreso),mposx(mposx), mposy(mposy), mposz(mposz), morientx(morientx),morienty(morienty), morientz(morientz), morientw(morientw),mwidth(mwidth), mheight(mheight)
{mapdata_init();mappub_init();lasersub_init();
}MySlam::~MySlam()
{
}void MySlam::lasercallback(const sensor_msgs::LaserScanConstPtr& laserdata)
{if(scan_count % scan_reso == 0){try {tflistener_.waitForTransform("map", "base_link", ros::Time(0), ros::Duration(3.0));tflistener_.lookupTransform("map", "base_link", ros::Time(0), base2map);}catch(tf::TransformException& ex){ROS_INFO("%s", ex.what());ros::Duration(1.0).sleep();}boost::mutex::scoped_lock map_lock(map_mutex);quat = base2map.getRotation();theta = quat.getAngle();trans_base2map = base2map.getOrigin();tx = trans_base2map.getX();ty = trans_base2map.getY();basex0 = int(tx/mapreso);basey0 = int(ty/mapreso);laserNum = laserdata->ranges.size();fopen.open("data.txt", ios::app);if(fopen.is_open()){cout << "open file successful!" << endl;}else{cout << "open file fail" << endl;}for(int i = 0; i < laserNum; i++){beamsAngle = laserdata->angle_min + i * laserdata->angle_increment;basex = laserdata->ranges[i] * cos(beamsAngle);basey = laserdata->ranges[i] * sin(beamsAngle);mapx = basex * cos(theta) + basey * sin(theta) + tx;mapy = basey * cos(theta) - basex * sin(theta) + ty;nx = int(mapx/mapreso);ny = int(mapy/mapreso);mapxn = nx + 1;mapyn = ny + 1;endpoint.x = mapxn;endpoint.y = mapyn;fopen << endpoint.x << " " << endpoint.y << std::endl;endpoints.push_back(endpoint);}fopen.close();for(vector<MapPoint>::iterator iter = endpoints.begin(); iter != endpoints.end(); iter++){mappoints = MySlam::bresenham(basex0, basey0, (*iter).x, (*iter).y);cout << "scan numbers: " << endpoints.size() << endl;cout << "bresenham point nums are: " << mappoints.size() << endl;cout << "x0, y0 is " << basex0 << " " << basey0 << std::endl;cout << "angle is " << theta << std::endl;for(vector<MapPoint>::iterator iter1 = mappoints.begin(); iter1 != mappoints.end(); iter1 ++){idx = mwidth * (*iter1).y + (*iter1).x;cout << "idx is " << (*iter1).x << " " << (*iter1).y << std::endl;mapdata_.data[idx] = 0;}mappoints.clear();}endpoints.clear();mappub_.publish(mapdata_);}scan_count ++;
}vector<MapPoint> MySlam::bresenham(int x0, int y0, int x1, int y1)
{vector<MapPoint> pp;MapPoint p;int dx, dy, h, a, b, x, y, flag, t;dx = abs(x1-x0);dy = abs(y1-y0);if(x1 > x0) a = 1; else a = -1;if(y1 > y0) b = 1; else b = -1;x = x0;y = y0;if(dx >= dy){flag = 0;}else{t = dx;dx = dy;dy = t;flag = 1;}h = 2 * dy - dx;for(int i = 1; i <= dx; ++i){p.x = x, p.y = y;pp.push_back(p);if(h >= 0){if(flag == 0) y = y+b;else x = x+a;h =h - 2*dx;}if(flag ==0) x = x+a;else y = y+b;h = h + 2*dy;}return pp;
}void MySlam::mappub_init()
{mappub_ = nh_.advertise<nav_msgs::OccupancyGrid>("map", 100);
}void MySlam::lasersub_init()
{lasersub_ = nh_.subscribe("scan", 1, &MySlam::lasercallback, this);
}void MySlam::mapdata_init()
{scan_count = 0;scan_reso = 1;ros::Time currtime = ros::Time::now();mapdata_.header.stamp = currtime;mapdata_.header.frame_id = "map";mapdata_.info.resolution = mapreso;mapdata_.info.width = mwidth;mapdata_.info.height = mheight;mapdata_.info.origin.position.x = mposx;mapdata_.info.origin.position.y = mposy;mapdata_.info.origin.position.z = mposz;mapdata_.info.origin.orientation.x = morientx;mapdata_.info.origin.orientation.y = morienty;mapdata_.info.origin.orientation.z = morientz;mapdata_.info.origin.orientation.w = morientw;int datasize = mwidth * mheight;mapdata_.data.resize(datasize);for(int i = 0;  i < datasize; i++){mapdata_.data[i] = -1;}
}int main(int argc, char* argv[])
{int debug_flag = 0;//while(debug_flag == 0) sleep(10);ros::init(argc, argv, "MySlam");ros::NodeHandle nh;double mapreso = 0.05;double mposx = 0;double mposy = 0;double mposz = 0;double morientx = 0;double morienty = 0;double morientz= 0;double morientw= 1;int mwidth = 300;int mheight = 300;MySlam myslam(nh, mapreso, mposx, mposy, mposz, morientx, morienty, morientz, morientw, mwidth, mheight);ros::spin();return 0;
}

4、准备编译脚本

        上面3个文件其实就是3个程序,所以我们在CMakeLists.txt里面做好三个程序的编译脚本就可以了。需要调试的话,可以添加上-Wall -g选项。

add_executable(slam_tfpub src/slam_tfpub.cpp)
target_link_libraries(slam_tfpub ${catkin_LIBRARIES})
add_dependencies(slam_tfpub beginner_tutorials_generate_messages_cpp)add_executable(slam_laser src/slam_laser.cpp)
target_link_libraries(slam_laser ${catkin_LIBRARIES})
add_dependencies(slam_laser beginner_tutorials_generate_messages_cpp)add_definitions("-Wall -g")add_executable(book_myslam src/book_myslam.cpp)
target_link_libraries(book_myslam ${catkin_LIBRARIES})
add_dependencies(book_myslam beginner_tutorials_generate_messages_cpp)

5、编译

        编译就很简单了,直接输入catkin_make即可。

6、构建launch文件

        因为启动的程序比较多,这里可以编写一个myslam.launch文件,使用起来方便一点。脚本文件注意放在launch目录下面。

<launch><node pkg="beginner_tutorials" type="slam_tfpub" name="tf_pub"/><node pkg="beginner_tutorials" type="slam_laser" name="laser_pub"/><node pkg="beginner_tutorials" type="book_myslam" name="myslam"/>
</launch>

7、实验步骤

        实验步骤稍微复杂一点,主要分成四步。第一,打开roscore;第二,用rostopic发送cmd_vel信息,

rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '[0.003, 0.0, 0.0]' '[0.0, 0.0, 0.0]'

        第三,启动myslam.launch文件,

roslaunch beginner_tutorials myslam.launch

        第四,就是输入rosrun rviz rviz命令,创建map,选中map之后进一步查看建图效果。这四个步骤需要严格按顺序执行,不然缺少了某个步骤,很有可能程序会发生闪退,主要是book_myslam这个程序。这样,不出意外的话,我们就可以在rviz上面看到这样的建图效果了,

相关文章:

SLAM从入门到精通(构建自己的slam包)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 我们学习了很多的开源包&#xff0c;比如hector、gmapping。但其实我们也可以自己编写一个slam包。这么做最大的好处&#xff0c;主要还是可以帮助…...

全球二氧化碳排放数据1deg产品(ODIAC)数据

简介 全球二氧化碳排放数据1deg产品(ODIAC)是一个空间分辨率为1deg*1deg的全球化石燃料燃烧产生的二氧化碳空间分布产品。它率先将基于空间的夜间灯光数据与单个发电厂的排放/位置相结合来估计化石燃料二氧化碳的排放。该产品被国际研究界广泛用于各种研究应用&#xff08;例如…...

Element-UI 日期选择器--禁用未来日期

在做项目的时候经常会遇到一些报表需要填写日期&#xff0c;一般是填写当日及当日以前&#xff0c;这时候我们的日期选择器就需要进行一些限制&#xff0c;比如&#xff1a; 这样之后&#xff0c;就不会误填写到明天啦&#xff0c;下面让我们看一下代码实现 html页面代码 这里…...

终端常用脚本命令

Mac编写shell脚本文件 Rvm切换Ruby Mac系统指定更新 Mac应用安装&#xff1a;允许任何来源 Mac终端常用命令与Vim常用命令 Mac退出VIM模式 git协议实现管理(三个步骤) GIT 常用命令 .gitignore git工具常用操作指令 prettier前端本地格式化工具 SourceTree撤销Commit提交 pod i…...

百度翻译很方便,几点注意事项

前几天修改资源&#xff0c;就想翻译一些字串。用了一下百度&#xff0c;还是很方便的。 昨天开通了开发者账号&#xff0c;试了一下批量翻译。也发现了一些问题&#xff1a; 有的语言不支持&#xff0c;如ben/tr/jav好像没有区分地区。也可能是我还不熟悉。使用太多会欠费。比…...

阿里云安装 redis

1、在opt目录下面安装redis https://download.redis.io/redis-stable.tar.gz redis的最新稳定版本。更多版本可见 redis cd /opt wget https://download.redis.io/redis-stable.tar.gz2、解压tar包&#xff0c;会生成redis-stable文件夹 tar -xzvf redis-stable.tar.gz3、安装…...

解释什么是异步非阻塞?

在IO和网络编程中&#xff0c;我们经常看到几个概念&#xff1a;同步、异步、阻塞、非阻塞。 同步和异步   同步和异步是针对应用程序和内核的交互而言的&#xff0c;同步指的是用户进程触发IO 操作并等待或者轮询的去查看IO 操作是否就绪&#xff0c;而异步是指用户进程触发…...

1024程序节特辑:一文读懂小程序支付流程

小程序支付流程 概述前置准备登录流程调用wx.login()向微信服务器发送请求 支付流程调用wx.requestPayment()部分后台处理逻辑支付功能要求 支付流程面试题 主页传送门&#xff1a;&#x1f4c0; 传送 概述 小程序支付是由微信支付推出的一种便捷支付方式&#xff0c;通过扫码…...

C- 使用原子变量实现信号量

信号量 信号量&#xff08;Semaphore&#xff09;是并发编程中的一个核心同步原语&#xff0c;它在多进程和多线程环境下被设计用来协调不同的执行单元&#xff0c;确保它们在对共享资源的访问上达到同步和互斥。信号量内部维护一个计数器&#xff0c;该计数器的初始值可以被视…...

Pytorch与Onnx的转换与推理

Open Neural Network Exchange&#xff08;ONNX&#xff0c;开放神经网络交换&#xff09;格式&#xff0c;是一个用于表示深度学习模型的标准&#xff0c;可使模型在不同框架之间进行转移。 一、pytorch模型保存/加载 有两种方式可用于保存/加载pytorch模型 1&#xff09;文件…...

Linux权限详解

文章目录 1. shell命令及运行原理2. Linux权限的概念&#xff08;1&#xff09;用户种类&#xff08;2&#xff09;切换用户&#xff08;3&#xff09;命令提权 3. Linux权限管理&#xff08;1&#xff09;文件访问者的分类&#xff08;人&#xff09;&#xff08;2&#xff09…...

基于react18+arco+zustand通用后台管理系统React18Admin

React-Arco-Admin轻量级后台管理系统解决方案 基于vite4构建react18后台项目ReactAdmin。使用了reactarco-designzustandbizcharts等技术架构非凡后台管理框架。支持 dark/light主题、i18n国际化、动态路由鉴权、3种经典布局、tabs路由标签 等功能。 技术框架 编辑器&#xff…...

BAT031:按列表名单将路径a下的文件夹批量剪切到路径b

引言&#xff1a;编写批处理程序&#xff0c;实现按列表名单将路径a下的文件夹批量剪切到路径b。 一、新建Windows批处理文件 参考博客&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/132137544 二、写入批处理代码 1.右键新建的批处理文件&#xff0c;点击…...

随机专享记录第一话 -- RustDesk的自我搭建和使用

1.介绍 RustDesk是继TeamView、向日葵等远程桌面软件后的新起之秀,最主要的是开源的可自己搭建中继服务。相比于公共服务器,连接一次等待的时间要多久,用过TeamView的都知道,而且还是免费的,不像某些远程搞各种个人证书,各种登录设备限制! 先看看软件图,这是待连接界…...

【数据库】拼接字段 使用别名

拼接字段 使用别名 e . g . e.g. e.g. Vendors 表包含供应商名和电话信息&#xff0c;name 和 mobile&#xff1b;需要输出这两个属性的值的组合作为供应商的基本信息组合。 SELECT concat(name, _, mobile) FROM Vendors; -- 语句通过 MySQL 环境下测试&#xff0c;其他 DBMS…...

Golang设计22种模式

什么是设计模式 设计模式是面向对象软件的设计经验,是通常设计问题的解决方案。每一种设计模式系统的命名、解释和评价了面向对象中一个重要的和重复出现的设计。 设计模式的分类 创建模式 - 用来帮助我们创建对象的 工厂模式 (Factory Pattern)抽象工厂模式 (Abstract F…...

MMKV(3)

使用时遇到的问题 在项目的构建配置文件&#xff08;如 Gradle 或 Maven&#xff09;中添加相应的依赖项。 MMKV 是一个键值存储库&#xff0c;它存储的是原始的字节数组数据。需要存储和检索复杂的对象或数据结构&#xff0c;需要自行进行序列化和反序列化操作。可以使用任何…...

vivado报错警告之[Vivado 12-1017] Problems encountered:

文章目录 方法一方法二方法三&#xff08;作者最终解决&#xff09; 我们对vivado 的程序进行综合(Run Synthesis)时&#xff0c;可能会出现[Vivado 12-1017] Problems encountered: 1. Failed to delete one or more files in run directory的一个警告信息&#xff0c;导致我们…...

基于springboot汽车租赁系统

功能如下图所示 摘要 Spring Boot汽车租赁系统的设计旨在满足不断增长的租车市场需求&#xff0c;并通过简化开发和部署流程来提供方便的租车解决方案。系统采用了现代化的架构&#xff0c;主要基于以下技术栈&#xff1a; Spring Boot&#xff1a;作为后端的核心框架&#xff…...

C++禁用赋值操作符

1.禁用赋值操作符 在C中&#xff0c;void operator(const ClassName&) delete; 是一种特殊的语法&#xff0c;用于明确地禁止赋值操作符&#xff08;assignment operator&#xff09;的默认实现或自定义实现。 这通常用于防止类的实例被意外赋值。通过明确地删除赋值操作…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...