Movelt为使用者提供了一个最通用且简单的接口 MoveGroupInterface 类,这个接口提供了很多控制机器人的常用基本操作,如:

  • 设置机械臂的位姿
  • 进行运动规划
  • 移动机器人本体
  • 将物品添加到环境 / 从环境移除
  • 将物体绑定到机器人 / 从机器人解绑

这个接口通过ROS话题topic、服务service和动作action等机制与 MoveGroup 节点进行通信。

有关ROS节点相关知识以后会介绍,参见ROS Nodes

1. 执行示例代码

1.1 运行过程

cd ~/ARM/ws_moveit/
source devel/setup.bash
roslaunch panda_moveit_config demo.launch

新开一个终端,在相同目录下执行

source devel/setup.bash
roslaunch moveit_tutorials move_group_interface_tutorial.launch

注意:例程使用 RvizVisualToolsGui 面板逐步完成演示。默认应当是直接出现的。

如果没有,要将此面板添加到 RViz,参考基础准备

程序执行过程中的 rviz 如下图所示,跟随第二个终端里的提示,在屏幕底部 RvizVisualToolsGui 面板中按下 Next 按钮。

或选择屏幕顶部工具面板 (Interact那一行) 中的Key Tool 工具,然后按键盘上的 N 来进行下一步。

1.2 debug 过程

1.2.1 visual_tools 缺包

在运行上面的第二个命令时,我发生报错:

[move_group_interface_tutorial-1] process has died [pid 10609, exit code 127, cmd /home/zzrs234/ARM/ws_moveit/devel/lib/moveit_tutorials/move_group_interface_tutorial __name:=move_group_interface_tutorial __log:=/home/zzrs234/.ros/log/fe0cbdfa-08cc-11ed-8c8a-bc6ee239de01/move_group_interface_tutorial-1.log].
log file: /home/zzrs234/.ros/log/fe0cbdfa-08cc-11ed-8c8a-bc6ee239de01/move_group_interface_tutorial-1*.log

因为是 devel 包中的问题,所以,我重新编译:

zzrs234@zzrs234:~/ARM/ws_moveit$ catkin build

发现果然有错误:

Could not find a package configuration file provided by
"moveit_visual_tools" with any of the following names: moveit_visual_toolsConfig.cmake
moveit_visual_tools-config.cmake

这种错误很经典,就是缺包,执行以下命令进行安装:

 sudo apt-get install ros-melodic-moveit-visual-tools# ubuntu18.04

再重新编译,运行上面 1.1 的命令即可。

1.2.2 只显示路径而机械臂不到位

在初步执行时,发现机械臂只运行一次,留下一个绿色带点的路径,就又复归原来位置,对于将物品附加到机械臂的效果体现不明显(因为机械臂回到了原位置)。

这是因为只是规划而不是执行,只会留下路径。

1.3 预期效果

在RViz中,我们应该能够看到以下效果:

  1. 机器人将其手臂移动到其前方的目标姿态。

  2. 机器人将其手臂移动到其侧面的目标姿态。

  3. 机器人将其手臂移到一个新的目标姿态,同时保持末端执行器水平。

  4. 机器人沿着设置好的笛卡尔路径(一个三角形,下、右、上+左)移动手臂。

  5. 将长方体对象添加到手臂右侧的环境中。

  6. 机器人将其手臂移动到目标位姿,越过箱子,避免与箱子碰撞。

  7. 该对象附加到腕部(末端爪的侧面),并将其颜色将更改为紫色/橙色/绿色。

  8. 物体从腕部分离(其颜色将变回绿色)。

  9. 将箱子将从环境中删除。

2. 代码分析

实现这个功能的代码在~/ARM/ws_moveit/src/moveit_tutorials/doc/move_group_interface/src下,Github地址为:完整代码地址

下面我们一步一步分析一下 Movelt 是如何实现这些操作的。

代码给我的印象是,名字很复杂,但从上向下通读并不难,逻辑很简单,阅读源码很容易知道各个函数是什么意思。

2.1 Setup 初始化

这部分是对 Movelt 的几个接口类的初始化。

2.1.1 MoveGroupInterface

MoveIt编程 是对 规划组 planning groups 进行操作的。

//创建接口对象 PLANNING_GROUP
static const std::string PLANNING_GROUP = "panda_arm";

