libfranka--cartesian_impedance_control分析

cartesian_impedance_control.cpp

部分内容来自链接,我个人以初学者的角度对代码进行了更加细致的解释。

先上笛卡尔空间阻抗控制官方例程(核心部分):

  // Compliance parameters
  const double translational_stiffness{150.0};
  const double rotational_stiffness{10.0};
  Eigen::MatrixXd stiffness(6, 6), damping(6, 6);
  stiffness.setZero();
  stiffness.topLeftCorner(3, 3) << translational_stiffness * Eigen::MatrixXd::Identity(3, 3);
  stiffness.bottomRightCorner(3, 3) << rotational_stiffness * Eigen::MatrixXd::Identity(3, 3);
  damping.setZero();
  damping.topLeftCorner(3, 3) << 2.0 * sqrt(translational_stiffness) *
                                     Eigen::MatrixXd::Identity(3, 3);
  damping.bottomRightCorner(3, 3) << 2.0 * sqrt(rotational_stiffness) *
  Eigen::MatrixXd::Identity(3, 3);
// connect to robot
franka::Robot robot(argv[1]);
setDefaultBehavior(robot);
// load the kinematics and dynamics model
franka::Model model = robot.loadModel();
franka::RobotState initial_state = robot.readOnce();
// equilibrium point is the initial position
Eigen::Affine3d initial_transform(Eigen::Matrix4d::Map(initial_state.O_T_EE.data()));
Eigen::Vector3d position_d(initial_transform.translation());
Eigen::Quaterniond orientation_d(initial_transform.linear());
// set collision behavior
robot.setCollisionBehavior({{100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0}},
                            {{100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0}},
                            {{100.0, 100.0, 100.0, 100.0, 100.0, 100.0}},
                            {{100.0, 100.0, 100.0, 100.0, 100.0, 100.0}});
// define callback for the torque control loop
std::function<franka::Torques(const franka::RobotState&, franka::Duration)> impedance_control_callback = [&](const franka::RobotState& robot_state, franka::Duration /*duration*/) -> franka::Torques {
  // get state variables
  std::array<double, 7> coriolis_array = model.coriolis(robot_state);
  std::array<double, 42> jacobian_array =
      model.zeroJacobian(franka::Frame::kEndEffector, robot_state);
  // convert to Eigen
  Eigen::Map<const Eigen::Matrix<double, 7, 1>> coriolis(coriolis_array.data());
  Eigen::Map<const Eigen::Matrix<double, 6, 7>> jacobian(jacobian_array.data());
  Eigen::Map<const Eigen::Matrix<double, 7, 1>> q(robot_state.q.data());
  Eigen::Map<const Eigen::Matrix<double, 7, 1>> dq(robot_state.dq.data());
  Eigen::Affine3d transform(Eigen::Matrix4d::Map(robot_state.O_T_EE.data()));
  Eigen::Vector3d position(transform.translation());
  Eigen::Quaterniond orientation(transform.linear());
  // compute error to desired equilibrium pose
  // position error
  Eigen::Matrix<double, 6, 1> error;
  error.head(3) << position - position_d;
  // orientation error
  // "difference" quaternion
  if (orientation_d.coeffs().dot(orientation.coeffs()) < 0.0) {
    orientation.coeffs() << -orientation.coeffs();
  }
  // "difference" quaternion
  Eigen::Quaterniond error_quaternion(orientation.inverse() * orientation_d);
  error.tail(3) << error_quaternion.x(), error_quaternion.y(), error_quaternion.z();
  // Transform to base frame
  error.tail(3) << -transform.linear() * error.tail(3);
  // compute control
  Eigen::VectorXd tau_task(7), tau_d(7);
  // Spring damper system with damping ratio=1
  tau_task << jacobian.transpose() * (-stiffness * error - damping * (jacobian * dq));
  tau_d << tau_task + coriolis;
  std::array<double, 7> tau_d_array{};
  Eigen::VectorXd::Map(&tau_d_array[0], 7) = tau_d;
  return tau_d_array;
};

我们依然拆分开解析:

  • 这个程序实现了什么功能?——固定末端位姿的阻抗控制,即机器人末端模拟一个弹簧阻尼机构。
  • 理论上是如何实现的?

