ros_control 介绍

简介: ros_control 脱胎于pr2 的 硬件封装层部分 pr2_mechanism,经过pal-robotics 和 hidof 两个公司的工程师进行了改写,变得适用于所有机器人的硬件封装库,负责统一管理硬件驱动与传感器底层细节,处理异常,分配资源,向上提供统一接口。作为一个end2end的ROS 机器人系统集成解决方案

来源

ros_control 脱胎于pr2 的 硬件封装层部分 pr2_mechanism,经过pal-robotics 和 hidof 两个公司的工程师进行了改写,变得适用于所有机器人的硬件封装库,负责统一管理硬件驱动与传感器底层细节,处理异常,分配资源,向上提供统一接口。作为一个end2end的ROS 机器人系统集成解决方案。

优势

ros_control 是一个类库!! 并不是直接的工具库,需要自己写程序继承特定基类来做到接口的统一。

Ros_control 使用的最直接好处是可以复用 ros-controllers 里面的一些控制算法,以及与支持ros_control的仿真器 gazebo 的良好支持。

缺点

ros_control 框架最大的问题,很多模块严重冗余,很多部分设计突出一个没必要,架构过于复杂,很多模块象征意义大于实际意义,更多是提供一个系统集成的思路或者雏形,具体完善到工业级还得好长的路要走,学习曲线陡峭没文档。

基本架构

在这里插入图片描述
基本架构是这样的:

机器人的硬件通讯库(串口,can, ethercat, 等等)

ros_control 的robot_hw部分

controller_manager 部分 与具体的 controller

controller 部分就会提供给ROS系统中其他上层组件标准控制与感知接口,比如 topic : cmd_vel,joint/command action:trajectory_server 等等。

简单实例

Controller_manager 与很多controller都已经很完善了,需要写的就是robothw部分

一个最简单的ros_control例子:

https://github.com/ros-controls/ros_controllers/blob/indigo-devel/diff_drive_controller/test/diffbot.h

https://github.com/ros-controls/ros_controllers/blob/indigo-devel/diff_drive_controller/test/diffbot.cpp

diffbot.h 中定义了一个双轮差速小车的模型。
Diffbot 类中定义了小车的joint 信息(位置,速度,扭矩? 力? ,pos, vel,effort),来描述关节的状态,在这里插入图片描述
又定义了cmd来存储对关节的控制量。
在这里插入图片描述
设置了不同的hardware_interface对相应数据进行注册,
在这里插入图片描述
然后将不同的控制接口注册到robothw上.
在这里插入图片描述
实现了 两个虚方法 read 和write ,因为是个demo,所以处理数据非常简单。
源码如下

template <unsigned int NUM_JOINTS = 2>
class Diffbot : public hardware_interface::RobotHW
{
public:
  Diffbot()//构造函数
  : running_(true)
  , start_srv_(nh_.advertiseService("start", &Diffbot::start_callback, this))
  , stop_srv_(nh_.advertiseService("stop", &Diffbot::stop_callback, this))
  {
    // Intialize raw data  //初始化原始数据  在私有部分有声明该四个变量
    std::fill_n(pos_, NUM_JOINTS, 0.0);
    std::fill_n(vel_, NUM_JOINTS, 0.0);
    std::fill_n(eff_, NUM_JOINTS, 0.0);
    std::fill_n(cmd_, NUM_JOINTS, 0.0);

    // Connect and register the joint state and velocity interface
    for (unsigned int i = 0; i < NUM_JOINTS; ++i)
    {
      std::ostringstream os;
      os << "wheel_" << i << "_joint";

// 设置不同的hardware_interface对应数据 并 进行注册,
      hardware_interface::JointStateHandle state_handle(os.str(), &pos_[i], &vel_[i], &eff_[i]);//设置jointstate 接口数据
      jnt_state_interface_.registerHandle(state_handle);

      hardware_interface::JointHandle vel_handle(jnt_state_interface_.getHandle(os.str()), &cmd_[i]);//设置joint vel handle 接口数据
      jnt_vel_interface_.registerHandle(vel_handle);
    }
     //进行注册
    registerInterface(&jnt_state_interface_);
    registerInterface(&jnt_vel_interface_);
  }

