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

ros2_control 6 自由度机械臂

系列文章目录


前言

        ros2_control 是一个实时控制框架,专为普通机器人应用而设计。标准的 c++ 接口用于与硬件交互和查询用户定义的控制器命令。这些接口增强了代码的模块化和与机器人无关的设计。具体的应用细节,例如使用什么控制器、机器人有多少个关节以及它们的运动学结构,则通过 YAML 参数配置文件和通用机器人描述文件(URDF)来指定。最后,通过 ROS 2 启动文件部署 ros2_control 框架。

        本教程将详细介绍 ros2_control 的各个组成部分,即

  1. ros2_control 概述
  2. 编写 URDF
  3. 编写硬件接口
  4. 编写控制器

一、ros2_control 概述

        ros2_control 引入了状态接口(state_interfaces)和命令接口(command_interfaces)来抽象硬件接口。state_interfaces 是只读数据句柄,通常代表传感器读数,如关节编码器。command_interfaces 是读写数据句柄,代表硬件命令,如设置关节速度参考。command_interfaces 是专用访问接口,也就是说,如果控制器 "认领 "了某个接口,那么在该接口被释放之前,任何其他控制器都不能使用它。这两种接口类型都有唯一的名称和类型。所有可用状态和命令接口的名称和类型都在 YAML 配置文件和 URDF 文件中指定。

        ros2_control 提供了控制器接口(ControllerInterface)和硬件接口(HardwareInterface)类,以实现与机器人无关的控制。在初始化过程中,控制器通过 ControllerInterface 申请运行所需的状态接口和命令接口。另一方面,硬件驱动程序通过硬件接口(HardwareInterface)提供状态接口和命令接口。ros2_control 确保在启动控制器之前,所有请求的接口都可用。接口模式允许供应商编写运行时加载的特定硬件驱动程序。

        主程序是一个实时读、更新、写循环。在读取调用期间,符合 HardwareInterface 的硬件驱动程序会用从硬件接收到的最新值更新其提供的状态_接口。在更新调用期间,控制器根据更新后的状态接口计算命令,并将其写入命令接口。最后,在写入调用期间,硬件驱动程序从其提供的 command_interfaces 中读取值,并将其发送给硬件。ros2_control 节点通过一个实时线程运行主循环。ros2_control 节点运行第二个非实时线程,与 ROS 发布者、订阅者和服务进行交互。

二、编写 URDF

        URDF 文件是一种基于 XML 的标准文件,用于描述机器人的特征。它可以表示任何具有树形结构的机器人,但具有循环结构的机器人除外。每个链接必须只有一个父链接。对于 ros2_control,有三个主要标签:link、joint 和 ros2_control。关节标签定义了机器人的运动学结构,而链接标签则定义了动态属性和 3D 几何结构。ros2_control 则定义硬件和控制器配置。

2.1 几何结构

        大多数商用机器人已经定义了机器人描述包(robot_description packages),请参阅通用机器人(Universal Robots)。不过,本教程将详细介绍如何从头开始创建一个机器人描述包。

        首先,我们需要一个机器人的 3D 模型。为了便于说明,我们将使用一个通用的 6 DOF 机器人操作器。

通用 6 DOF 机器人操作器

         机器人的 6 个刚体需要分别处理并导出为各自的 .stl 和 .dae 文件。一般来说,.stl 3D 模型文件是用于快速碰撞检查的粗网格,而 .dae 文件仅用于可视化目的。为简单起见,我们将使用相同的网格。

        按照惯例,每个 .stl 文件都在自己的坐标系中表达其顶点的位置。因此,我们需要指定每个刚体之间的线性变换(旋转和平移),以定义机器人的完整几何体。每个刚体的三维模型都应调整为近端关节轴(连接刚体与其父体的轴)位于 Z 轴方向。三维模型的原点也应调整为网格底面与 xy 平面共面。下面的网格说明了这种配置。

刚体 1
刚体 2 已对齐

        每个网格都应在处理后导出到各自的文件中。Blender 是一款开源三维建模软件,可以导入/导出 .stl 和 .dae 文件并操作其顶点。本教程使用 Blender 处理机器人模型。

        最后,我们可以计算机器人关节之间的变换,并开始编写 URDF。首先,对其坐标系中的刚体 2 应用负 90 度翻滚。

刚体 2 带 -90 度滚动

         为了使示例简单明了,我们现在不应用俯仰角。然后,我们施加 90 度的正偏航。

刚体 2,-90 度滚动和 90 度偏航

        最后,我们在刚体 2 和刚体 1 的坐标系帧之间进行 x 轴-0.1 米和 z 轴 0.18 米的平移。最终结果如下所示。

刚体 2 带有-90 度滚动、90 度偏航和平移功能

        然后对所有刚体重复上述过程。

2.2 URDF 文件

        URDF 文件一般按照以下模板格式化。