τ d = J T ( q ) ( − α ⋅ e − β ⋅ J ( q ) q ˙ ) + C ( q , q ˙ ) \boldsymbol{\tau }_d=\boldsymbol{J}^T(\boldsymbol{q})(−\boldsymbol{\alpha }⋅\boldsymbol{e}−\boldsymbol{\beta }⋅\boldsymbol{J}(\boldsymbol{q})\boldsymbol{\dot{q}})+\boldsymbol{C}(\boldsymbol{q},\boldsymbol{\dot{q}}) τd=JT(q)(αeβJ(q)q˙)+C(q,q˙)

  • 其中 α \boldsymbol{\alpha} α β \boldsymbol{\beta} β分别为刚度(弹簧)和阻尼, e \boldsymbol{e} e是位姿偏差。科氏力 C ( q , q ˙ ) \boldsymbol{C}(\boldsymbol{q},\dot{\boldsymbol{q}}) C(q,q˙)比较小,影响不大。注意机器人本身自带重力补偿。
  • 本例中雅可比矩阵 J J J和科氏力均是通过libfranka提供的franka::Model类获取,注意构造函数输入可以是franka::RobotState结构体。
  • 这里多说一点偏差 e \boldsymbol{e} e,这个偏差是位置与姿态的混合偏差( e ∈ R 6 \boldsymbol{e}\in\mathbb{R}^{6} eR6),此处位置偏差欧式距离姿态偏差四元数虚部之差
    理清上述问题后,再看程序就没有那么复杂。
  1. 首先定义刚度阻尼参数:

此处刚度 α ∈ R 6 × 6 \boldsymbol{\alpha} \in \mathbb{R}^{6\times 6} αR6×6 ,阻尼 β ∈ R 6 × 6 \boldsymbol{\beta}\in\mathbb{R}^{6\times 6} βR6×6 ,且左上角 3 × 3 3 \times 3 3×3 区块为位置刚度/阻尼,右下角 3 × 3 3\times 3 3×3区块为姿态刚度/阻尼。这里设定每个维度上阻尼相同。

α = β = [ t r a n s l a t i o n 0 0 0 0 0 0 t r a n s l a t i o n 0 0 0 0 0 0 t r a n s l a t i o n 0 0 0 0 0 0 r o t a t i o n 0 0 0 0 0 0 r o t a t i o n 0 0 0 0 0 0 r o t a t i o n ] \alpha = \beta = \left[ \begin{matrix} translation & 0 & 0 & 0 & 0 & 0\\ 0 & translation & 0 & 0 & 0 & 0\\ 0 & 0 & translation & 0 & 0 & 0\\ 0 & 0 & 0 & rotation & 0 & 0\\ 0 & 0 & 0 & 0 & rotation & 0\\ 0 & 0 & 0 & 0 & 0 & rotation\\ \end{matrix} \right] α=β=translation000000translation000000translation000000rotation000000rotation000000rotation

  const double translational_stiffness{150.0}; // 位置刚度或阻尼
  const double rotational_stiffness{10.0}; // 姿态刚度或阻尼
  // 刚度矩阵、阻尼矩阵
  Eigen::MatrixXd stiffness(6, 6), damping(6, 6);
  
  stiffness.setZero(); // 刚度矩阵初始化为0
  stiffness.topLeftCorner(3, 3) << translational_stiffness * Eigen::MatrixXd::Identity(3, 3); // 左上角3X3区块为位置刚度
  stiffness.bottomRightCorner(3, 3) << rotational_stiffness * Eigen::MatrixXd::Identity(3, 3); // 右下角3X3区块为姿态刚度

  damping.setZero(); // 阻尼矩阵初始化为0
  damping.topLeftCorner(3, 3) << 2.0 * sqrt(translational_stiffness) * Eigen::MatrixXd::Identity(3, 3); // 左上角3X3区块为位置阻尼
  damping.bottomRightCorner(3, 3) << 2.0 * sqrt(rotational_stiffness) * Eigen::MatrixXd::Identity(3, 3); // 右下角3X3区块为姿态阻尼
  1. 加载机器人运动学与动力学模型,并读取机器人当前状态:
    // 加载机器人运动学与动力学模型
    franka::Model model = robot.loadModel();
    // 获取当前机器人状态信息
    franka::RobotState initial_state = robot.readOnce();

设置机器人最后平衡点为当前初始位姿。对当前末端执行器的笛卡尔位置、姿态进行提取。
我们知道齐次变换矩阵 O E E T ^{EE}_OT OEET中包含了关节坐标相对于基座标的平移和旋转,在数学中这种矩阵变换被称为仿射变换,即在三维空间中仿射变换 = 线性变换 + 平移 = 齐次变换矩阵。其中线性变换包含旋转、缩放、推移,线性变换不移动其坐标原点。注意姿态由四元数表示,更准确地说应该是单位四元数
p o s i t i o n d = [ p x p y p z ] position_d = \left[ \begin{matrix} p_x \\ p_y \\ p_z \\ \end{matrix} \right] positiond=pxpypz