MoveGroupInterface类只需放入 想要控制和规划的 planning group 的名称 即可设置。

规划组planning group:

是我们设置的,设置过程会在后续讲解倒入机器人模型时讲解。

例如 panda机械臂,在Rviz的 Planning Request 中可以看到它有 3 个 Planning Group 规划组:

hand \ panda_arm \ panda_arm_hand

决定我们操作的机器人对象。

moveit::planning_interface::MoveGroupInterface move_group(PLANNING_GROUP);

2.1.2 PlanningSceneInterface

PlanningSceneInterface 类将被用来在 "virtual world" 场景中添加和删除碰撞对象 :

moveit::planning_interface::PlanningSceneInterface planning_scene_interface;

2.1.3 JointModelGroup

创建 MoveGroupInterface 对象后,Planning Group 会被保存到 JointModelGroup 对象。所以在 MoveIt 编程中通常会把 planning group 叫做 joint model group

构建一个指针指向 JointModelGroup 对象,有些情况下会直接对 joint model group 进行操作。

// 获取当前关节状态
//调用 getJointModelGroup 将位置姿态 放入 joint_model_group,这个后续2.6会用。
const robot_state::JointModelGroup* joint_model_group =
move_group.getCurrentState()->getJointModelGroup(PLANNING_GROUP);

2.2 Visualization 可视化

MoveItVisualTools包是一个在 Rviz 中可视化的工具包。比如:

  • 物体 | 机器人 | 轨迹 | 调试信息
namespace rvt = rviz_visual_tools;
moveit_visual_tools::MoveItVisualTools visual_tools("panda_link0");
visual_tools.deleteAllMarkers();

远程控制 Remote control 是一种自省工具 (introspection tool ),可以通过RViz中的按钮和键盘快捷键逐步完成高级功能。

visual_tools.loadRemoteControl();

RViz提供了多种类型的标记markers,用于记录历史轨迹和表征信息。在本演示中,我们将使用文本 test、圆柱体 cylinders,和球体 spheres:

Eigen::Isometry3d text_pose = Eigen::Isometry3d::Identity();
text_pose.translation().z() = 1.75;
visual_tools.publishText(text_pose, "MoveGroupInterface Demo", rvt::WHITE, rvt::XLARGE);

Eigen 参见 相关博客

rviz 中的 markers参见:

  1. 各种marker的详细介绍
  2. RVIZ中利用markers标记历史路径
  3. Rviz官网

批量发布trigger 可以减少发送到RViz进行可视化的消息数量:

visual_tools.trigger();

2.3 打印基本信息

我们可以打印一些基本信息到终端里显示出来:

  • 参考系的名字

    ROS_INFO_NAMED("tutorial", "Planning frame: %s", move_group.getPlanningFrame().c_str());
  • 机器人的末端执行器

    ROS_INFO_NAMED("tutorial", "End effector link: %s", move_group.getEndEffectorLink().c_str());
  • 机器人的规划组

    ROS_INFO_NAMED("tutorial", "Available Planning Groups:");
    std::copy(move_group.getJointModelGroupNames().begin(), move_group.getJointModelGroupNames().end(),
    std::ostream_iterator<std::string>(std::cout, ", "));

2.4 Start the demo

调用 visual_tools 的 prompt 提示函数 来开始操作:

visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to start the demo");

后续执行完一次规划都会来这么一句,在 RViz 中进行交互。

2.5 Planning to a Pose goal

2.5.1 设置目标末端姿态

可以为规划组(上面指定过为 panda_arm) 指定一个末端执行器的目标位姿(就是机械爪,end-effector):

//设置目标位姿
geometry_msgs::Pose target_pose1;
target_pose1.orientation.w = 1.0;
target_pose1.position.x = 0.28;
target_pose1.position.y = -0.2;
target_pose1.position.z = 0.5;
move_group.setPoseTarget(target_pose1);

接下来,就可以调用规划器来计算、规划并将其可视化。请注意,我们只是在规划,而不是调用 move_group 实际移动机器人。

//调用规划器进行规划

//创建规划器
moveit::planning_interface::MoveGroupInterface::Plan my_plan;
//成功标志位
bool success = (move_group.plan(my_plan) == moveit::planning_interface::MoveItErrorCode::SUCCESS);
//输出
ROS_INFO_NAMED("tutorial", "Visualizing plan 1 (pose goal) %s", success ? "" : "FAILED");