<robot name="robot_6_dof"><!-- create link fixed to the "world" --><link name="base_link"><visual><origin rpy="0 0 0" xyz="0 0 0"/><geometry><mesh filename="package://robot_6_dof/meshes/visual/link_0.dae"/></geometry></visual><collision><origin rpy="0 0 0" xyz="0 0 0"/><geometry><mesh filename="package://robot_6_dof/meshes/collision/link_0.stl"/></geometry></collision><inertial><mass value="1"/><inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/></inertial></link><!-- additional links ... --><link name="world"/><link name="tool0"/><joint name="base_joint" type="fixed"><parent link="world"/><child link="base_link"/><origin rpy="0 0 0" xyz="0 0 0"/><axis xyz="0 0 1"/></joint><!-- joints - main serial chain --><joint name="joint_1" type="revolute"><parent link="base_link"/><child link="link_1"/><origin rpy="0 0 0" xyz="0 0 0.061584"/><axis xyz="0 0 1"/><limit effort="1000.0" lower="-3.141592653589793" upper="3.141592653589793" velocity="2.5"/></joint><!-- additional joints ... --><!-- ros2 control tag --><ros2_control name="robot_6_dof" type="system"><hardware><plugin><!-- {Name_Space}/{Class_Name}--></plugin></hardware><joint name="joint_1"><command_interface name="position"><param name="min">{-2*pi}</param><param name="max">{2*pi}</param></command_interface><!-- additional command interfaces ... --><state_interface name="position"><param name="initial_value">0.0</param></state_interface><!-- additional state interfaces ... --></joint><!-- additional joints ...--><!-- additional hardware/sensors ...--></ros2_control>
</robot>
  • robot 标签包含 URDF 文件的所有内容。它有一个必须指定的名称属性。
  • link 标签定义了机器人的几何形状和惯性属性。它有一个名称属性,将被关节标签引用。
  • visual 标记指定视觉网格的旋转和平移。如果网格是按照前面所述的方法处理的,那么原点标签可以全部为零。
  • geometry 和 mesh 标签指定三维网格文件相对于指定 ROS 2 软件包的位置。
  • collision 标签等同于视觉标签,只是指定的网格在某些应用中用于碰撞检测。
  • inertial 标签指定 link 的质量和惯性。origin 标签指定刚体的质心。这些值用于计算正向和反向动力学。由于我们的应用程序不使用动力学,因此使用统一的任意值。
  • <!-- additional links ... --> 注释表示将定义多个连续的链接标记,每个链接一个。
  • <link name="world"/> 和 <link name="tool0"/> 元素不是必需的。不过,约定俗成的做法是将机器人顶端的 link 设置为 tool0,并定义机器人相对于世界坐标系的 base link。
  • joint 标签指定机器人的运动结构。它有两个必备属性:名称和类型。类型指定两个相连刚体之间的可行运动。随后的父刚体 parent 和子刚体 child 则指定关节连接了哪两个链接。
  • axis 标签规定了关节的自由度。如果网格是按前述方法处理的,则轴值始终为 "0 0 1"。
  • limits 标签指定关节的运动学和动力学限制。
  • ros2_control 标签指定机器人的硬件配置。更具体地说,就是可用的状态和命令接口。该标签有两个必填属性:名称和类型。该标签还包含传感器等附加元素。
  • hardware 和 plugin 标签指示 ros2_control 框架将符合 HardwareInterface 标准的硬件驱动程序作为插件动态加载。插件被指定为 <{Name_Space}/{Class_Name} 。
  • 最后,joint 标记指定了加载的插件将提供的状态和命令接口。关节点用 name 属性指定。command_interface 和 state_interface 标签指定接口类型,通常是位置、速度、加速度或力。

        为简化 URDF 文件,使用 xacro 来定义宏,请参阅本教程。本教程中机器人的完整 xacro 文件可在此处获取。在 xacro 生成 URDF 后,可以使用 urdf_to_graphviz 工具验证运动学链。运行

xacro description/urdf/r6bot.urdf.xacro > r6bot.urdf
urdf_to_graphviz r6bot.urdf r6bot

        生成 r6bot.pdf,显示机器人的运动链。

三、编写硬件接口

        在 ros2_control 中,硬件系统组件是通过符合 HardwareInterface 公共接口的用户定义驱动插件集成的。URDF 中指定的硬件插件在初始化过程中使用 pluginlib 接口动态加载。为了运行 ros2_control_node,必须设置名为 robot_description 的参数。这通常在 ros2_control 启动文件中完成。

        以下代码块将解释编写新硬件接口的要求。

        教程机器人的硬件插件是一个名为 RobotSystem 的类,它继承自 hardware_interface::SystemInterface。SystemInterface 是为完整机器人系统设计的硬件接口之一。例如,UR5 就使用了该接口。机器人系统必须实现五个公共方法。

  1. 启动(on_init)
  2. 输出状态接口(export_state_interfaces)
  3. 导出命令接口(export_command_interfaces)
  4. 读取(read)
  5. 写(write)
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
#include "hardware_interface/types/hardware_interface_return_values.hpp"class HARDWARE_INTERFACE_PUBLIC RobotSystem : public hardware_interface::SystemInterface {public:CallbackReturn on_init(const hardware_interface::HardwareInfo &info) override;std::vector<hardware_interface::StateInterface> export_state_interfaces() override;std::vector<hardware_interface::CommandInterface> export_command_interfaces() override;return_type read(const rclcpp::Time &time, const rclcpp::Duration &period) override;return_type write(const rclcpp::Time & /*time*/, const rclcpp::Duration & /*period*/) override;// private members// ...
}

        如果在 URDF 中指定了机器人系统,on_init 方法会在 ros2_control 初始化过程中被调用一次。在该方法中,需要设置机器人硬件之间的通信,并分配动态内存。由于教程机器人是仿真的,因此不会建立明确的通信。相反,将初始化代表所有硬件状态的向量,例如描述关节角度的 double 向量等。