  ros::Time getTime() const {return ros::Time::now();}
  ros::Duration getPeriod() const {return ros::Duration(0.01);}

  void read()
  {
    std::ostringstream os;
    for (unsigned int i = 0; i < NUM_JOINTS - 1; ++i)
    {
      os << cmd_[i] << ", ";
    }
    os << cmd_[NUM_JOINTS - 1];

    ROS_INFO_STREAM("Commands for joints: " << os.str());
  }

  void write()
  {
    if (running_)
    {
      for (unsigned int i = 0; i < NUM_JOINTS; ++i)
      {
        // Note that pos_[i] will be NaN for one more cycle after we start(),
        // but that is consistent with the knowledge we have about the state
        // of the robot.
        pos_[i] += vel_[i]*getPeriod().toSec(); // update position
        vel_[i] = cmd_[i]; // might add smoothing here later
      }
    }
    else
    {
      std::fill_n(pos_, NUM_JOINTS, std::numeric_limits<double>::quiet_NaN());
      std::fill_n(vel_, NUM_JOINTS, std::numeric_limits<double>::quiet_NaN());
    }
  }

  bool start_callback(std_srvs::Empty::Request& /*req*/, std_srvs::Empty::Response& /*res*/)
  {
    running_ = true;
    return true;
  }

  bool stop_callback(std_srvs::Empty::Request& /*req*/, std_srvs::Empty::Response& /*res*/)
  {
    running_ = false;
    return true;
  }

private:
  hardware_interface::JointStateInterface    jnt_state_interface_;//声明joint state 接口 类内部实例
  hardware_interface::VelocityJointInterface jnt_vel_interface_;//声明joint vel接口 类内部实例
  double cmd_[NUM_JOINTS];//存储对关节的控制量
  double pos_[NUM_JOINTS];//joint位置信息
  double vel_[NUM_JOINTS];//joint速度信息
  double eff_[NUM_JOINTS];//joint力信息
  bool running_;

  ros::NodeHandle nh_;
  ros::ServiceServer start_srv_;
  ros::ServiceServer stop_srv_;
};

diffbot.cpp 中给出了ros_control的运行流程

将Diffbot (继承于hardware_interface::RobotHW)类传给ControlerManager,ControlerManager会查看相对应hardware_interface,使用的时候通过service告诉controller_manager load对应类型及其相应的参数(launch文件种),启动controller,即可

源码如下

// ROS
#include <ros/ros.h>

// ros_control
#include <controller_manager/controller_manager.h>

#include "diffbot.h"

int main(int argc, char **argv)
{
  ros::init(argc, argv, "diffbot");
  ros::NodeHandle nh;

  Diffbot<> robot;
  ROS_WARN_STREAM("period: " << robot.getPeriod().toSec());
  controller_manager::ControllerManager cm(&robot, nh);

  ros::Rate rate(1.0 / robot.getPeriod().toSec());//制定数据更新周期
  ros::AsyncSpinner spinner(1);
  spinner.start();
  while(ros::ok())
  {
    robot.read();//读数据
    cm.update(robot.getTime(), robot.getPeriod());//更新数据
    robot.write();//将更新过的数据写数据
    rate.sleep();
  }
  spinner.stop();

  return 0;
}

ros_control核心概念

hardware_interface:作为ros_control组件最重要的一部分,做了以下抽象:

将能动的部分(类似于机器人关节)抽象为joint,joint类型有旋转,平动,固定等等。
执行器模型抽象为actuator,与joint不同的是actuator的属性值需要一定变换才能对应到joint可以理解为电机减速,或者机构传动。

根据不同的控制方式或者不同的传感器暴露出相应的数据接口,一般移动机器人底盘是速度闭环,而机械臂上是位置闭环,根据这些控制方法的不同分出了不同的控制接口,暴露给上层的controller。

作为机器人的硬件资源与上层的直接接口,可以被运行时产生与删除,结合机器人本身的通讯组件这样实际上实现了一种机器人硬件资源的低层次管理。

