ROS控制:ROS Control软件包

一、说明

        在文中,我们将了解一个有用的软件包ros_control,但它难以理解。我们将首先简要了解什么是控制及其在机器人技术中的重要性。然后了解ros_control包如何派上用场来控制我们的机器人。​让我列出我们将要讨论的主题。​

二、内容:

1. 为什么要控制。

2. 什么是ROS控制。

3. ros_control封装中的控制器类型。

4.如何写hardware_interface。

5.如何在机器人上使用ros_control。

​让我们开始吧。

三、为什么选择Control..?

        我们在工程中听说过很多关于控制系统和PID控制器的信息。如今,控制系统无处不在,从汽车(用于控制克鲁斯模式下的速度)到月球着陆器(用于控制软着陆的方向和速度)。

        无论如何,此页面没有解释控制系统,但我们将看到如何在机器人中使用 ROS 控制包。我假设您对什么是 PID 控制系统及其工作原理有先验知识。

 图1反馈回路中PID控制器的 框图

        图片来源:https://en.wikipedia.org/wiki/PID_controller

        表1独立增加参数的影响

        表格来自:https://en.wikipedia.org/wiki/PID_controller

        首先,让我们谈谈为什么我们需要机器人控制。

        当我们处理机器人时,我们需要确保它的运动受到控制,即动作既不太慢也不太快,并且遵循指定的轨迹。


        想象一下,你想从桌子上拿起/放置一个物体,然后你移动你的手,使你/物体不会用力撞击桌子,而且你在可能在很短的时间内放置它。在这里,您可以在整个运动过程中控制手的位置和速度。我们也必须将同样的东西应用于机器人。每当我们需要机器人在正确的时间将物体放置在正确的位置时,我们都需要控制它的运动。

        ROS为我们提供了一组软件包,可用于使用PID控制器控制机器人的运动。如果您愿意,也可以编写自己的控制器插件。但是,当我们开始编写自己的控制器时,我们需要处理很多事情,例如采样率、导数踢、动态调谐参数、复位、结束缓解等。所有这些方面都得到了照顾和打包,供我们在 ROS 中使用。

        ROS提供了一个称为ros_control的通用控制包,可用于我们的机器人,从而节省了我们重写控制器代码的时间。

        现在让我们继续回答什么是ROS控制的问题。

四、什么是ROS控制..?

        ROS_Control 是一组软件包,包括控制器接口、控制器管理器、传输、硬件接口和控制工具箱。所有这些软件包将允许您交互和控制机器人的关节执行器。

 图2ROS控制 框图

        图片来源:ros_control - ROS Wiki

        如图 3 所示,ros_control将联合状态数据和输入设定点(目标)作为用户/第三方应用程序的输入,并将适当的命令作为输出发送到执行器。为了达到提供的设定点(目标),它使用通用的控制回路反馈机制(通常是PID控制器)来控制输出。该输出通过硬件接口传递给机器人。

 图3 ROS控制大图

       

        图片来源:ROSCon 2014

        第三方块 (Moveit/Navigation Stack) 表示向ros_control包发送目标的块。控制器(base_controller/arm_controller/基本控制器)负责计算实现设定目标所需的输出命令。为了移动关节,控制器必须与执行器进行通信。硬件接口节点(RobotHW)位于控制器和真实硬件之间,它将命令执行器移动并从关节传感器获得反馈。这就是ros_control的工作方式。

        查看下面的视频以更好地理解,

        在上面的视频中,我使用了工作量控制器。点击这里获取源代码。

        还有其他控制器。让我们检查一下控制器列表。

五、ROS控制包中的控制器类型

        ROS控制包提供了一组控制器插件,以不同的方式与机器人的关节进行交互。列表如下:

  • joint_state_controller:该控制器读取所有联合位置,并将它们发布到主题“/joint_states”上。该控制器不向执行器发送任何命令。用作“joint_state_controller/JointStateController”。

  • effort_controllers:当您要向工作量接口发送命令时使用。这意味着您要控制的联合执行器接受力 [当前(或)电压] 命令。

JointPosition控制器

        用作“effort_controllers/JointPositionController”。此控制器插件接受位置 [弧度(或)米] 值作为输入。错误(目标位置 - 当前位置)通过 PID 循环映射到输出努力命令。

