机器人环境仿真软件:Gazebo_(7).控制器仿真

控制器仿真

在机器人环境仿真中,控制器仿真是一个重要的环节。控制器仿真可以帮助开发人员在虚拟环境中测试和验证控制算法,而无需实际部署到物理机器人上。Gazebo 提供了丰富的控制器仿真功能,可以通过插件和 ROS(Robot Operating System)接口来实现复杂的控制逻辑。

1. 控制器插件

Gazebo 的控制器仿真主要通过插件来实现。插件是一种动态链接库,可以插入到 Gazebo 的模型中,以实现特定的控制功能。Gazebo 提供了多种内置控制器插件,同时也支持自定义插件的开发。

1.1 内置控制器插件

Gazebo 内置了一些常用的控制器插件,例如:

  • JointTrajectoryController:用于控制关节的位置、速度和加速度。

  • DiffDrivePlugin:用于控制差速驱动机器人的运动。

  • PIDController:用于实现比例-积分-微分(PID)控制。

1.1.1 JointTrajectoryController

JointTrajectoryController 是一个用于控制关节轨迹的插件。它通过接收关节位置、速度和加速度的指令,使机器人按照指定的轨迹运动。

1.1.1.1 配置文件示例

在 Gazebo 中使用 JointTrajectoryController 插件时,需要在模型的 SDF(Simulation Description Format)文件中进行配置。以下是一个配置示例:




<model name="robot">

  <link name="base_link">

    <pose>0 0 0.5 0 0 0pose>

    <inertial>

      <mass>1.0mass>

      <inertia>

        <ixx>0.1ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.1iyy>

        <iyz>0.0iyz>

        <izz>0.1izz>

      inertia>

    inertial>

    <collision name="base_collision">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    collision>

    <visual name="base_visual">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    visual>

  link>



  <joint name="joint1" type="revolute">

    <parent>base_linkparent>

    <child>link1child>

    <axis>

      <xyz>0 0 1xyz>

    axis>

  joint>



  <link name="link1">

    <pose>0.5 0 0.5 0 0 0pose>

    <inertial>

      <mass>0.5mass>

      <inertia>

        <ixx>0.05ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.05iyy>

        <iyz>0.0iyz>

        <izz>0.05izz>

      inertia>

    inertial>

    <collision name="link1_collision">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    collision>

    <visual name="link1_visual">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    visual>

  link>



  <plugin name="joint_trajectory_controller" filename="libgazebo_ros_joint_trajectory_controller.so">

    <namespace>/robot/joint_trajectory_controllernamespace>

    <joint_name>joint1joint_name>

    <update_rate>100.0update_rate>

  plugin>

model>

1.1.1.2 ROS 节点示例

接下来,我们可以通过 ROS 节点来发送关节轨迹指令。以下是一个使用 JointTrajectoryController 的 ROS 节点示例:


// joint_trajectory_publisher.cpp

#include 

#include 

#include 



int main(int argc, char** argv) {

  ros::init(argc, argv, "joint_trajectory_publisher");

  ros::NodeHandle nh;



  // 创建一个发布者

  ros::Publisher joint_trajectory_pub = nh.advertise<trajectory_msgs::JointTrajectory>("/robot/joint_trajectory_controller/command", 10);



  // 创建一个关节轨迹消息

  trajectory_msgs::JointTrajectory joint_trajectory;

  joint_trajectory.joint_names.push_back("joint1");



  // 创建一个关节轨迹点

  trajectory_msgs::JointTrajectoryPoint point;

  point.positions.push_back(1.0); // 设置关节的目标位置

  point.time_from_start = ros::Duration(5.0); // 设置到达目标位置的时间



  // 将关节轨迹点添加到关节轨迹消息中

  joint_trajectory.points.push_back(point);



  // 发布关节轨迹消息

  joint_trajectory_pub.publish(joint_trajectory);



  // 保持节点运行

  ros::spin();



  return 0;

}

2. 自定义控制器插件

除了使用内置的控制器插件,Gazebo 还支持自定义控制器插件的开发。自定义控制器插件可以实现特定的控制逻辑,满足特定的仿真需求。

2.1 开发自定义控制器插件

开发自定义控制器插件通常需要以下几个步骤:

  1. 创建插件类:继承 gazebo::ModelPlugin 类,并实现必要的回调函数。

  2. 加载模型:在 Load 函数中初始化插件,获取模型和关节的信息。

  3. 实现控制逻辑:在 OnUpdate 回调函数中实现控制逻辑。

  4. 编译和链接:编译插件并生成动态链接库。