2.5.2 规划可视化

还记得上面 2.2 中的 markers 吗?

我们还可以在RViz 中将规划轨迹用 markers 展示出来:

//可视化规划轨迹

ROS_INFO_NAMED("tutorial", "Visualizing plan 1 as trajectory line");
visual_tools.publishAxisLabeled(target_pose1, "pose1");
//markers创建
visual_tools.publishText(text_pose, "Pose Goal", rvt::WHITE, rvt::XLARGE);
//画线
visual_tools.publishTrajectoryLine(my_plan.trajectory_, joint_model_group);
visual_tools.trigger();
//完成一次规划,与RViz 界面进行交互,使其点击 next 再继续执行。
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo");

2.5.3 移动到目标姿态

移动到姿势目标与上述步骤类似,只是我们现在使用move()函数。请注意,之前设置的目标姿势仍处于活动状态,因此机器人将尝试移动到该目标。

本教程中不会使用该函数,因为它是一个阻塞函数,需要 控制器controller(也就是真实硬件) 处于活动状态并报告轨迹执行成功。

/* Uncomment below line when working with a real robot */
/* move_group.move(); */ //这里注释掉因为是模拟,没有在硬件上执行,调用的话会阻塞。

2.6 Planning to a joint-space goal

除了上面提到的 Pose Goal,还有 Joint-space Goal ,这种方式通过在关节空间定义一个目标关节状态的方式进行控制。

2.6.1 设置目标关节姿态

  • 首先,创建一个指向 RobotState 的指针 current_state 。RobotState 记录了 Planning Group 中各个关节的 位置 / 速度 / 加速度数据

    moveit::core::RobotStatePtr current_state = move_group.getCurrentState();
  • 接下来,获取当前状态下的关节位置,关节位置是由 弧度radians 来表征的:

    std::vector<double> joint_group_positions;
    //调用copyJointGroupPositions将之前的位姿拷贝到关节数组中。
    current_state->copyJointGroupPositions(joint_model_group, joint_group_positions);
  • 现在,让我们修改数组中其中一个关节的值,规划到新的关节空间目标 joint-space goal,并将规划可视化。

    这里调用了 setJointValueTarget()函数。

    joint_group_positions[0] = -1.0;  // radians,弧度制
    //将新得到的关节数组设为目标姿态
    move_group.setJointValueTarget(joint_group_positions);
    //规划,返回标志位
    success = (move_group.plan(my_plan) == moveit::planning_interface::MoveItErrorCode::SUCCESS); ROS_INFO_NAMED("tutorial", "Visualizing plan 2 (joint space goal) %s", success ? "" : "FAILED");

2.6.2 规划可视化

//删除之前的markers
visual_tools.deleteAllMarkers();
//新画一个start、target、trajectory
visual_tools.publishText(text_pose, "Joint Space Goal", rvt::WHITE, rvt::XLARGE);
visual_tools.publishTrajectoryLine(my_plan.trajectory_, joint_model_group);
//批量发布
visual_tools.trigger();
//交互
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo");

2.7 路径约束下的规划

Planning with Path Constraints.

2.7.1 设置路径约束

在实际情况中,机械臂的路径常常是有约束的,下面使用 OrientationConstraint 为机械臂添加约束:

moveit_msgs::OrientationConstraint ocm;
ocm.link_name = "panda_link7";
ocm.header.frame_id = "panda_link0";
ocm.orientation.w = 1.0;
ocm.absolute_x_axis_tolerance = 0.1;
ocm.absolute_y_axis_tolerance = 0.1;
ocm.absolute_z_axis_tolerance = 0.1;
ocm.weight = 1.0;

将上面规定好的约束添加到规划组:

moveit_msgs::Constraints test_constraints;
//将约束添加到约束条件
test_constraints.orientation_constraints.push_back(ocm);
move_group.setPathConstraints(test_constraints);

2.7.2 设置目标姿态

下一步进行规划,我们这里再次使用之前定义的目标姿态target_pose1。

因为在路径约束下,仅当当前状态满足路径约束时,操作才有效。因此,我们需要将开始状态设置为一个新的姿态来确保符合约束。