JointVelocity控制器

        用作“effort_controllers/JointVelocityController”。此控制器插件接受速度 [弧度/秒(或)米/秒] 值作为输入。误差(目标速度 - 当前速度)通过 PID 回路映射到输出努力命令。

JointEffort控制器

        用作“effort_controllers/JointEffortController”。此控制器插件接受力 [力(或)扭矩] 值作为输入。输入力作为输出力命令简单地转发到联合执行器。P、I 和 D 的值对输出工作量没有影响。

JointGroupPositionController

        用作“effort_controllers/JointGroupPositionController”。此控制器的功能与一组关节的 effort_controllers/JointPositionController 相同。

JointGroupEffortController

        用作“effort_controllers/JointGroupEffortController”。此控制器的功能与一组关节的 effort_controllers/JointEffortController 相同。

  • velocity_controllers:当您要向速度接口发送命令时使用。这意味着您要控制的联合执行器直接接受速度命令。

JointPosition控制器

        用作“velocity_controllers/JointPositionController”。此控制器插件接受位置 [弧度(或)米] 值作为输入。误差(目标位置 - 当前位置)通过 PID 回路映射到输出速度命令。

JointVelocity控制器

        用作“velocity_controllers/JointVelocityController”。此控制器插件接受速度 [弧度/秒(或)米/秒] 值作为输入。输入速度值作为输出命令简单地转发给关节执行器。P、I 和 D 的值对输出命令没有影响。

JointGroupVelocityController

        用作“velocity_controllers/JointGroupVelocityController”。此控制器的功能与一组关节的 velocity_controllers/JointVelocityController 相同。

  • position_controllers:当您想向仓位接口发送命令时使用。这意味着您要控制的联合执行器直接接受位置命令。

JointPosition控制器

        用作“position_controllers/JointPositionController”。此控制器插件接受位置 [弧度(或)米] 值作为输入。输入位置值作为输出命令简单地转发给关节执行器。P、I 和 D 的值对输出命令没有影响。

JointGroupPositionController

        用作“position_controllers/JointGroupPositionController”。此控制器的功能与一组关节的 position_controllers/JointPositionController 相同。

  • joint_trajectory_controllers:该控制器用于在一组关节上执行关节空间轨迹。轨迹被指定为一组要到达的航点。航点由位置以及可选的速度和加速度组成。有关此控制器的更多信息,请单击此处

effort_controllers 

        用作“effort_controllers/JointTrajectoryController”。该控制器插件用于接受力 [电流(或)电压] 的联合执行器。误差后的位置+速度轨迹通过PID回路映射到输出努力命令。

velocity_controllers 

        用作“velocity_controllers/JointTrajectoryController”。该控制器插件用于直接接受速度命令的联合执行器。位置+速度轨迹跟随误差通过PID回路映射到输出速度命令。

position_controllers 

        用作“position_controllers/JointTrajectoryController”。该控制器插件用于直接接受位置命令的联合执行器。指定轨迹中的所需位置被简单地转发到关节。P、I 和 D 的值对输出命令没有影响。

呵......!!这个名单很大,不是吗。所以你认为控制器列表到此为止?

        还没有,我上面列出的控制器类型是基本和最常用的控制器。ros_control软件包提供了一些更有用的控制器。我把它留给你的兴趣。如果您想了解控制器的完整列表,请查看ros_control包中ros_controllers的源代码。

        现在,您必须根据您使用的关节执行器/电机的类型以及要控制的内容(位置、速度、轨迹或力)来决定使用哪个控制器插件,joint_state_controller因为joint_state_controller始终设置,因为它提供了机器人的当前关节状态。顺便说一句,不要忘记,您不仅限于使用ros_control包中的控制器。如果您对现有控制器不满意,您可以随时编写自己的控制器插件并使用它。

目前为止,一切都好。

        现在我们已经准备好了执行器/电机(真正的硬件)和 ros 控制器,但我们仍然需要其他东西来控制机器人。你能猜到缺少的部分是什么吗?

        它是硬件接口节点。现在让我们学习如何编写一个硬件接口节点,让 ros 控制器和真正的硬件进行通信。