2.1.1 创建插件类

以下是一个简单的自定义控制器插件类的示例:


// custom_controller.cpp

#include 

#include 

#include 

#include 

#include 



namespace gazebo {

  class CustomController : public ModelPlugin {

    public: void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {

      // 获取模型和关节

      this->model = _model;

      this->joint = model->GetJoint("joint1");



      // 创建一个更新连接

      this->updateConnection = event::Events::ConnectWorldUpdateBegin(

          std::bind(&CustomController::OnUpdate, this, std::placeholders::_1));



      // 初始化 PID 控制器

      this->pid = common::PID(1.0, 0.1, 0.01);

      this->joint->SetPositionPID(this->pid);

    }



    public: void OnUpdate(const common::UpdateInfo & /*_info*/) {

      // 获取当前关节位置

      double currentPosition = this->joint->Position(0);



      // 设置目标位置

      double targetPosition = 1.0;



      // 计算 PID 控制器的输出

      double output = this->pid.Update(currentPosition, targetPosition, 0.01);



      // 应用控制输出到关节

      this->joint->SetForce(0, output);

    }



    private: physics::ModelPtr model;

    private: physics::JointPtr joint;

    private: common::PID pid;

    private: event::ConnectionPtr updateConnection;

  };



  // 注册插件

  GZ_REGISTER_MODEL_PLUGIN(CustomController)

}

2.1.2 编译和链接

将上述代码保存为 custom_controller.cpp,然后编写 CMakeLists.txt 文件来编译插件:


# CMakeLists.txt

cmake_minimum_required(VERSION 2.8.3)

project(custom_controller)



find_package(gazebo REQUIRED)

include_directories(${GAZEBO_INCLUDE_DIRS})

link_directories(${GAZEBO_LIBRARY_DIRS})

list(APPEND CMAKE_CXX_FLAGS "${GAZEBO_CXX_FLAGS}")



# 编译插件

add_library(custom_controller SHARED custom_controller.cpp)

target_link_libraries(custom_controller ${GAZEBO_LIBRARIES})



# 安装插件

install(TARGETS custom_controller

  LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/gazebo/plugins

)

在终端中运行以下命令来编译插件:


mkdir build

cd build

cmake ..

make

将生成的动态链接库 libcustom_controller.so 放到 Gazebo 的插件目录中,例如 ~/.gazebo/plugins

2.1.3 配置模型

在 SDF 文件中配置自定义控制器插件:




<model name="robot">

  <link name="base_link">

    <pose>0 0 0.5 0 0 0pose>

    <inertial>

      <mass>1.0mass>

      <inertia>

        <ixx>0.1ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.1iyy>

        <iyz>0.0iyz>

        <izz>0.1izz>

      inertia>

    inertial>

    <collision name="base_collision">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    collision>

    <visual name="base_visual">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    visual>

  link>



  <joint name="joint1" type="revolute">

    <parent>base_linkparent>

    <child>link1child>

    <axis>

      <xyz>0 0 1xyz>

    axis>

  joint>



  <link name="link1">

    <pose>0.5 0 0.5 0 0 0pose>

    <inertial>

      <mass>0.5mass>

      <inertia>

        <ixx>0.05ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.05iyy>

        <iyz>0.0iyz>

        <izz>0.05izz>

      inertia>

    inertial>

    <collision name="link1_collision">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    collision>

    <visual name="link1_visual">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    visual>

  link>



  <plugin name="custom_controller" filename="libcustom_controller.so">

    <joint_name>joint1joint_name>

  plugin>

model>

3. 高级控制器仿真

除了基本的控制器仿真,Gazebo 还支持更高级的控制逻辑仿真,例如:

  • 多关节控制:同时控制多个关节。

  • 力矩控制:直接控制关节的力矩。

  • 状态反馈控制:使用传感器数据进行状态反馈控制。

3.1 多关节控制

多关节控制可以通过扩展 JointTrajectoryController 插件或自定义插件来实现。以下是一个自定义多关节控制插件的示例:


// multi_joint_controller.cpp

#include 

#include 

#include 



namespace gazebo {

  class MultiJointController : public ModelPlugin {

