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;
### 解决 libfranka 中因 cartesian_reflex 导致的 move 命令中断问题 当遇到 `libfranka` 的 `Move command aborted: motion aborted by reflex! ["communication_constraints_violation"]` 错误时,通常是因为机器人控制器检测到通信约束违反或运动不连续性而触发了保护机制[^1]。以下是针对该问题的具体解决方案: #### 1. **检查网络连接稳定性** 控制器的成功率 (`control_command_success_rate`) 较低 (如 0.733765),表明存在数据包丢失的情况。这可能是由于网络不稳定引起的。可以通过以下方法优化网络环境: - 使用有线网络代替无线网络。 - 减少其他高带宽应用对同一网络资源的竞争。 #### 2. **调整控制频率** 如果主频降低可能导致性能下降,可以尝试提高控制循环的频率设置。通过修改配置参数确保其满足最低要求 (例如 1 kHz)。具体实现如下: ```python import franka_gym.envs.lib_franka as lf # 设置更高的控制频率 controller_frequency = 1000 # Hz robot.set_controller_frequency(controller_frequency) ``` #### 3. **重置错误状态** 当出现 `[ERROR] libfranka: Move command rejected: command not possible in the current mode!` 类型的错误时,可通过发布消息至 `/franka_control/error_recovery/goal` 主题来恢复正常操作模式[^3]。命令如下所示: ```bash rostopic pub -1 /franka_control/error_recovery/goal franka_msgs/ErrorRecoveryActionGoal {} ``` #### 4. **验证笛卡尔空间控制逻辑** 若需测试末端执行器沿 Z 轴方向移动的功能,建议启动对应的仿真脚本以观察行为是否符合预期[^2]: ```bash ros2 launch lbr_simulation_gazebo_command cartesian_pose_control.launch.py ``` #### 5. **分析姿态目标设定** 对于涉及四元数表示的姿态目标向量 \(orientation_d\) ,应确认其定义无误并合理分配权重给各分量 \(x, y, z, w\)[^4]。例如,在 Python 环境下初始化此类变量可能像这样完成: ```python from tf.transformations import quaternion_from_euler roll, pitch, yaw = 0, 0, math.pi/4 # 定义欧拉角角度 orientation_d = quaternion_from_euler(roll, pitch, yaw).tolist() print(f"Desired Orientation Quaternion: {orientation_d}") ``` --- ### 总结 上述措施涵盖了从硬件层面(网络质量)、软件参数调节(控制频率),再到特定 ROS 工具链的操作指导等多个方面,旨在全面应对由 `cartesian_reflex` 引发的一系列异常状况。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值