六、如何写hardware_interface:

        hardware_interface实现了用于构建机器人硬件抽象的所有构建块。它是机器人的软件表示。我们有一组用于不同类型的电机/执行器和传感器的接口,如下所示:

  • 关节执行器接口:

        - EffortJoint接口:

        该接口用于接受力(电压或电流)命令的联合执行器。正如您现在可能已经猜到的那样,此接口与effort_controllers一起使用。

        - VelocityJoint接口:

        该接口用于直接接受速度命令的联合执行器。与velocity_controllers一起使用。

        - PositionJoint接口:

        该接口用于直接接受位置命令的联合执行器。与position_controllers一起使用。

  • 关节传感器接口:

        - JointState接口:

        当您有传感器来获取关节的当前位置或/和速度或/和力(力/扭矩)时,使用此接口。与joint_state_controller一起使用。

        JointStateInterface 几乎用于所有机器人,因为它获取机器人的当前关节位置,tf/tf2 反过来使用这些数据来计算机器人的正向运动学。

        - ImuSensorInterface:

        当您有一个 IMU 传感器时,会使用此接口,该传感器用于获取关节/机器人的方向、角速度和线性加速度。这与imu_sensor_controller一起使用。

        根据您使用的电机和传感器的类型,您必须相应地选择接口。现在我们已经了解了可用于关节执行器和传感器的接口类型,让我们学习如何编写hardware_interface节点(我们机器人的软件表示)。

        因此,让我们以一个具有三个关节的机器人为例,并为此编写硬件接口。让我们开始吧。

        我们的机器人有三个执行器:JointA、JointB 和 JointC,每个关节处都有位置传感器。假设 JointA 和 JointB 接受力度命令,而 JointC 接受位置命令。好的,我们已经有足够的机器人信息来编写硬件接口,所以让我们开始编写代码。

 七、现实的构建程序

        好吧,我们需要在机器人的catkin包中创建一个头文件和一个cpp文件。让我将它们命名为 MyRobot_hardware_interface.h 和 MyRobot_hardware_interface.cpp

7.1 先来看看MyRobot_hardware_interface.h

#include <hardware_interface/joint_state_interface.h>
#include <hardware_interface/joint_command_interface.h>
#include <hardware_interface/robot_hw.h>
#include <joint_limits_interface/joint_limits.h>
#include <joint_limits_interface/joint_limits_interface.h>
#include <controller_manager/controller_manager.h>
#include <boost/scoped_ptr.hpp>
#include <ros/ros.h>

class MyRobot : public hardware_interface::RobotHW 
{
    public:
        MyRobot(ros::NodeHandle& nh);
        ~MyRobot();
        void init();
        void update(const ros::TimerEvent& e);
        void read();
        void write(ros::Duration elapsed_time);
        
    protected:
        hardware_interface::JointStateInterface joint_state_interface_;
        hardware_interface::EffortJointInterface effort_joint_interface_;
        hardware_interface::PositionJointInterface position_joint_interface_;
        
        joint_limits_interface::JointLimits limits;
        joint_limits_interface::EffortJointSaturationInterface effortJointSaturationInterface;
        joint_limits_interface::PositionJointSaturationInterface positionJointSaturationInterface;
        

        double joint_position_[3];
        double joint_velocity_[3];
        double joint_effort_[3];
        double joint_effort_command_[2];
        double joint_position_command_;
        
        ros::NodeHandle nh_;
        ros::Timer my_control_loop_;
        ros::Duration elapsed_time_;
        double loop_hz_;
        boost::shared_ptr<controller_manager::ControllerManager> controller_manager_;
};

        让我们分解上面头文件的代码并理解它。

#include <hardware_interface/joint_state_interface.h>
#include <hardware_interface/joint_command_interface.h>
#include <hardware_interface/robot_hw.h>
#include <joint_limits_interface/joint_limits.h>
#include <joint_limits_interface/joint_limits_interface.h>
#include <controller_manager/controller_manager.h>
#include <boost/scoped_ptr.hpp>
#include <ros/ros.h>

包括所有必需的头文件。

class MyRobot : public hardware_interface::RobotHW 

{

        这是一类机器人硬件,包括读取关节传感器数据、向电机发送命令、控制回路以及关节接口数据的方法。

 public:
        MyRobot(ros::NodeHandle& nh);
        ~MyRobot();
        void init();
        void update(const ros::TimerEvent& e);
        void read();
        void write(ros::Duration elapsed_time);

        以下是 MyRobot 类的构造函数、析构函数和其他方法。

  • init() 方法是我们定义所有关节句柄、关节接口和关节限制接口的地方。