CallbackReturn RobotSystem::on_init(const hardware_interface::HardwareInfo &info) {if (hardware_interface::SystemInterface::on_init(info) != CallbackReturn::SUCCESS) {return CallbackReturn::ERROR;}// setup communication with robot hardware// ...return CallbackReturn::SUCCESS;
}

        值得注意的是,on_init 的行为预计会因 URDF 文件的不同而不同。SystemInterface::on_init(info) 调用会用 URDF 中的具体信息填充 info 对象。例如,info 对象有 joints、sensors、gpios 等字段。假设传感器字段的名称值为 tcp_force_torque_sensor。那么 on_init 必须尝试与该传感器建立通信。如果失败,则返回错误值。

        接下来,将依次调用 export_state_interfaces 和 export_command_interfaces 方法。export_state_interfaces 方法会返回一个 StateInterface 向量,描述每个关节的状态接口。StateInterface 对象是只读数据句柄。它们的构造函数需要一个接口名称、接口类型和一个指向 double 数据值的指针。对于机器人系统来说,数据指针引用的是类成员变量。这样,每个方法都可以访问数据。

std::vector<hardware_interface::StateInterface> RobotSystem::export_state_interfaces() {std::vector<hardware_interface::StateInterface> state_interfaces;// add state interfaces to ``state_interfaces`` for each joint, e.g. `info_.joints[0].state_interfaces_`, `info_.joints[1].state_interfaces_`, `info_.joints[2].state_interfaces_` ...// ...return state_interfaces;}

        export_command_interfaces 方法与前一个方法几乎完全相同。不同之处在于返回的是一个 CommandInterface 向量。该向量包含描述每个关节的命令接口的对象。

std::vector<hardware_interface::CommandInterface> RobotSystem::export_command_interfaces() {std::vector<hardware_interface::CommandInterface> command_interfaces;// add command interfaces to ``command_interfaces`` for each joint, e.g. `info_.joints[0].command_interfaces_`, `info_.joints[1].command_interfaces_`, `info_.joints[2].command_interfaces_` ...// ...return command_interfaces;
}

        read 方法是 ros2_control 循环的核心方法。在主循环中,ros2_control 会遍历所有硬件组件并调用 read 方法。该方法在实时线程上执行,因此必须遵守实时约束。read 方法负责更新状态接口的数据值。由于数据值指向类成员变量,因此可以用相应的传感器值填充这些值,进而更新每个导出状态接口对象的值。

return_type RobotSystem::read(const rclcpp::Time & time, const rclcpp::Duration &period) {// read hardware values for state interfaces, e.g joint encoders and sensor readings// ...return return_type::OK;
}

        write 方法是 ros2_control 循环中的另一个核心方法。它在实时循环中更新后被调用。因此,它也必须遵守实时约束。write 方法负责更新命令接口的数据值。与读取方法不同,写入方法访问由导出的命令接口对象指针指向的数据值,并将其发送给相应的硬件。例如,如果硬件支持通过 TCP 协议设置关节速度,那么该方法就会访问相应命令接口的数据并发送包含该值的数据包。

return_type write(const rclcpp::Time & time, const rclcpp::Duration & period) {// send command interface values to hardware, e.g joint set joint velocity// ...return return_type::OK;
}

        最后,所有 ros2_control 插件都应在文件末尾添加以下两行代码。

#include "pluginlib/class_list_macros.hpp"PLUGINLIB_EXPORT_CLASS(robot_6_dof_hardware::RobotSystem, hardware_interface::SystemInterface)

        PLUGINLIB_EXPORT_CLASS 是一个使用 pluginlib 创建插件库的 c++ 宏。

3.1 插件说明文件(hardware)

        插件描述文件是一个必需的 XML 文件,用于描述插件的库名、类类型、命名空间、描述和接口类型。该文件允许 ROS 2 自动发现和加载插件。其格式如下

<library path="{Library_Name}"><classname="{Namespace}/{Class_Name}"type="{Namespace}::{Class_Name}"base_class_type="hardware_interface::SystemInterface"><description>{Human readable description}</description></class>
</library>

        库标记的路径属性指的是用户自定义硬件插件的 cmake 库名称。完整的 XML 文件请参见此处。

3.2 CMake 库(hardware)

        在 ros2_control 中制作硬件插件的一般 CMake 模板如下所示。请注意,使用插件源代码创建的库就像其他 cmake 库一样。此外,还需要添加额外的编译定义和 cmake 导出宏 (pluginlib_export_plugin_description_file)。请点击此处查看完整的 CMakeLists.txt 文件。

add_library(robot_6_dof_hardwareSHAREDsrc/robot_hardware.cpp
)

四、编写控制器

        在 ros2_control 中,控制器是作为符合 ControllerInterface 公共接口的插件来实现的。与硬件接口类似,要加载的控制器插件也是通过 ROS 参数指定的。这通常是通过向 ros2_control_node 传递 YAML 参数文件来实现的。与硬件接口不同,控制器有一组有限的状态:

  1. 未配置(Unconfigured)
  2. 未激活(Inactive)
  3. 激活(Active)
  4. 最终完成(Finalized)

        在这些状态之间转换时,会调用某些接口方法。在主控制循环期间,控制器处于激活状态。

        以下代码块将解释编写新控制器的要求。

        教程机器人的控制器插件是一个名为 RobotController 的类,它继承自 controller_interface::ControllerInterface。机器人控制器必须实现九个公共方法。最后六个是管理节点转换回调。

  1. 命令接口配置(command_interface_configuration)
  2. 状态界面配置(state_interface_configuration)
  3. 更新(update)
  4. 配置(on_configure)
  5. 激活(on_activate)
  6. 停用时(on_deactivate)
  7. 清除(on_cleanup)
  8. 出错时(on_error)
  9. 关闭(on_shutdown)
class RobotController : public controller_interface::ControllerInterface {public:controller_interface::InterfaceConfiguration command_interface_configuration() const override;controller_interface::InterfaceConfiguration state_interface_configuration() const override;controller_interface::return_type update(const rclcpp::Time &time, const rclcpp::Duration &period) override;controller_interface::CallbackReturn on_init() override;controller_interface::CallbackReturn on_configure(const rclcpp_lifecycle::State &previous_state) override;controller_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State &previous_state) override;controller_interface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &previous_state) override;controller_interface::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &previous_state) override;controller_interface::CallbackReturn on_error(const rclcpp_lifecycle::State &previous_state) override;controller_interface::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &previous_state) override;
// private members
// ...
}

        控制器插件动态加载后,会立即调用 on_init 方法。该方法在控制器的生命周期内只被调用一次,因此应分配控制器生命周期内存在的内存。此外,应声明并访问关节(joints)、命令接口(command_interfaces)和状态接口(state_interfaces)的参数值。接下来的两个方法都需要这些参数值。

