移动机器人激光SLAM导航(四):GMapping SLAM 篇

参考引用

  • GMapping ROS-Wiki
  • 从零开始搭二维激光SLAM
  • GMapping 漫谈
  • 小白学移动机器人
  • 机器人工匠阿杰
  • wpr_simulation

移动机器人激光SLAM导航(文章链接汇总)

1. GMapping

1.1 FastSLAM 问题分解

概率论相关公式

  • 1. 条件概率公式 p ( x , y ) = p ( x ∣ y ) p ( y ) = p ( y ∣ x ) p ( x ) p(x,y)=p(x|y)p(y)=p(y|x)p(x) p(x,y)=p(xy)p(y)=p(yx)p(x)
  • 2. 贝叶斯公式 p ( x ∣ y ) = p ( y ∣ x ) p ( x ) p ( y ) = η p ( y ∣ x ) p ( x ) , η 为归一化常数 p(x|y)=\frac{p(y|x)p(x)}{p(y)}=\eta p(y|x)p(x), \eta为归一化常数 p(xy)=p(y)p(yx)p(x)=ηp(yx)p(x),η为归一化常数
  • 3. 条件贝叶斯公式 p ( x ∣ y , z ) = p ( y ∣ x , z ) p ( x ∣ z ) p ( y ∣ z ) = η p ( y ∣ x , z ) p ( x ∣ z ) p(x|y,z)=\frac{p(y|x,z)p(x|z)}{p(y|z)}=\eta p(y|x,z)p(x|z) p(xy,z)=p(yz)p(yx,z)p(xz)=ηp(yx,z)p(xz)
  • 4. 条件联合概率公式 p ( x , y ∣ z ) = p ( x ∣ y , z ) p ( y ∣ z ) p(x,y|z)=p(x|y,z)p(y|z) p(x,yz)=p(xy,z)p(yz)
  • FastSLAM 算法采用 RBPF(Rao-Blackwellized Particle Filters) 方法,将 SLAM 分解成两个问题:机器人定位问题 & 已知机器人定位进行地图构建问题。在定位问题中,每一个粒子只需要表示一个可能的姿态,但 SLAM 中的姿态和地图都是变量,所以一个粒子需要同时保存所有历史时刻的机器人位姿和整个地图
    • p ( x 1 : t ∣ z 1 : t , u 1 : t − 1 ) p(x_{1:t}\mid z_{1:t},u_{1:t-1}) p(x1:tz1:t,u1:t1) :利用上一时刻里程计数据和当前时刻的传感器观测来估计机器人的轨迹/位姿(定位)
    • p ( m ∣ x 1 : t , z 1 : t ) p(m\mid x_{1:t},z_{1:t}) p(mx1:t,z1:t) :利用当前时刻的机器人的轨迹/位姿和传感器观测来估计环境地图(建图)
    • 通过上一个时刻的地图和运动模型预测当前的位姿,然后计算权重,即在当前位姿下得到当前观测的可能性,再进行重采样,进而更新粒子地图,如此往复

p ( x 1 : t , m ∣ z 1 : t , u 1 : t − 1 ) = p ( m ∣ x 1 : t , z 1 : t ) ⋅ p ( x 1 : t ∣ z 1 : t , u 1 : t − 1 ) . \begin{matrix}p(x_{1:t},m\mid z_{1:t},u_{1:t-1})=\\p(m\mid x_{1:t},z_{1:t})\cdot p(x_{1:t}\mid z_{1:t},u_{1:t-1}).\end{matrix} p(x1:t,mz1:t,u1:t1)=p(mx1:t,z1:t)p(x1:tz1:t,u1:t1).

1.2 GMapping 改善方案

1.2.1 改善提议分布,降低粒子数量