  • update() 方法是控制 loop()。

  • read() 方法用于读取关节传感器数据。

  • write() 方法向电机发送命令。​

        我们将在 cpp 文件中看到这些定义。

 protected:
        hardware_interface::JointStateInterface joint_state_interface_;
        hardware_interface::EffortJointInterface effort_joint_interface_;
        hardware_interface::PositionJointInterface position_joint_interface_;
        
        joint_limits_interface::JointLimits limits;
        joint_limits_interface::EffortJointSaturationInterface effortJointSaturationInterface;
        joint_limits_interface::PositionJointSaturationInterface positionJointSaturationInterface;

        声明机器人执行器/电机正在使用的关节接口和关节限制接口的类型。由于我们的示例机器人只有力和位置接受电机,因此我声明了关节接口和力和位置的限制接口。如果有一些电机接受速度命令,则声明速度对应的接口。

        double joint_position_[3];
        double joint_velocity_[3];
        double joint_effort_[3];
        double joint_effort_command_[2];
        double joint_position_command_;

        joint_position_[3]、joint_velocity_[3]、joint_effort_[3]是用于读取机器人关节位置、速度和力的数组变量。这些变量由joint_state_interface使用。数组的大小为 3,因为我们的示例机器人总共有 3 个关节。joint_effort_command_[2] 数组用于向 JointA 和 JointB 发送命令。joint_position_command_用于向 JointC 发送命令。

        ros::NodeHandle nh_;
        ros::Timer my_control_loop_;
        ros::Duration elapsed_time_;
        double loop_hz_;
        boost::shared_ptr<controller_manager::ControllerManager> controller_manager_;
};

        以下是我们的 hardware_interface 节点中使用的其他一些变量。my_control_loop_是一个定时器,它以设定的频率(loop_hz_)定期调用控制循环(更新方法)。

        好的,我们已经完成了对听者文件的理解。所以接下来让我们来看看

7.2 再看看MyRobot_hardware_interface.cpp

#include <YOUR_PACKAGE_NAME/MYRobot_hardware_interface.h>​

MyRobot::MyRobot(ros::NodeHandle& nh) : nh_(nh) { 

// Declare all JointHandles, JointInterfaces and JointLimitInterfaces of the robot.
    init();
    
// Create the controller manager
    controller_manager_.reset(new controller_manager::ControllerManager(this, nh_));
    
//Set the frequency of the control loop.
    loop_hz_=10;
    ros::Duration update_freq = ros::Duration(1.0/loop_hz_);
    
//Run the control loop
    my_control_loop_ = nh_.createTimer(update_freq, &MyRobot::update, this);
}​

MyRobot::~MyRobot() {
}

​

void MyRobot::init() {
        
// Create joint_state_interface for JointA
    hardware_interface::JointStateHandle jointStateHandleA("JointA", &joint_position_[0], &joint_velocity_[0], &joint_effort_[0]);
    joint_state_interface_.registerHandle(jointStateHandleA);
// Create effort joint interface as JointA accepts effort command.
    hardware_interface::JointHandle jointEffortHandleA(jointStateHandleA, &joint_effort_command_[0]);
    effort_joint_interface_.registerHandle(jointEffortHandleA); 
// Create Joint Limit interface for JointA
    joint_limits_interface::getJointLimits("JointA", nh_, limits);
    joint_limits_interface::EffortJointSaturationHandle jointLimitsHandleA(jointEffortHandleA, limits);
    effortJointSaturationInterface.registerHandle(jointLimitsHandleA);    

    
// Create joint_state_interface for JointB
    hardware_interface::JointStateHandle jointStateHandleB("JointB", &joint_position_[1], &joint_velocity_[1], &joint_effort_[1]);
    joint_state_interface_.registerHandle(jointStateHandleB);

// Create effort joint interface as JointB accepts effort command..
    hardware_interface::JointHandle jointEffortHandleB(jointStateHandleB, &joint_effort_command_[1]);
    effort_joint_interface_.registerHandle(jointEffortHandleB);
// Create Joint Limit interface for JointB
    joint_limits_interface::getJointLimits("JointB", nh_, limits);
    joint_limits_interface::EffortJointSaturationHandle jointLimitsHandleB(jointEffortHandleB, limits);
    effortJointSaturationInterface.registerHandle(jointLimitsHandleB);    
    
// Create joint_state_interface for JointC
    hardware_interface::JointStateHandle jointStateHandleC("JointC", &joint_position_[2], &joint_velocity_[2], &joint_effort_[2]);
    joint_state_interface_.registerHandle(jointStateHandleC);

// Create position joint interface as JointC accepts position command.
    hardware_interface::JointHandle jointPositionHandleC(jointStateHandleC, &joint_position_command_);
    position_joint_interface_.registerHandle(jointPositionHandleC);
// Create Joint Limit interface for JointC
    joint_limits_interface::getJointLimits("JointC", nh_, limits);
    joint_limits_interface::PositionJointSaturationHandle jointLimitsHandleC(jointPositionHandleC, limits);
    positionJointSaturationInterface.registerHandle(jointLimitsHandleC);    


// Register all joints interfaces    
    registerInterface(&joint_state_interface_);
    registerInterface(&effort_joint_interface_);
    registerInterface(&position_joint_interface_);
    registerInterface(&effortJointSaturationInterface);
    registerInterface(&positionJointSaturationInterface);    
}