using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;controller_interface::CallbackReturn on_init(){// declare and get parameters needed for controller initialization// allocate memory that will exist for the life of the controller// ...return CallbackReturn::SUCCESS;
}

        控制器设置为非激活状态后,on_configure 方法会立即被调用。这种状态不仅会在控制器首次启动时出现,也会在重新启动时出现。应在此方法中读取可重新配置的参数。此外,还应创建发布者和订阅者。

controller_interface::CallbackReturn on_configure(const rclcpp_lifecycle::State &previous_state){// declare and get parameters needed for controller operations// setup realtime buffers, ROS publishers, and ROS subscribers// ...return CallbackReturn::SUCCESS;
}

        命令接口配置(command_interface_configuration)方法在 on_configure 之后调用。该方法返回一个 InterfaceConfiguration 对象列表,用于指明控制器需要运行哪些命令接口。命令接口由其名称和接口类型唯一标识。如果已加载的硬件接口不提供所请求的接口,那么控制器就会失败。

controller_interface::InterfaceConfiguration command_interface_configuration(){controller_interface::InterfaceConfiguration conf;// add required command interface to `conf` by specifying their names and interface types.// ..return conf
}

        然后调用 state_interface_configuration 方法,该方法与上一个方法类似。不同的是,将返回一个 InterfaceConfiguration 对象列表,该列表代表运行所需的状态接口。

controller_interface::InterfaceConfiguration state_interface_configuration() {controller_interface::InterfaceConfiguration conf;// add required state interface to `conf` by specifying their names and interface types.// ..return conf
}

        控制器启动时,on_activate 会被调用一次。该方法应处理控制器重启,例如将重置引用设置为安全值。它还应执行控制器特定的安全检查。激活控制器时,还会再次调用 command_interface_configuration 和 state_interface_configuration 方法。

controller_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State &previous_state){// Handle controller restarts and dynamic parameter updating// ...return CallbackReturn::SUCCESS;
}

        更新(update)方法是主控制环的一部分。由于该方法是实时控制环的一部分,因此必须执行实时约束。控制器应从其状态接口读取数据,读取参考值并计算控制输出。通常情况下,参考点是通过 ROS 2 用户访问的。由于订阅器运行在非实时线程上,因此需要使用实时缓冲区将消息传递给实时线程。实时缓冲区最终是一个指向 ROS 消息的指针,它带有一个互斥器,可以保证线程安全,实时线程永远不会被阻塞。然后,计算出的控制输出将写入命令接口,进而控制硬件。

controller_interface::return_type update(const rclcpp::Time &time, const rclcpp::Duration &period){// Read controller inputs values from state interfaces// Calculate controller output values and write them to command interfaces// ...return controller_interface::return_type::OK;
}

        控制器停止运行时会调用 on_deactivate。在此方法中释放已申请的命令接口非常重要,这样其他控制器就可以在需要时使用它们。这可以通过 release_interfaces 函数来实现。

controller_interface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &previous_state){release_interfaces();// The controller should be properly shutdown during this// ...return CallbackReturn::SUCCESS;
}

        当控制器的生命周期节点过渡到关闭时,会调用 on_cleanup 和 on_shutdown。释放已分配的内存和一般清理工作应在这些方法中完成。