问题:建图对机器人位姿要求较高,对任意一个粒子,仅仅依靠运动模型采样(里程计)的结果构建地图,误差会非常大,通过运动模型得到的提议分布(proposal)太过于分散,与真实分布相差过大,则需要较多的粒子才能更好的表示机器人位姿的估计,这样将会造成内存使用与计算量急剧上升

  • 方案 1:采用极大似然估计

    • 里程计的概率模型比较平滑,范围较大,如果对整个范围采样将需要很多的粒子,如果能找到一个位姿优值,在其周围进行小范围采样,就可以降低粒子数量
    • 因此直接采用极大似然估计的方式,根据粒子的位姿预测分布与地图的匹配程度,通过扫描匹配找到粒子的最优位姿参数,就用该位姿参数直接当做新粒子的位姿
  • 方案 2:激光雷达的观测模型

    • 粒子滤波采用里程计运动模型作为提议分布,导致大部分粒子都偏离真实位置,而与运动模型相反,激光雷达的观测模型则可以给出一个相对集中的分布
    • 通过激光雷达最近一帧的观测(scan),把 proposal 分布限制在一个狭小的有效区域,然后再对 proposal 分布进行采样,这样可以用更少的粒子来覆盖机器人位姿的概率分布,从下图可以看到激光雷达观测模型方差较小
      在这里插入图片描述
  • 总结如下

    • 1、首先按照里程计运动模型给出预测,图 1 中所有粒子通过里程计运动模型预测出新的位置如图 2 所示
    • 2、以图 2 粒子的预测位置为初始值,对每一个粒子进行一次扫描匹配,通过扫描匹配得到该粒子的最优位姿如图 3,使得当前观测与该粒子所携带的地图最为贴合
    • 3、得到每个粒子的最优位姿后,再在每个粒子的附近撒播 k 个粒子,用高斯函数来模拟提议分布如图 4 所示,根据这 k 个粒子的里程计和观测模型来计算高斯分布均值和方差
    • 4、新的粒子从高斯分布中采样得到,对于每个粒子,还需要赋予一个权值,以供后续的重采样步骤使用
      在这里插入图片描述
1.2.2 限制重采样次数,缓解粒子耗散

问题:GMapping 采用粒子滤波算法对移动机器人轨迹进行估计,必然少不了粒子重采样过程,但随着重采样次数的增多,会出现所有粒子都从一个粒子复制而来,粒子的多样性会消失

  • 解决方案:自适应重采样
    • 重采样的目的是抛弃那些明显远离真实值的粒子,增强那些离真实值近的粒子
      • 如果所有的粒子都在真实值附近且分布均匀,那么就没有理由执行重采样
      • 在回环发生之前,即使有些粒子已经远离了真实值,但现有的观测不足以区分正确的粒子和错误的粒子,也没有理由执行重采样
      • 只有在回环发生之后,新的观测彻底拉开正确粒子与错误粒子权重差距,此时重采样才能有效
    • 根据所有粒子自身权重的离散程度(也就是权重方差)来决定是否进行粒子重采样的操作
      • Neff 越大,粒子的权重差距越小,当 Neff 小于某个阈值,说明粒子的权重差距较大,即粒子的分布与真实分布差距很大,在粒子层面表现为某些粒子离真实值很近,而很多粒子离真实值较远。这正是回环发生时经常出现的情况,此时就应该进行重采样,否则不进行采样

N e f f = 1 ∑ i = 1 N ( w ~ ( i ) ) 2 N_{\mathrm{eff}}=\frac1{\sum_{i=1}^N\left(\tilde{w}^{(i)}\right)^2} Neff=i=1N(w~(i))21

1.3 GMapping 解析

1.3.1 简介
  • GMapping 是一个基于 2D 激光雷达使用 RBPF 算法完成二维栅格地图构建的 SLAM 算法
    • 通过里程计数据获取粒子群的先验位姿,再通过雷达数据与地图的匹配程度对所有粒子进行打分,通过分数高的粒子群来近似机器人的真实位姿
    • 作为 ROS 节点称为 slam_gmapping
  • 硬件要求
    • 需要一个提供里程计数据并配备一个水平安装的、固定的 2D 激光雷达的移动机器人,slam_gmapping 节点将尝试将每次传入的扫描(scan)转化为 odom (里程计) tf 坐标系
  • 优缺点
    • 可实时构建室内环境地图,在小场景中计算量少且地图精度较高,对激光雷达扫描频率要求较低
    • 不适合大场景建图

    对于 200 x 200 米的范围,如果栅格分辨率是 5cm,每个栅格占用一个字节内存,那么每个粒子携带的地图都要 16M 的内存,如果是 100 粒子就是 1.6G 内存