​

//This is the control loop
void MyRobot::update(const ros::TimerEvent& e) {
    elapsed_time_ = ros::Duration(e.current_real - e.last_real);
    read();
    controller_manager_->update(ros::Time::now(), elapsed_time_);
    write(elapsed_time_);
}

​

void MyRobot::read() {​

  // Write the protocol (I2C/CAN/ros_serial/ros_industrial)used to get the current joint position and/or velocity and/or effort       

  //from robot.
  // and fill JointStateHandle variables joint_position_[i], joint_velocity_[i] and joint_effort_[i]

}

​

void MyRobot::write(ros::Duration elapsed_time) {
  // Safety
  effortJointSaturationInterface.enforceLimits(elapsed_time);   // enforce limits for JointA and JointB
  positionJointSaturationInterface.enforceLimits(elapsed_time); // enforce limits for JointC


  // Write the protocol (I2C/CAN/ros_serial/ros_industrial)used to send the commands to the robot's actuators.
  // the output commands need to send are joint_effort_command_[0] for JointA, joint_effort_command_[1] for JointB and 

  //joint_position_command_ for JointC.

}

​

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

    //Initialze the ROS node.
    ros::init(argc, argv, "MyRobot_hardware_inerface_node");
    ros::NodeHandle nh;
    
    //Separate Sinner thread for the Non-Real time callbacks such as service callbacks to load controllers
    ros::MultiThreadedspinner(2); 
    
    
    // Create the object of the robot hardware_interface class and spin the thread. 
    MyRobot ROBOT(nh);
    spinner.spin();
    
    return 0;
}

        我希望上面的 cpp 文件的大部分内容在评论的帮助下是不言自明的,我将只解释重要的行。

my_control_loop_ = nh_.createTimer(update_freq, &MyRobot::update, this);

        此行将使 update() 方法(PID 控制回路)以指定的频率周期性循环。

//This is the control loop
void MyRobot::update(const ros::TimerEvent& e) {
    elapsed_time_ = ros::Duration(e.current_real - e.last_real);
    read();
    controller_manager_->update(ros::Time::now(), elapsed_time_);
    write(elapsed_time_);
}

        update() 方法将简单地调用 read() 方法来获取当前的联合状态。然后,将当前关节状态更新到控制器管理器,以计算误差(当前 - 目标),并使用 PID 环路为每个关节生成输出命令。最后调用 write() 方法将输出命令发送到执行器/关节。

        有了这个,我们就完全理解了如何为任何机器人编写hardware_interface。您可以将上述代码用作样板代码,用于为机器人编写硬件接口和控制回路。

        我衷心感谢 Slate Robotics。这篇博客帮助我理解了如何编写硬件接口节点。您可以在此处查看TR1机器人的hardware_interface节点。

        到目前为止,我们已经了解了 ros 控制器的类型、hardware_interfaces以及如何为自定义机器人编写 hardware_interface 节点,但我们仍然没有开始控制机器人。我们只落后一步就可以开始控制机器人了。因此,让我们迈出这一步,看看还需要什么才能开始控制机器人。

