关于ROS_Contorller的详解

ROS_Control框架介绍

官方给出的ros_control的框架图
  1. Controller:一个机器人包含多个controller,类似ros_controllers中现提供的、effort_controllersjoint_state_controllerposition_controllersvelocity_controllersjoint_trajectory_controllers,不同的controller可以完成对不同模块的控制。例如完成对关节力的控制、速度控制等等。请求下层的硬件资源,并提供PID控制器,读取底层硬件资源的状态,发送控制指令。
  1. Controller Manager:顾名思义,用来管理和控制各个controller的管理器,其主要提供统一的接口来管理不同的manager。

  2. Hardware Rescource:为上下两层提供硬件资源的接口,内部有一个hardware resource manager来完成硬件资源管理。

  3. RobotHW:硬件抽象层和硬件直接打交道,通过write和read方法来完成硬件的操作,这一层也包含关节限位、力矩转换、状态转换等功能。

  4. Real Robot:实际的机器人上也需要有自己的嵌入式控制器,接收到命令后需要反映到执行器上,比如接收到位置1的命令后,那就需要让执行器快速、稳定的到达位置。

contorller_manager

此处我们看一下controller_manager.cpp的源码,可以看出通过一个控制机器加载器工具来进行初始化之后,最后再通过订阅六个service,完成了controller_manager的初始化,六个service分别是list_controllers、list_controller_types、load_controller、unload_controller、switch_controller、reload_controller_libraries,从其名字的字面意思也不难理解这六个服务的作用是什么。

加载控制器时,controller_manager将使用控制器名称作为所有控制器特定参数的根,最重要的是,标识要加载哪个插件的类型。控制器管理器提供与控制器交互的基础框架。根据你是从启动文件,命令行还是ROS节点运行控制器,控制器管理器提供不同的工具来运行控制器。

ControllerManager::ControllerManager(hardware_interface::RobotHW *robot_hw, const ros::NodeHandle& nh) :
  robot_hw_(robot_hw),
  root_nh_(nh),
  cm_node_(nh, "controller_manager"),
  start_request_(0),
  stop_request_(0),
  please_switch_(false),
  current_controllers_list_(0),
  used_by_realtime_(-1)
{
  // create controller loader
  controller_loaders_.push_back( 
    ControllerLoaderInterfaceSharedPtr(new ControllerLoader<controller_interface::ControllerBase>("controller_interface",
                                                                                                  "controller_interface::ControllerBase") ) );

  // Advertise services (this should be the last thing we do in init)
  srv_list_controllers_ = cm_node_.advertiseService("list_controllers", &ControllerManager::listControllersSrv, this);
  srv_list_controller_types_ = cm_node_.advertiseService("list_controller_types", &ControllerManager::listControllerTypesSrv, this);
  srv_load_controller_ = cm_node_.advertiseService("load_controller", &ControllerManager::loadControllerSrv, this);
  srv_unload_controller_ = cm_node_.advertiseService("unload_controller", &ControllerManager::unloadControllerSrv, this);
  srv_switch_controller_ = cm_node_.advertiseService("switch_controller", &ControllerManager::switchControllerSrv, this);
  srv_reload_libraries_ = cm_node_.advertiseService("reload_controller_libraries", &ControllerManager::reloadControllerLibrariesSrv, this);
}

可以使用controller_manager脚本从命令行与controller_manager进行交互,command包括:
load: 加载一个控制器(创建和初始化,但是不启动)
unload: 卸载一个控制器(销毁)
start: 启动一个控制器
stop: 停止一个控制器
spawn: 加载并启动一个控制器
kill: 停止并卸载一个控制器

rosrun controller_manager controller_manager <command> <controller_name>

要获取控制器的状态,command包括:
list: 按照执行顺序列出所有控制器,并给出每个控制器的状态
list-types: 列出控制器管理器知道的所有控制器类型。如果控制器不在此列表中,将无法获取。
reload-libraries: 重新加载所有可用作插件的控制器库。当您开发控制器并且想要测试新的控制器代码时,这很方便,而无需每次都重新启动机器人。而且不会重新启动之前运行的控制器
reload-libraries --restore: 重新加载所有可用作插件的控制器库,并将所有控制器恢复到其原始状态

rosrun controller_manager controller_manager <command>

