ROS2与MoveIt2入门教程-实时机械臂伺服
说明:
- 介绍如何实时机械臂伺服
概述:
MoveIt Servo允许您将末端执行器(EEF)速度指令数据流传输到您的机械臂,并让其同步执行这些速度指令。这可以通过广泛的输入方案实现远程操作,或者让其它自主软件来控制机器人—例如在视觉伺服或闭环位置控制中。
本教程将会说明如何使用MoveIt Servo向支持ROS的机器人发送实时伺服命令。伺服节点的一些不错的功能包括奇异性处理和碰撞检测,这可以防止操作人员破坏机器人。
启动独立伺服节点
MoveIt Servo提供了一个组件节点moveit_servo::ServoServer允许您通过ROS话题发送指令。指令可以来自任何地方,例如操纵杆、键盘或其它控制器。
本演示是为Xbox 1控制器编写的,但可以通过更改 joystick_servo_example.cpp文件轻松修改以使用任何与Joy软件包兼容的控制器。
要运行本演示,请确保已插上您的控制器并且可以被ros2 run joy joy_node命令检测到。这通常会在插入控制器后自动发生。然后用以下命令来启动本演示:
ros2 launch moveit2_tutorials servo_teleop.launch.py
现在您应该可以使用控制器来控制机械臂了,MoveIt Servo会自动避开奇点和碰撞。
- 不使用控制器
如果您没有控制器,仍然可以使用键盘来尝试本演示。在演示仍在运行的情况下,在一个新终端中运行以下命令:
ros2 run moveit2_tutorials servo_keyboard_input
这样您将能够使用键盘来伺服机器人。使用箭头键以及“.”键和“;”键来发送笛卡尔指令。使用“W”字母键来更改笛卡尔指令的世界坐标系,使用“E”字母键来更改末端执行器的坐标系。使用数字键1-7来发送关节点动指令(使用字母键R来反转方向)。
预期输出
预期输出应该如下面这个视频所示:https://link.zhihu.com/?target=http%3A//moveit2_tutorials.picknik.ai/doc/realtime_servo/realtime_servo_tutorial.html
请注意,该视频中的控制器覆盖仅仅用于演示目的,实际上并没有包含控制器操作过程在内。
使用C++接口
除了将Servo作为其自身的组件来启动之外,也可以通过C++接口将Servo包含在您自己的节点中。在这两种情况下,向机器人发送的指令非常相似,但是对于C++接口而言,需要对Servo进行一些设置。作为对此项设置工作的补偿,您将能够通过其C++ API直接与Servo进行交互。
这个基本的C++接口演示会以预定的方式移动机器人,且可以使用以下命令来启动该演示:
ros2 launch moveit2_tutorials servo_cpp_interface_demo.launch.py
这样就应该会出现一个Rviz窗口,窗口中会有一个Panda机械臂和一个碰撞物体。在切换到笛卡尔运动之前,机械臂会点动关节几秒钟。当机械臂接近碰撞物体时,它会减慢速度并停下来。
完整代码说明
本演示的完整代码可以在此处获取。
- 设置
首先来声明节点指针和会向Servo发布指令消息的发布者指针:
rclcpp::Node::SharedPtr node_;
rclcpp::Publisher<control_msgs::msg::JointJog>::Share dPtr joint_cmd_pub_;
rclcpp::Publisher<geometry_msgs::msg::TwistStamped>::SharedPtr twist_cmd_pub_;
size_t count_ = 0;
接着来设置该节点、planning_scene_monitor和碰撞物体:
int main(int argc, char** argv)
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions node_options;
node_options.use_intra_process_comms(true);
node_ = std::make_shared<rclcpp::Node> ("servo_demo_node", node_options);
休眠暂停4秒钟以等待Rviz启动出现。这在具有单个启动文件的集成演示中是必需的:
rclcpp::sleep_for(std::chrono::seconds(4));
然后创建planning_scene_monitor。我们需要将其传递给Servo的构造函数,且在初始化任何碰撞物体之前应该要先设置它:
auto tf_buffer = std::make_shared<tf2_ros::Buffer>(node_->get_clock());
auto planning_scene_monitor = std::make_shared<planning_scene_monitor::PlanningSceneMonitor>(
node_, "robot_description", tf_buffer, "planning_scene_monitor");
这里要确保planning_scene_monitor正在从关节状态话题进行实时更新:
if (planning_scene_monitor->getPlanningScene())
{
planning_scene_monitor->startStateMonitor("/joint_states");
planning_scene_monitor->setPlanningScenePublishingFrequency(25);
planning_scene_monitor->startPublishingPlanningScene(planning_scene_monitor::PlanningSceneMonitor::UPDATE_SCENE, "/moveit_servo/publish_planning_scene");
planning_scene_monitor->startSceneMonitor();
}
else
{
RCLCPP_ERROR(LOGGER, "Planning scene not configured");
return EXIT_FAILURE;
}
这些是会将指令发送到MoveIt Servo的发布者。支持两种指令类型:会直接在关节空间中点动机器人的JointJog消息,以及会使用笛卡尔速度指令移动指定链接的TwistStamped消息。在本演示中点动了末端执行器链接。
joint_cmd_pub_ = node_->create_publisher<control_msgs::msg::JointJog>("servo_demo_node/delta_joint_cmds", 10);
twist_cmd_pub_ = node_->create_publisher<geometry_msgs::msg::TwistStamped>("servo_demo_node/delta_twist_cmds",
接下来会在机械臂移动的路径上创建一个碰撞物体。当伺服机械臂移向该碰撞物体时,机械臂会在碰撞前减速并停下来:
moveit_msgs::msg::CollisionObject collision_object;
collision_object.header.frame_id = "panda_link0";
collision_object.id = "box";
制作一个方盒并将其放在机械臂移动的路径上:
shape_msgs::msg::SolidPrimitive box;
box.type = box.BOX;
box.dimensions = { 0.1, 0.4, 0.1 };
geometry_msgs::msg::Pose box_pose;
box_pose.position.x = 0.6;
box_pose.position.y = 0.0;
box_pose.position.z = 0.6;
将该方盒添加为一个碰撞物体:
collision_object.primitives.push_back(box);
collision_object.primitive_poses.push_back(box_pose);
collision_object.operation = collision_object.ADD;
创建会发布该碰撞物体的消息:
moveit_msgs::msg::PlanningSceneWorld psw;
psw.collision_objects.push_back(collision_object);
moveit_msgs::msg::PlanningScene ps;
ps.is_diff = true;
ps.world = psw;
将该碰撞物体消息发布到规划场景中:
auto scene_pub = node_->create_publisher<moveit_msgs::msg::PlanningScene>("planning_scene", 10);
scene_pub->publish(ps);
- 初始化伺服(Servo)
伺服(Servo)需要许多参数来决定其行为。这些可以通过使用makeServoParameters辅助函数自动读取这些参数:
auto servo_parameters = moveit_servo::ServoParameters::makeServoParameters(node_, LOGGER);
if (!servo_parameters)
{
RCLCPP_FATAL(LOGGER, "Failed to load the servo parameters");
return EXIT_FAILURE;
}
通过传递节点指针、参数和PSM来初始化Servo C++接口:
auto servo = std::make_unique<moveit_servo::Servo>(node_, servo_parameters, planning_scene_monitor);
可以使用C++接口直接启动Servo。如果使用替代选项ServoServer来启动,则要使用一个ROS服务来启动Servo。在启动之前,MoveIt Servo不会接受任何指令或者移动机器人:
servo->start();
- 发送指令
本演示将会使用一个简单的ROS计时器来向机器人发送关节指令和twist指令:
rclcpp::TimerBase::SharedPtr timer = node_->create_wall_timer(50ms, publishCommands);
这是用于发布指令的计时器回调函数。C++接口通过ROS内部话题来发送指令,与使用ServoServer来启动Servo完全一样。
void publishCommands()
{
首先会发布100个关节点动指令。Joint_names字段允许您指定要以相应的velocities字段中的速度移动的单个关节。消息中包含最新的时间戳非常重要,否则Servo会认为该指令已过时且不会移动机器人。
if (count_ < 100)
{
auto msg = std::make_unique<control_msgs::msg::JointJog>();
msg->header.stamp = node_->now();
msg->joint_names.push_back("panda_joint1");
msg->velocities.push_back(0.3);
joint_cmd_pub_->publish(std::move(msg));
++count_;
}
过一会儿之后,就切换到发布twist指令。提供的坐标系就是定义twist的坐标系,而不是会遵循该指令的机器人坐标系。同样地也需要消息中的最新时间戳:
else
{
auto msg = std::make_unique<geometry_msgs::msg::TwistStamped>();
msg->header.stamp = node_->now();
msg->header.frame_id = "panda_link0";
msg->twist.linear.x = 0.3;
msg->twist.angular.z = 0.5;
twist_cmd_pub_->publish(std::move(msg));
}
}
这里使用了多线程执行器,因为Servo具有用于移动机器人和避免碰撞的并发进程:
auto executor = std::make_unique<rclcpp::executors::MultiThreadedExec utor>();
executor->add_node(node_);
executor->spin();
伺服概述
后续两个小节会提供有关MoveIt Servo的一些背景信息,以及描述在您的机器人上设置MoveIt Servo的首要步骤。
Servo包括以下多个不错的功能:
笛卡尔末端执行器twist指令;
关节指令;
碰撞检测;
奇异性检测;
关节位置和速度限制强制执行;
输入是通用的ROS消息。
在新机器人上进行Servo设置
- 前提条件
让您的机器人运行 MoveIt Servo的最低要求包括:
机器人的有效URDF和SRDF;
一个可以接受来自ROS话题的关节位置或速度指令的控制器;
能提供快速准确的关节位置反馈的关节编码器。
由于运动学是由MoveIt核心部分进行处理的,因此建议为您的机器人准备一个有效的配置软件包,并且可以运行其中包含的演示启动文件。
输入设备
MoveIt Servo的两个主要输入就是笛卡尔指令和关节指令。它们分别以TwistStamped和JointJog消息传入Servo。这些指令的输入来源几乎可以是以下任何设备:游戏手柄、语音指令、SpaceNav鼠标,或者PID控制器(例如用于视觉伺服的控制器)。
无论输入设备如何,对传入的指令消息要求如下:
TwistStamped和JointJog:发布该消息时,需要在消息标头中有该消息的最新时间戳;
JointJog:必须在joint_names字段中具有与displacements或velocities字段中给出的指令相对应的有效关节名称;
(可选项)TwistStamped:可以在该消息的标头中提供任意的frame_id,该坐标系将会应用于该twist。如果为空,则会使用配置中的默认值。
Servo配置
这个演示配置文件显示了MoveIt Servo需要的参数,并且有详细的说明文档。
可以从该演示文件中开始进行参数设置,但必须针对您的具体设置更改其中一些参数:
robot_link_command_frame:将其更新为您机器人中的有效坐标系,推荐设置为规划坐标系或EEF坐标系;
command_in_type:如果您的输入来自操纵杆,则设置为“unitless”,如果您的输入是以米/秒或弧度/秒为单位,则设置为“speed_units”;
command_out_topic:将此参数值更改成您控制器的输入话题;
command_out_type:根据您控制器需要的消息类型更改此参数;
publish_joint_positions和publish_joint_velocities:根据您控制器的需要更改这两个参数的值。注意,如果command_out_type == std_msgs/Float64MultiArray,则这两个参数中只能有一个为True;
joint_topic:将此参数值更改为您机械臂的joint_state话题,通常是/joint_states;
move_group_name:将此参数值更改为您移动组的名称,正如在您的SRDF中所定义的移动组;
planning_frame:此参数值应该设置为您移动组的规划坐标系。
英语原文地址:https://moveit2_tutorials.picknik.ai/doc/realtime_servo/realtime_servo_tutorial.html
获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号