八、如何在机器人上使用ros_control

        在为您的机器人编写硬件接口节点后,您必须编写一些配置文件来声明控制机器人的关节执行器的控制器和关节限制。让我们跳回我们的 3 关节机器人并编写配置文件和启动文件以开始控制我们的机器人。

        在编写配置文件之前,我们必须首先确定我们实际想要控制的内容(是关节的位置、力还是速度)。如果你还记得,在以这个机器人为例时,我说我们只有关节位置传感器,这意味着我们只能获得关节的位置反馈。因此,我们将编写配置文件来控制机器人的位置。我将用于定义控制器的配置文件命名为 controllers.yaml,将用于描述联合限制的 joint_limits.yaml 命名为 controllers.yaml

因此,让我们浏览controllers.yaml文件

MyRobot:


    # Publish all joint states
    joints_update:
      type: joint_state_controller/JointStateController
      publish_rate: 50
    
    JointA_EffortController:                                               # Name of the controller
      type: effort_controllers/JointPositionController         # Since JointA uses effort interface this controller type is  used      
      joint: JointA                                                                # Name of the joint for which this controller belongs to.
      pid: {p: 100.0, i: 10.0, d: 1.0}                                    # PID values
    
    JointB_EffortController:                                             
      type: effort_controllers/JointPositionController        # Since JointB uses effort interface this controller type is  used   
      joint: JointB                                                                 
      pid: {p: 1.0, i: 1.0, d: 0.0}

    JointC_PositionController:
      type: position_controllers/JointPositionController    # Since JointC uses position interface this controller type is  used 
      joint: JointC

      # No PID values defined since this controller simply passes the input position command to the actuators.

        示例机器人的 controllers.yaml 文件到此结束。因此,根据联合接口和需要控制的内容,决定控制器的类型。希望您已经阅读了上面的控制器类型部分,然后您可以轻松决定将哪种控制器类型用于机器人的关节。

        现在让我们检查格式以定义接头的限制。看看下面的 joint_limts.yaml 文件,

joint_limits:


    JointA:
      has_position_limits: true
      min_position: -1.57
      max_position: 1.57
      has_velocity_limits: true
      max_velocity: 1.5
      has_acceleration_limits: false
      max_acceleration: 0.0
      has_jerk_limits: false
      max_jerk: 0
      has_effort_limits: true
      max_effort: 255

​

    JointB:
      has_position_limits: true
      min_position: 0
      max_position: 3.14
      has_velocity_limits: true
      max_velocity: 1.5
      has_acceleration_limits: false
      max_acceleration: 0.0
      has_jerk_limits: false
      max_jerk: 0
      has_effort_limits: true
      max_effort: 255
      
    JointC:
      has_position_limits: true
      min_position: 0
      max_position: 3.14
      has_velocity_limits: false
      max_velocity: 0
      has_acceleration_limits: false
      max_acceleration: 0.0
      has_jerk_limits: false
      max_jerk: 0
      has_effort_limits: false
      max_effort: 0

        这是编写机器人关节限制的格式。这些关节极限值由hardware_interface节点中定义的joint_limits_interface使用,通过强制执行输出命令将机器人保持在安全限值内。

        这样就完成了为我们的机器人编写配置文件。因此,让我们编写一个启动文件来加载配置文件,运行hardware_interface节点,启动控制器管理器并加载控制器。让我将其命名为 MyRobot_control.launch。

        让我们看看下面的MyRobot_control.launch文件,

<launch>
    
  <rosparam file="$(find YOUR_PACKAGE_NAME)/config/controllers.yaml" command="load"/>
  <rosparam file="$(find YOUR_PACKAGE_NAME)/config/joint_limits.yaml" command="load"/>

​

  <node name="MyRobotHardwareInterface" pkg="YOUR_PACKAGE_NAME" type="MyRobot_hardware_inerface_node" output="screen"/>

​

  <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"/ >
    
  <node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false" output="screen"
        args="
            /MyRobot/joints_update
            /MyRobot/JointA_EffortController
            /MyRobot/JointB_EffortController
            /MyRobot/JointC_PositionController
        "/> 