还有其他命令行脚本工具来帮住我们完成不同的任务,具体的可以参考官网给出的说明,就不在此处进行罗列了。

我们还可以通过launch文件来启动我们的控制器,上面的一种是加载并启动,第二种是仅加载不启动。

<launch>
   <node pkg="controller_manager"
         type="spawner"
         args="controller_name1 controller_name2" />
 </launch>

<launch>
   <node pkg="controller_manager"
         type="spawner"
         args="--stopped controller_name1 controller_name2" />
 </launch>

还可以通过图形界面的方式对控制器进行操作,以图形方式加载,卸载,启动和停止控制器,以及显示加载的有关控制器的信息。

rosrun rqt_controller_manager rqt_controller_manager

controller_interface

#ifndef CONTROLLER_INTERFACE_CONTROLLER_H
#define CONTROLLER_INTERFACE_CONTROLLER_H

#include <controller_interface/controller_base.h>
#include <hardware_interface/internal/demangle_symbol.h>
#include <hardware_interface/robot_hw.h>
#include <hardware_interface/hardware_interface.h>
#include <ros/ros.h>


namespace controller_interface
{

/* 
 *具有特定硬件接口的控制器
 * 模板类,该控制器使用的硬件接口类型。这强制了控制器与其要控制的硬件之间的语义兼容性。
 */
template <class T>
class Controller: public virtual ControllerBase
{
public:
  Controller()  {state_ = CONSTRUCTED;}
  virtual ~Controller<T>(){}

  /*
   * 调用init函数来从非实时线程初始化控制器,其中指针指向硬件接口本身,而不是指向RobotHW的指针
   * 参数 hw 此控制器使用的特定硬件接口
   * 参数controller_nh命名空间中的NodeHandle,控制器应从中读取其配置,以及应在何处设置其ROS接口
   * 初始化成功返回ture并且做好启动准备工作
   */
  virtual bool init(T* /*hw*/, ros::NodeHandle& /*controller_nh*/) {return true;};

  /*
   * 参数controller_nh控制器命名空间中的NodeHandle。这是特定于控制器的配置所在的位置。
   */
  virtual bool init(T* /*hw*/, ros::NodeHandle& /*root_nh*/, ros::NodeHandle& /*controller_nh*/) {return true;};


protected:
  /* 
   * 从RobotHW指针初始化控制器
   * 如果它可以从robot_hw中成功获取接口,则对控制器的硬件接口进行初始化
   */
  virtual bool initRequest(hardware_interface::RobotHW* robot_hw,
                           ros::NodeHandle&             root_nh,
                           ros::NodeHandle&             controller_nh,
                           ClaimedResources&            claimed_resources)
  {
    // check if construction finished cleanly
    if (state_ != CONSTRUCTED){
      ROS_ERROR("Cannot initialize this controller because it failed to be constructed");
      return false;
    }

    // 获得硬件接口的指针
    T* hw = robot_hw->get<T>();
    if (!hw)
    {
      ROS_ERROR("This controller requires a hardware interface of type '%s'."
                " Make sure this is registered in the hardware_interface::RobotHW class.",
                getHardwareInterfaceType().c_str());
      return false;
    }

    // 返回此控制器声明的资源
    hw->clearClaims();
    if (!init(hw, controller_nh) || !init(hw, root_nh, controller_nh))
    {
      ROS_ERROR("Failed to initialize the controller");
      return false;
    }
    hardware_interface::InterfaceResources iface_res(getHardwareInterfaceType(), hw->getClaims());
    claimed_resources.assign(1, iface_res);
    hw->clearClaims();

    // success
    state_ = INITIALIZED;
    return true;
  }

  /// 获取此控制器的硬件接口类型的名称
  std::string getHardwareInterfaceType() const
  {
    return hardware_interface::internal::demangledTypeName<T>();
  }

private:
  Controller<T>(const Controller<T> &c);
  Controller<T>& operator =(const Controller<T> &c);

};

}
#endif

从上面的源码中不难看出,contorller通过controlle_interface来完成hardware_interface的初始化。

hardware_interface

最重要的交互接口,与机器人硬件完成连接,我们通过类图来理清接口之间关系。


类图.png

这里需要补充说明的是HardwareResourceManager通过getHandle获取到不同的handle对应到到图中右侧的不同的HardwareInterface。这个在类图上并没有体现出来,流程图如下:


流程图.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容