controller_interface::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &previous_state){// Callback function for cleanup transition// ...return CallbackReturn::SUCCESS;
}
controller_interface::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &previous_state){// Callback function for shutdown transition// ...return CallbackReturn::SUCCESS;
}

        如果托管节点的状态转换失败,则会调用 on_error 方法。这种情况一般不会发生。

controller_interface::CallbackReturn on_error(const rclcpp_lifecycle::State &previous_state){// Callback function for erroneous transition// ...return CallbackReturn::SUCCESS;
}

4.1 插件说明文件(控制器)

        控制器也需要插件说明文件,因为它是作为库导出的。控制器插件说明文件格式如下。完整的 XML 文件请参见此处。

<library path="{Library_Name}"><classname="{Namespace}/{Class_Name}"type="{Namespace}::{Class_Name}"base_class_type="controller_interface::ControllerInterface"><description>{Human readable description}</description></class>
</library>

4.2 CMake 库(控制器)

        必须在构建控制器插件的 CMake 文件中指定该插件。完整的 CMakeLists.txt 文件请参见此处。

add_library(r6bot_controllerSHAREDsrc/robot_controller.cpp
)

五、启动示例

        首先构建工作区,即可运行完整的教程示例。

git clone -b iron https://github.com/ros-controls/ros2_control_demos.git
cd ros2_control_demos
colcon build --symlink-install
source install/setup.bash

        要查看机器人,请打开终端并启动 ros2_control_demo_example_7 软件包中的 view_r6bot.launch.py 文件。

ros2 launch ros2_control_demo_example_7 view_r6bot.launch.py

        现在,您可以使用 joint_state_publisher_gui 改变每个关节的位置。

        接下来,杀死启动文件中的进程,开始仿真受控机器人。打开终端,启动 ros2_control_demo_example_7 软件包中的 r6bot_controller.launch.py 文件。

ros2 launch ros2_control_demo_example_7 r6bot_controller.launch.py

        最后,打开一个新的终端,运行以下命令。

ros2 launch ros2_control_demo_example_7 send_trajectory.launch.py

        您应该能在 RViz 中看到机器人做圆周运动的教程。

相关文章:

ros2_control 6 自由度机械臂

系列文章目录 前言 ros2_control 是一个实时控制框架&#xff0c;专为普通机器人应用而设计。标准的 c 接口用于与硬件交互和查询用户定义的控制器命令。这些接口增强了代码的模块化和与机器人无关的设计。具体的应用细节&#xff0c;例如使用什么控制器、机器人有多少个关节以…...

Python 在自动化中的实际应用:用 Python 简化繁琐任务

文章目录 1、概述2、自动化文件和目录管理3.数据处理与分析4.网页爬虫5. 系统管理6。定时任务7.结语 1、概述 这篇文章将深入探讨Python在自动化中的实际应用&#xff0c;帮助您用Python简化繁琐任务。 我们将从多个方面入手&#xff0c;展示如何利用Python进行文件管理、数据…...

解释 Spring 框架的核心模块(如 IoC 容器、AOP )及其工作原理。描述如何使用 Spring Boot 快速搭建一个 RESTful Web服务?

Spring框架是一个广泛使用的Java企业级应用程序开发框架&#xff0c;它提供了一系列的模块来帮助开发者构建健壮、可测试、可维护的应用程序。 其中&#xff0c;最核心的模块包括IoC容器和AOP&#xff08;Aspect Oriented Programming&#xff0c;面向切面编程&#xff09;。 …...

数据分析详解

一、数据分析教程 1. 入门教程 在线课程&#xff1a;如Coursera、Udemy、网易云课堂等平台提供了大量数据分析的入门课程&#xff0c;涵盖统计学基础、Python/R语言编程、数据可视化等内容。书籍推荐&#xff1a;《Python数据分析实战》、《R语言实战》等书籍是数据分析入门的…...

SpringCloud之@FeignClient()注解的使用方式

FeignClient介绍 FeignClient 是 Spring Cloud 中用于声明一个 Feign 客户端的注解。由于SpringCloud采用分布式微服务架构&#xff0c;难免在各个子模块下存在模块方法互相调用的情况。比如订单服务要调用库存服务的方法&#xff0c;FeignClient()注解就是为了解决这个问题的…...

20.rabbitmq插件实现延迟队列

问题 前面谈到基于死信的延迟队列&#xff0c;存在的问题&#xff1a;如果第一个消息延时时间很长&#xff0c;而第二个消息延时时间很短&#xff0c;第二个消息并不会优先得到执行。 下载插件 地址&#xff1a;https://github.com/rabbitmq/rabbitmq-delayed-message-excha…...

TS如何处理js模块的类型?

现在很多插件都直接用ts开发了&#xff0c;本身包含了类型定义常见的第三方插件&#xff0c;都有’types/xxx’包&#xff0c;安装即可使用其他的&#xff0c;可通过declare module定义类型 比如&#xff1a; // someModule.js export function greet(name) {return Hello, $…...

GPS定位系统(VUE框架)

