Franka例程学习——examples_common
这一次我们学习Franka所有例程里面都要调用的examples_common.h和examples_common.cpp,一个是.h头文件放置声明的函数、类、变量以及宏等内容,.c文件里面是具体的函数实现。
一、源代码
examples_common.h
// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#pragma once#include <array>#include <Eigen/Core>#include <franka/control_types.h>
#include <franka/duration.h>
#include <franka/robot.h>
#include <franka/robot_state.h>/*** @file examples_common.h* Contains common types and functions for the examples.*//*** Sets a default collision behavior, joint impedance, Cartesian impedance, and filter frequency.** @param[in] robot Robot instance to set behavior on.*/
void setDefaultBehavior(franka::Robot& robot);/*** An example showing how to generate a joint pose motion to a goal position. Adapted from:* Wisama Khalil and Etienne Dombre. 2002. Modeling, Identification and Control of Robots* (Kogan Page Science Paper edition).*/
class MotionGenerator {public:/*** Creates a new MotionGenerator instance for a target q.** @param[in] speed_factor General speed factor in range [0, 1].* @param[in] q_goal Target joint positions.*/MotionGenerator(double speed_factor, const std::array<double, 7> q_goal);/*** Sends joint position calculations** @param[in] robot_state Current state of the robot.* @param[in] period Duration of execution.** @return Joint positions for use inside a control loop.*/franka::JointPositions operator()(const franka::RobotState& robot_state, franka::Duration period);private:using Vector7d = Eigen::Matrix<double, 7, 1, Eigen::ColMajor>;using Vector7i = Eigen::Matrix<int, 7, 1, Eigen::ColMajor>;bool calculateDesiredValues(double t, Vector7d* delta_q_d) const;void calculateSynchronizedValues();static constexpr double kDeltaQMotionFinished = 1e-6;const Vector7d q_goal_;Vector7d q_start_;Vector7d delta_q_;Vector7d dq_max_sync_;Vector7d t_1_sync_;Vector7d t_2_sync_;Vector7d t_f_sync_;Vector7d q_1_;double time_ = 0.0;Vector7d dq_max_ = (Vector7d() << 2.0, 2.0, 2.0, 2.0, 2.5, 2.5, 2.5).finished();Vector7d ddq_max_start_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished();Vector7d ddq_max_goal_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished();
};
examples_common.cpp
// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include "examples_common.h"#include <algorithm>
#include <array>
#include <cmath>#include <franka/exception.h>
#include <franka/robot.h>void setDefaultBehavior(franka::Robot& robot) {robot.setCollisionBehavior({{20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0}}, {{20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0}},{{10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0}}, {{10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0}},{{20.0, 20.0, 20.0, 20.0, 20.0, 20.0}}, {{20.0, 20.0, 20.0, 20.0, 20.0, 20.0}},{{10.0, 10.0, 10.0, 10.0, 10.0, 10.0}}, {{10.0, 10.0, 10.0, 10.0, 10.0, 10.0}});robot.setJointImpedance({{3000, 3000, 3000, 2500, 2500, 2000, 2000}});robot.setCartesianImpedance({{3000, 3000, 3000, 300, 300, 300}});
}MotionGenerator::MotionGenerator(double speed_factor, const std::array<double, 7> q_goal): q_goal_(q_goal.data()) {dq_max_ *= speed_factor;ddq_max_start_ *= speed_factor;ddq_max_goal_ *= speed_factor;dq_max_sync_.setZero();q_start_.setZero();delta_q_.setZero();t_1_sync_.setZero();t_2_sync_.setZero();t_f_sync_.setZero();q_1_.setZero();
}bool MotionGenerator::calculateDesiredValues(double t, Vector7d* delta_q_d) const {Vector7i sign_delta_q;sign_delta_q << delta_q_.cwiseSign().cast<int>();Vector7d t_d = t_2_sync_ - t_1_sync_;Vector7d delta_t_2_sync = t_f_sync_ - t_2_sync_;std::array<bool, 7> joint_motion_finished{};for (size_t i = 0; i < 7; i++) {if (std::abs(delta_q_[i]) < kDeltaQMotionFinished) {(*delta_q_d)[i] = 0;joint_motion_finished[i] = true;} else {if (t < t_1_sync_[i]) {(*delta_q_d)[i] = -1.0 / std::pow(t_1_sync_[i], 3.0) * dq_max_sync_[i] * sign_delta_q[i] *(0.5 * t - t_1_sync_[i]) * std::pow(t, 3.0);} else if (t >= t_1_sync_[i] && t < t_2_sync_[i]) {(*delta_q_d)[i] = q_1_[i] + (t - t_1_sync_[i]) * dq_max_sync_[i] * sign_delta_q[i];} else if (t >= t_2_sync_[i] && t < t_f_sync_[i]) {(*delta_q_d)[i] =delta_q_[i] + 0.5 *(1.0 / std::pow(delta_t_2_sync[i], 3.0) *(t - t_1_sync_[i] - 2.0 * delta_t_2_sync[i] - t_d[i]) *std::pow((t - t_1_sync_[i] - t_d[i]), 3.0) +(2.0 * t - 2.0 * t_1_sync_[i] - delta_t_2_sync[i] - 2.0 * t_d[i])) *dq_max_sync_[i] * sign_delta_q[i];} else {(*delta_q_d)[i] = delta_q_[i];joint_motion_finished[i] = true;}}}return std::all_of(joint_motion_finished.cbegin(), joint_motion_finished.cend(),[](bool x) { return x; });
}void MotionGenerator::calculateSynchronizedValues() {Vector7d dq_max_reach(dq_max_);Vector7d t_f = Vector7d::Zero();Vector7d delta_t_2 = Vector7d::Zero();Vector7d t_1 = Vector7d::Zero();Vector7d delta_t_2_sync = Vector7d::Zero();Vector7i sign_delta_q;sign_delta_q << delta_q_.cwiseSign().cast<int>();for (size_t i = 0; i < 7; i++) {if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) {if (std::abs(delta_q_[i]) < (3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_start_[i]) +3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_goal_[i]))) {dq_max_reach[i] = std::sqrt(4.0 / 3.0 * delta_q_[i] * sign_delta_q[i] *(ddq_max_start_[i] * ddq_max_goal_[i]) /(ddq_max_start_[i] + ddq_max_goal_[i]));}t_1[i] = 1.5 * dq_max_reach[i] / ddq_max_start_[i];delta_t_2[i] = 1.5 * dq_max_reach[i] / ddq_max_goal_[i];t_f[i] = t_1[i] / 2.0 + delta_t_2[i] / 2.0 + std::abs(delta_q_[i]) / dq_max_reach[i];}}double max_t_f = t_f.maxCoeff();for (size_t i = 0; i < 7; i++) {if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) {double a = 1.5 / 2.0 * (ddq_max_goal_[i] + ddq_max_start_[i]);double b = -1.0 * max_t_f * ddq_max_goal_[i] * ddq_max_start_[i];double c = std::abs(delta_q_[i]) * ddq_max_goal_[i] * ddq_max_start_[i];double delta = b * b - 4.0 * a * c;if (delta < 0.0) {delta = 0.0;}dq_max_sync_[i] = (-1.0 * b - std::sqrt(delta)) / (2.0 * a);t_1_sync_[i] = 1.5 * dq_max_sync_[i] / ddq_max_start_[i];delta_t_2_sync[i] = 1.5 * dq_max_sync_[i] / ddq_max_goal_[i];t_f_sync_[i] =(t_1_sync_)[i] / 2.0 + delta_t_2_sync[i] / 2.0 + std::abs(delta_q_[i] / dq_max_sync_[i]);t_2_sync_[i] = (t_f_sync_)[i] - delta_t_2_sync[i];q_1_[i] = (dq_max_sync_)[i] * sign_delta_q[i] * (0.5 * (t_1_sync_)[i]);}}
}franka::JointPositions MotionGenerator::operator()(const franka::RobotState& robot_state,franka::Duration period) {time_ += period.toSec();if (time_ == 0.0) {q_start_ = Vector7d(robot_state.q_d.data());delta_q_ = q_goal_ - q_start_;calculateSynchronizedValues();}Vector7d delta_q_d;bool motion_finished = calculateDesiredValues(time_, &delta_q_d);std::array<double, 7> joint_positions;Eigen::VectorXd::Map(&joint_positions[0], 7) = (q_start_ + delta_q_d);franka::JointPositions output(joint_positions);output.motion_finished = motion_finished;return output;
}
二、.h代码解读
examples_common.h文件包含以下内容
setDefaultBehavior
函数:设置机器人实例默认行为
MotionGenerator
类:用于生成机器人的关节位置轨迹
1.setDefaultBehavior
函数:
void setDefaultBehavior(franka::Robot& robot);
该函数接受一个 franka::Robot
实例作为参数,并设置该机器人实例的默认行为。默认行为通常涉及冲突检测(collision behavior)、关节阻抗(joint impedance)、笛卡尔阻抗(Cartesian impedance)和滤波频率等参数。此函数在代码中并没有实现,但可以猜测它会通过调用 robot
的方法来设置这些控制参数。
2. MotionGenerator 类
这个类是核心部分,用于生成机器人的关节位置轨迹。目标是从当前关节位置(q_start_)生成一个平滑的运动轨迹,到达目标位置 q_goal_。
2.1 构造函数
MotionGenerator(double speed_factor, const std::array<double, 7> q_goal);
- speed_factor: 控制运动速度的因子,值范围为 [0, 1]。速度因子用于调节运动的快慢。
- q_goal: 目标关节位置,存储了一个7维数组,每个值代表一个关节的目标位置。
该构造函数初始化了机器人运动所需的各种参数。q_goal_ 被设定为目标位置,q_start_ 和其他参数会在后续计算中动态更新。
2.2 operator() 运算符重载
franka::JointPositions operator()(const franka::RobotState& robot_state, franka::Duration period);
该方法是类的核心方法之一,表示根据当前的机器人状态和执行时间,计算目标的关节位置。它接受以下参数:
- robot_state: 当前机器人的状态,包含了关节的位置、速度等信息。
- period: 执行的时间,控制当前计算步骤的时长。
返回值是一个 franka::JointPositions 类型,表示机器人的关节位置。在控制循环中,机器人的位置会持续更新。
2.3 私有成员
using Vector7d = Eigen::Matrix<double, 7, 1, Eigen::ColMajor>;using Vector7i = Eigen::Matrix<int, 7, 1, Eigen::ColMajor>;
这些成员主要用于计算和控制目标运动轨迹。
- Vector7d 是一个 7 维的向量,使用Eigen::Matrix<double, 7, 1>来表示机器人的关节位置和速度。
2.4 计算运动轨迹的辅助函数
bool calculateDesiredValues(double t, Vector7d*_q_d) const;
void calculateSynchronizedValues();
- calculateDesiredValues:该方法会根据时间 t 计算目标关节位置的变化量 _q_d,具体的计算方式可以依赖于不同的运动模型。
- calculateSynchronizedValues:该方法计算一组与同步相关的参数,例如可能是加速度、速度、目标位置的变化等。
2.5 常量和默认值
static constexpr double kDeltaQMotionFinished = 1e-6;const Vector7d q_goal_;Vector7d q_start_;Vector7d delta_q_;Vector7d dq_max_sync_;Vector7d t_1_sync_;Vector7d t_2_sync_;Vector7d t_f_sync_;Vector7d q_1_;double time_ = 0.0;Vector7d dq_max_ = (Vector7d() << 2.0, 2.0, 2.0, 2.0, 2.5, 2.5, 2.5).finished();Vector7d ddq_max_start_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished();Vector7d ddq_max_goal_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished();
- kDeltaQMotionFinished 是一个常量,用于表示运动是否已经完成。如果关节位置之间的差异小于这个值,表示运动已经完成。
- q_goal_: 目标关节位置,用户指定的目标位置。
- q_start_: 起始关节位置,通过机器人当前状态来初始化。
- dq_max_: 最大关节速度(单位:rad/s),在运动过程中可能会限制最大速度。
- ddq_max_start_ 和 ddq_max_goal_: 最大加速度限制,用于控制起始和结束的加速度。
这段代码的主要目的是生成一个目标关节位置的运动轨迹。它通过 MotionGenerator 类和各种辅助函数来计算运动轨迹,并返回新的关节位置值以供控制循环使用。关键的计算涉及关节位置、速度、加速度等物理量的计算,以及确保运动平稳且在允许的范围内。
三、.c代码解读
3.1 setDefaultBehavior
函数
void setDefaultBehavior(franka::Robot& robot) {robot.setCollisionBehavior({{20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0}}, {{20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0}},{{10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0}}, {{10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0}},{{20.0, 20.0, 20.0, 20.0, 20.0, 20.0}}, {{20.0, 20.0, 20.0, 20.0, 20.0, 20.0}},{{10.0, 10.0, 10.0, 10.0, 10.0, 10.0}}, {{10.0, 10.0, 10.0, 10.0, 10.0, 10.0}});robot.setJointImpedance({{3000, 3000, 3000, 2500, 2500, 2000, 2000}});robot.setCartesianImpedance({{3000, 3000, 3000, 300, 300, 300}});
}
- 该函数设置机器人的 碰撞行为 和 阻抗控制参数。
setCollisionBehavior
设置碰撞检测的参数,数值越高,机器人遇到障碍物时的反应越强。setJointImpedance
设置关节的阻抗控制,阻抗高则机器人的刚度更大。setCartesianImpedance
设置笛卡尔空间的阻抗,控制机器人末端执行器的刚度。
3.2 MotionGenerator
类的构造函数
MotionGenerator::MotionGenerator(double speed_factor, const std::array<double, 7> q_goal): q_goal_(q_goal.data()) {dq_max_ *= speed_factor;ddq_max_start_ *= speed_factor;ddq_max_goal_ *= speed_factor;dq_max_sync_.setZero();q_start_.setZero();delta_q_.setZero();t_1_sync_.setZero();t_2_sync_.setZero();t_f_sync_.setZero();q_1_.setZero();
}
MotionGenerator
类是用于生成机器人运动轨迹的类。- 构造函数接收一个 速度因子 和目标关节位置
q_goal
。 - 各种最大速度(
dq_max_
)和加速度(ddq_max_start_
,ddq_max_goal_
)根据 速度因子 调整,从而控制运动的快慢。 - 初始化了其他成员变量为零,包括起始位置、关节差异等。
3.3 calculateDesiredValues
函数
bool MotionGenerator::calculateDesiredValues(double t, Vector7d*_q_d) const {// 计算各个关节的期望运动值(_q_d)...
}
calculateDesiredValues 函数根据当前时间 t 和机器人每个关节的运动参数,计算出每个关节期望的位移变化量 _q_d。它通过平滑加速、匀速和减速阶段来保证机器人运动的流畅性,并且通过返回 true 或 false 来指示运动是否完成。这个函数对于精确控制机器人关节运动非常重要,确保了机器人能够平滑且精确地从初始位置移动到目标位置。
3.3.1 初始化和符号处理
Vector7i sign_q;
sign_q <<_q_.cwiseSign().cast<int>();
Vector7d t_d = t_2_sync_ - t_1_sync_;
Vector7d_t_2_sync = t_f_sync_ - t_2_sync_;
std::array<bool, 7> joint_motion_finished{};
- sign_q:计算 _q_(目标关节位置的变化量)中每个分量的符号(正或负),以便在计算过程中知道每个关节的运动方向。
- t_d:这是每个关节的加速阶段时间和减速阶段时间之间的差异,表示“总的同步时间差”。
- _t_2_sync:这表示从同步的减速阶段开始到最终的目标时间点之间的差值,用于调整每个关节的运动时间。
- joint_motion_finished:这是一个布尔数组,表示每个关节的运动是否完成。
3.3.2 逐关节计算期望位移(_q_d)
for (size_t i = 0; i < 7; i++) {if (std::abs(delta_q_[i]) < kDeltaQMotionFinished) {(*delta_q_d)[i] = 0;joint_motion_finished[i] = true;} else {if (t < t_1_sync_[i]) {(*delta_q_d)[i] = -1.0 / std::pow(t_1_sync_[i], 3.0) * dq_max_sync_[i] * sign_delta_q[i] *(0.5 * t - t_1_sync_[i]) * std::pow(t, 3.0);} else if (t >= t_1_sync_[i] && t < t_2_sync_[i]) {(*delta_q_d)[i] = q_1_[i] + (t - t_1_sync_[i]) * dq_max_sync_[i] * sign_delta_q[i];} else if (t >= t_2_sync_[i] && t < t_f_sync_[i]) {(*delta_q_d)[i] =delta_q_[i] + 0.5 *(1.0 / std::pow(delta_t_2_sync[i], 3.0) *(t - t_1_sync_[i] - 2.0 * delta_t_2_sync[i] - t_d[i]) *std::pow((t - t_1_sync_[i] - t_d[i]), 3.0) +(2.0 * t - 2.0 * t_1_sync_[i] - delta_t_2_sync[i] - 2.0 * t_d[i])) *dq_max_sync_[i] * sign_delta_q[i];} else {(*delta_q_d)[i] = delta_q_[i];joint_motion_finished[i] = true;}}}
- 判断是否已经完成运动:如果 _q_[i](目标位置变化量)小于 kDeltaQMotionFinished(表示已经接近目标位置),则认为该关节的运动完成,(*_q_d)[i] 置为 0,并将 joint_motion_finished[i] 设置为 true。
- 加速阶段(
t < t_1_sync_[i]
):如果当前时间 t 处于加速阶段(即小于 t_1_sync_[i]),则根据加速度和最大速度计算当前关节的运动。具体采用三次多项式插值公式来平滑地加速。 - 匀速阶段(
t_1_sync_[i] <= t < t_2_sync_[i]
):如果时间 t 处于匀速阶段(即在 t_1_sync_[i] 到 t_2_sync_[i] 之间),则关节的变化速度是恒定的,按照线性关系计算关节位移。 - 减速阶段(
t_2_sync_[i] <= t < t_f_sync_[i]
):如果时间 t 处于减速阶段(即在 t_2_sync_[i] 到 t_f_sync_[i] 之间),则通过三次多项式插值来平滑地减速,使得关节的运动接近目标。 - 完成阶段:如果当前时间 t 超过了目标时间,表示关节运动已经完成,(*_q_d)[i] 设置为目标位置的变化量 _q_[i],并将 joint_motion_finished[i] 设置为 true。
3.3.3 返回运动是否完成
return std::all_of(joint_motion_finished.cbegin(), joint_motion_finished.cend(),[](bool x) { return x; });
最后,函数通过 std::all_of 来检查 joint_motion_finished 数组中的所有关节是否都完成了运动。如果所有关节的 joint_motion_finished 都是 true,则表示所有关节的运动已完成,返回 true;否则,返回 false。
3.4 calculateSynchronizedValues
函数
void MotionGenerator::calculateSynchronizedValues() {// 计算运动的同步时长和参数
}
函数
calculateSynchronizedValues()
计算了机器人每个关节在完成从当前位置到目标位置的运动时,如何根据速度和加速度的限制,计算各个关节的同步运动参数。特别地,它考虑了加速、减速和匀速运动的时间分配,并通过计算得到同步的最大速度、时间和位移,确保多个自由度的运动能够协调一致。这种计算方式对于需要精确控制多个关节的机器人系统是非常常见的,尤其是在关节数较多的情况下。
3.4.1 变量概述
Vector7d dq_max_reach(dq_max_);Vector7d t_f = Vector7d::Zero();Vector7d delta_t_2 = Vector7d::Zero();Vector7d t_1 = Vector7d::Zero();Vector7d delta_t_2_sync = Vector7d::Zero();Vector7i sign_delta_q;sign_delta_q << delta_q_.cwiseSign().cast<int>();
- dq_max_: 最大速度(或最大关节速度)的向量(7维),表示每个关节的最大速度。
- q: 目标角度变化量(7维),即每个关节从当前角度到目标角度的变化量。
- ddq_max_start_ 和 ddq_max_goal_: 启动阶段和目标阶段的最大加速度(7维向量)。
- kDeltaQMotionFinished: 用于判断是否已完成运动的小阈值。
- dq_max_reach: 计算的“最大速度”值,代表在一定的加速度限制下,某个关节的最大可达到速度。
- t_f, t_1,_t_2, t_1_sync,_t_2_sync, t_f_sync: 这些是与时间相关的参数,用来控制运动的时间分配,t_f 代表运动结束时的总时间,t_1 和_t_2 分别表示加速阶段和减速阶段的时间,t_f_sync 是同步版本的总时间,t_2_sync 和 q_1_ 与这些时间相关联,控制不同阶段的运动。
- sign_q: 存储每个关节的目标运动方向,使用
cwiseSign()
计算每个角度变化的符号
3.4.2 计算最大速度 dq_max_reach
和总时间 t_f
for (size_t i = 0; i < 7; i++) {if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) {if (std::abs(delta_q_[i]) < (3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_start_[i]) +3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_goal_[i]))) {dq_max_reach[i] = std::sqrt(4.0 / 3.0 * delta_q_[i] * sign_delta_q[i] *(ddq_max_start_[i] * ddq_max_goal_[i]) /(ddq_max_start_[i] + ddq_max_goal_[i]));}t_1[i] = 1.5 * dq_max_reach[i] / ddq_max_start_[i];delta_t_2[i] = 1.5 * dq_max_reach[i] / ddq_max_goal_[i];t_f[i] = t_1[i] / 2.0 + delta_t_2[i] / 2.0 + std::abs(delta_q_[i]) / dq_max_reach[i];}}
- 该段代码首先检查每个关节的运动变化量(
_q_
),如果运动足够大(大于一个预定的阈值kDeltaQMotionFinished
),则计算该关节的最大速度dq_max_reach
。这个计算基于启动和目标阶段的最大加速度(ddq_max_start_
和ddq_max_goal_
)。 t_1
和_t_2
分别是加速阶段和减速阶段所需的时间。这里的1.5
是与加速度和速度关系的系数,通常是为了保证运动平滑。- 运动的总时间
t_f
是加速阶段、减速阶段和匀速阶段的时间的总和。
3.4.3 计算同步时间 t_f_sync
和同步速度 dq_max_sync
double max_t_f = t_f.maxCoeff();for (size_t i = 0; i < 7; i++) {if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) {double a = 1.5 / 2.0 * (ddq_max_goal_[i] + ddq_max_start_[i]);double b = -1.0 * max_t_f * ddq_max_goal_[i] * ddq_max_start_[i];double c = std::abs(delta_q_[i]) * ddq_max_goal_[i] * ddq_max_start_[i];double delta = b * b - 4.0 * a * c;if (delta < 0.0) {delta = 0.0;}dq_max_sync_[i] = (-1.0 * b - std::sqrt(delta)) / (2.0 * a);t_1_sync_[i] = 1.5 * dq_max_sync_[i] / ddq_max_start_[i];delta_t_2_sync[i] = 1.5 * dq_max_sync_[i] / ddq_max_goal_[i];t_f_sync_[i] =(t_1_sync_)[i] / 2.0 + delta_t_2_sync[i] / 2.0 + std::abs(delta_q_[i] / dq_max_sync_[i]);t_2_sync_[i] = (t_f_sync_)[i] - delta_t_2_sync[i];q_1_[i] = (dq_max_sync_)[i] * sign_delta_q[i] * (0.5 * (t_1_sync_)[i]);}}
- 计算出最大总时间
max_t_f
,然后为每个关节计算同步的最大速度dq_max_sync_
和同步的时间参数。这里通过解一个二次方程来计算同步速度dq_max_sync_
,目的是使得所有关节的运动在相同的总时间内完成,保持运动的协调性。 - 之后,计算加速阶段(
t_1_sync_
)、减速阶段(_t_2_sync
)的同步时间,以及整个同步运动的总时间t_f_sync_
。 - 最后,
q_1_
计算的是在加速阶段某一时刻的位移,用来确保在同步过程中每个关节的运动量是协调的。
3.5 operator()
重载
这段代码的核心是根据机器人的当前状态和目标位置,计算出一个目标关节位置,并返回给调用者。它会:
- 跟踪时间,更新机器人的运动状态。
- 在首次调用时记录初始状态并计算目标位置的变化量。
- 根据时间计算期望的关节位置,并返回计算出的目标关节位置。
- 使用
motion_finished
标志指示是否完成运动。
operator()
的功能是动态地生成机器人运动轨迹,以便机器人每次调用时都能得到适当的目标关节位置,进而进行连续的运动控制。
3.5.1 更新当前时间 time_
time_ += period.toSec();
3.5.2 初始化开始的关节位置 q_start_
和计算关节变化量 _q_
if (time_ == 0.0) {q_start_ = Vector7d(robot_state.q_d.data());_q_ = q_goal_ - q_start_;calculateSynchronizedValues();
}
- 如果
time_ == 0.0
(意味着是第一次调用或新的运动开始),首先记录机器人的当前关节位置(robot_state.q_d
是机器人当前的关节位置),并将其存储在q_start_
中,q_start_
是一个Vector7d
类型的向量,表示机器人每个关节的初始位置(7个自由度)。 q_goal_
是目标位置,它是在其他地方定义的目标关节位置。_q_
计算为目标位置与当前起始位置的差值,用来表示目标关节位置的变化量。- 然后调用
calculateSynchronizedValues()
来计算同步的速度、加速度等参数,以确保在多个关节间进行协调运动。
3.5.3 计算期望的关节变化量 _q_d
Vector7d_q_d;
bool motion_finished = calculateDesiredValues(time_, &_q_d);
calculateDesiredValues(time_, &_q_d)
用于计算当前时间time_
对应的期望关节变化量_q_d
。_q_d
是机器人从当前关节位置到目标关节位置的期望变化量。calculateDesiredValues
可能会基于当前时间、加速度、速度限制等参数计算期望的运动轨迹,并将期望的变化量存储在_q_d
中。返回值motion_finished
是一个布尔值,表示运动是否完成,通常这个值是根据是否已经接近目标位置来判断的。
3.5.4 更新关节位置并创建 franka::JointPositions
对象
std::array<double, 7> joint_positions;
Eigen::VectorXd::Map(&joint_positions[0], 7) = (q_start_ +_q_d);
franka::JointPositions output(joint_positions);
joint_positions
是一个存储7个关节位置的std::array<double, 7>
数组,用来保存计算后的目标关节位置。Eigen::VectorXd::Map(&joint_positions[0], 7)
将Eigen::VectorXd
类型的向量q_start_ +_q_d
映射到joint_positions
数组中。q_start_ +_q_d
是当前起始关节位置加上期望的变化量,表示期望的关节位置。franka::JointPositions output(joint_positions)
创建了一个franka::JointPositions
对象output
,它包含了目标的关节位置。
3.5.5 设置 motion_finished
标志
output.motion_finished = motion_finished;
- 将
motion_finished
标志(表示运动是否完成)赋值给output
对象。motion_finished
是由calculateDesiredValues
函数返回的布尔值,指示是否完成了整个运动。
相关文章:
Franka例程学习——examples_common
这一次我们学习Franka所有例程里面都要调用的examples_common.h和examples_common.cpp,一个是.h头文件放置声明的函数、类、变量以及宏等内容,.c文件里面是具体的函数实现。 一、源代码 examples_common.h // Copyright (c) 2017 Franka Emika GmbH /…...
浅谈计算机网络02 | SDN控制平面
计算机网络控制平面 一、现代计算机网络控制平面概述1.1 与数据平面、管理平面的关系1.2 控制平面的发展历程 二、控制平面的关键技术剖析2.1 网络层协议2.1.1 OSPF协议2.1.2 BGP协议 2.2 SDN控制平面技术2.2.1 SDN架构与原理2.2.2 OpenFlow协议2.2.3 SDN控制器 一、现代计算机…...
Golang概述
文章目录 1. 什么是程序2. Go语言的诞生小故事2.1 Go 语言的核心开发团队--三个大牛2.2 Google 创造 Golang 的原因2.3 Golang 的发展历程 3. Golang 的语言的特点 1. 什么是程序 程序:就是完成某个功能的指令的集合。画一个图理解: 2. Go语言的诞生小故…...
【STM8S】STM8S之IIC从机
本文最后修改时间:2018年10月30日 18:48 一、本节简介 本文介绍STM8S系列如何使用IIC从机接收来自IIC主机的数据。 二、实验平台 编译软件:IAR for STM8 1.42.2 硬件平台:stm8s003f3p6开发板 仿真器:ST-LINK 库函数版本&…...
SDK调用文心一言如何接入,文心一言API接入教程
一、前期准备 注册百度智能云账号: 前往百度智能云官网注册一个账号。这是接入文心一言API的基础。 了解API接口: 在百度智能云开放平台中,找到文心一言API的详情页,了解提供的API接口类型(如云端API、移动端API、离线…...
实战:FRP内网穿透部署-支持ssh、web访问
目录 1 准备工作2 公网服务器部署server端2.1 frps.ini配置 3 内网客户端部署client端3.1 frpc.ini配置(内网服务器01)3.2 frpc.ini配置(内网服务器02) 4 服务启动脚本4.1 公网服务器 server4.2 内网服务器 client 2 systemctl常见…...
基于Web的宠物医院看诊系统设计与实现(源码+定制+开发)在线预约平台、宠物病历管理、医生诊疗记录、宠物健康数据分析 宠物就诊预约、病历管理与健康分析
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
Pytorch基础教程:从零实现手写数字分类
文章目录 1.Pytorch简介2.理解tensor2.1 一维矩阵2.2 二维矩阵2.3 三维矩阵 3.创建tensor3.1 你可以直接从一个Python列表或NumPy数组创建一个tensor:3.2 创建特定形状的tensor3.3 创建三维tensor3.4 使用随机数填充tensor3.5 指定tensor的数据类型 4.tensor基本运算…...
使用Flink-JDBC将数据同步到Doris
在现代数据分析和处理环境中,数据同步是一个至关重要的环节。Apache Flink和Doris是两个强大的工具,分别用于实时数据处理和大规模并行处理(MPP)SQL数据库。本文将介绍如何使用Flink-JDBC连接器将数据同步到Doris。 一、背景介绍…...
【深度学习】自编码器(Autoencoder, AE)
自编码器(Autoencoder, AE)是一种无监督学习模型,主要用于特征提取、数据降维、去噪和生成模型等任务。它的核心思想是通过将输入压缩到一个低维的潜在空间表示(编码过程),然后再从这个潜在表示重构输入&am…...
跨专业毕业论文写作
跨专业毕业论文写作是一项具有挑战性的任务,但通过合理的规划和方法,你可以顺利完成这篇论文。以下是一些关键步骤和建议,帮助你撰写一篇高质量的跨专业毕业论文。 一、确定研究方向和课题 选择与本科专业相关或感兴趣的研究方向:…...
在 Go语言中一个字段可以包含多种类型的值的设计与接种解决方案
在 Go 中,如果你希望一个字段可以包含多种类型的值,你可以使用以下几种方式来实现: ### 1. **使用空接口 (interface{})** Go 的空接口 interface{} 可以接受任何类型的值,因此,你可以将字段定义为一个空接口&#x…...
为AI聊天工具添加一个知识系统 之32 三“中”全“会”:推理式的ISA(父类)和IOS(母本)以及生成式CMN (双亲委派)之1
本文要点和问题 要点 三“中”全“会”:推理式的ISA的(父类-父类源码)和IOS的(母本-母类脚本)以及生成式 CMN (双亲委派-子类实例)。 数据中台三端架构的中间端(信息系统架构ISA :…...
手撕Transformer -- Day6 -- DecoderBlock
手撕Transformer – Day6 – DecoderBlock 目录 手撕Transformer -- Day6 -- DecoderBlockTransformer 网络结构图DecoderBlock 代码Part1 库函数Part2 实现一个解码器Block,作为一个类Part3 测试 参考 Transformer 网络结构图 Transformer 网络结构 DecoderBlock 代…...
Docker常用命令大全
Docker容器相关命令: 创建并启动容器: docker run:创建一个新的容器并运行一个命令。例如:docker run -d -p 8080:80 nginx这将后台(-d)运行一个Nginx容器,并映射宿主机的8080端口到容器的80端口。 列出容器&#x…...
【Linux探索学习】第二十五弹——动静态库:Linux 中静态库与动态库的详细解析
Linux学习笔记: https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言: 在 Linux 系统中,静态库和动态库是开发中常见的两种库文件类型。它们在编译、链接、内存管理以及程序的性能和可维护性方面有着…...
Vue 实现当前页面刷新的几种方法
以下是 Vue 中实现当前页面刷新的几种方法: 方法一:使用 $router.go(0) 方法 通过Vue Router进行重新导航,可以实现页面的局部刷新,而不丢失全局状态。具体实现方式有两种: 实现代码: <template&g…...
python mysql库的三个库mysqlclient mysql-connector-python pymysql如何选择,他们之间的区别
三者的区别 1. mysqlclient 特点: 是一个用于Python的MySQL数据库驱动程序,用于与MySQL数据库进行交互。 依赖于MySQL的本地库,因此在安装时需要确保系统上已安装了必要的依赖项,如libmysqlclient-dev等。 性能较好,…...
【可持久化线段树】 [SDOI2009] HH的项链 主席树(两种解法)
文章目录 1.题目描述2.思路3.解法一解法一代码 4.解法二解法二代码(版本一)解法二代码(版本二) 1.题目描述 原题:https://www.luogu.com.cn/problem/P1972 [SDOI2009] HH的项链 题目描述 HH 有一串由各种漂亮的贝壳…...
【C语言】线程----同步、互斥、条件变量
目录 3. 同步 3.1 概念 3.2 同步机制 3.3 函数接口 1. 同步 1.1 概念 同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情 1.2 同步机制 通过信号量实现线程间的同步 信号量:通过信号量实现同步操作;由信号量来决定…...
15. 三数之和【力扣】--三指针
三数之和 已解答 中等 相关标签 相关企业 提示 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#x…...
大数据学习(35)- spark- action算子
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦ᾑ…...
vim使用指南
🏝️专栏:计算机操作系统 🌅主页:猫咪-9527-CSDN博客 “欲穷千里目,更上一层楼。会当凌绝顶,一览众山小。” 目录 一、Vim 的基本概念 1.Vim 的主要模式: 1.1普通模式 (Normal Mode) 1.2插入…...
Docker 镜像制作原理 做一个自己的docker镜像
一.手动制作镜像 启动容器进入容器定制基于容器生成镜像 1.启动容器 启动容器之前我们首先要有一个镜像,这个镜像可以是从docker拉取,例如:现在pull一个ubuntu镜像到本机。 docker pull ubuntu:22.04 我们接下来可以基于这个容器进行容器…...
基于Java+SpringBoot+Vue的前后端分离的在线BLOG网
基于JavaSpringBootVue的前后端分离的在线BLOG网 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末附源码下载链接dz…...
Linux网络_套接字_UDP网络_TCP网络
一.UDP网络 1.socket()创建套接字 #include<sys/socket.h> int socket(int domain, int type, int protocol);domain (地址族): AF_INET网络 AF_UNIX本地 AF_INET:IPv4 地址族,适用于 IPv4 协议。用于网络通信AF_INET6:IPv6 地址族&a…...
Java学习教程,从入门到精通,JDBC驱动程序类型及语法知识点(91)
JDBC驱动程序类型及语法知识点 一、JDBC驱动程序类型 JDBC驱动程序主要有以下四种类型: 1. Type 1:JDBC - ODBC桥驱动程序(JDBC - ODBC Bridge Driver) 特点:这种驱动程序是Java与ODBC(Open Database C…...
YOLOv8从菜鸟到精通(二):YOLOv8数据标注以及模型训练
数据标注 前期准备 先打开Anaconda Navigator,点击Environment,再点击new(new是我下载anaconda的文件夹名称),然后点击创建 点击绿色按钮,并点击Open Terminal 输入labelimg便可打开它,labelimg是图像标注工具,在上篇…...
3D目标检测数据集——Nusence数据集
链接地址 [官网] nuScenes[arXiv] nuScenes: A multimodal dataset for autonomous driving[GitHub] nuScenes devkitnuScenes devkit教程数据集概述 2.1 数据采集 2.1.1 传感器配置 nuScenes的数据采集车辆为Renault Zoe迷你电动车,配备6个周视相机&#x...
网站收录入口提交的方法有哪些(网站收录的方式都有哪些)
网站被搜索引擎收录是获得流量和曝光的重要前提,以下为你介绍常见的网站收录方式: 搜索引擎提交入口 各大搜索引擎都设有专门的网站收录入口,供站长提交网站。例如百度搜索资源平台、谷歌搜索控制台等。以百度为例,在百度搜索资…...
移动端H5缓存问题
移动端页面缓存问题是指页面的静态资源(如图片、JS 和 CSS 文件)在浏览器中被缓存后,用户在下次访问时可以直接从本地获取缓存数据,而不需要每次都从服务器重新获取,不过这样可能会导致页面不能正确地更新或者加载最新…...
11-1.Android 项目结构 - androidTest 包与 test 包(单元测试与仪器化测试)
androidTest 包与 test 包 在 Android 项目中,androidTest 包与 test 包用于存放不同类型的测试代码的 1、测试类型 (1)androidTest 包 主要用于存放单元测试(Unit Tests)代码 单元测试是针对应用程序中的独立模块…...
计算机网络(五)——传输层
一、功能 传输层的主要功能是向两台主机进程之间的通信提供通用的数据传输服务。功能包括实现端到端的通信、多路复用和多路分用、差错控制、流量控制等。 复用:多个应用进程可以通过同一个传输层发送数据。 分用:传输层在接收数据后可以将这些数据正确分…...
ZCC9159 -7V 300mA 超低功耗高速 LDO
功能描述 ZCC9195是一款超低功耗并具有快速响应、关断快速放电功能的高速LDO。静态电流低至 0.8uA,输出电流最大为300mA。 ZCC9195具有输出过流保护、输出短路保护、温度保护等功能,确保芯片在异常工作条件 下不会损坏。 ZCC9195只需要1uF的陶瓷电容即…...
微信小程序实现个人中心页面
文章目录 1. 官方文档教程2. 编写静态页面3. 关于作者其它项目视频教程介绍 1. 官方文档教程 https://developers.weixin.qq.com/miniprogram/dev/framework/ 2. 编写静态页面 mine.wxml布局文件 <!--index.wxml--> <navigation-bar title"个人中心" ba…...
【C语言算法刷题】第7题
题目描述 一个XX产品行销总公司,只有一个boss,其有若干一级分销,一级分销又有若干二级分销,每个分销只有唯一的上级分销。 规定,每个月,下级分销需要将自己的总收入(自己的下级上交的…...
BERT与CNN结合实现糖尿病相关医学问题多分类模型
完整源码项目包获取→点击文章末尾名片! 使用HuggingFace开发的Transformers库,使用BERT模型实现中文文本分类(二分类或多分类) 首先直接利用transformer.models.bert.BertForSequenceClassification()实现文本分类 然后手动实现B…...
RocketMQ消息发送---源码解析
我们知道rocketMQ的消息发送支持很多特性,如同步发送,异步发送,oneWay发送,也支持超时机制,回调机制,并且能够保证消息的可靠性和消息发送的限流,底层使用netty框架等等,如此多的特性…...
机器学习06-正则化
机器学习06-正则化 文章目录 机器学习06-正则化0-核心逻辑脉络1-参考网址3-大模型训练中的正则化1.正则化的定义与作用2.常见的正则化方法及其应用场景2.1 L1正则化(Lasso)2.2 L2正则化(Ridge)2.3 弹性网络正则化(Elas…...
如何开放2375和2376端口供Docker daemon监听
Linux (以 Ubuntu 为例) 1. 修改 Docker 配置文件 打开 Docker 的配置文件 /etc/docker/daemon.json。如果该文件不存在,则可以创建一个新的。 bash sudo nano /etc/docker/daemon.json在配置文件中添加以下内容: json {"hosts": ["un…...
Vue.js组件开发-如何实现路由懒加载
在Vue.js应用中,路由懒加载是一种优化性能的技术,它允许在需要时才加载特定的路由组件,而不是在应用启动时加载所有组件。这样可以显著减少初始加载时间,提高用户体验。在Vue Router中,实现路由懒加载非常简单…...
rclone,云存储备份和迁移的瑞士军刀,千字常文解析,附下载链接和安装操作步骤...
一、什么是rclone? rclone是一个命令行程序,全称:rsync for cloud storage。是用于将文件和目录同步到云存储提供商的工具。因其支持多种云存储服务的备份,如Google Drive、Amazon S3、Dropbox、Backblaze B2、One Drive、Swift、…...
集成学习算法
目录 1.必要的导入 2.Bagging集成 3.基于matplotlib写一个函数对决策边界做可视化 4.总结图中结论 5.扩展说明 1.必要的导入 # To support both python 2 and python 3 from __future__ import division, print_function, unicode_literals# Common imports import numpy as np…...
vue3之pinia学习
最近查看了pinia这个状态管理管理,想跟大家一起学习下,下面是我的个人理解,希望对大家有帮助,我们开始吧! 第一步:安装pinia npm install pinia 第二步:创建pinia <script setup langts&…...
Flink (七): DataStream API (四) Watermarks
1. Event Time and Processing Time 1. 1 处理时间(Processing time) 处理时间是指执行相应操作的机器的系统时间。当流处理程序基于处理时间运行时,所有基于时间的操作(如时间窗口)将使用执行相应算子的机器的系统时…...
卷积神经05-GAN对抗神经网络
卷积神经05-GAN对抗神经网络 使用Python3.9CUDA11.8Pytorch实现一个CNN优化版的对抗神经网络 简单的GAN图片生成 CNN优化后的图片生成 优化模型代码对比 0-核心逻辑脉络 1)Anacanda使用CUDAPytorch2)使用本地MNIST进行手写图片训练3)…...
【原创】大数据治理入门(2)《提升数据质量:质量评估与改进策略》入门必看 高赞实用
提升数据质量:质量评估与改进策略 引言:数据质量的概念 在大数据时代,数据的质量直接影响到数据分析的准确性和可靠性。数据质量是指数据在多大程度上能够满足其预定用途,确保数据的准确性、完整性、一致性和及时性是数据质量的…...
GLM: General Language Model Pretraining with Autoregressive Blank Infilling论文解读
论文地址:https://arxiv.org/abs/2103.10360 参考:https://zhuanlan.zhihu.com/p/532851481 GLM混合了自注意力和masked注意力,而且使用了2D位置编码。第一维的含义是在PartA中的位置,如5 5 5。第二维的含义是在Span内部的位置&a…...
总结SpringBoot项目中读取resource目录下的文件多种方法
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目…...
云原生第四次作业
下载 [rootopenEuler-1 ~]# wget https://archive.apache.org/dist/httpd/httpd-2.4.46.tar.gz 压缩 配置实验环境 [rootopenEuler-1 httpd-2.4.46]# yum -y install apr apr-devel cyrus-sasl-devel expat-devel libdb-devel openldap-devel apr-util-devel apr-util pcre-d…...