一、说明
在文中,我们将了解一个有用的软件包ros_control,但它难以理解。我们将首先简要了解什么是控制及其在机器人技术中的重要性。然后了解ros_control包如何派上用场来控制我们的机器人。让我列出我们将要讨论的主题。
二、内容:
1. 为什么要控制。
2. 什么是ROS控制。
3. ros_control封装中的控制器类型。
4.如何写hardware_interface。
5.如何在机器人上使用ros_control。
让我们开始吧。
三、为什么选择Control..?
我们在工程中听说过很多关于控制系统和PID控制器的信息。如今,控制系统无处不在,从汽车(用于控制克鲁斯模式下的速度)到月球着陆器(用于控制软着陆的方向和速度)。
无论如何,此页面没有解释控制系统,但我们将看到如何在机器人中使用 ROS 控制包。我假设您对什么是 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 是一组软件包,包括控制器接口、控制器管理器、传输、硬件接口和控制工具箱。所有这些软件包将允许您交互和控制机器人的关节执行器。
图片来源:ros_control - ROS Wiki
如图 3 所示,ros_control将联合状态数据和输入设定点(目标)作为用户/第三方应用程序的输入,并将适当的命令作为输出发送到执行器。为了达到提供的设定点(目标),它使用通用的控制回路反馈机制(通常是PID控制器)来控制输出。该输出通过硬件接口传递给机器人。
图片来源: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需要什么。
九、总结
-
您必须为机器人编写 hardware_interface 节点。
-
编写配置文件,根据接头接口和应用程序以及PID值(如果需要)选择接头的控制器类型。
-
编写配置文件以定义机器人的关节限制。
-
通过controller_manager加载和启动控制器。
-
使用 topic 接口将输入目标发送到控制器。
因此,您可以使用上面的示例机器人代码作为模板,在您自己的机器人上设置ros_control。我做过的所有ROS项目都在使用ros_control。因此,您可以查看我的项目,了解使用ros_control的机器人的工作示例。
希望您已经理解了这里解释的关于ros_control的每一件事。如果您有任何问题,请随时使用下面的“联系我们”栏给我发消息。ROS Control | ROS Robotics