1.3.2 话题节点
  • 订阅的话题(Topic)
    • /tf (tf/tfMessage):订阅激光雷达坐标系、基坐标系和里程计坐标系之间的变换
    • /scan (sensor_msgs/LaserScan):订阅 2D 激光雷达扫描数据
  • 发布的话题(Topic)
    • map_metadata (nav_msgs/MapMetaData):发布 Meta(元)地图数据
    • map (nav_msgs/OccupancyGrid):发布占据栅格地图数据
    • ~entropy (std_msgs/Float64):发布机器人定位的置信度(越大则定位错误的可能性越大)
  • Service
    • dynamic_map (nav_msgs/GetMap):获取地图数据
1.3.3 TF 变换

有关 TF 基础请查看往期博客:ROS学习5:ROS常用组件

  • 必要的 TF 变换

    • <scan frame> → base_link:激光雷达坐标系 与 基坐标系 之间的变换,一般由 robot_state_publisher 或 static_transform_publisher 发布
    • base_link → odom:基坐标系 与 里程计坐标系 之间的变换,一般由里程计节点发布
  • 发布的 TF 变换

    • map → odom:地图坐标系 与 里程计坐标系 之间的变换,估计机器人在地图 map 坐标系中的位姿

1.4 建图测试

本小节使用 移动机器人激光SLAM导航(二):运动控制与传感器篇 中安装的测试环境 wpr_simulation(GMapping 已在此环境中通过脚本安装)

  • 沿用 移动机器人激光SLAM导航(三):Hector SLAM 篇 中 2.4 小节创建的功能包 slam_pkg,在 slam_pkg 中的 launch 文件夹中新建 gmapping.launch 文件
    <launch><include file="$(find wpr_simulation)/launch/wpb_stage_robocup.launch" /><node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" /><node pkg="rviz" type="rviz" name="rviz" args="-d $(find slam_pkg)/rviz/slam.rviz" /><node pkg="wpr_simulation" type="keyboard_vel_ctrl" name="keyboard_vel_ctrl" />
    </launch>
    
  • 编译并启动 gmapping.launch 建图
    $ cd ~/wpr_ws
    $ catkin_make
    $ source devel/setup.bash
    $ roslaunch slam_pkg gmapping.launch
    

