二自由度机械臂软件系统(三)ros2_control硬件底层插件
- ros2_control实现了两个功能,一个是控制算法插件即控制的实现,另一个是底层插件即跟硬件通信的功能。
参考资料:https://zhuanlan.zhihu.com/p/682574842
1、创建功能包
ros2 pkg create --build-type ament_cmake robot_control_test
- 在src中创建功能包并编译
2、创建launch文件
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition
from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitutionfrom launch_ros.actions import Node
from launch_ros.substitutions import FindPackageSharedef generate_launch_description():# Declare arguments 1、声明参数declared_arguments = []declared_arguments.append(DeclareLaunchArgument("description_file",default_value="robottest.urdf.xacro", #这个名字是跟description中的xacro文件有关的description="URDF/XACRO description file with the robot.带有机器人的URDF/XACRO描述文件。",))declared_arguments.append(DeclareLaunchArgument("gui",default_value="true",description="Start Rviz2 and Joint State Publisher gui automatically \with this launch file.使用此启动文件自动启动Rviz2和Joint State Publisher gui。",))declared_arguments.append(DeclareLaunchArgument("prefix",default_value='""',description="Prefix of the joint names, useful for \multi-robot setup. If changed than also joint names in the controllers' configuration \have to be updated.\关节名称的前缀,用于多机器人设置。如果改变了,那么控制器配置中的联合名称也必须更新",))# Initialize Arguments 2、初始化参数description_file = LaunchConfiguration("description_file")gui = LaunchConfiguration("gui")prefix = LaunchConfiguration("prefix")# Get URDF via xacro 3、通过xacro给出urdfrobot_description_content = Command([PathJoinSubstitution([FindExecutable(name="xacro")])," ",PathJoinSubstitution([FindPackageShare("robot_test_control"), "urdf", description_file]),])robot_description = {"robot_description": robot_description_content}# rviz_config_file = PathJoinSubstitution(# [FindPackageShare("motor_test_control"), "rviz", "motorbot_view.rviz"]# )########################################################################################joint_state_publisher_node = Node(package="joint_state_publisher_gui",executable="joint_state_publisher_gui",condition=IfCondition(gui),)#发布关节数据信息robot_state_publisher_node = Node(package="robot_state_publisher",executable="robot_state_publisher",output="both",parameters=[robot_description],)#发布模型信息rviz_node = Node(package="rviz2",executable="rviz2",name="rviz2",output="log",# arguments=["-d", rviz_config_file],condition=IfCondition(gui),)#rviz节点nodes = [joint_state_publisher_node,robot_state_publisher_node,rviz_node,]return LaunchDescription(declared_arguments + nodes)
- 启动文件分成了两部分,参数的存储和节点生成。前面都是在准备所需的参数,后面设置节点,最终利用LaunchDescription生成了节点。
节点名 | 作用 |
---|---|
rviz_node | 运行rviz |
joint_state_publisher_node | 用一个ui获得控制命令,并发布到状态节点 |
robot_state_publisher_node | 主要用于发布机器人的urdf模型数据 |
3、机器人的xacro文件
(1)robottest.urdf.xacro
<?xml version="1.0"?>
<!-- Basic differential drive mobile base -->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="robotdrive_robot"><!-- 导入基本的几何形状 --><xacro:include filename="$(find robot_control_test)/urdf/robottest_description.urdf.xacro" /><!-- 导入 ros2_control 描述部分 --><xacro:include filename="$(find robot_control_test)/urdf/robottest.ros2_control.xacro" /></robot>
- 分别导入模型和控制器
(2)robottest.ros2_control.xacro
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro"><ros2_control name="test_control123" type="system"><hardware><plugin>robot_control_test/RobotTestSystemHardware</plugin></hardware><joint name="link1_joint"><command_interface name="position"/><state_interface name="position"/><state_interface name="velocity"/></joint><joint name="link2_joint"><command_interface name="position"/><state_interface name="position"/><state_interface name="velocity"/></joint></ros2_control></robot>
- 这是导入控制器的代码,注意在这里引入了底层控制器的插件。这个插件就是之后要写代码编译生成的动态库
4、底层硬件控制器代码
#include "motor_test_system.hpp"#include <chrono>
#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <vector>#include "hardware_interface/types/hardware_interface_type_values.hpp"
#include "rclcpp/rclcpp.hpp"
#include "utils_can.h"utils_can motor1(1);
utils_can motor2(2);namespace
{
constexpr auto DEFAULT_COMMAND_TOPIC = "motor_test_cmd";
constexpr auto DEFAULT_STATE_TOPIC = "motor_test_state";} // namespacenamespace motor_test_control
{
hardware_interface::CallbackReturn MotorTestSystemHardware::on_init(const hardware_interface::HardwareInfo & info)
{ std::cout << "初始化" << std::endl;//1、打开can盒int state = 0;state = VCI_OpenDevice(VCI_USBCAN2A,0,0);if(state==1)//打开设备{std::cout << "open deivce success!\n" <<std::endl;//打开设备成功}else if(state==-1){std::cout << "no can device!\n" <<std::endl;}else{printf(">>open deivce fail!\n");//这个就算成功打开,还是为0}//2、初始化两路can口//2.1 can1初始化VCI_INIT_CONFIG config_can;config_can.AccCode=0;config_can.AccMask=0xFFFFFFFF;config_can.Filter=1;//接收所有帧config_can.Mode=0;//正常模式config_can.Timing0=0x00;/*波特率1000 Kbps*/config_can.Timing1=0x14;std::cout <<"1000" <<std::endl;if(VCI_InitCAN(VCI_USBCAN2,0,0,&config_can)!=1){std::cout << ">>Init CAN fail" <<std::endl;VCI_CloseDevice(VCI_USBCAN2,0);}else{std::cout << ">>Init CAN success" <<std::endl;}if(VCI_StartCAN(VCI_USBCAN2,0,0)!=1){VCI_CloseDevice(VCI_USBCAN2,0);}else{std::cout << ">>start CAN success" <<std::endl;}motor1.CanInit();motor2.CanInit();if (hardware_interface::SystemInterface::on_init(info) !=hardware_interface::CallbackReturn::SUCCESS){return hardware_interface::CallbackReturn::ERROR;}if (info_.joints.size() != 2){RCLCPP_FATAL(rclcpp::get_logger("MotorTestSystemHardware"),"The number of crawlers is set not correactly,please check your urdf. 2 is expected,but now it is %zu.", info_.joints.size());return hardware_interface::CallbackReturn::ERROR;}else{RCLCPP_INFO(rclcpp::get_logger("MotorTestSystemHardware"), "Found 2 crawlers sucessfully!");}hw_positions_.resize(info_.joints.size(), std::numeric_limits<double>::quiet_NaN());hw_velocities_.resize(info_.joints.size(), std::numeric_limits<double>::quiet_NaN());hw_commands_.resize(info_.joints.size(), std::numeric_limits<double>::quiet_NaN());for (const hardware_interface::ComponentInfo & joint : info_.joints){// DiffBotSystem has exactly two states and one command interface on each jointif (joint.command_interfaces.size() != 1){RCLCPP_FATAL(rclcpp::get_logger("MotorTestSystemHardware"),"Joint '%s' has %zu command interfaces found. 1 expected.", joint.name.c_str(),joint.command_interfaces.size());return hardware_interface::CallbackReturn::ERROR;}if (joint.command_interfaces[0].name != hardware_interface::HW_IF_POSITION){RCLCPP_FATAL(rclcpp::get_logger("MotorTestSystemHardware"),"Joint '%s' have %s command interfaces found. '%s' expected.", joint.name.c_str(),joint.command_interfaces[0].name.c_str(), hardware_interface::HW_IF_POSITION);return hardware_interface::CallbackReturn::ERROR;}if (joint.state_interfaces.size() != 2){RCLCPP_FATAL(rclcpp::get_logger("MotorTestSystemHardware"),"Joint '%s' has %zu state interface. 2 expected.", joint.name.c_str(),joint.state_interfaces.size());return hardware_interface::CallbackReturn::ERROR;}if (joint.state_interfaces[0].name != hardware_interface::HW_IF_POSITION){RCLCPP_FATAL(rclcpp::get_logger("MotorTestSystemHardware"),"Joint '%s' have '%s' as first state interface. '%s' expected.", joint.name.c_str(),joint.state_interfaces[0].name.c_str(), hardware_interface::HW_IF_POSITION);return hardware_interface::CallbackReturn::ERROR;}if (joint.state_interfaces[1].name != hardware_interface::HW_IF_VELOCITY){RCLCPP_FATAL(rclcpp::get_logger("MotorTestSystemHardware"),"Joint '%s' have '%s' as second state interface. '%s' expected.", joint.name.c_str(),joint.state_interfaces[1].name.c_str(), hardware_interface::HW_IF_VELOCITY);return hardware_interface::CallbackReturn::ERROR;}}return hardware_interface::CallbackReturn::SUCCESS;
}std::vector<hardware_interface::StateInterface> MotorTestSystemHardware::export_state_interfaces()
{std::vector<hardware_interface::StateInterface> state_interfaces;for (auto i = 0u; i < info_.joints.size(); i++){state_interfaces.emplace_back(hardware_interface::StateInterface(info_.joints[i].name, hardware_interface::HW_IF_POSITION, &hw_positions_[i]));state_interfaces.emplace_back(hardware_interface::StateInterface(info_.joints[i].name, hardware_interface::HW_IF_VELOCITY, &hw_velocities_[i]));}return state_interfaces;
}std::vector<hardware_interface::CommandInterface> MotorTestSystemHardware::export_command_interfaces()
{std::vector<hardware_interface::CommandInterface> command_interfaces;for (auto i = 0u; i < info_.joints.size(); i++){command_interfaces.emplace_back(hardware_interface::CommandInterface(info_.joints[i].name, hardware_interface::HW_IF_POSITION, &hw_commands_[i]));}return command_interfaces;
}hardware_interface::CallbackReturn MotorTestSystemHardware::on_activate(const rclcpp_lifecycle::State & /*previous_state*/)
{std::cout << "激活" << std::endl;// BEGIN: This part here is for exemplary purposes - Please do not copy to your production codeRCLCPP_INFO(rclcpp::get_logger("MotorTestSystemHardware"), "Activating ...please wait...");// END: This part here is for exemplary purposes - Please do not copy to your production code// set some default valuesfor (auto i = 0u; i < hw_positions_.size(); i++){if (std::isnan(hw_positions_[i])){hw_positions_[i] = 0;hw_velocities_[i] = 0;hw_commands_[i] = 0;}}subscriber_is_active_ = true;this->node_ = std::make_shared<rclcpp::Node>("hardware_node");std_msgs::msg::Float64MultiArray empty_int16array;for(std::size_t i = 0; i < hw_positions_.size(); i++){empty_int16array.data.push_back(0.0);}received_fb_msg_ptr_.set(std::make_shared<std_msgs::msg::Float64MultiArray>(empty_int16array));fb_subscriber_ =this->node_->create_subscription<std_msgs::msg::Float64MultiArray>(DEFAULT_STATE_TOPIC, rclcpp::SystemDefaultsQoS(),[this](const std::shared_ptr<std_msgs::msg::Float64MultiArray> msg) -> void{if (!subscriber_is_active_){RCLCPP_WARN(this->node_->get_logger(), "Can't accept new commands. subscriber is inactive");return;}received_fb_msg_ptr_.set(std::move(msg));});//创建实时Publishercmd_publisher = this->node_->create_publisher<std_msgs::msg::Float64MultiArray>(DEFAULT_COMMAND_TOPIC, rclcpp::SystemDefaultsQoS());realtime_cmd_publisher_ = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Float64MultiArray>>(cmd_publisher);RCLCPP_INFO(rclcpp::get_logger("MotorTestSystemHardware"), "Successfully activated!");return hardware_interface::CallbackReturn::SUCCESS;
}hardware_interface::CallbackReturn MotorTestSystemHardware::on_deactivate(const rclcpp_lifecycle::State & /*previous_state*/)
{std::cout << "反激活" << std::endl;// BEGIN: This part here is for exemplary purposes - Please do not copy to your production codeRCLCPP_INFO(rclcpp::get_logger("MotorTestSystemHardware"), "Deactivating ...please wait...");subscriber_is_active_ = false;fb_subscriber_.reset();received_fb_msg_ptr_.set(nullptr);// END: This part here is for exemplary purposes - Please do not copy to your production codeRCLCPP_INFO(rclcpp::get_logger("MotorTestSystemHardware"), "Successfully deactivated!");return hardware_interface::CallbackReturn::SUCCESS;
}hardware_interface::return_type motor_test_control::MotorTestSystemHardware::read(const rclcpp::Time & /*time*/, const rclcpp::Duration & period)
{std::cout << "读" << std::endl;std::shared_ptr<std_msgs::msg::Float64MultiArray> fb_msg;received_fb_msg_ptr_.get(fb_msg);rclcpp::spin_some(this->node_);for (std::size_t i = 0; i < hw_positions_.size(); i++){// Update the joint status: this is a revolute joint without any limit.double pos_ = 0;int id_ = 0;motor1.receive_callback(pos_,id_);if(id_==1){std::cout << "id==1" << std::endl;
hw_positions_[0] = pos_;}else if(id_==2){std::cout << "id==2" << std::endl;
hw_positions_[1] = pos_;}else{std::cout << "else:" << id_<<std::endl;}if(i < hw_velocities_.size()){hw_velocities_[i] = fb_msg->data[i];// hw_positions_[i] = fb_msg->data[i];//+= period.seconds() * hw_velocities_[i];std::cout << "读:" << hw_positions_[i]<<std::endl;}}return hardware_interface::return_type::OK;
}hardware_interface::return_type motor_test_control::MotorTestSystemHardware::write(const rclcpp::Time & /*time*/, const rclcpp::Duration & /*period*/)
{std::cout << "写" << std::endl;if(realtime_cmd_publisher_->trylock()){auto & cmd_msg = realtime_cmd_publisher_->msg_;cmd_msg.data.resize(hw_commands_.size());for (auto i = 0u; i < hw_commands_.size(); i++){cmd_msg.data[i] = hw_commands_[i];std::cout << "写:" <<cmd_msg.data[i] <<std::endl;}motor1.send_callback(cmd_msg.data[0]/3.14*180.0);motor2.send_callback(cmd_msg.data[1]/3.14*180.0);realtime_cmd_publisher_->unlockAndPublish();}return hardware_interface::return_type::OK;
}} // namespace diff_test_control#include "pluginlib/class_list_macros.hpp"
PLUGINLIB_EXPORT_CLASS(motor_test_control::MotorTestSystemHardware, hardware_interface::SystemInterface)
相关文章:

二自由度机械臂软件系统(三)ros2_control硬件底层插件
ros2_control实现了两个功能,一个是控制算法插件即控制的实现,另一个是底层插件即跟硬件通信的功能。 参考资料:https://zhuanlan.zhihu.com/p/682574842 1、创建功能包 ros2 pkg create --build-type ament_cmake robot_control_test在sr…...

24.8.9.11数据结构|链栈和队列
链栈 1、理解 实际上是一个仅在表头进行操作的单链表,头指针指向栈顶结点或头结点,以下恋栈均指带头结点的链栈. 2、 基本操作 1、定义结构:节点含有数据域和指针域 2、初始化操作:建立一个带头结点的空栈 3、取栈顶元素操作:取出栈的栈顶元…...
StarSpider:一款高效的网络爬虫框架解析与实战
文章目录 引言官网链接StarSpider 原理简介基础使用1. 添加依赖2. 编写PageProcessor3. 启动爬虫 高级使用1. 分布式抓取2. 自定义下载器3. 深度定制 优点结语 引言 在大数据时代,数据成为了推动业务增长和创新的关键。网络爬虫作为数据获取的重要手段之一…...

LVS详细解析及其NAT模式与DR模式部署(理论+实验全方位指导)
目录 1. 集群 2. 分布式系统 3. 集群与分布式的比较 4.通俗的解释 集群 分布式系统 总结 LVS(Linux Virtual Server)简介 LVS专业术语 工作原理 LVS调度算法 静态调度算法 动态调度算法 ipvsadm脑图 NAT模式集群 LVS的配置 在LVS主机内打开…...
负载均衡相关概念介绍(一)
负载均衡(Load Balance)是集群技术的一种重要应用,旨在将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,从而提高系统的并发处理能力、增加吞吐量、加强网络处理能力,并提供故障转移以…...

二叉树详解(1)
文章目录 目录1. 树的概念及结构1.1 树的相关概念1.2 树的表示1.3 树在实际中的运用(表示文件系统的目录树结构) 2. 二叉树的概念及结构2.1 概念2.2 特殊的二叉树2.3 二叉树的存储结构 3. 二叉树的顺序结构及实现3.1 二叉树的顺序结构3.2 堆的概念及结构…...
Spring定时任务注解
Service EnableScheduling public class xxxServiceImpl implement xxxService{Scheduled(cron "0 15 11 * * ?") // 每天的11:15执行public void reportCurrentTime() {aaa();}Scheduled(cron "0 15 17 * * ?") // 每天的17:15执行public void report…...

数据结构-绪论
学习目标: 认识数据结构的基本内容 学习内容: 了解:数据结构的研究内容掌握:数据结构的基本概念和术语了解:数据元素间的结构关系掌握:算法及算法的描述 数据结构的发展: 数据结构的发展简史 …...

Web开发:web服务器-Nginx的基础介绍(含AI文稿)
目录 一、Nginx的功能: 二、正向代理和反向代理的区别 三、Nginx负载均衡的主要功能 四、nginx安装目录下的各个文件(夹)的作用: 五、常用命令 一、Nginx的功能: 1.反向代理:例如我有三台服务器&#x…...

共享经济背景下校园、办公闲置物品交易平台-计算机毕设Java|springboot实战项目
🍊作者:计算机毕设残哥 🍊简介:毕业后就一直专业从事计算机软件程序开发,至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长:按照需求定制化开发项目、 源…...
Linux 服务器上简单配置 minio
Linux 服务器上简单配置 minio 初始化结构目录 mkdir -p /data/minio/bin mkdir -p /data/minio/conf mkdir -p /data/minio/data 下载 minio cd /data/minio/bin curl -O https://dl.min.io/server/minio/release/linux-amd64/minio 添加执行权限 chmod x minio 创建配置文件…...
TypeScript 面试题汇总
引言 TypeScript 是一种由微软开发的开源、跨平台的编程语言,它是 JavaScript 的超集,为 JavaScript 添加了静态类型系统和其他高级功能。随着 TypeScript 在前端开发领域的广泛应用,掌握 TypeScript 已经成为很多开发者必备的技能之一。本文…...
杰卡德系数
杰卡德系数(Jaccard Index 或 Jaccard Similarity Coefficient) 杰卡德系数是一种用于衡量两个集合相似度的重要指标。 从数学定义上来看,如前面所述,杰卡德系数计算公式为: J ( A , B ) ∣ A ∩ B ∣ ∣ A ∪ B ∣…...

微服务实现-sleuth+zipkin分布式链路追踪和nacos配置中心
1. sleuthzipkin分布式链路追踪 在大型系统的微服务化构建中,一个系统被拆分成了许多微服务。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。 这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软…...
数学中常用的解题方法
文章目录 待定系数法应用示例1. 多项式除法2. 分式化简3. 数列通项公式 总结 递归数列特征方程特征根的求解通项公式的求解示例 错位相减,差分错位相减法差分的应用结合理解 韦达定理二项式定理二项式定理的通项公式二项式系数的性质应用示例 一元二次求解1. 因式分…...
pytorch 1 张量
张量 文章目录 张量torch.Tensor 的 主要属性torch.Tensor 的 其他常用属性和方法叶子张量(Leaf Tensors)定义叶子张量的约定深入理解示例代码总结 中间计算结果与 detach() 方法定义中间计算结果不是叶子节点使用 detach() 方法使中间结果成为叶子张量示…...

音视频开发继续学习
RGA模块 RGA模块定义 RGA模块是RV1126用于2D图像的裁剪、缩放、旋转、镜像、图片叠加等格式转换的模块。比方说:要把一个原分辨率1920 * 1080的视频压缩成1280 * 720的视频,此时就要用到RGA模块了。 RGA模块结构体定义 RGA区域属性结构体 imgType&am…...

【Datawhale X 魔搭 】AI夏令营第四期大模型方向,Task1:智能编程助手(持续更新)
在一个数据驱动的世界里,人工智能的未来应由每一个愿意学习和探索的人共同塑造和掌握。希望这里是你实现AI梦想的起点。 大模型小白入门:https://linklearner.com/activity/14/11/25 大模型开发工程师能力测试:https://linklearner.com/activ…...

如何判断监控设备是否支持语音对讲
目录 一、大华摄像机 二、海康摄像机 三、宇视摄像机 一、大华摄像机 注意:大华摄像机支持跨网语音对讲,即设备和服务器可以不在同一网络内,大华设备的语音通道填写:34020000001370000001 配置接入示例: 音频输入…...

Grafana+Influxdb(Prometheus)+Apache Jmeter搭建可视化性能测试监控平台
此性能测试监控平台,架构可以是: GrafanaInfluxdbJmeterGrafanaPrometheusJmeter Influxdb和Prometheus在这里都是时序性数据库 在测试环境中,压测数据对存储和持久化的要求不高,所以这里的组件可以都通过docker-compose.yml文件…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...