robot_state::RobotState start_state(*move_group.getCurrentState());
//设置一个新的初始姿态
geometry_msgs::Pose start_pose2;
start_pose2.orientation.w = 1.0;
start_pose2.position.x = 0.55;
start_pose2.position.y = -0.05;
start_pose2.position.z = 0.8;
start_state.setFromIK(joint_model_group, start_pose2);
move_group.setStartState(start_state);

start_state.setFromIK()解释:

start_state.setFromIK(joint_model_group, start_pose2);

含义是,如果 start_pose2 对应的规划组joint_model_group 是链,并且解算器可用(实际上是可用的),则可通过 moveit 中自带的逆运动学求解器进行计算得到规划组中各关节位置值,前提是设置的start_pose2要在运动模型的参考系中。

参考:https://www.freesion.com/article/8268839796/

回头我也想总结一下常用函数和常用ROS消息。

2.7.3 规划可视化

现在,我们将从上面新的start state开始规划之前的target_pose1。

move_group.setPoseTarget(target_pose1);

2.7加入了路径约束的规划会比较慢,因为每个样本都必须调用逆运动学解算器。所以把规划时间从默认的5秒增加到10秒,确保规划器有足够的时间规划成功。

move_group.setPlanningTime(10.0);

success = (move_group.plan(my_plan) == moveit::planning_interface::MoveItErrorCode::SUCCESS);
ROS_INFO_NAMED("tutorial", "Visualizing plan 3 (constraints) %s", success ? "" : "FAILED");

所以机械臂的路径规划实际上也是规划问题。

在RViz 中可视化表达。

visual_tools.deleteAllMarkers();
visual_tools.publishAxisLabeled(start_pose2, "start");
visual_tools.publishAxisLabeled(target_pose1, "goal");
visual_tools.publishText(text_pose, "Constrained Goal", rvt::WHITE, rvt::XLARGE);
visual_tools.publishTrajectoryLine(my_plan.trajectory_, joint_model_group);
visual_tools.trigger();
visual_tools.prompt("next step");

2.8 执行笛卡尔路径

笛卡尔路径 Cartesian Paths.

2.8.1 规划路径

以 **2.5节 的规划方法 **,通过指定末端效应器要通过的路径点 waypoints 的列表 list,可以直接规划一段笛卡尔路径。

注意,我们从上面的新开始状态开始。初始位姿(start state)不需要添加到 waypoints 列表中,但把它加进去就可以在RViz 中显示出来:

//waypoints 是一个变长的路点数组,数组中每个点的类型是 geometry_msgs::Pose 类型
std::vector<geometry_msgs::Pose> waypoints;
//起始点
waypoints.push_back(start_pose2);
//初始化 目标点
geometry_msgs::Pose target_pose3 = start_pose2;
//第一段
target_pose3.position.z -= 0.2;
waypoints.push_back(target_pose3); // 向下
//第二段
target_pose3.position.y -= 0.2;
waypoints.push_back(target_pose3); //向右
//第三段
target_pose3.position.z += 0.2;
target_pose3.position.y += 0.2;
target_pose3.position.x -= 0.2;
waypoints.push_back(target_pose3); // 左上
//这样waypoints 就存入了一个三段两折的轨迹

2.8.2 调节关节速度

对于 接近物体 和 后退抓取 这些动作,笛卡尔运动通常较慢。我们通过降低每个关节最大速度的比例因子来降低机器人手臂的速度:

move_group.setMaxVelocityScalingFactor(0.1);

注意,这不是末端执行器点的速度。

2.8.3 设置差值步长

如果我们要让笛卡尔路径以1厘米的分辨率插值,就将笛卡尔平移中的最大步长指定为0.01。具体操作为:调用computeCartesianPath()按一定步长插补得到一个plan

computeCartesianPath()

该函数用来计算笛卡尔路径,该路径会经过之前设置好的 waypoints,步长为 eef_step,误差不超过 jump_threshold ,将该值设置为0可以有效禁用该变化

但是在操作真实机械臂的时候 jump_threshold 设为0,可能导致冗余连接,发生大量不可预测的动作。

返回值是0.0~1.0之间的一个数值,表示成功规划了给定路径点的比例(1.0表示都能成功经过)。

moveit_msgs::RobotTrajectory trajectory;
const double jump_threshold = 0.0;
//这里的步长eef_step设置为0.01,路径的分辨率就是1cm。
const double eef_step = 0.01;
double fraction = move_group.computeCartesianPath(waypoints, eef_step, jump_threshold, trajectory);
ROS_INFO_NAMED("tutorial", "Visualizing plan 4 (Cartesian path) (%.2f%% acheived)", fraction * 100.0);