</launch>

        同样,上面的启动文件是不言自明的,但我想谈谈controller_manager。在上面的启动中,文件控制器通过控制器“spawner”python脚本加载到controller_manager中。还有另一种加载控制器的方法。控制器通过服务请求加载到controller_manager中。这些服务请求可以在命令行中手动发送。单击此处了解有关如何使用命令行加载/启动/停止控制器的更多信息。

        加载并启动控制器后,您可以通过以下主题界面发送所需的目标。ros_controllers将向执行器发送适当的输出命令以实现目标。

        用于控制示例机器人的主题名称。

  • 关节A:/MyRobot/JointA_EffortController/command

  • 关节B:/MyRobot/JointB_EffortController/command

  • JointC:/MyRobot/JointC_PositionController/command

        最后,我们来到了这篇文章的结尾。让我最后一次总结一下在您的机器人上使用ros_control需要什么。

九、总结 

  1. 您必须为机器人编写 hardware_interface 节点。

  2. 编写配置文件,根据接头接口和应用程序以及PID值(如果需要)选择接头的控制器类型。

  3. 编写配置文件以定义机器人的关节限制。

  4. 通过controller_manager加载和启动控制器。

  5. 使用 topic 接口将输入目标发送到控制器。

        因此,您可以使用上面的示例机器人代码作为模板,在您自己的机器人上设置ros_control。我做过的所有ROS项目都在使用ros_control。因此,您可以查看我的项目,了解使用ros_control的机器人的工作示例。​

        希望您已经理解了这里解释的关于ros_control的每一件事。如果您有任何问题,请随时使用下面的“联系我们”栏给我发消息。ROS Control | ROS Robotics

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端框架Vue学习 ——(七)Vue路由(Vue Router)

文章目录 Vue路由使用场景Vue Router 介绍Vue Router 使用 Vue路由使用场景 使用场景&#xff1a;如下图&#xff0c;点击部门管理的时候显示部门管理的组件&#xff0c;员工管理的时候显示员工管理的组件。 前端路由&#xff1a;指的是 URL 中的 hash(#号)与组件之间的对应关…

vue分片上传视频并转换为m3u8文件并播放

开发环境&#xff1a; 基于若依开源框架的前后端分离版本的实践&#xff0c;后端java的springboot&#xff0c;前端若依的vue2&#xff0c;做一个分片上传视频并分段播放的功能&#xff0c;因为是小项目&#xff0c;并没有专门准备文件服务器和CDN服务&#xff0c;后端也是套用…

登录Tomcat控制台,账号密码输入正确但点击登录没反应不跳转到控制台页面

在tomcat-users.xml里面可以查看登录tomcat控制台的账号密码&#xff0c;如果账号密码输入正确还是登录不进去&#xff0c;则很有可能是tomcat的账号被锁了&#xff08;可在catalina.xxx.log里面查看&#xff09;。tomcat账号被锁定后默认情况是不访问控制台后5分钟自动解锁&am…

「Verilog学习笔记」使用函数实现数据大小端转换

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 分析 题目要求使用函数实现4bit数据大小端转换的功能&#xff0c;所谓大端&#xff0c;即数据的高位写在左侧&#xff0c;低位写在右侧。小端则反过来&#xff1a;高位写在…

技术分享 | app自动化测试(Android)--触屏操作自动化

导入TouchAction Python 版本 from appium.webdriver.common.touch_action import TouchActionJava 版本 import io.appium.java_client.TouchAction;常用的手势操作 press 按下 TouchAction 提供的常用的手势操作有如下操作&#xff1a; press 按下 release 释放 move_…

基于SSM的公务用车管理智慧云服务监管平台

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

scrapy案例教程

文章目录 1 scrapy简介2 创建项目3 自定义初始化请求url4 定义item5 定义管道 1 scrapy简介 scrapy常用命令 |命令 | 格式 |说明| |–|–|–| |startproject |scrapy startproject <项目名> |创建一个新项目| |genspider| scrapy genspider <爬虫文件名> <域名…

手机怎么打包?三个方法随心选!

有的时候&#xff0c;电脑不在身边&#xff0c;只有随身携带的手机&#xff0c;这个时候又急需把文件打包发送给同事或者同学&#xff0c;如何利用手机操作呢&#xff1f;下面介绍了具体的操作步骤。 一、通过手机文件管理自带压缩功能打包 1、如果是iOS系统&#xff0c;就在手…

python3+requests接口自动化测试框架

前段时间由于公司测试方向的转型&#xff0c;由原来的web页面功能测试转变成接口测试&#xff0c;之前大多都是手工进行&#xff0c;利用postman和jmeter进行的接口测试&#xff0c;后来&#xff0c;组内有人讲原先web自动化的测试框架移驾成接口的自动化框架&#xff0c;使用的…