源码下载&#xff1a;小宅博客网 博主之前写的《GPS定位系统&#xff08;MVC框架&#xff09;》版本&#xff0c;并没有做到前后端分离&#xff0c;不太适合多人协作开发&#xff0c;这边博主分享一个基于asp.net web api vue3的GPS定位系统框架&#xff0c;本框架继承了MVC框…...

分布式光伏并网AM5SE-IS防孤岛保护装置介绍——安科瑞 叶西平

产品简介 功能&#xff1a; AM5SE-IS防孤岛保护装置主要适用于35kV、10kV及低压380V光伏发电、燃气发电等新能源并网供电系统。当发生孤岛现象时&#xff0c;可以快速切除并网点&#xff0c;使本站与电网侧快速脱离&#xff0c;保证整个电站和相关维护人员的生命安全。 应用…...

神奇的方法解决Navicat闪退

原因 打开Navicat操作上面的工具等就会闪退&#xff0c;原因竟然是屏幕划词&#xff01;&#xff01;&#xff01; 解决方法 看别人提到有道词典的划词功能的原因 我没有安装有道词典&#xff0c;但我安装豆包&#xff0c;它也有划词翻译的功能&#xff0c;关闭即可...

openmv学习笔记(24电赛笔记)

感光元件 openmv采用小孔摄像模式&#xff0c;将图像映射到感光原件上面&#xff0c;来传递图片&#xff0c;通过图片快速的刷新行成视频&#xff0c;在IDE中通过对感光原件的编辑可以控制视频的效果。 重置感光元件到默认状态 import sensor #导入感光元件这个库sensor.res…...

Linux shell编程学习笔记67: tracepath命令 追踪数据包的路由信息

0 前言 网络信息是电脑网络信息安全检查中的一块重要内容&#xff0c;Linux和基于Linux的操作系统&#xff0c;提供了很多的网络命令&#xff0c;今天我们研究tracepath命令。 Tracepath 在大多数 Linux 发行版中都是可用的。如果在你的系统中没有预装&#xff0c;请根据你的…...

生鲜云订单零售系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;订单评价管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;商品信息&#…...

BLE自适应跳频算法详解

前言 &#xff08;1&#xff09;自适应跳频算法是相当的简单&#xff0c;小学生都能够看懂&#xff0c;而且网上已经有相当多的关于自适应跳频算法的介绍。既然如此&#xff0c;为什么我还要写这样一篇博客呢&#xff1f; &#xff08;2&#xff09;原因很简单&#xff0c;我发…...

[Meachines] [Easy] Beep Elastix-CMS-LFI