而hardware_interface 具体实现方式是存储对应状态变量的指针,用字符串表示不同的joint与 Actuator 资源。robothw 基类是 硬件通讯库与hardware_interface交互的部分。硬件通讯库具体的读写过程都在read 与 write 两个虚方法中实现,更新的数据放在robothw类的成员变量中,这些存储着joint 与actuator 状态与命令的空间被hardware_interface索引,传给controller_manager ,通过controller_manager的接口将数据接给controller。所以ros_control内部不存在进程间通讯。

相关实践学习
使用ROS创建VPC和VSwitch
本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
ROS入门实践
本课程将基于基础设施即代码 IaC 的理念,介绍阿里云自动化编排服务ROS的概念、功能和使用方式,并通过实际应用场景介绍如何借助ROS实现云资源的自动化部署,使得云上资源部署和运维工作更为高效。
相关文章
|
Web App开发 编解码 安全
【WebRTC 入门教程】全面解析WebRTC:从底层原理到Qt和FFmpeg的集成应用
【WebRTC 入门教程】全面解析WebRTC:从底层原理到Qt和FFmpeg的集成应用
5699 2
|
Python 容器
在Python中,键值对(key-value pair)结构
在Python中,键值对(key-value pair)结构
501 1
|
传感器 Ubuntu 算法
【6. 激光雷达接入ROS】(1)
【6. 激光雷达接入ROS】(1)
557 0
|
JSON 数据格式 Python
对Labelme标注图像,进行90、180、270的旋转,实现标注数据的扩充。
对Labelme标注图像,进行90、180、270的旋转,实现标注数据的扩充。
1452 0
对Labelme标注图像,进行90、180、270的旋转,实现标注数据的扩充。
|
存储 缓存 算法
【ROS】如何让ROS中节点获取数据 III --参数服务器通信及ros常用工具指令介绍
相较于之前的通信模型,参数服务器是最为简单的。在之前的模型中,ROSMASTER都是扮演一个帮二者连接在一起的桥梁。
656 0
|
传感器 NoSQL 算法
ROS Moveit 配置全网最详细教程
本文是关于ROS Moveit配置的全网最详细教程,提供了一键安装脚本,以及如何使用Moveit进行机器人运动规划的详细步骤和说明。文中还深入解析了Moveit的配置包文件、Moveit的源码,以及如何使用不同的运动规划算法(如CHOMP、LERP、STOMP)进行路径规划。
2032 1
ROS Moveit 配置全网最详细教程
|
机器学习/深度学习 机器人 数据库
FoundationPose复现及Realsense应用
文章概述了FoundationPose项目,一个由Nvidia开发的用于新对象6D姿态估计和跟踪的统一模型,支持模型基础和无模型设置,通过合成数据和大型语言模型实现强泛化能力,并提供了复现和应用的详细步骤。
1216 0
FoundationPose复现及Realsense应用
|
10月前
|
存储 人工智能 程序员
【C语言】一篇通关所有 “关键字”,值得收藏篇!
关键字是编程语言预定义的保留词,代表特定的操作或结构。C语言中的关键字用于定义变量类型、控制语句、存储类、数据类型等。使用这些关键字可以创建函数、控制程序的流程、声明变量和常量等。
1721 0
|
Rust 监控 网络协议
EtherCAT主站IgH解析(一)--主站初始化、状态机与EtherCAT报文
本文介绍了IgH EtherCAT Master整体运行原理
1879 0
EtherCAT主站IgH解析(一)--主站初始化、状态机与EtherCAT报文
|
11月前
|
关系型数据库 MySQL OLAP
快速入门:搭建你的第一个AnalyticDB实例
【10月更文挑战第25天】在大数据时代,高效的在线分析处理(OLAP)成为企业决策的关键。AnalyticDB是阿里云推出的一款完全托管的实时数据仓库服务,它能够支持PB级的数据量和高并发的查询需求。作为一名数据工程师,我有幸在工作中使用了AnalyticDB,并积累了丰富的实践经验。本文将从个人角度出发,详细介绍如何快速搭建你的第一个AnalyticDB实例,包括创建实例、连接数据库、导入数据和执行简单查询等步骤。
446 0