2.8.4 规划可视化

在RViz 中显示出来上面的规划:

visual_tools.deleteAllMarkers();
visual_tools.publishText(text_pose, "Joint Space Goal", rvt::WHITE, rvt::XLARGE);
visual_tools.publishPath(waypoints, rvt::LIME_GREEN, rvt::SMALL);
for (std::size_t i = 0; i < waypoints.size(); ++i)
visual_tools.publishAxisLabeled(waypoints[i], "pt" + std::to_string(i), rvt::SMALL);
visual_tools.trigger();
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to continue the demo");

2.9 添加/去除 物体 && 附着/解绑 物体

以代码顺序介绍添加物体、避障规划、将物体附着在机械臂上,解绑,删除物体的操作实现。

2.9.1 设置物体

利用 moveit_msgs::CollisionObject 定义一个物体 collision object

moveit_msgs::CollisionObject collision_object;
collision_object.header.frame_id = move_group.getPlanningFrame();

给物体起一个名字:

collision_object.id = "box1";

描述这个物体的大小形状:

shape_msgs::SolidPrimitive primitive;
//规定它是一个盒子(立方体)
primitive.type = primitive.BOX;
//长宽高
primitive.dimensions.resize(3);
primitive.dimensions[0] = 0.4;
primitive.dimensions[1] = 0.1;
primitive.dimensions[2] = 0.4;

设置这个盒子的姿态:

geometry_msgs::Pose box_pose;
box_pose.orientation.w = 1.0;
box_pose.position.x = 0.4;
box_pose.position.y = -0.2;
box_pose.position.z = 1.0;
//把上面设置的大小和姿态都放入 collision_object的属性
collision_object.primitives.push_back(primitive);
collision_object.primitive_poses.push_back(box_pose);
//用CollisionObject 类中的 operation属性add表示添加到场景中,此外还有 REMOVE删除,APPEND附加,MOVE移动
collision_object.operation = collision_object.ADD; //把这个物体放入一个数组,便于后续增添
std::vector<moveit_msgs::CollisionObject> collision_objects;
collision_objects.push_back(collision_object);

2.9.2 加入环境并显示

添加到环境中

ROS_INFO_NAMED("tutorial", "Add an object into the world");
planning_scene_interface.addCollisionObjects(collision_objects);

RViz 显示结果:

visual_tools.publishText(text_pose, "Add object", rvt::WHITE, rvt::XLARGE);
visual_tools.trigger();
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to once the collision object appears in RViz");

2.9.3 避障规划

这样我们规划一个运动时,解算器就会把这个物体考虑进去,生成避开这个物体的路径:

调用的函数与此前的路径规划相同。

//以当前状态作为start state
move_group.setStartState(*move_group.getCurrentState());
geometry_msgs::Pose another_pose;
another_pose.orientation.w = 1.0;
another_pose.position.x = 0.4;
another_pose.position.y = -0.4;
another_pose.position.z = 0.9;
//以2.5 中的规划设置为 target state
move_group.setPoseTarget(another_pose); success = (move_group.plan(my_plan) == moveit::planning_interface::MoveItErrorCode::SUCCESS);
ROS_INFO_NAMED("tutorial", "Visualizing plan 5 (pose goal move around cuboid) %s", success ? "" : "FAILED");

2.9.4 规划可视化

visual_tools.deleteAllMarkers();
visual_tools.publishText(text_pose, "Obstacle Goal", rvt::WHITE, rvt::XLARGE);
visual_tools.publishTrajectoryLine(my_plan.trajectory_, joint_model_group);
visual_tools.trigger();
visual_tools.prompt("next step");

2.9.5 附着在机械臂后解绑

使用 MoveGroupInterface 类 attachObject() 函数将物体附着在机械臂上,参数是物体的名字 id。

ROS_INFO_NAMED("tutorial", "Attach the object to the robot");
move_group.attachObject(collision_object.id);

显示在 RViz 中:

visual_tools.publishText(text_pose, "Object dettached from robot", rvt::WHITE, rvt::XLARGE);
visual_tools.trigger(); /* Wait for MoveGroup to recieve and process the attached collision object message */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to once the collision object detaches to the "
"robot");