o r i e n t a t i o n d = [ x y z w ] orientation_d = \left[ \begin {matrix} x \\ y \\ z \\ w \end{matrix} \right] orientationd=xyzw

通过上述的解释,下面这几行代码理解起来就简单多了:

  // 设置机器人最后平衡点为当前初始位姿
  Eigen::Affine3d initial_transform(Eigen::Matrix4d::Map(initial_state.O_T_EE.data())); // 将齐次变换矩阵转换为仿射变换
  Eigen::Vector3d position_d(initial_transform.translation()); // 提取仿射变换(或齐次变换矩阵)中的平移向量,"_d"代表"desire",即期望的位置
  Eigen::Quaterniond orientation_d(initial_transform.linear()); // 提取仿射变换(或齐次变换矩阵)中的旋转矩阵,并转换为四元数表达,"_d"代表"desire",即期望的姿态
  1. 定义力矩控制循环的回调函数

控制策略开始后,实时读取当前状态,即反馈信号:位置、速度、末端姿态、当前雅可比矩阵、当前科氏力。

  // get state variables
  std::array<double, 7> coriolis_array = model.coriolis(robot_state);
  std::array<double, 42> jacobian_array = model.zeroJacobian(franka::Frame::kEndEffector, robot_state);

注意通过 franka::RobotState& robot_state 读取的数据都是 std::array 模板类的对象,我们需要将其转化成 Eigen::Matrix 模板类的对象:

  // convert to Eigen
  Eigen::Map<const Eigen::Matrix<double, 7, 1>> coriolis(coriolis_array.data());
  Eigen::Map<const Eigen::Matrix<double, 6, 7>> jacobian(jacobian_array.data());
  Eigen::Map<const Eigen::Matrix<double, 7, 1>> q(robot_state.q.data());
  Eigen::Map<const Eigen::Matrix<double, 7, 1>> dq(robot_state.dq.data());

同上述一样,对运动过程中末端执行器的笛卡尔位置、姿态进行提取。:

  Eigen::Affine3d transform(Eigen::Matrix4d::Map(robot_state.O_T_EE.data())); 
  Eigen::Vector3d position(transform.translation()); // position后面没有"_d",为当前运动的位置
  Eigen::Quaterniond orientation(transform.linear()); // orientation后面没有"_d",为当前运动的姿态
  1. 计算位姿误差

位置偏差采用欧式距离,直接用当前末端位置减平衡点位置即可。姿态偏差采用四元数减法。这里稍微详细地说一下:由于同一个姿态可以用两个单位四元数表示,即 Q \boldsymbol{Q} Q − Q \boldsymbol{−Q} Q 表达的是同一个姿态,但是旋转的方向完全相反。为了计算两个四元数之间最小差值,需要在计算差之前首先调整符号(此处类似于0 ° \degree °与60 ° \degree °的差值,可以是 60 ° − 0 ° = 60 ° 60\degree-0\degree=60\degree 60°0°=60°,也可以是 60 ° − 360 ° = − 300 ° 60\degree-360\degree=-300\degree 60°360°=300°。但要取最小差值,即 60 ° 60\degree 60°);另外,此处采用四元数虚部来表示距离,这个定义在小范围的偏差上可用。

  // position error(位置误差)
  Eigen::Matrix<double, 6, 1> error;
  error.head(3) << position - position_d; // 将位置误差储存到error向量中前三位中

  // orientation error(姿态误差)
  // "difference" quaternion
  if (orientation_d.coeffs().dot(orientation.coeffs()) < 0.0) {
    orientation.coeffs() << -orientation.coeffs();
  }
  Eigen::Quaterniond error_quaternion(orientation.inverse() * orientation_d);
  error.tail(3) << error_quaternion.x(), error_quaternion.y(), error_quaternion.z();
  // Transform to base frame(转换到基坐标)
  error.tail(3) << 0 - transform.linear() * error.tail(3); // 将姿态误差储存到error向量中后三位中
  1. 计算关节力矩控制律

τ d = J T ( q ) ( − α ⋅ e − β ⋅ J ( q ) q ˙ ) + C ( q , q ˙ ) \boldsymbol{\tau }_d=\boldsymbol{J}^T(\boldsymbol{q})(−\boldsymbol{\alpha }⋅\boldsymbol{e}−\boldsymbol{\beta }⋅\boldsymbol{J}(\boldsymbol{q})\boldsymbol{\dot{q}})+\boldsymbol{C}(\boldsymbol{q},\boldsymbol{\dot{q}}) τd=JT(q)(αeβJ(q)q˙)+C(q,q˙)

  // compute control
  Eigen::VectorXd tau_task(7), tau_d(7);

  // Spring damper system with damping ratio = 1(阻尼比为1的弹簧-阻尼系统)
  tau_task << jacobian.transpose() * (-stiffness * error - damping * (jacobian * dq));
  tau_d << tau_task + coriolis; // 补偿科氏力