01MyBatisPlus入门案例,常见注解,常用配置

一、入门案例 需求&#xff1a;基于课前资料提供的项目&#xff0c;实现下列功能&#xff1a; 新增用户功能根据id查询用户根据id批量查询用户根据id更新用户根据id删除用户 1.引入MybatisPlus的起步依赖 MybatisPlus官方提供的starter&#xff0c;其中集成了Mybatis和Myba…

Java自学第5课:Java web开发环境概述,更换Eclipse版本

1 Java web开发环境 前面我们讲了java基本开发环境&#xff0c;但最终还是要转到web来的&#xff0c;先看下怎么搭建开发环境。 这个图就是大概讲了下开发和应用环境&#xff0c;其实很简单&#xff0c;对于一台裸机&#xff0c;win7 系统的&#xff0c;首先第1步&#xff0c;…

harmonyOS开发

在Cocos Creator中&#xff0c;场景是一个独立的文件资源&#xff0c;可以像打开PSD文件一样在编辑器中双击打开&#xff1b; 场景文件是数据驱动工作流的核心&#xff0c;场景中包括图像资源、动画、特效以及驱动游戏逻辑和表现的脚本&#xff1b; Cocos Creator是一个数据驱…

uni-app 、Spring Boot 、ant Design 打造的一款跨平台包含小说(仿真翻页、段落听书)、短视频、壁纸等功能含完备后台管理的移动应用

简介 咪哩快看&#xff0c;为用户提供优质阅读&#xff0c;短视频&#xff0c;共同记录美好生活的移动应用&#xff0c;并含有一套完备的后台管理体系&#xff0c;助力开发者快速数字化&#xff0c;开启你的财富之门&#xff01; 官网&#xff1a; https://miliqkdoc.motopa.…

mysql图书管理系统(49-56)源代码

-- 九、 子查询 -- 无关子查询 -- 比较子查询&#xff1a;能确切知道子查询返回的是单值时&#xff0c;可以用>&#xff0c;<&#xff0c;&#xff0c;>&#xff0c;<&#xff0c;!或<>等比较运算符。 -- 49、 查询与“俞心怡”在同一个部门的读者的借…

C# OpenCvSharp 去除字母后面的杂线

效果 项目 代码 using OpenCvSharp; using System; using System.Drawing; using System.Windows.Forms;namespace OpenCvSharp_Demo {public partial class frmMain : Form{public frmMain(){InitializeComponent();}string image_path "";private void Form1_Loa…

【Proteus仿真】【Arduino单片机】数码管显示

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用TM1637、共阳数码管等。 主要功能&#xff1a; 系统运行后&#xff0c;数码管显示数字、字符。 二、软件设计 /* 作者&#xff1a;嗨小易&am…

com.genuitec.eclipse.springframework.springnature

Your IDE is missing natures to properly support your projects. Some extensions on the eclipse marketplace can be installed to support those natures. com.genuitec.eclipse.springframework.springnature 移除 <nature>om.genuitec.eclipse.springframework.…

【机器学习4】降维

常见的降维方法有主成分分析、 线性判别分析、 等距映射、 局部线性嵌入、 拉普拉斯特征映射、 局部保留投影等。 1 PCA最大方差角度理解 PCA无监督学习算法。 PCA的目标&#xff0c; 即最大化投影方差&#xff0c; 也就是让数据在主轴上投影的方差最大。 在黄线所处的轴上&…

【第2章 Node.js基础】2.1 JavaScript基本语法

文章目录 学习目标JavaScript版本与JavaScript运行环境JavaScript版本JavaScript运行环境 JavaScript语句与注释语句语句块注释 变量变量的命名变量的声明与赋值变量提升变量泄露全局作用域和函数作用域块级作用域与let关键字使用const关键字声明只读常量注意 数据类型数值&…

电脑软件:推荐一款电脑多屏幕管理工具DisplayFusion

下载https://download.csdn.net/download/mo3408/88514558 一、软件简介 DisplayFusion是一款多屏幕管理工具&#xff0c;它可以让用户更轻松地管理连接到同一台计算机上的多个显示器。 二、软件功能 2.1 多个任务栏 通过在每个显示器上显示任务栏&#xff0c;让您的窗口管理更…