解绑操作使用 MoveGroupInterface 类 的detachObject() 函数,参数也是物体名:

ROS_INFO_NAMED("tutorial", "Detach the object from the robot");
move_group.detachObject(collision_object.id);

显示在 RViz 中:

visual_tools.publishText(text_pose, "Object dettached from robot", rvt::WHITE, rvt::XLARGE);
visual_tools.trigger(); /* Wait for MoveGroup to recieve and process the attached collision object message */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to once the collision object detaches to the "
"robot");

2.9.6 删除物体

将盒子box1从环境移除,调用planning_scene_interface 类的removeCollisionObjects()

添加物体:

planning_scene_interface.addCollisionObjects(collision_objects);
ROS_INFO_NAMED("tutorial", "Remove the object from the world");
std::vector<std::string> object_ids;
object_ids.push_back(collision_object.id);
planning_scene_interface.removeCollisionObjects(object_ids);

RViz 显示:

visual_tools.publishText(text_pose, "Object removed", rvt::WHITE, rvt::XLARGE);
visual_tools.trigger(); /* Wait for MoveGroup to recieve and process the attached collision object message */
visual_tools.prompt("Press 'next' in the RvizVisualToolsGui window to once the collision object disapears");

3. 总结

运行的程序已经讲解完毕,下面简单总结一下:

3.1 机械臂规划的两种方式

对机械臂规划有两种方式:

  • 2.5 中通过设置末端执行器的位置作为target;

    • 输入初始姿态:move_group.setStartState(*move_group.getCurrentState());

    • 设置目标姿态:

      move_group.setPoseTarget(target_pose1); target_pose1 是 描述末端执行器姿态的数组。

    • 附注:

      设置初始姿态也可以向设置目标姿态一样给数组赋值:

      geometry_msgs::Pose start_pose2;
      start_pose2.orientation.w = 1.0;
      start_pose2.position.x = 0.55;
      start_pose2.position.y = -0.05;
      start_pose2.position.z = 0.8;
      //setFromIK() 是一个检验函数
      start_state.setFromIK(joint_model_group, start_pose2);
      move_group.setStartState(start_state);
  • 2.6 中设置各个关节的姿态数组作为target。

    • 设置初始姿态:

      moveit::core::RobotStatePtr current_state = move_group.getCurrentState();
    • 修改姿态:

      std::vector<double> joint_group_positions;
      //调用copyJointGroupPositions将之前的位姿拷贝到关节数组中。
      current_state->copyJointGroupPositions(joint_model_group, joint_group_positions);
      //然后修改joint_group_positions数组即可修改
    • 设置目标姿态:

      move_group.setJointValueTarget(joint_group_positions);

3.2 其他函数

  1. MoveGroupInterface 类的绑定与解绑函数:
  • attachObject()
  • detachObject()
  1. PlanningSceneInterface 类的添加物体删除物体:

    • addCollisionObjects(collision_objects)
    • removeCollisionObjects(object_ids)

参考资料:

  1. https://www.jianshu.com/p/371605a36a6f
  2. https://www.freesion.com/article/8268839796/
  3. http://docs.ros.org/en/melodic/api/moveit_tutorials/html/doc/move_group_interface/move_group_interface_tutorial.html#planning-to-a-joint-space-goal