信息收集 IP AddressOpening Ports10.10.10.7TCP:22, 25, 80, 110, 111, 143, 443, 993, 995, 3306 $ nmap -p- 10.10.10.7 --min-rate 1000 -sC -sV Nmap scan report for 10.10.10.7 (10.10.10.7) Host is up (0.53s latency). Not shown: 65486 filtered tcp ports (no-…...

甘肃麻花:酥脆香甜的陇原美味

在甘肃的美食画卷中&#xff0c;甘肃麻花以其独特的魅力占据着重要的一席之地。甘肃食家巷麻花&#xff0c;那金黄酥脆的外形&#xff0c;宛如一件件精美的艺术品。每一根麻花的纹理都清晰可见&#xff0c;缠绕交织&#xff0c;散发着诱人的光泽。 制作甘肃麻花是一门传统的手艺…...

C语言刷题小记2

前言 本篇博客还是为大家分享一些C语言的OJ题目&#xff0c;如果你感兴趣&#xff0c;希望大佬一键三连。多多支持。下面进入正文部分。 题目1竞选社长 分析&#xff1a;本题要求我们输入一串字符&#xff0c;并且统计个数的多少&#xff0c;那么我们可以通过getchar函数来获…...

JavaScript图片轮播

代码在文章最后面&#xff08;含图片URL&#xff09; 实现功能 按向左按钮图片显示上一张按向右按钮图片显示下一张每隔2000毫秒显示下一张图底部三个圆点显示当前的图片的编号 实现流程 初始化图片数组 创建一个包含图片URL的数组&#xff0c;轮播时会通过这个数组来切换图…...

MSSQL注入前置知识

简述 Microsoft SQL server也叫SQL server / MSSQL&#xff0c;由微软推出的关系型数据库&#xff0c;默认端口1433 常见搭配C# / .net IISmssql mssql的数据库文件 数据文件&#xff08;.mdf&#xff09;&#xff1a;主要的数据文件&#xff0c;包含数据表中的数据和对象信息…...

idea一键为实体类赋值

file -> settings -> plugins -> marketplace 把这个插件装上 找个实体&#xff0c;选中&#xff0c;altenter进入edit界面 我是选择只保留右边这种生成方法&#xff0c;然后选择ok 返回到那个实体&#xff0c;选择&#xff0c;altenter generate生成...

秋招突击——7/24——知识补充——JVM类加载机制

文章目录 引言类加载机制知识点复习类的生命周期1、加载2、连接——验证3、连接——准备4、连接——解析5、初始化 类加载器和类加载机制类加载器类加载机制——双亲委派模型 面试题整理1、类加载是什么2、类加载的过程是什么3、有哪些类加载器&#xff1f;4、双亲委派模型是什…...

如何在 Microsoft SQL Server 中增加字段-完整指南

在使用 Microsoft SQL Server (MSSQL) 进行数据库管理时,添加新字段(列)是一项常见的任务。无论你是需要存储额外的信息,还是调整数据模型以适应新的业务需求,本指南都将帮助你轻松完成这项操作。 目录 1. 使用 T-SQL 添加字段2. 使用 SQL Server Management Studio (SSMS) 添加…...

快手电商Android一面凉经(2024)

快手电商Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《快手电商Android一面凉经(2024)》。 面试职位: Android工程师 技术一面 面试形式…...

随机点名器

练习1 package lx;import java.io.*; import java.util.ArrayList; import java.util.Collections; import java.util.Random;/*需求&#xff1a;需求&#xff1a;有一个文件里面存储了班级同学的信息&#xff0c;每一个信息占一行。格式为&#xff1a;张三-男-23要求通过程序…...

添加动态云层

<template> <div class"topbox"> xx卫星管理 </div> <div class"selectbox"> <div class"title"> 卫星列表 </div> <el-table :data"tableData" style"width: 100%;height:230px;" …...

Spring Boot组成的分布式系统中实现日志跟踪

Spring Boot组成的分布式系统中实现日志跟踪 首发2024-07-25 08:54潘多编程 在分布式系统中&#xff0c;日志跟踪是一项非常重要的功能&#xff0c;它帮助开发者了解请求在整个系统中的流转过程&#xff0c;这对于调试、监控和故障排查至关重要。Spring Boot应用通常作为微服…...

GPT-4o Mini 模型的性能与成本优势全解析

GPT-4o Mini 模型的性能与成本优势全解析 &#x1f4c8; &#x1f31f; GPT-4o Mini 模型的性能与成本优势全解析 &#x1f4c8;摘要引言正文内容GPT-4o Mini 模型简介 &#x1f680;性能测试与对比 &#x1f4ca;应用场景 &#x1f310;自然语言处理对话系统内容生成 ✍️ &am…...

web前端 - HTML 基础知识大揭秘

HTML 大揭秘 什么是 HTML HTML&#xff08;Hyper Text Markup Language&#xff09;&#xff0c;中文译为超文本标记语言。其中&#xff0c;我们需要注意两个关键词。一个是 超文本&#xff0c;一个是 标记。所谓超文本&#xff0c;就是将不同空间的文字信息通过超链接的方式…...

HTML meta

<meta>标签用于提供html文档的元信息&#xff08;metadata&#xff09;。这些信息不会显示在页面上&#xff0c;但会被浏览器或搜索引擎用来识别页面的编码方式、关键字、描述、作者信息、刷新时间等。 基本语法 <meta name"属性名" content"属性值&q…...

【学习笔记】子集DP

背景 有一类问题和子集有关。 给你一个集合 S S S&#xff0c;令 T T T 为 S S S 的超集&#xff0c;也就是 S S S 所有子集的集合&#xff0c;求 T T T 中所有元素的和。 暴力1 先预处理子集的元素和 A i A_i Ai​&#xff0c;再枚举子集。 for(int s0; s<(1<…...

苦学Opencv的第十四天:人脸检测和人脸识别

Python OpenCV入门到精通学习日记&#xff1a;人脸检测和人脸识别 前言 经过了十三天的不懈努力&#xff0c;我们终于也是来到了人脸检测和人脸识别啦&#xff01;相信大家也很激动吧。接下来我们开始吧&#xff01; 人脸识别是基于人的脸部特征信息进行身份识别的一种生物识…...

PyTorch学习(1)

PyTorch学习&#xff08;1&#xff09; CIFAR-10数据集-图像分类 数据集来源是官方提供的&#xff1a; torchvision.datasets.CIFAR10()共有十类物品&#xff0c;需要用CNN实现图像分类问题。 代码如下&#xff1a;(CIFAR_10_Classifier_Self_1.py) import torch import t…...

三思而后行:计算机行业的决策智慧

在计算机行业&#xff0c;"三思而后行"这一原则显得尤为重要。在这个快速发展、技术不断更新换代的领域&#xff0c;每一个决策都可能对项目的成功与否产生深远的影响。以下是一篇关于在计算机行业中三思重要性的文章。 三思而后行&#xff1a;计算机行业的决策智慧 …...

Linux--Socket编程UDP

前文&#xff1a;Socket套接字编程 UDP协议特点 无连接&#xff1a;UDP在发送数据之前不需要建立连接&#xff0c;减少了开销和发送数据之前的时延。尽最大努力交付&#xff1a;UDP不保证可靠交付&#xff0c;主机不需要维持复杂的连接状态表。面向报文&#xff1a;UDP对应用层…...

《javaEE篇》--单例模式详解

目录 单例模式 饿汉模式 懒汉模式 懒汉模式(优化) 指令重排序 总结 单例模式 单例模式属于一种设计模式&#xff0c;设计模式就好比是一种固定代码套路类似于棋谱&#xff0c;是由前人总结并且记录下来我们可以直接使用的代码设计思路。 单例模式就是&#xff0c;在有…...

Java核心 - Lambda表达式详解与应用示例

作者&#xff1a;逍遥Sean 简介&#xff1a;一个主修Java的Web网站\游戏服务器后端开发者 主页&#xff1a;https://blog.csdn.net/Ureliable 觉得博主文章不错的话&#xff0c;可以三连支持一下~ 如有疑问和建议&#xff0c;请私信或评论留言&#xff01; 前言 Lambda表达式是…...

算法通关:006_1二分查找

二分查找 查找一个数组里面是否存在num主要代码运行结果 详细写法自动生成数组和num&#xff0c;利用对数器查看二分代码是否正确 查找一个数组里面是否存在num 主要代码 /*** Author: ggdpzhk* CreateTime: 2024-07-27*/ public class cg {//二分查找public static boolean …...

总结一些vue3小知识3

总结一些vue3小知识1&#xff1a;http://t.csdnimg.cn/C5vER 总结一些vue3小知识2&#xff1a;http://t.csdnimg.cn/sscid 1.限制时间选择器只能选择后面的日期 说明&#xff1a;disabled-date属性是一个用来判断该日期是否被禁用的函数&#xff0c;接受一个 Date 对象作为参…...

JAVAWeb实战(前端篇)

项目实战一 0.项目结构 1.创建vue3项目&#xff0c;并导入所需的依赖 npm install vue-router npm install axios npm install pinia npm install vue 2.定义路由&#xff0c;axios&#xff0c;pinia相关的对象 文件&#xff08;.js&#xff09; 2.1路由(.js) import {cre…...

axios请求大全

本文讲解axios封装方式以及针对各种后台接口的请求方式 axios的介绍和基础配置可以看这个文档: 起步 | Axios中文文档 | Axios中文网 axios的封装 axios封装的重点有三个&#xff0c;一是设置全局config,比如请求的基础路径&#xff0c;超时时间等&#xff0c;第二点是在每次…...

C# 简单的单元测试

文章目录 前言参考文档新建控制台项目新建测试项目添加引用添加测试方法测试结果(有错误)测试结果&#xff0c;通过正规的方法抛出异常 总结 前言 听说复杂的项目最好都要单元测试一下。我这里也试试单元测试这个功能。到时候调试起来也方便。 参考文档 C# 单元测试&#xf…...

Linux中Mysql5.7主从架构(一主多从)配置教程

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…...

BACnet物联网关BL103:Modbus协议转BACnet/MSTP

随着物联网技术在楼宇自动化与暖通控制系统中的迅猛发展&#xff0c;构建一种既经济高效又高度可靠的协议转换物联网关成为了不可或缺的核心硬件组件。在此背景下&#xff0c;我们钡铼特别推荐一款主流的BAS&#xff08;楼宇自动化系统&#xff09;与BACnet物联网关——BL103&a…...

Go 语言条件变量 Cond

1.Cond 的使用方法 Go 标准库提供 Cond 同步原语的目的是为等待/通知场景下的并发操作提供支持。Cond 通常用于等待某个条件的一组 goroutine,当条件变为 true 时,其中一个或者所有的 goroutine 会被唤醒执行。 Cond 与某个条件相关,这个条件需要一组 goroutine 协作达到。当这…...

PostgreSQL 中如何重置序列值:将自增 ID 设定为特定值开始

我是从excel中将数据导入&#xff0c;然后再通过sql插入数据&#xff0c;就报错。 需要设置自增ID开始值 1、确定序列名称&#xff1a; 首先&#xff0c;需要找到与的增字段相关的序列名称。假设表名是 my_table 和自增字段是 id&#xff0c;可以使用以下查询来获取序列名称…...

Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示

Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示 目录 Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示 一、简单介绍 二、共享纹理 1、共享纹理的原理 2、共享纹理涉及到的关键知识点 3、什么可以实现共享 不能实现共享…...

Go语言的数据结构

数据结构 数组 支持多维数组&#xff0c;属于值类型&#xff0c;支持range遍历 例子&#xff1a;随机生成长度为10整数数组 package main import ("fmt""math/rand" ) // 赋值 随机获取100以内的整数 func RandomArrays() {var array [10]int //声明var…...

python_在sqlite中创建表并写入表头

python_在sqlite中创建表并写入表头 import sqlite3def write_title_to_sqlite(tableName,titleList,dataTypeGroupsList,database_path):conn sqlite3.connect(database_path)# 创建游标cursor conn.cursor()#MEMO 长文本#create_table_bodycreate_table_body "序号 …...

1.c#(winform)编程环境安装

目录 安装vs创建应用帮助查看器安装与使用&#xff08; msdn&#xff09; 安装vs 安装什么版本看个人心情&#xff0c;或者公司开发需求需要 而本栏全程使用vs2022进行开发c#&#xff0c;着重讲解winform桌面应用开发 使用***.net framework***开发 那先去官网安装企业版的vs…...

图中的最短环

2608. 图中的最短环 现有一个含 n 个顶点的 双向 图&#xff0c;每个顶点按从 0 到 n - 1 标记。图中的边由二维整数数组 edges 表示&#xff0c;其中 edges[i] [ui, vi] 表示顶点 ui 和 vi 之间存在一条边。每对顶点最多通过一条边连接&#xff0c;并且不存在与自身相连的顶…...