在这里插入图片描述

  • 机器人运动控制节点 keyboard_vel_ctrl
    #include <ros/ros.h>
    #include <geometry_msgs/Twist.h>
    #include <stdio.h>
    #include <termios.h>static float linear_vel = 0.1;
    static float angular_vel = 0.1;
    static int k_vel = 3;int GetCh() {static struct termios oldt, newt;tcgetattr(STDIN_FILENO, &oldt);newt = oldt;newt.c_lflag &= ~(ICANON);tcsetattr(STDIN_FILENO, TCSANOW, &newt);int c = getchar();tcsetattr(STDIN_FILENO, TCSANOW, &oldt);return c;
    }int main(int argc, char** argv) {ros::init(argc, argv, "keyboard_vel_cmd");printf("键盘控制 WPR 机器人: \n");printf("w - 向前加速 \n");printf("s - 向后加速 \n");printf("a - 向左加速 \n");printf("d - 向右加速 \n");printf("q - 左旋加速 \n");printf("e - 右旋加速 \n");printf("空格 - 刹车 \n");printf("x - 退出 \n");printf("------------- \n");ros::NodeHandle n;ros::Publisher cmd_vel_pub = n.advertise<geometry_msgs::Twist>("/cmd_vel", 10);geometry_msgs::Twist base_cmd;base_cmd.linear.x = 0;base_cmd.linear.y = 0;base_cmd.angular.z = 0;while(n.ok()) {int cKey = GetCh();if (cKey == 'w') {base_cmd.linear.x += linear_vel;if (base_cmd.linear.x > linear_vel*k_vel)base_cmd.linear.x = linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 's') {base_cmd.linear.x += -linear_vel;if (base_cmd.linear.x < -linear_vel*k_vel)base_cmd.linear.x = -linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'a') {base_cmd.linear.y += linear_vel;if (base_cmd.linear.y > linear_vel*k_vel)base_cmd.linear.y = linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'd') {base_cmd.linear.y += -linear_vel;if (base_cmd.linear.y < -linear_vel*k_vel)base_cmd.linear.y = -linear_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);}  else if (cKey == 'q') {base_cmd.angular.z += angular_vel;if (base_cmd.angular.z > angular_vel*k_vel)base_cmd.angular.z = angular_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'e') {base_cmd.angular.z += -angular_vel;if (base_cmd.angular.z < -angular_vel*k_vel)base_cmd.angular.z = -angular_vel*k_vel;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == ' ') {base_cmd.linear.x = 0;base_cmd.linear.y = 0;base_cmd.angular.z = 0;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);} else if (cKey == 'x') {base_cmd.linear.x = 0;base_cmd.linear.y = 0;base_cmd.angular.z = 0;cmd_vel_pub.publish(base_cmd);printf(" - linear.x= %.2f linear.y= %.2f angular.z= %.2f \n",base_cmd.linear.x,base_cmd.linear.y,base_cmd.angular.z);printf("退出! \n");return 0;} else {printf(" - 未定义指令 \n");}}return 0;
    }
    

1.5 建图参数设置 Parameters

在这里插入图片描述

在这里插入图片描述

<launch><include file="$(find wpr_simulation)/launch/wpb_stage_robocup.launch" /><node pkg="gmapping" type="slam_gmapping" name="slam_gmapping"><param name="maxUrange" value="3.0" /><param name="map_update_interval" value="0.5" /><param name="linearUpdate" value="0.1" /></node><node pkg="rviz" type="rviz" name="rviz" args="-d $(find slam_pkg)/rviz/slam.rviz" /><node pkg="wpr_simulation" type="keyboard_vel_ctrl" name="keyboard_vel_ctrl" />
</launch>

2. 地图的保存与加载

使用 map_server 软件包实现占据栅格地图的保存与加载

2.1 保存地图

  • GMapping 建图

    $ cd ~/wpr_ws
    $ source devel/setup.bash
    $ roslaunch slam_pkg gmapping.launch
    
  • 保存地图

    $ rosrun map_server map_saver -f mymap
    

    通过上述保存地图指令会在 /home 目录生成 mymap.pgm(地图图片)和 mymap.yaml(地图信息) 两个文件

  • mymap.yaml

    image: mymap.pgm  # 到包含占用数据的图像文件的路径;可以是绝对的,也可以是相对于 YAML 文件的位置
    resolution: 0.050000   # 地图分辨率,单位:米/像素
    origin: [-100.000000, -100.000000, 0.000000]  # 地图中左下方像素的二维姿态为 (x, y, 偏航),偏航为逆时针旋转(偏航 = 0表示无转动)
    negate: 0    # 白/黑的空闲/被占用 语义是否应该颠倒 (阈值的解释不受影响)
    occupied_thresh: 0.65  # 占据概率大于该阈值的像素被认为是完全占据的
    free_thresh: 0.196     # 占有率小于这个阈值的像素被认为是完全自由的
    

2.2 加载地图

  • 加载地图
    $ roscore
    
    $ cd ~
    $ rosrun map_server map_server mymap.yaml
    
    $ rosrun rviz rviz
    

在这里插入图片描述