    public: void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {

      this->model = _model;



      // 获取关节列表

      this->joint1 = model->GetJoint("joint1");

      this->joint2 = model->GetJoint("joint2");



      // 创建一个更新连接

      this->updateConnection = event::Events::ConnectWorldUpdateBegin(

          std::bind(&MultiJointController::OnUpdate, this, std::placeholders::_1));



      // 初始化 PID 控制器

      this->pid1 = common::PID(1.0, 0.1, 0.01);

      this->pid2 = common::PID(1.0, 0.1, 0.01);



      this->joint1->SetPositionPID(this->pid1);

      this->joint2->SetPositionPID(this->pid2);

    }



    public: void OnUpdate(const common::UpdateInfo & /*_info*/) {

      // 获取当前关节位置

      double currentPosition1 = this->joint1->Position(0);

      double currentPosition2 = this->joint2->Position(0);



      // 设置目标位置

      double targetPosition1 = 1.0;

      double targetPosition2 = -1.0;



      // 计算 PID 控制器的输出

      double output1 = this->pid1.Update(currentPosition1, targetPosition1, 0.01);

      double output2 = this->pid2.Update(currentPosition2, targetPosition2, 0.01);



      // 应用控制输出到关节

      this->joint1->SetForce(0, output1);

      this->joint2->SetForce(0, output2);

    }



    private: physics::ModelPtr model;

    private: physics::JointPtr joint1;

    private: physics::JointPtr joint2;

    private: common::PID pid1;

    private: common::PID pid2;

    private: event::ConnectionPtr updateConnection;

  };



  // 注册插件

  GZ_REGISTER_MODEL_PLUGIN(MultiJointController)

}

3.2 力矩控制

力矩控制可以直接控制关节的力矩,而不是位置或速度。以下是一个自定义力矩控制插件的示例:


// torque_controller.cpp

#include 

#include 

#include 



namespace gazebo {

  class TorqueController : public ModelPlugin {

    public: void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {

      this->model = _model;

      this->joint = model->GetJoint("joint1");



      // 创建一个更新连接

      this->updateConnection = event::Events::ConnectWorldUpdateBegin(

          std::bind(&TorqueController::OnUpdate, this, std::placeholders::_1));

    }



    public: void OnUpdate(const common::UpdateInfo & /*_info*/) {

      // 设置目标力矩

      double targetTorque = 0.5;



      // 应用力矩到关节

      this->joint->SetForce(0, targetTorque);

    }



    private: physics::ModelPtr model;

    private: physics::JointPtr joint;

    private: event::ConnectionPtr updateConnection;

  };



  // 注册插件

  GZ_REGISTER_MODEL_PLUGIN(TorqueController)

}

3.3 状态反馈控制

状态反馈控制使用传感器数据来调整控制输出。以下是一个使用 IMU 传感器数据进行状态反馈控制的示例:


// imu_controller.cpp

#include 

#include 

#include 

#include 



namespace gazebo {

  class IMUController : public ModelPlugin {

    public: void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {

      this->model = _model;

      this->joint = model->GetJoint("joint1");



      // 获取 IMU 传感器

      this->imuSensor = sensors::get_sensor<sensors::ImuSensor>("imu_sensor");



      // 创建一个更新连接

      this->updateConnection = event::Events::ConnectWorldUpdateBegin(

          std::bind(&IMUController::OnUpdate, this, std::placeholders::_1));

    }



    public: void OnUpdate(const common::UpdateInfo & /*_info*/) {

      // 获取 IMU 传感器数据

      math::Vector3 gyro = this->imuSensor->GetAngularVelocity();

      math::Vector3 acc = this->imuSensor->GetLinearAcceleration();



      // 设置目标位置

      double targetPosition = 1.0;



      // 计算 PID 控制器的输出

      double currentPosition = this->joint->Position(0);

      double output = this->pid.Update(currentPosition, targetPosition, 0.01);



      // 应用控制输出到关节

      this->joint->SetForce(0, output);



      // 打印传感器数据

      ROS_INFO_STREAM("Gyro: " << gyro << " Acc: " << acc);

    }



    private: physics::ModelPtr model;

    private: physics::JointPtr joint;

    private: sensors::ImuSensorPtr imuSensor;

    private: common::PID pid;

    private: event::ConnectionPtr updateConnection;

  };



  // 注册插件

  GZ_REGISTER_MODEL_PLUGIN(IMUController)

}

4. 控制器仿真优化

为了提高控制器仿真的性能和准确性,可以采取以下几种优化措施:

  • 减少更新频率:根据控制算法的需求,适当减少控制器的更新频率。

  • 使用高精度传感器:选择高精度的传感器模型,以提高反馈数据的准确性。

  • 优化 PID 参数:通过调整 PID 参数,使控制算法更加稳定和高效。

