ROS参数服务器(Param):通信模型、Hello World与拓展

参数服务器在ROS中主要用于实现不同节点之间的数据共享。

参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据。

使用场景一般存储一些机器人的固有参数,如产品定义、全局配置等。

主要思想就是一个共享数据域,供不同节点使用。

一、参数服务器通讯模型

参数服务器模型涉及到三个角色:

  • Master (管理者)
  • Setter(设置者)
  • User(使用者)

Master 负责管理参数与 Setter/User 的操作,Setter 可以向 Master 设置参数,User 可以从 Master 获取参数。

这里只是方便说明,实际上通讯方操作参数前不会向 ROS Master 注册身份信息,所以对 ROS Master 而言,没有 SetterUser 之分,每个访问参数服务器的通讯方都是使用者。

在这里插入图片描述

通讯流程:

  • 1)Setter设置参数

Setter 通过 RPC 向参数服务器设置参数(包括参数名与参数值),ROS Master 将参数保存到参数列表中。

  • 2)User获取参数

User 通过 RPC 向参数服务器发送参数查找请求,请求中包含要查找的参数名。

  • 3)ROS Master返回参数信息

ROS Master 根据请求提供的参数名查找参数值,并将查询结果通过 RPC 发送给 User

参数服务器使用 XMLRPC 数据格式存储参数,支持的数据类型如下:

  • 32-bit integers
  • booleans
  • strings
  • doubles
  • iso8601 dates
  • lists
  • base64-encoded binary data

Note:

二、Param Hello World

万物始于Hello World,同样,使用Hello World介绍参数服务器的简单使用。

使用参数服务器,通讯方操作参数前没有向 ROS Master 注册身份信息,直接对参数进行操作。

接下来实现一个简单的参数操作,设置不同数据类型的参数,如机器人的名字(name)长(length)宽(width)高(height)等,并对其进行读取删除等操作。

2.1 创建并初始化功能包

(这一步不是必须,这里只是为了方便清晰的说明,也可以使用已有的包,在包里新增节点等方法)

首先创建 param_hello_world 包,命令如下:

catkin_creat_pkg param_hello_world roscpp rospy

创建后,文件结构如下:

在这里插入图片描述

2.2 操作参数(C++版)

ROSC++ 提供了两套 API,如下:

  • 通过 ros::NodeHandle 对象调用
  • 通过 ros::param 名空间调用

示例如下:

在创建的 param_hello_world 包路径下有一个 src 目录,在这里存储C++源码,我们创建 param_hello_world_set.cppparam_hello_world_get.cpp ,修改 CMakeLists.txt ,添加如下内容:

add_executable(${PROJECT_NAME}_set src/param_hello_world_set.cpp)
add_executable(${PROJECT_NAME}_get src/param_hello_world_get.cpp)target_link_libraries(${PROJECT_NAME}_set${catkin_LIBRARIES}
)target_link_libraries(${PROJECT_NAME}_get${catkin_LIBRARIES}
)

编辑 param_hello_world_set.cpp 内容如下:

#include <ros/ros.h>int main(int argc, char **argv)
{setlocale(LC_ALL, "");ros::init(argc, argv, "param_hello_world_set");ros::NodeHandle nh;std::cout << std::endl<< "********** ros::NodeHandle **********" << std::endl;{std::string name = "vbot";std::string geometry = "rectangle";double wheel_radius = 0.1;int wheel_num = 4;bool vision = true;std::vector<double> base_size = {0.7, 0.6, 0.3};std::map<std::string, int> sensor_id = {{"camera", 0}, {"laser", 2}};// 设置参数std::cout << "-- 设置参数 --" << std::endl;nh.setParam("name", "vbot");               // 字符串, char*nh.setParam("geometry", geometry);         // 字符串, stringnh.setParam("wheel_radius", wheel_radius); // doublenh.setParam("wheel_num", wheel_num);       // intnh.setParam("vision", vision);             // boolnh.setParam("base_size", base_size);       // vectornh.setParam("sensor_id", sensor_id);       // map// 验证是否设置成功system("rosparam get name");system("rosparam get geometry");system("rosparam get wheel_radius");system("rosparam get wheel_num");system("rosparam get vision");system("rosparam get base_size");system("rosparam get sensor_id");}std::cout << std::endl<< "********** ros::param **********" << std::endl;{std::string name = "vbot";std::string geometry = "rectangle";double wheel_radius = 0.1;int wheel_num = 4;bool vision = true;std::vector<double> base_size = {0.7, 0.6, 0.3};std::map<std::string, int> sensor_id = {{"camera", 0}, {"laser", 2}};// 设置参数std::cout << "-- 设置参数 --" << std::endl;ros::param::set("name_p", "vbot");               // 字符串, char*ros::param::set("geometry_p", geometry);         // 字符串, stringros::param::set("wheel_radius_p", wheel_radius); // doubleros::param::set("wheel_num_p", wheel_num);       // intros::param::set("vision_p", vision);             // boolros::param::set("base_size_p", base_size);       // vectorros::param::set("sensor_id_p", sensor_id);       // map// 验证是否设置成功system("rosparam get name_p");system("rosparam get geometry_p");system("rosparam get wheel_radius_p");system("rosparam get wheel_num_p");system("rosparam get vision_p");system("rosparam get base_size_p");system("rosparam get sensor_id_p");}return 0;
}

编译运行,结果如下:

在这里插入图片描述

编辑 param_hello_world_get.cpp 内容如下:

#include <ros/ros.h>int main(int argc, char **argv)
{setlocale(LC_ALL, "");ros::init(argc, argv, "param_hello_world_get");ros::NodeHandle nh;std::cout << std::endl<< "********** ros::NodeHandle **********" << std::endl;{// 修改参数std::cout << std::endl<< "-- 修改参数 --" << std::endl;nh.setParam("name", "mybot");        // 字符串, char*nh.setParam("geometry", "circular"); // 字符串, char*nh.setParam("wheel_radius", 0.15);   // doublenh.setParam("wheel_num", 2);         // intnh.setParam("vision", false);        // boolstd::vector<double> base_size = {0.2, 0.04};nh.setParam("base_size", base_size); // vectorstd::map<std::string, int> sensor_id = {{"camera", 0}, {"laser", 2}};sensor_id.insert({"ultrasonic", 5});ros::param::set("sensor_id", sensor_id); // map// 获取参数std::cout << std::endl<< "-- 获取参数 --" << std::endl;std::string name;std::string geometry;double wheel_radius;int wheel_num;bool vision;nh.getParam("name", name);nh.getParam("geometry", geometry);nh.getParam("wheel_radius", wheel_radius);nh.getParam("wheel_num", wheel_num);nh.getParam("vision", vision);nh.getParam("base_size", base_size);nh.getParam("sensor_id", sensor_id);ROS_INFO("ros::NodeHandle getParam, name: %s, geometry: %s, wheel_radius: %lf, wheel: %d, vision: %s, base_size: (%lf, %lf)",name.c_str(), geometry.c_str(), wheel_radius, wheel_num, vision ? "true" : "false",base_size[0], base_size[1]);for (auto sensor : sensor_id){ROS_INFO("ros::NodeHandle getParam, %s_id: %d", sensor.first.c_str(), sensor.second);}// 删除参数std::cout << std::endl<< "-- 删除参数 --" << std::endl;nh.deleteParam("vision");system("rosparam get vision");// 其他操作函数std::cout << std::endl<< "-- 其他操作函数 --" << std::endl;double wheel_radius1;wheel_radius1 = nh.param("wheel_radius", wheel_radius1);ROS_INFO("param, wheel_radius: %lf", wheel_radius1);nh.getParamCached("wheel_radius", wheel_radius1);std::vector<std::string> keys_v;nh.getParamNames(keys_v);for (auto key : keys_v){ROS_INFO("getParamNames, key: %s", key.c_str());}if (nh.hasParam("vision")){ROS_INFO("hasParam, 存在该参数");}else{ROS_INFO("hasParam, 不存在该参数");}std::string result;nh.searchParam("name", result);ROS_INFO("searchParam, result: %s", result.c_str());}std::cout << std::endl<< "********** ros::param **********" << std::endl;{// 修改参数std::cout << std::endl<< "-- 修改参数 --" << std::endl;ros::param::set("name_p", "mybot");        // 字符串, char*ros::param::set("geometry_p", "circular"); // 字符串, char*ros::param::set("wheel_radius_p", 0.15);   // doubleros::param::set("wheel_num_p", 2);         // intros::param::set("vision_p", false);        // boolstd::vector<double> base_size = {0.2, 0.04};ros::param::set("base_size_p", base_size); // vectorstd::map<std::string, int> sensor_id = {{"camera", 0}, {"laser", 2}};sensor_id.insert({"ultrasonic", 5});ros::param::set("sensor_id_p", sensor_id); // map// 获取参数std::cout << std::endl<< "-- 获取参数 --" << std::endl;std::string name;std::string geometry;double wheel_radius;int wheel_num;bool vision;ros::param::get("name_p", name);ros::param::get("geometry_p", geometry);ros::param::get("wheel_radius_p", wheel_radius);ros::param::get("wheel_num_p", wheel_num);ros::param::get("vision_p", vision);ros::param::get("base_size_p", base_size);ros::param::get("sensor_id_p", sensor_id);ROS_INFO("ros::param get, name: %s, geometry: %s, wheel_radius: %lf, wheel: %d, vision: %s, base_size: (%lf, %lf)",name.c_str(), geometry.c_str(), wheel_radius, wheel_num, vision ? "true" : "false",base_size[0], base_size[1]);for (auto sensor : sensor_id){ROS_INFO("ros::param getParam, %s_id: %d", sensor.first.c_str(), sensor.second);}// 删除参数std::cout << std::endl<< "-- 删除参数 --" << std::endl;ros::param::del("vision_p");system("rosparam get vision_p");// 其他操作函数std::cout << std::endl<< "-- 其他操作函数 --" << std::endl;double wheel_radius1;wheel_radius1 = ros::param::param("wheel_radius", wheel_radius1);ROS_INFO("param, wheel_radius: %lf", wheel_radius1);ros::param::getCached("wheel_radius", wheel_radius1);std::vector<std::string> keys_v;ros::param::getParamNames(keys_v);for (auto key : keys_v){ROS_INFO("getParamNames, key: %s", key.c_str());}if (ros::param::has("vision")){ROS_INFO("has, 存在该参数");}else{ROS_INFO("has, 不存在该参数");}std::string result;ros::param::search("name", result);ROS_INFO("search, result: %s", result.c_str());}return 0;
}

编译运行,结果如下:

在这里插入图片描述

2.3 其他操作参数的函数

除了上文提到的setParam()getParam()deleteParam() 函数,还有一些其他的参数操作函数,如下:

这里只以通过 ros::NodeHandle 对象调用为例,通过 ros::param 名空间调用类似,只多了一个 unsubscribeCachedParam函数,后面说明

1.param

获取 param_name 的值,如果 param_name 不存在,则返回 default_val

原型: T param(const std::string& param_name, const T& default_val) const

double wheel_radius2;
wheel_radius2 = nh.param("wheel_radius", wheel_radius2);
ROS_INFO("param, wheel_radius: %lf", wheel_radius2);

2.getParamCached()

getParam()使用方法一样。

首次调用会判断该参数是否获取过,如果获取过则从缓存读取,并向 Master 订阅该参数的变化,不再像getParam()一样通过 RPCMaster获取,以提高效率。

示例参考 getParam()

3.getParamNames()

获取所有设置到 Master 的参数的键,并通过 vector 返回。

原型:bool getParamNames(std::vector<std::string>& keys) const;

std::vector<std::string> keys_v;
nh.getParamNames(keys_v);
for (auto key : keys_v)
{ROS_INFO("getParamNames, key: %s", key.c_str());
}

4.hasParam()

判断是否存在该参数

原型:bool hasParam(const std::string& key) const;

if (nh.hasParam("vision"))
{ROS_INFO("存在该参数");
}
else
{ROS_INFO("不存在该参数");
}

5.searchParam()

搜索给定参数名,如果存在,返回键名,不存在返回空字符串。

原型:bool searchParam(const std::string& key, std::string& result) const;

std::string result;
nh.searchParam("name", result);
ROS_INFO("searchParam, result: %s", result.c_str());

6.unsubscribeCachedParam() (ros::param特有)

不明白该函数有什么具体作用,如果你知道欢迎交流(留言或加下方微信)。

没有找到官方说明,源码及注释如下:

头文件:param.h

在这里插入图片描述

源文件:param.cpp

在这里插入图片描述

直译注释为:取消订阅master中的缓存参数

猜测和 getCached() 有关, getCached() 会订阅参数变化,unsubscribeCachedParam则是取消订阅,但验证未生效:

// 设置参数
ros::param::set("wheel_radius", 0.15);// 首次调用getCached,这里会订阅"wheel_radius"的变化
double wheel_radius;
ros::param::getCached("wheel_radius", wheel_radius);
ROS_INFO("before unsubscribeCachedParam, wheel_radius: %lf", wheel_radius);// 调用unsubscribeCachedParam取消订阅
ros::param::unsubscribeCachedParam("wheel_radius");// 修改master中的"wheel_radius"值
// 由于已取消参数变化的订阅,此次变化不会同步到缓存
// 所以master中的值是0.5,而缓存中的值是0.15
ros::param::set("wheel_radius", 0.5);// 再次调用getCached,
// 理论上,再次调用getCached,会从缓存读取,此时缓存中的值是0.15
double wheel_radius1;
ros::param::getCached("wheel_radius", wheel_radius1);
ROS_INFO("after  unsubscribeCachedParam, wheel_radius1: %lf", wheel_radius1);

实际输出为:

before unsubscribeCachedParam, wheel_radius: 0.15
after  unsubscribeCachedParam, wheel_radius: 0.50

欢迎交流(留言或加下方微信)。

2.4 操作参数(Python版)

C++ 不同,ROS 只为 Python 提供了一套操作参数的 API

在创建的 param_hello_world 包路径下 src 目录的同级,创建一个 scripts 目录,在这里存储脚本(如python脚本),修改 CMakeLists.txt ,添加如下内容:

catkin_install_python(PROGRAMSscripts/param_hello_world_set.pyscripts/param_hello_world_get.pyDESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

scripts 中创建 param_hello_world_set.py 编辑内容如下:

import rospy
import osif __name__ == "__main__":rospy.init_node("param_hello_world_set")# 设置参数rospy.set_param("name", "vbot")                         # 字符串, stringrospy.set_param("geometry", "rectangle")                # 字符串, stringrospy.set_param("wheel_radius", 0.1)                    # doublerospy.set_param("wheel_num", 4)                         # introspy.set_param("vision", True)                         # boolrospy.set_param("base_size", [0.7, 0.6, 0.3])           # listrospy.set_param("sensor_id", {"camera": 0, "laser": 2}) # dictionary# 验证是否设置成功os.system("rosparam get name")os.system("rosparam get geometry")os.system("rosparam get wheel_radius")os.system("rosparam get wheel_num")os.system("rosparam get vision")os.system("rosparam get base_size")os.system("rosparam get sensor_id")

scripts 中创建 param_hello_world_get.py 编辑内容如下:

import rospyif __name__ == "__main__":rospy.init_node("param_hello_world_get")# 修改参数rospy.set_param("name", "mybot")             # 字符串, stringrospy.set_param("geometry", "circular")      # 字符串, stringrospy.set_param("wheel_radius", 0.15)        # doublerospy.set_param("wheel_num", 2)              # introspy.set_param("vision", False)             # boolrospy.set_param("base_size", [0.2, 0.04])    # listrospy.set_param("sensor_id", {"camera": 0, "laser": 2, "ultrasonic": 5}) # dictionary# 获取参数name = rospy.get_param("name")                    # 字符串, stringgeometry = rospy.get_param("geometry")            # 字符串, stringwheel_radius = rospy.get_param("wheel_radius")    # doublewheel_num = rospy.get_param("wheel_num")          # intvision = rospy.get_param("vision")                # boolbase_size = rospy.get_param("base_size")          # listsensor_id = rospy.get_param("sensor_id")          # dictionaryrospy.loginfo("get_param, name: {}, geometry: {}, wheel_radius: {}, wheel: {}, vision: {}, base_size: ({}, {})".format(name, geometry, wheel_radius, wheel_num, vision, base_size[0], base_size[1]))for key, value in sensor_id.items():rospy.loginfo("get_param, sensor: {}, id: {}".format(key, value))# 删除参数rospy.delete_param("vision")# 其他操作wheel_radius1 = rospy.get_param_cached("wheel_radius")keys = rospy.get_param_names()for key in keys:rospy.loginfo("get_param_names, key: {}".format(key))if rospy.has_param("vision"):rospy.loginfo("has_param, 存在该参数")else:rospy.loginfo("has_param, 不存在该参数")result = rospy.search_param("name")rospy.loginfo("search_param, result: {}".format(result))

编译执行结果如下:

在这里插入图片描述

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

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

相关文章

C语言实现冒泡排序(超详细)

排序算法 - 冒泡排序 什么是冒泡排序&#xff1f;冒泡排序有啥用呢&#xff1f;冒泡排序的实现代码讲解冒泡排序的总结 什么是冒泡排序&#xff1f; 冒泡排序是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序…

OpenHarmony源码下载

OpenHarmony源码下载 现在的 OpenHarmony 4.0 源码已经有了&#xff0c;在 https://gitee.com/openharmony 地址中&#xff0c;描述了源码获取的方式&#xff0c;但那是基于 ubuntu 或者说是 Linux 的下载方式。在 windows 平台下的下载方式没有做出介绍。 我自己尝试了 wind…

Python 爬虫入门

文章目录 Python 爬虫入门requests 库beautifulsoup4库函数findall()&#xff0c;find()函数get() 爬虫实例 1&#xff1a;抓小说爬虫实例 2&#xff1a;抓豆瓣 top 250 的电影信息后记 Python 爬虫入门 Python 的爬虫功能使得程序员可以快速抓取并分析网页中的信息&#xff0…

Spring Cloud学习(十)【Elasticsearch搜索功能 分布式搜索引擎02】

文章目录 DSL查询文档DSL查询分类全文检索查询精准查询地理坐标查询组合查询相关性算分Function Score Query复合查询 Boolean Query 搜索结果处理排序分页高亮 RestClient查询文档快速入门match查询精确查询复合查询排序、分页、高亮 黑马旅游案例 DSL查询文档 DSL查询分类 …

大数据HCIE成神之路之数学(2)——线性代数

线性代数 1.1 线性代数内容介绍1.1.1 线性代数介绍1.1.2 代码实现介绍 1.2 线性代数实现1.2.1 reshape运算1.2.2 转置实现1.2.3 矩阵乘法实现1.2.4 矩阵对应运算1.2.5 逆矩阵实现1.2.6 特征值与特征向量1.2.7 求行列式1.2.8 奇异值分解实现1.2.9 线性方程组求解 1.1 线性代数内…

谈谈如何写作(二)

序言 没有什么比一套好理论更有用了。——库尔特勒温 谈谈如何写作系列今天进入第二篇&#xff0c;第一篇请速戳&#xff1a;谈谈如何写作&#xff08;一&#xff09; 今天&#xff0c;博主从如何写报告讲起。 Q&#xff1a;如何写报告 如何写报告呢&#xff1f; 当每位盆友接到…

【华为HCIP | 华为数通工程师】刷题日记1116(一个字惨)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

阿里云ECS11月销量王 99元/年

这一波好像真没得说&#xff0c;老用户居然都有份&#xff0c;买来练习、测试冒似已经够了&#xff01; 阿里云ECS11月销量王 99元/年 2核2G 3M固定带宽不限流量&#xff0c;新老同享&#xff0c;新购、续费同价&#xff0c;开发必备&#xff01; 活动规则 云服务器ECS 云创季…

DevToys:开发者的多功能瑞士军刀,让编程更高效!

DevToys&#xff1a;开发者的多功能瑞士军刀&#xff0c;让编程更高效&#xff01; DevToys 是一款专为开发者设计的实用工具&#xff0c;它能够帮助用户完成日常的开发任务&#xff0c;如格式化 JSON、比较文本和测试正则表达式&#xff08;RegExp&#xff09;。它的优势在于…

OpenAI Assistants-API简明教程

OpenAI在11月6号的开发者大会上&#xff0c;除了公布了gpt4-v、gpt-4-turbo等新模型外&#xff0c;还有一个assistants-api&#xff0c;基于assistants-api开发者可以构建自己的AI助手&#xff0c;目前assistants-api有三类的工具可以用。首先就是之前大火的代码解释器(Code In…

leetcode刷题日志-68.文本左右对齐

给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff1b;也就是说&#xff0c;尽可能多地往每行中放置单词。必要时可…

『亚马逊云科技产品测评』活动征文|借助AWS EC2搭建服务器群组运维系统Zabbix+spug

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道。 本文基于以下软硬件工具&#xff1a; aws ec2 frp-0.52.3 zabbix 6…

【STM32】ADC(模拟/数字转换)

一、ADC的简介 1.什么是ADC 1&#xff09;将【电信号】-->【电压】-->【数字量】 2&#xff09;ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字量&#xff0c;建立模拟电路到数字电路的桥梁。 3&#xff09;12位逐次逼近型ADC&#xff0c;1us转换时间&#xf…

中间件安全:Apache 目录穿透.(CVE-2021-41773)

中间件安全&#xff1a;Apache 目录穿透.&#xff08;CVE-2021-41773&#xff09; Apache 的 2.4.49、2.4.50 版本 对路径规范化所做的更改中存在一个路径穿越漏洞&#xff0c;攻击者可利用该漏洞读取到Web目录外的其他文件&#xff0c;如系统配置文件、网站源码等&#xff0c…

Python---return返回值

return返回值 返回值&#xff1a;很多函数在执行完毕后&#xff0c;会通过return关键字返回一个结果给 调用它的位置。 return 英 /rɪˈtɜːn/ n. 回来&#xff0c;返回&#xff1b; 思考&#xff1a;如果一个函数需要两个return (如下所示)&#xff0c;程序如何执行&…

基于SSM的北海旅游网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

大型语言模型中的幻觉研究综述:原理、分类、挑战和未决问题11.15+11.16+11.17

大型语言模型中的幻觉研究综述&#xff1a;原理、分类、挑战和未决问题11.15 摘要1 引言2 定义2.1 LLM2.3 大语言模型中的幻觉 3 幻觉的原因3.1 数据的幻觉3.1.1 有缺陷的数据源3.1.2 较差的数据利用率3.1.3 摘要 3.2 来自训练的幻觉3.2.1训练前的幻觉3.2.2来自对齐的幻觉3.2.3…

git基本用法和操作

文章目录 创建版本库方式&#xff1a;Git常用操作命令&#xff1a;远程仓库相关命令分支(branch)操作相关命令版本(tag)操作相关命令子模块(submodule)相关操作命令忽略一些文件、文件夹不提交其他常用命令 创建版本库方式&#xff1a; 创建文件夹 在目录下 右键 Git Bush H…

北邮22级信通院数电:Verilog-FPGA(10)第十周实验 实现移位寄存器74LS595

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.代码部分 二.管脚分配 三.实现过程讲解及效…

SQL基础理论篇(七):多表关联的连接算法

文章目录 简介Nested LoopsMerge JoinHash Join总结参考文献 简介 多表之间基础的关联算法一共有三种&#xff1a; Hash JoinNested LoopsMerge Join 还有很多基于这三种基础算法的变体&#xff0c;以Nested Loops为例&#xff0c;就有用于in和exist的半连接&#xff08;Nes…