3. 2D 激光雷达运动畸变去除

  • 详解2D激光雷达运动畸变去除

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

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

相关文章

C语言的基础学习

C语言的变量 ## C语言中的变量 在C语言中,变量是对程序中数据所占内存空间的一种抽象定义。定义变量时,用户定义变量的名、变量的类型,这些都是变量的操作属性。不仅可以通过变量名访问该变量,系统还通过该标识符确定变量在内存中的位置 [❷](https://www.dotcpp.com/cour…

将链表反转

反转链表在解决需要从尾节点开始遍历到头节点的地方很实用&#xff0c;是一种常用的解题技巧。在反转时&#xff0c;我们可以考虑从前向后反转和从后向前反转两种方式。 法一&#xff1a;递归 每次将链表的头节点的下一个节点作为新的头节点&#xff0c;然后对剩余部分调用递归…

深度学习入门笔记(五)前馈网络与反向传播

接着上一节,本节讲解模型自我学习的数学计算过程究竟是怎么样的。 5.1 前馈网络 一个最简单的前馈神经网络如图所示,对于每一个隐藏层,输入对应前一层每一个节点权重乘以节点输出值,输出则是经过激活函数(例如sigmoid函数)计算后的值。 在这样的网络中,输入的数据 x 经…

HSMES中的计划管理

MES&#xff08;生产制造执行系统&#xff09;&#xff0c;旨在帮助制造企业实现高质量和高效率生产&#xff0c;通过实时监控生产过程&#xff0c;智能的生产计划排程&#xff0c;灵活的工艺设置&#xff0c;进而实现企业的数字化生产。对于生产计划的管控是其中不可或缺的一环…

jQuery 获取并设置 CSS 类 —— W3school 详解 简单易懂(十五)

通过 jQuery&#xff0c;可以很容易地对 CSS 元素进行操作。 jQuery 操作 CSS jQuery 拥有若干进行 CSS 操作的方法。我们将学习下面这些&#xff1a; addClass() - 向被选元素添加一个或多个类removeClass() - 从被选元素删除一个或多个类toggleClass() - 对被选元素进行添…

Python 抽象类

在Python中,抽象类是一种特殊的类,不能直接实例化,而是被用作其他类的基类。它定义了一组方法的接口,但没有具体的实现。子类必须实现这些方法才能实例化。 要创建一个抽象类,您需要使用abc模块中的ABC(Abstract Base Class)类,并通过将metaclass设置为ABCMeta来指定它…

小白水平理解面试经典题目_数组类Leetcode 412. Fizz Buzz【数学解法】

412 FizzBuzz 小白渣翻译&#xff1a; 给定一个整数 n &#xff0c;返回一个字符串数组 answer &#xff08;从 1 开始索引&#xff09;&#xff0c;其中&#xff1a; answer[i] “FizzBuzz” 如果 i 能被 3 和 5 整除。answer[i] “Fizz” 如果 i 能被 3 整除。answer[i]…

8. 实现VLAN间的通信

文章目录 一. 初识VLAN通信1.1. VLAN的概述1.2. Dot1q 终结子接口(单臂路由 )1.3. VLANIF接口 二. 实验专题2.1. 实验1&#xff1a; Dotlq 终结子接口2.1.1. 实验目的2.1.2. 实验拓扑图2.1.3. 实验步骤&#xff08;1&#xff09;配置PC机的网络&#xff08;2&#xff09;交换机…

八、访存顺序(Memory Ordering)

前言 这部分的内容比较抽象&#xff0c;很多内容我无法理解&#xff0c;都是直接翻译过来的。虽然难&#xff0c;但是不可不看&#xff0c;如果遇到无法理解的都直接跳过&#xff0c;那后面都无法学习下去了。觉得无法理解是因为目前的知识还很欠缺&#xff0c;到后面具备了这…

React16源码: React中event事件触发的源码实现

