MoveIt2-humble----Planning Around Objects(a)

news2024/11/24 22:46:18

4.Pick and Place with MoveIt Task Constructor

本节教程会教你创建一个功能包,使用MoveIt Task Constructor规划一个抓取和放置的操作。MoveIt Task Constructor 提供了一种方式,去规划由多种不同子任务(也称为阶段)所组成的任务

创建新功能包
ros2 pkg create --build-type ament_cmake --node-name mtc_tutorial mtc_tutorial

这会创建一个名为mtc_tutorial的新文件夹,下一步,在package.xml添加依赖,像这样:

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>mtc_tutorial</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="youremail@domain.com">user</maintainer>
<license>TODO: License declaration</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<depend>moveit_task_constructor_core</depend>
<depend>rclcpp</depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<export>
    <build_type>ament_cmake</build_type>
</export>
</package>

同样,也要在CMakeLists.txt中添加依赖:

cmake_minimum_required(VERSION 3.8)
project(mtc_tutorial)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(moveit_task_constructor_core REQUIRED)
find_package(rclcpp REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

add_executable(mtc_tutorial src/mtc_tutorial.cpp)
ament_target_dependencies(mtc_tutorial moveit_task_constructor_core rclcpp)
target_include_directories(mtc_tutorial PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(mtc_tutorial PUBLIC c_std_99 cxx_std_17)  # Require C99 and C++17

install(TARGETS mtc_tutorial
DESTINATION lib/${PROJECT_NAME})

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()

使用 MoveIt Task Constructor 建立程序

本章节教你使用 MoveIt Task Constructor 建立一个小任务

The Code

打开mtc_tutorial.cpp,添加以下代码:

#include "rclcpp/rclcpp.hpp"
#include <moveit/planning_scene/planning_scene.h>
#include <moveit/planning_scene_interface/planning_scene_interface.h>
#include <moveit/task_constructor/task.h>
#include <moveit/task_constructor/solvers.h>
#include <moveit/task_constructor/stages.h>
#if __has_include(<tf2_geometry_msgs/tf2_geometry_msgs.hpp>)
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
#else
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#endif
#if __has_include(<tf2_eigen/tf2_eigen.hpp>)
#include <tf2_eigen/tf2_eigen.hpp>
#else
#include <tf2_eigen/tf2_eigen.h>
#endif

static const rclcpp::Logger LOGGER = rclcpp::get_logger("mtc_tutorial");
namespace mtc = moveit::task_constructor;

class MTCTaskNode
{
public:
  // 构造函数
  MTCTaskNode(const rclcpp::NodeOptions& options);
  // 用于获取节点的基本接口
  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr getNodeBaseInterface();
  // 任务执行函数
  void doTask();
  // 建立规划场景
  void setupPlanningScene();

private:
  // 从一系列的阶段中组成一个 MTC 任务 
  mtc::Task createTask(); // 实例化 Task 对象
  mtc::Task task_; // 声明 Task 类指针
  rclcpp::Node::SharedPtr node_; // 声明共享指针->node
};
// 获取节点的基本接口,用于后面的计算
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr MTCTaskNode::getNodeBaseInterface()
{
  return node_->get_node_base_interface();
}
// 构造函数,同时初始化其成员变量 node_
MTCTaskNode::MTCTaskNode(const rclcpp::NodeOptions& options)
  : node_{ std::make_shared<rclcpp::Node>("mtc_node", options) }
{
}
// 建立规划场景
void MTCTaskNode::setupPlanningScene()
{
  moveit_msgs::msg::CollisionObject object;
  object.id = "object";
  object.header.frame_id = "world";
  object.primitives.resize(1);
  object.primitives[0].type = shape_msgs::msg::SolidPrimitive::CYLINDER;
  object.primitives[0].dimensions = { 0.1, 0.02 };

  geometry_msgs::msg::Pose pose;
  pose.position.x = 0.5;
  pose.position.y = -0.25;
  object.pose = pose;

  moveit::planning_interface::PlanningSceneInterface psi;
  psi.applyCollisionObject(object);
}
// 任务执行函数
void MTCTaskNode::doTask()
{
  // 令 task_ 指向 createTask 类对象
  task_ = createTask();

  try
  {
    task_.init();
  }
  catch (mtc::InitStageException& e)
  {
    RCLCPP_ERROR_STREAM(LOGGER, e);
    return;
  }

  if (!task_.plan(5))
  {
    RCLCPP_ERROR_STREAM(LOGGER, "Task planning failed");
    return;
  }
  task_.introspection().publishSolution(*task_.solutions().front());

  auto result = task_.execute(*task_.solutions().front());
  if (result.val != moveit_msgs::msg::MoveItErrorCodes::SUCCESS)
  {
    RCLCPP_ERROR_STREAM(LOGGER, "Task execution failed");
    return;
  }

  return;
}

mtc::Task MTCTaskNode::createTask()
{
  // 创建 task 对象
  mtc::Task task;
  task.stages()->setName("demo task"); // 设置 task 的名称
  task.loadRobotModel(node_); // 从给定节点中获取机械臂模型

  const auto& arm_group_name = "panda_arm";
  const auto& hand_group_name = "hand";
  const auto& hand_frame = "panda_hand";

  // 设置 task 属性
  task.setProperty("group", arm_group_name);
  task.setProperty("eef", hand_group_name);
  task.setProperty("ik_frame", hand_frame);

// 用于取消这行的警告, 因为在本例中这个变量没有使用
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
  mtc::Stage* current_state_ptr = nullptr;  // Forward current_state on to grasp pose generator
#pragma GCC diagnostic pop
  // 创建一个 CurrentState 阶段
  auto stage_state_current = std::make_unique<mtc::stages::CurrentState>("current");
  current_state_ptr = stage_state_current.get(); // 储存这个阶段,方便以后复用
  task.add(std::move(stage_state_current)); // 将新建的阶段添加到任务中
  
  // 三种规划方式
  auto sampling_planner = std::make_shared<mtc::solvers::PipelinePlanner>(node_);
  auto interpolation_planner = std::make_shared<mtc::solvers::JointInterpolationPlanner>();

  auto cartesian_planner = std::make_shared<mtc::solvers::CartesianPath>();
  cartesian_planner->setMaxVelocityScaling(1.0);
  cartesian_planner->setMaxAccelerationScaling(1.0);
  cartesian_planner->setStepSize(.01);

  auto stage_open_hand =
      std::make_unique<mtc::stages::MoveTo>("open hand", interpolation_planner);
  stage_open_hand->setGroup(hand_group_name);
  stage_open_hand->setGoal("open");
  task.add(std::move(stage_open_hand));

  return task;
}

int main(int argc, char** argv)
{
  // 初始化 ROS
  rclcpp::init(argc, argv);
  // 实例化 node_options 并设置其参数
  rclcpp::NodeOptions options;
  options.automatically_declare_parameters_from_overrides(true);
  // 创建节点
  auto mtc_task_node = std::make_shared<MTCTaskNode>(options);
  // 声明一个多线程执行器
  rclcpp::executors::MultiThreadedExecutor executor;
  // 实例化一个线程对象,使用 lambda表达式来构造,在此线程中加入执行器
  auto spin_thread = std::make_unique<std::thread>([&executor, &mtc_task_node]() {
    executor.add_node(mtc_task_node->getNodeBaseInterface()); // 加入节点
    executor.spin(); // 循环执行,并阻塞进程
    executor.remove_node(mtc_task_node->getNodeBaseInterface()); // 移除节点
  });
  // 调用成员函数,执行相应功能
  mtc_task_node->setupPlanningScene();
  mtc_task_node->doTask();

  spin_thread->join(); // 加入子线程,等待子线程结束,主进程才可以退出
  rclcpp::shutdown(); // 关闭主进程
  return 0;
}
代码分析

代码顶部 include 了 本章使用的 ROS 和 MoveIt 库:

  • rclcpp/rclcpp.hpp 包含了 ROS2 核心功能
  • moveit/planning_scene/planning_scene.hmoveit/planning_scene_interface/planning_scene_interface.h 包含了机器人模型和碰撞物体的接口功能
  • moveit/task_constructor/task.h, moveit/task_constructor/solvers.h, 和moveit/task_constructor/stages.h 包含了本例所使用 MoveIt Task Constructor 中的不同成分
  • tf2_geometry_msgs/tf2_geometry_msgs.hpptf2_eigen/tf2_eigen.hpp 不会在这个初始例子中使用, 它们在我们向 MoveIt Task Constructor task 添加更多步骤时,用来位姿生成。

下一行取得新节点的日志记录器(logger),并为moveit::task_constructor创建一个命名空间别名mtc,以方便我们使用。

#include <rclcpp/rclcpp.hpp>
#include <moveit/planning_scene/planning_scene.h>
#include <moveit/planning_scene_interface/planning_scene_interface.h>
#include <moveit/task_constructor/task.h>
#include <moveit/task_constructor/solvers.h>
#include <moveit/task_constructor/stages.h>
#if __has_include(<tf2_geometry_msgs/tf2_geometry_msgs.hpp>)
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
#else
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#endif
#if __has_include(<tf2_eigen/tf2_eigen.hpp>)
#include <tf2_eigen/tf2_eigen.hpp>
#else
#include <tf2_eigen/tf2_eigen.h>
#endif

static const rclcpp::Logger LOGGER = rclcpp::get_logger("mtc_tutorial");
namespace mtc = moveit::task_constructor;

首先,定义一个类来包含主要的 MoveIt Task Constructor 功能,并且声明了 MoveIt Task Constructor 任务对象作为类中的成员变量(有助于保存任务,方便后面的可视化)。

在下面,我们将单独探讨每个功能的使用:

class MTCTaskNode
{
public:
  MTCTaskNode(const rclcpp::NodeOptions& options);

  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr getNodeBaseInterface();

  void doTask();

  void setupPlanningScene();

private:
  // Compose an MTC task from a series of stages.
  mtc::Task createTask();
  mtc::Task task_;
  rclcpp::Node::SharedPtr node_;
};

下面的代码定义了一个函数去获取节点的基本接口,用于后面的计算。

rclcpp::node_interfaces::NodeBaseInterface::SharedPtr MTCTaskNode::getNodeBaseInterface()
{
  return node_->get_node_base_interface();
}

下面的代码使用指定的选项来初始化节点:

注:这里用到了构造函数初始化列表的内容,即在初始化时也同时初始化了其成员对象node_,从而完成了初始化节点mtc_node的操作。

MTCTaskNode::MTCTaskNode(const rclcpp::NodeOptions& options)
  : node_{ std::make_shared<rclcpp::Node>("mtc_node", options) }
{
}

下面这个方法被用来去建立例子中用到的规划场景,它创建了一个圆柱,该圆柱由object.primitives[0].dimensions来指定尺寸,由pose.position.zpose.position.x来指定位置。你可以尝试改变这些参数来更改尺寸或移动圆柱,如果将圆柱移动到机械臂工作空间外,则规划会报错。

void MTCTaskNode::setupPlanningScene()
{
  moveit_msgs::msg::CollisionObject object;
  object.id = "object";
  object.header.frame_id = "world";
  object.primitives.resize(1);
  object.primitives[0].type = shape_msgs::msg::SolidPrimitive::CYLINDER;
  object.primitives[0].dimensions = { 0.1, 0.02 };

  geometry_msgs::msg::Pose pose;
  pose.position.x = 0.5;
  pose.position.y = -0.25;
  object.pose = pose;

  moveit::planning_interface::PlanningSceneInterface psi;
  psi.applyCollisionObject(object);
}

下面的函数与MoveIt Task Constructor 任务对象连接。首先创建了一个任务,包括设置属性和添加阶段,这将在后面定义的createTask函数中进行讨论。

其次,task.init()初始化了这个任务,task.plan(5)生成了一个规划,在找到5个成功的规划后停止。

然后,发布要在 RViz 中实现可视化的解决方案,如果不在意可视化可以删掉。

最后,task.execute()用来执行规划,这个执行动作通过 RViz 插件中的一个 action server 接口来实现。

void MTCTaskNode::doTask()
{
  task_ = createTask();

  try
  {
    task_.init();
  }
  catch (mtc::InitStageException& e)
  {
    RCLCPP_ERROR_STREAM(LOGGER, e);
    return;
  }

  if (!task_.plan(5))
  {
    RCLCPP_ERROR_STREAM(LOGGER, "Task planning failed");
    return;
  }
  task_.introspection().publishSolution(*task_.solutions().front());

  auto result = task_.execute(*task_.solutions().front());
  if (result.val != moveit_msgs::msg::MoveItErrorCodes::SUCCESS)
  {
    RCLCPP_ERROR_STREAM(LOGGER, "Task execution failed");
    return;
  }

  return;
}

如上所述,这个函数创建了一个 MoveIt Task Construcor 对象并设置了一些初始属性。在这种情况下,我们设置任务的名称为“demo_task”,加载机械臂模型,定义了一些用到的坐标系名,并通过task.setProperty(property_name, value)将这些坐标系名称设置为任务的属性。

mtc::Task MTCTaskNode::createTask()
{
  // 创建 task 对象
  mtc::Task task;
  task.stages()->setName("demo task"); // 设置 task 的名称
  task.loadRobotModel(node_); // 从给定节点中获取机械臂模型

  const auto& arm_group_name = "panda_arm";
  const auto& hand_group_name = "hand";
  const auto& hand_frame = "panda_hand";

  // 设置 task 属性
  task.setProperty("group", arm_group_name);
  task.setProperty("eef", hand_group_name);
  task.setProperty("ik_frame", hand_frame);

现在,我们添加一个示例阶段到节点中。第一行创建了一个指向 Stage 类型的指针current_state_ptr,将其设置为nullptr;这样我们就能在特定场景中复用 Stage 信息。(现在我们没有用到这个功能,但是在之后有更多的阶段添加到任务中时会用到。)

现在,我们创建一个current_state阶段并将其添加到我们的任务中——这使机械臂以当前状态启动。

现在我们已经创建了CurrentState阶段(stage_state_current),我们令current_state_ptr指向stage_state_current的指针,以便后续使用。

// 取消这行的警告, 因为在本例中这个变量没有使用
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
  mtc::Stage* current_state_ptr = nullptr;  // Forward current_state on to grasp pose generator
#pragma GCC diagnostic pop
// 创建智能指针 stage_state_current ,其指向对象的类型为CurrentState
auto stage_state_current = std::make_unique<mtc::stages::CurrentState>("current");
current_state_ptr = stage_state_current.get();
task.add(std::move(stage_state_current));

为了去规划所有的机械臂动作,我们需要去指定一个 solver,对于 solver,MoveIt Task Constructor 有三个选项:

  • PipelinePlanner使用了 MoveIt 的规划管道(planning pipeline),该管道通常默认为 OMPL;
  • CartesianPath被用来在笛卡尔空间中移动末端执行器走直线;
  • JointInterpolation是一个简单的规划器,它在起始和目标关节状态中进行插补。因为其计算很快,通常用于简单的运动,但是不支持复杂的运动。

我们也设置一些属性指定给 Cartesian planner

// 三种规划方式
auto sampling_planner = std::make_shared<mtc::solvers::PipelinePlanner>(node_);
auto interpolation_planner = std::make_shared<mtc::solvers::JointInterpolationPlanner>();

auto cartesian_planner = std::make_shared<mtc::solvers::CartesianPath>();
cartesian_planner->setMaxVelocityScaling(1.0);
cartesian_planner->setMaxAccelerationScaling(1.0);
cartesian_planner->setStepSize(.01);

现在我们已经添加了规划器,我们可以加入一个移动机械臂的阶段。下面的代码使用了一个MoveTo阶段(这是一个广播阶段)。由于打开机械手是一个相对简单的动作,所以我们使用关节插值规划器。这个阶段规划了到“open hand”位姿的运动,这个位姿是一个已经在 panda 机械臂的 SRDF 中命名好的位姿。我们返回任务(return the task)并完成 createTask() 函数。

  auto stage_open_hand =
      std::make_unique<mtc::stages::MoveTo>("open hand", interpolation_planner);
  stage_open_hand->setGroup(hand_group_name);
  stage_open_hand->setGoal("open");
  task.add(std::move(stage_open_hand));

  return task;
}

最后,编写 main函数:使用上文中定义的类来初始化节点,并调用类方法去建立和执行一个基础的 MTC 任务。在本例子中,我们没有在任务结束后取消执行器,以此来保持节点存活,让我们能在 RViz 中检查结果。

int main(int argc, char** argv)
{
  // 初始化 ROS
  rclcpp::init(argc, argv);
  // 实例化 node_options 并设置其参数
  rclcpp::NodeOptions options;
  options.automatically_declare_parameters_from_overrides(true);
  // 创建节点
  auto mtc_task_node = std::make_shared<MTCTaskNode>(options);
  // 声明一个多线程执行器
  rclcpp::executors::MultiThreadedExecutor executor;
  // 实例化一个线程对象,使用 lambda表达式来构造,在此线程中加入执行器
  auto spin_thread = std::make_unique<std::thread>([&executor, &mtc_task_node]() {
    executor.add_node(mtc_task_node->getNodeBaseInterface()); // 加入节点
    executor.spin(); // 循环执行,并阻塞进程
    executor.remove_node(mtc_task_node->getNodeBaseInterface()); // 移除节点
  });
  // 调用成员函数,执行相应功能
  mtc_task_node->setupPlanningScene();
  mtc_task_node->doTask();

  spin_thread->join(); // 加入子线程,等待子线程结束,主进程才可以退出
  rclcpp::shutdown(); // 关闭主进程
  return 0;
}

运行 Demo

Launch files

我们需要一个 launch 文件去启动 move_groupros2_controlstatic_tfrobot_state_publisherrviz。Here is the launch file we use in the tutorials package。把这个 launch 文件添加到你功能包的 launch 文件夹里,然后在 CMakeLists 中添加:

install(DIRECTORY launch
  DESTINATION share/${PROJECT_NAME}
)

为了运行 MoveIt Task Constructor 节点,我们需要第二个 launch 文件去使用适当的参数来启动 mtc_tutorial。要么加载你的 URDF,SRDF 和 OMPL 参数,要么使用 MoveIt Configs Utils 来实现。你的 launch 文件应该像下面这样:

from launch import LaunchDescription
from launch_ros.actions import Node
from moveit_configs_utils import MoveItConfigsBuilder

def generate_launch_description():
    moveit_config = MoveItConfigsBuilder("moveit_resources_panda").to_dict()

    # MTC Demo node
    pick_place_demo = Node(
        package="mtc_tutorial",
        executable="mtc_tutorial",
        output="screen",
        parameters=[
            moveit_config,
        ],
    )

    return LaunchDescription([pick_place_demo])

在你功能包的 launch 文件夹中保存此文件,命名为 pick_place_demo.launch.py,然后编译并source你的工作空间。

cd ~/tutorials_ws
colcon build --mixin release
source install/setup.bash

启动第一个 launch 文件,如果你想使用教程中的这个 launch 文件,输入以下指令:

ros2 launch moveit2_tutorials mtc_demo.launch.py

如果你使用自己的 launch 文件,我们需要配置 RViz。如果使用教程的则不用,已经配置好了。

Viz 配置

为了在 RViz 中看见你的机械臂和 MoveIt Task Constructor 结果,我们需要改变一些 RViz 的配置。首先,启动 RViz,下面的步骤将会教你如何去设置 RViz 来让 MoveIt Task Constructor 的结果可视化:

  1. 如果 MotionPlanning 显示已经激活,取消选中来暂时隐藏它;
  2. Global Options 中,将 Fixed Frame 改为 panda_link0
  3. 在窗口左下角,点击 Add 按钮;
  4. moveit_task_constructor_visualization 中选择Motion Planning Tasks 并点击 OK。Motion Planning Tasks 应该在左下角出现显示;
  5. Displays 中的 Motion Planning Tasks 下,将 Task Solution Topic 改为/solution

你应该在主视角中看见 panda 机械臂,以及左下角的 Motion Planning Tasks 的显示面板。在启动mtc_tutorial节点后,你的 MTC 任务将在这个面板中显示。

启动 Demo

启动你的mtc_tutorial节点:

ros2 launch mtc_tutorial pick_place_demo.launch.py

你将会看见机械臂执行单独阶段的任务——打开机械手,以及显示其面前的绿色圆柱,像这样:

如果你没做你自己的功能包,可以直接打开教程写好的文件来显示: 

ros2 launch moveit2_tutorials mtc_demo_minimal.launch.py

添加阶段(Adding Stages)

到现在为止,我们已经创建并执行了一个简单的任务。在后半章节中,我们将开始添加抓取-放置阶段到任务中。

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

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

相关文章

详解Redis分布式锁在SpringBoot的@Async方法中没锁住的坑

背景 Redis分布式锁很有用处&#xff0c;在秒杀、抢购、订单、限流特别是一些用到异步分布式并行处理任务时频繁的用到&#xff0c;可以说它是一个BS架构的应用中最高频使用的技术之一。 但是我们经常会碰到这样的一个问题&#xff0c;那就是我们都按照标准做了但有时运行着、…

分层解耦-05.IOCDI-DI详解

一.依赖注入的注解 在我们的项目中&#xff0c;EmpService的实现类有两个&#xff0c;分别是EmpServiceA和EmpServiceB。这两个实现类都加上Service注解。我们运行程序&#xff0c;就会报错。 这是因为我们依赖注入的注解Autowired默认是按照类型来寻找bean对象的进行依赖注入…

基于Qt的速度仪表盘控件实现

本文将详细讲解一个基于Qt的速度仪表盘控件的实现过程&#xff0c;并对代码进行详细的注释说明。该控件可以模拟汽车仪表盘的外观&#xff0c;并通过滑动条动态改变速度显示。本文将从代码结构、绘制组件到实现细节进行讲解&#xff0c;帮助您理解如何使用Qt框架自定义绘制控件…

CSRF | GET 型 CSRF 漏洞攻击

关注这个漏洞的其他相关笔记&#xff1a;CSRF 漏洞 - 学习手册-CSDN博客 0x01&#xff1a;GET 型 CSRF 漏洞攻击 —— 理论篇 GET 型 CSRF 漏洞是指攻击者通过构造恶意的 HTTP GET 请求&#xff0c;利用用户的登录状态&#xff0c;在用户不知情的情况下&#xff0c;诱使浏览器…

Cortex-M3/M4/M7 芯片 Fault 分析原理与实战

目录 一、简介1、异常类型2、异常优先级3、同步异步问题4、异常具体类型 二、Fault exception registers1、Control registers1.1 CCR1.2 SHP1.3 SHCSR 2、Status and address registers2.1 HardFault Status Register——HSFR2.2 Configurable Fault Status Register——CFSR2…

《Linux从小白到高手》进阶实操篇:用户及权限有关的实际工作场景应用

List item 本篇为《Linux从小白到高手》进阶实操篇的第一篇&#xff0c;主要介绍分享一些用户及权限有关的实际工作场景应用。 场景1&#xff1a; 实际工作中你一定会碰到如下图所示的情景&#xff1a;本部门有5个组&#xff0c;分别为&#xff1a;①Root组&#xff1a;用户…

Python中对象obj类型确定最pythonic的方式——isinstance()函数

python中确定对象obj的类型&#xff0c;isinstance函数最是优雅&#xff0c;type、issubclass等函数也可以&#xff0c;但终究“曲折”。 (笔记模板由python脚本于2024年10月07日 19:42:38创建&#xff0c;本篇笔记适合喜欢python的coder翻阅) 【学习的细节是欢悦的历程】 Pyth…

Vue2电商项目(七)、订单与支付

文章目录 一、交易业务Trade1. 获取用户地址2. 获取订单信息 二、提交订单三、支付1. 获取支付信息2. 支付页面--ElementUI(1) 引入Element UI(2) 弹框支付的业务逻辑(这个逻辑其实没那么全)(3) 支付逻辑知识点小总结 四、个人中心1. 搭建二级路由2. 展示动态数据(1). 接口(2).…

【Kubernetes】常见面试题汇总(六十)

目录 131. pod 一直处于 pending 状态&#xff1f; 132. helm 安装组件失败&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-113 属于…

企业经营异常怎么解除

经营异常是怎么回事&#xff1f;是什么意思&#xff1f;了解异常原因&#xff1a;我们到所属工商营业执照异常的具体原因。原因可能包括未按时提交年报、未履行即时信息公示义务、公示信息隐瞒真实情况或弄xu作jia、失联等。纠正违规行为&#xff1a;查到了异常原因&#xff0c…

洛谷P5723、P5728、P1428、P1319 Python解析

P5723 完整代码 def is_prime(y):if y < 2:return Falsefor i in range(2, int(y**0.5) 1):if y % i 0:return Falsereturn Truen int(input()) sum_primes 0 x 0if n < 2:print("0") elif n 2:print("2\n1") else:for i in range(2, n 1):i…

计数原理与组合 - 离散数学系列(三)

目录 1. 计数原理的基本概念 加法原理&#xff08;Rule of Sum&#xff09; 乘法原理&#xff08;Rule of Product&#xff09; 2. 排列与组合 排列&#xff08;Permutation&#xff09; 组合&#xff08;Combination&#xff09; 日常生活中的例子 3. 二项式定理 4. 实…

Mysql锁机制解读(敲详细)

目录 锁的概念 全局锁 表级锁 表锁 元数据锁 意向锁 锁的概念 全局锁 表级锁 表锁 元数据锁 主要是对未提交事务&#xff0c;修改表结构造成表结构混乱&#xff0c;进行控制。 在不涉及表结构变化的情况下,元素锁可以忽略。 意向锁 避免有行级锁影响加表级锁&#xff0…

Mysql(六) --- 聚合函数,分组和联合查询

文章目录 前言1.聚合函数1.1.常用的函数1.2.COUNT()1.3.SUM()1.4.AVG()1.5.MIN()、MAX() 2.GROUP BY 分组查询2.1.语法2.2.示例2.3.HAVING 子句 3.联合查询3.1.为什么要进行联合查询3.2.那么是如何进行联合查询的3.3.示例&#xff1a;一个完整的联合查询的过程3.4.内连接3.5.外…

Error:WPF项目中使用oxyplot,错误提示命名空间中不存在“Plot”名称

在OxyPlot中&#xff0c;<oxy:PlotView>和<oxy:Plot>都是用来显示图表的控件&#xff0c;在WPF项目中使用oxyplot之前&#xff0c;先通过NuGet安装依赖包&#xff1a;OxyPlot.Wpf。 <oxy:PlotView>和<oxy:Plot>使用示例&#xff1a; <oxy:PlotVie…

【算法】双指针(续)

一、盛最多水的容器 11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09; 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多…

OJ在线评测系统 微服务 OpenFeign调整后端下 nacos注册中心配置 不给前端调用的代码 全局引入负载均衡器

OpenFeign内部调用二 4.修改各业务服务的调用代码为feignClient 开启nacos注册 把Client变成bean 该服务仅内部调用&#xff0c;不是给前端的 将某个服务标记为“内部调用”的目的主要有以下几个方面&#xff1a; 安全性: 内部API通常不对外部用户公开&#xff0c;这样可以防止…

Nginx05-基础配置案例

零、文章目录 Nginx05-基础配置案例 1、案例需求 &#xff08;1&#xff09;有如下访问 http://192.168.119.161:8081/server1/location1 访问的是&#xff1a;index_sr1_location1.htmlhttp://192.168.119.161:8081/server1/location2 访问的是&#xff1a;index_sr1_loca…

慢接口分析与优化总结

文章目录 1. 慢接口优化的意义2. 接口耗时构成3. 优化技巧3.1. 内部代码逻辑异步执行[异步思想]并行优化拒绝阻塞等待预分配与循环使用[池化思想]线程池合理设计锁粒度避免过粗优化程序结构 3.2. 缓存恰当引入缓存[空间换时间思想]缓存延迟优化提前初始化缓存[预取思想] 3.3. 数…

工具函数(截取文本第一个字为图片)

const subStringToImage (params) > {const { str ,color #FFF,background #4F54FF,size 60,fontSize 20 } paramsif(str.length < 0) return console.error(字符不能为空!)const text str.slice(0, 1)const canvas document.createElement(canvas)const ctx …