4.1 减少更新频率

在插件的 Load 函数中,可以设置更新频率来控制插件的更新速率。减少更新频率可以降低计算负载,提高仿真效率。以下是一个示例,展示了如何在自定义插件中设置更新频率:


// custom_controller.cpp

#include 

#include 

#include 

#include 

#include 



namespace gazebo {

  class CustomController : public ModelPlugin {

    public: void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {

      // 获取模型和关节

      this->model = _model;

      this->joint = model->GetJoint("joint1");



      // 设置更新频率

      double updateRate = 100.0; // 100 Hz

      if (_sdf->HasElement("update_rate")) {

        updateRate = _sdf->Get<double>("update_rate");

      }

      this->updatePeriod = 1.0 / updateRate;



      // 创建一个更新连接

      this->updateConnection = event::Events::ConnectWorldUpdateBegin(

          std::bind(&CustomController::OnUpdate, this, std::placeholders::_1));



      // 初始化 PID 控制器

      this->pid = common::PID(1.0, 0.1, 0.01);

      this->joint->SetPositionPID(this->pid);

    }



    public: void OnUpdate(const common::UpdateInfo & _info) {

      // 获取当前仿真时间

      double currentTime = _info.simTime.Double();



      // 检查是否需要更新

      if (currentTime - this->lastUpdateTime >= this->updatePeriod) {

        // 获取当前关节位置

        double currentPosition = this->joint->Position(0);



        // 设置目标位置

        double targetPosition = 1.0;



        // 计算 PID 控制器的输出

        double output = this->pid.Update(currentPosition, targetPosition, this->updatePeriod);



        // 应用控制输出到关节

        this->joint->SetForce(0, output);



        // 更新上次更新时间

        this->lastUpdateTime = currentTime;

      }

    }



    private: physics::ModelPtr model;

    private: physics::JointPtr joint;

    private: common::PID pid;

    private: event::ConnectionPtr updateConnection;

    private: double updatePeriod;

    private: double lastUpdateTime = 0.0;

  };



  // 注册插件

  GZ_REGISTER_MODEL_PLUGIN(CustomController)

}

在 SDF 文件中,可以通过添加 update_rate 元素来设置插件的更新频率:




<model name="robot">

  <link name="base_link">

    <pose>0 0 0.5 0 0 0pose>

    <inertial>

      <mass>1.0mass>

      <inertia>

        <ixx>0.1ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.1iyy>

        <iyz>0.0iyz>

        <izz>0.1izz>

      inertia>

    inertial>

    <collision name="base_collision">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    collision>

    <visual name="base_visual">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    visual>

  link>



  <joint name="joint1" type="revolute">

    <parent>base_linkparent>

    <child>link1child>

    <axis>

      <xyz>0 0 1xyz>

    axis>

  joint>



  <link name="link1">

    <pose>0.5 0 0.5 0 0 0pose>

    <inertial>

      <mass>0.5mass>

      <inertia>

        <ixx>0.05ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.05iyy>

        <iyz>0.0iyz>

        <izz>0.05izz>

      inertia>

    inertial>

    <collision name="link1_collision">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    collision>

    <visual name="link1_visual">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    visual>

  link>



  <plugin name="custom_controller" filename="libcustom_controller.so">

    <joint_name>joint1joint_name>

    <update_rate>50.0update_rate> 

  plugin>

model>

4.2 使用高精度传感器

选择高精度的传感器模型可以提高反馈数据的准确性,从而提高控制算法的性能。Gazebo 提供了多种传感器模型,例如 IMU、激光雷达(LIDAR)、相机等。在配置传感器时,可以调整传感器的参数以提高其精度。

4.2.1 配置 IMU 传感器

以下是一个配置高精度 IMU 传感器的 SDF 文件示例:




<model name="robot">

  <link name="base_link">

    <pose>0 0 0.5 0 0 0pose>

    <inertial>

      <mass>1.0mass>

      <inertia>

        <ixx>0.1ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.1iyy>

        <iyz>0.0iyz>

        <izz>0.1izz>

      inertia>

    inertial>

    <collision name="base_collision">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    collision>

    <visual name="base_visual">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    visual>



    

    <sensor name="imu_sensor" type="imu">

      <pose>0 0 0.5 0 0 0pose>

      <topic>imutopic>

      <update_rate>100.0update_rate> 

      <always_on>1always_on>

      <visualize>1visualize>

      <imu>

        <noise>

          <type>gaussiantype>

          <mean>0.0mean>

          <stddev>0.001stddev>

        noise>

      imu>

    sensor>

  link>



  <joint name="joint1" type="revolute">

    <parent>base_linkparent>

    <child>link1child>

    <axis>

      <xyz>0 0 1xyz>

    axis>

  joint>



  <link name="link1">

    <pose>0.5 0 0.5 0 0 0pose>

    <inertial>

      <mass>0.5mass>

      <inertia>

        <ixx>0.05ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.05iyy>

        <iyz>0.0iyz>

        <izz>0.05izz>

      inertia>

    inertial>

    <collision name="link1_collision">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    collision>

    <visual name="link1_visual">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    visual>

  link>



  <plugin name="imu_controller" filename="libimu_controller.so">

    <joint_name>joint1joint_name>

  plugin>

model>

4.3 优化 PID 参数

PID 控制器的参数(比例、积分、微分)对控制算法的性能有显著影响。通过调整这些参数,可以使控制算法更加稳定和高效。以下是一个调整 PID 参数的示例:


// custom_controller.cpp

#include 

#include 

#include 



namespace gazebo {

  class CustomController : public ModelPlugin {

    public: void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {

      this->model = _model;

      this->joint = model->GetJoint("joint1");



      // 创建一个更新连接

      this->updateConnection = event::Events::ConnectWorldUpdateBegin(

          std::bind(&CustomController::OnUpdate, this, std::placeholders::_1));



      // 初始化 PID 控制器

      double p = 1.0;

      double i = 0.1;

      double d = 0.01;

      if (_sdf->HasElement("p")) {

        p = _sdf->Get<double>("p");

      }

      if (_sdf->HasElement("i")) {

        i = _sdf->Get<double>("i");

      }

      if (_sdf->HasElement("d")) {

        d = _sdf->Get<double>("d");

      }

      this->pid = common::PID(p, i, d);

      this->joint->SetPositionPID(this->pid);

    }



    public: void OnUpdate(const common::UpdateInfo & /*_info*/) {

      // 获取当前关节位置

      double currentPosition = this->joint->Position(0);



      // 设置目标位置

      double targetPosition = 1.0;



      // 计算 PID 控制器的输出

      double output = this->pid.Update(currentPosition, targetPosition, 0.01);



      // 应用控制输出到关节

      this->joint->SetForce(0, output);

    }



    private: physics::ModelPtr model;

    private: physics::JointPtr joint;

    private: common::PID pid;

    private: event::ConnectionPtr updateConnection;

  };



  // 注册插件

  GZ_REGISTER_MODEL_PLUGIN(CustomController)

}

在 SDF 文件中,可以通过添加 pid 元素来配置 PID 参数:




<model name="robot">

  <link name="base_link">

    <pose>0 0 0.5 0 0 0pose>

    <inertial>

      <mass>1.0mass>

      <inertia>

        <ixx>0.1ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.1iyy>

        <iyz>0.0iyz>

        <izz>0.1izz>

      inertia>

    inertial>

    <collision name="base_collision">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    collision>

    <visual name="base_visual">

      <geometry>

        <box>

          <size>0.5 0.5 1.0size>

        box>

      geometry>

    visual>

  link>



  <joint name="joint1" type="revolute">

    <parent>base_linkparent>

    <child>link1child>

    <axis>

      <xyz>0 0 1xyz>

    axis>

  joint>



  <link name="link1">

    <pose>0.5 0 0.5 0 0 0pose>

    <inertial>

      <mass>0.5mass>

      <inertia>

        <ixx>0.05ixx>

        <ixy>0.0ixy>

        <ixz>0.0ixz>

        <iyy>0.05iyy>

        <iyz>0.0iyz>

        <izz>0.05izz>

      inertia>

    inertial>

    <collision name="link1_collision">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    collision>

    <visual name="link1_visual">

      <geometry>

        <cylinder>

          <radius>0.1radius>

          <length>0.5length>

        cylinder>

      geometry>

    visual>

  link>



  <plugin name="custom_controller" filename="libcustom_controller.so">

    <joint_name>joint1joint_name>

    <p>2.0p>

    <i>0.05i>

    <d>0.005d>

  plugin>

model>

通过这些优化措施,可以显著提高控制器仿真的性能和准确性,使仿真更加接近实际情况。

在这里插入图片描述

你可能感兴趣的:(机器人仿真,机器人,图像处理,计算机视觉,机器学习,深度学习)