ROS机械臂 Movelt 学习笔记2 | Move Group 接口 C++的更多相关文章

  1. ROS机械臂 Movelt 学习笔记4 | Move Group 接口 Python

    Python 的使用总是比 C++ 简单许多,Move Group 的 Python 接口更为便捷,也为使用者提供了很多用于操纵机器人和机械臂的函数,能够和 C++ 接口实现相同的功能: 设置机械臂的 ...

  2. ROS机械臂 Movelt 学习笔记1 | 基础准备

    环境:Ubuntu18.04 + ROS Melodic 1. 安装ROS 官网下载安装步骤:http://wiki.ros.org/melodic/Installation/Ubuntu 一键安装的 ...

  3. ROS机械臂 Movelt 学习笔记3 | kinect360相机(v1)相关配置

    目标是做一个机械臂视觉抓取的demo,在基地里翻箱倒柜,没有找到学长所说的 d435,倒是找到了一个老古董 kinect 360. 前几天就已经在旧电脑上配置好了,现在记录在新电脑上的配置过程. 1. ...

  4. ROS机械臂 Movelt 学习笔记5 | MoveIt Commander Scripting

    前一讲python接口中提到moveit_commander 包.这个包提供了用于运动规划.笛卡尔路径计算以及拾取和放置的接口. moveit_commander 包还包括一个命令行接口程序movei ...

  5. java WEB学习笔记32:HttpSession 接口常用方法 及 HttpServletRequest接口中的Session方法 Demo

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  6. [原创]java WEB学习笔记06:ServletContext接口

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  7. typescript学习笔记(三)---接口

    关于第二章的学习笔记是变量声明. 接口:TypeScript的核心原则之一是对值所具有的结构进行类型检查. 它有时被称做“鸭式辨型法”或“结构性子类型化”. 在TypeScript里,接口的作用就是为 ...

  8. PHP 开发 APP 接口 学习笔记与总结 - APP 接口实例 [3] 首页 APP 接口开发方案 ② 读取缓存方式

    以静态缓存为例. 修改 file.php line:11 去掉 path 参数(方便),加上缓存时间参数: public function cacheData($k,$v = '',$cacheTim ...

  9. MySQL学习笔记:一道group by+group_concat解决的小问题

    闲来无事,逛逛V2EX发现一道MySQL数据库题目,原题如下: 遂打开很长一段时间都没用过SQLyog,噗呲噗呲的干起活来…… 建测试表: CREATE TABLE test_001 ( id INT ...

随机推荐

  1. MySQL实时在线备份恢复方案

    开源Linux 长按二维码加关注~ 上一篇:2020年MySQL数据库面试题总结 快照和复制技术的结合可以保证我们得到一个实时的在线MySQL备份解决方案. 当主库发生误操作时,只需要恢复备库上的快照 ...

  2. 使用 .net + blazor 做一个 kubernetes 开源文件系统

    背景 据我所知,目前 kubernetes 本身或者其它第三方社区都没提供 kubernetes 的文件系统.也就是说要从 kubernetes 的容器中下载或上传文件,需要先进入容器查看目录结构,然 ...

  3. 公司官网建站笔记(一):腾讯云服务器装CentOS8.2系统、重置密码、远程ssh登陆、sftp传递文件以及新建开发者账户

    前言   本篇使用的是腾讯云服务器,讲解了部署安装服务器CentOS8.2系统,重置密码,添加用户,远程登陆,远程传递文件等基本流程.   前提条件   购买了腾讯云服务器,如下图:     云服务器 ...

  4. Java高并发-Java内存模型和线程安全

    一.原子性 原子性是指一个操作是不可中断的.即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰. i++是原子操作吗? 不是,包含3个操作:读i,i=i+1,写i 32位的机子上读取 ...

  5. C# 与LINQ有关的语言特性

    1.隐式类型 我们知道强类型语言 C  C++ C#  Java 对变量的定义前必须要确定这个变量是什么类型的   例如  string str="abc";    int num ...

  6. 解决WIN7无法安装高版本Node.js问题

    网上很多文章都让去安装低版本node 由于业务需求,低版本node npm 有一些包支持的不好 npm出cb() never call 本着更新npm 顺带弄个高版本的node 单独更新npm npm ...

  7. 28.MysQL的日志管理及备份与恢复

    MySQL 索引.事务与存储引擎 目录 MySQL 索引.事务与存储引擎 MySQL 索引 索引的概念 索引的作用及副作用 索引的作用 索引的副作用 创建索引的原则依据 索引的分类和创建 普通索引 唯 ...

  8. 文件上传漏洞靶场分析 UPLOAD_LABS

    文件上传漏洞靶场(作者前言) 文件上传漏洞 产生原理 PASS 1) function checkFile() { var file = document.getElementsByName('upl ...

  9. 02 RESTFul接口和HTTP的幂等性分析

    RESTFul接口和HTTP的幂等性分析 REST全称是Representational State Transfer,中文为表述性状态转移,REST指的是一组架构约束条件和原则 RESTful表述的 ...

  10. NET架构师的基本职责

    NET架构师的基本职责1 职责 对本公司大健康平台提出技术研究及可行性报告; 结合需求设计高扩展性.高性能.安全.稳定.可靠的技术系统; 可以通过配置实现业务需求的变化,跟踪并研究***并应用于产品; ...