注意返回值的类型不能是 Eigen::Matrix ,需要变回 std::array

  std::array<double, 7> tau_d_array{};
  Eigen::VectorXd::Map(&tau_d_array[0], 7) = tau_d;
  return tau_d_array;
  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
`compute_cartesian_path`函数是MoveIt中一个非常有用的函数,可以用来计算机器人末端执行器的直线或圆弧路径。该函数的完整定义如下: ```python def compute_cartesian_path(self, waypoints, eef_step, jump_threshold, avoid_collisions=True, path_constraints=None, attached_object_touch_links=None, moveit_msgs_opts=None): """ Compute a sequence of waypoints that make the end-effector move in straight line segments that follow the poses specified as a list of waypoints. :param waypoints: A list of waypoints that specify the poses for the end-effector to move through. Each waypoint is specified as a `geometry_msgs.msg.PoseStamped` message. :param eef_step: The distance in meters between consecutive waypoints on the returned path. This value effectively controls the granularity of the computed path. :param jump_threshold: The maximum distance in configuration space that the end-effector is allowed to travel in a single step. If the distance between consecutive configurations along the resulting path exceeds this value, the path is considered invalid and will not be returned. :param avoid_collisions: Whether to check for collisions between the robot and the environment as the path is being computed. If collisions are detected, the computed path will be modified to avoid the obstacles. :param path_constraints: A `moveit_msgs.msg.Constraints` message that specifies constraints to be enforced on the computed path. If `None`, no constraints will be used. :param attached_object_touch_links: A list of link names that specify which links the attached object is allowed to touch during the motion. If `None`, the default value of the `touch_links` parameter in the `planning_scene_interface` constructor will be used. :param moveit_msgs_opts: Advanced options for the computation of the path. This is a dictionary of key-value pairs, where the keys are strings and the values are either integer, float or string values. The available options are the same as those that can be set in the `moveit_msgs.msg.RobotTrajectory` message. :returns: `(plan, fraction)` where `plan` is a `moveit_msgs.msg.RobotTrajectory` message that describes the computed path, and `fraction` is a float value between 0.0 and 1.0 that specifies the fraction of the path that was successfully computed. If `fraction` is less than 1.0, the returned plan should be considered invalid. """ ``` 其中,参数的含义如下: - `waypoints`:一个包含机器人末端执行器路径的列表,每个路径点都是一个`geometry_msgs.msg.PoseStamped`消息。 - `eef_step`:机器人末端执行器在计算出的路径中使用的步长,以米为单位,控制路径的粒度。 - `jump_threshold`:在计算出的路径中允许的连续步长之间的最大距离,以配置空间中的距离为单位。如果连续的配置之间的距离超过此值,则该路径被认为是无效的,不会返回。 - `avoid_collisions`:在计算路径时是否检查机器人与环境之间的碰撞。如果检测到碰撞,则计算出的路径将被修改以避免障碍物。 - `path_constraints`:一个`moveit_msgs.msg.Constraints`消息,指定要强制执行的计算路径上的约束条件。如果为`None`,则不使用任何约束。 - `attached_object_touch_links`:一个字符串列表,指定附加对象在运动过程中允许触摸的链接。如果为`None`,则将使用`planning_scene_interface`构造函数中`touch_links`参数的默认值。 - `moveit_msgs_opts`:计算路径的高级选项。这是一个键值对字典,其中键是字符串,值可以是整数、浮点数或字符串。可用选项与可以在`moveit_msgs.msg.RobotTrajectory`消息中设置的选项相同。 该函数的返回值为一个元组`(plan, fraction)`,其中`plan`是一个`moveit_msgs.msg.RobotTrajectory`消息,描述了计算出的路径,`fraction`是介于0.0和1.0之间的浮点值,指定成功计算的路径的比例。如果`fraction`小于1.0,则返回的计划应视为无效。 该函数通过计算机器人末端执行器的直线或圆弧路径,生成一条路径,将机器人从起始点运动到目标点。在计算路径时,可以指定一些约束条件,例如机器人与环境之间的碰撞,机器人的关节角度等。这使得计算出的路径更加精确和安全。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值