event 事件触发过程 1 &#xff09;概述 在之前事件绑定时&#xff0c;绑定的是两个方法 一个是 dispatchInteractiveEvent另外一个 dispatchEvent 其实它们调用的方法都是差不多的&#xff0c;一开始会有一点小的区别 2 &#xff09;源码 定位到 packages/react-dom/src/ev…

MySQL 函数参考手册(MySQL 高级函数)

目录 MySQL BIN() 函数 MySQL BINARY 函数 MySQL CASE 函数 MySQL CAST() 函数 MySQL COALESCE() 函数 MySQL CONNECTION_ID() 函数 MySQL CONV() 函数 MySQL CONVERT() 函数 MySQL CURRENT_USER() 函数 MySQL DATABASE() 函数 MySQL IF() 函数 MySQL IFNULL() 函数…

在PostgreSQL中不开归档?恭喜你!锅你背定了

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

k8s中deployment模板

一、清单文件 apiVersion: apps/v1 kind: Deployment metadata:annotations:version: "1.1.6"labels:k8s-app: deployment-nginxname: deployment-nginxnamespace: test spec:minReadySeconds: 30progressDeadlineSeconds: 600replicas: 2revisionHistoryLimit: 5sel…

智能汽车竞赛摄像头处理——摄像头入门(2)

前言 在上一节中&#xff0c;我们学习了如何将总钻风摄像头的图像显示在1.8寸TFT显示屏上&#xff0c;其实我建议大家显示屏还是要选用ips200&#xff0c;像素点多一些&#xff0c;显示的图像更加清晰。 二值化处理 &#xff08;1&#xff09;对原始的灰度图像进行二值化&am…

【操作宝典】IntelliJ IDEA新建maven项目详细教程

目录 &#x1f33c;1. 配置maven环境 &#x1f33c;2. 创建maven项目 &#x1f33c;3. 创建maven项目完整示例 a. 导入spring boot环境 b. 修改maven配置 c. 下载jar包 d. 创建Java类 &#x1f33c;1. 配置maven环境 【安装指南】maven下载、安装与配置详细教程-CSDN博客…

反物质(anti matter)和湮灭反应(Annihilation)浅读

反物质 反物质是正常物质的反状态。当正反物质相遇时&#xff0c;双方就会相互湮灭抵消&#xff0c;发生爆炸并产生巨大能量。 概念 正电子、负质子都是反粒子&#xff0c;它们跟通常所说的电子、质子相比较&#xff0c;电量相等但电性相反。科学家设想在宇宙中可能存在完全由…

【GitHub项目推荐--一个 C++ 实现快速存储的库】【转载】

一个提供可嵌入、持久键值存储以实现快速存储的库。 github地址 https://github.com/facebook/rocksdb 国内镜像 http://www.gitpp.com/ag/rocksdb RocksDB 是一个开源的嵌入式键值存储库&#xff0c;由 Facebook 开发&#xff0c;用于处理大量的数据&#xff0c;特别适合于…

【Eclipse插件开发】2运行时环境Runtime总览-【中篇】

系列文章目录 文章目录 系列文章目录三、并发基础架构并发基础运行作业Job常用作业操作作业的状态作业改变监听器作业管理器工作族关闭前完成作业3.1 报告进度进度报告简述进度监视器和UI系统作业用户作业进度组3.2 作业调度

sql注入绕过原理

了解SQL注入攻击的绕过技术对于加强网络安全具有重要意义。以下是对常见的绕过技术的整理,以及如何防御这些攻击策略。 1. 大小写绕过 原理:如果过滤机制仅识别特定大小写的关键字,攻击者可以通过改变关键字的大小写来尝试绕过过滤。 实验示例: SELECT * FROM users oR…

2024牛客寒假算法基础集训营1

H&#xff1a;01背包&#xff0c;但是bit 这题一看数据范围很大&#xff0c;重量和价值都是1e8级别的&#xff0c;当时还在想是不是背包&#xff0c;原来就是位运算 具体来说&#xff0c;我们枚举m的每一位为1的1&#xff0c;强制这一位为0&#xff0c;这样m被分为前后两部分…