Call the libfranka package to run the code of the Franka robotic arm in your own workspace

After installing the libfranka and franka_ros packages, you can directly run the executable files in the devel->lib->function package in the libfranka workspace, and you can directly control the robotic arm. If you have friends who have not configured libfranka and franka_ros, you can check the Franka environment configuration

So how to call the libfranka package in your own workspace to successfully compile your own code? Take the official routine as an example and show it step by step. There will be many pictures below to ensure you learn it.

Table of Contents

One. Create your own workspace

2. Add path in c_cpp_properties.json file

3. Write CMakeList file

4. Compile file

Five. Execution file


We use the official website routine generate_joint_position_motion.cpp, this routine is to design the seven joint angles of the robotic arm to control the movement of the robotic arm. Obviously, it cannot run in our own workspace, because we lack the header files, so let’s demonstrate how to call the header files in the libfranka package to compile this routine.

// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include <cmath>
#include <iostream>

#include <franka/exception.h>
#include <franka/robot.h>

#include "examples_common.h"

/**
 * @example generate_joint_position_motion.cpp
 * An example showing how to generate a joint position motion.
 *
 * @warning Before executing this example, make sure there is enough space in front of the robot.
 */

int main(int argc, char** argv) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " <robot-hostname>" << std::endl;
    return -1;
  }
  try {
    franka::Robot robot(argv[1]);
    setDefaultBehavior(robot);

    // First move the robot to a suitable joint configuration
    std::array<double, 7> q_goal = {<!-- -->{0, -M_PI_4, 0, -3 * M_PI_4, 0, M_PI_2, M_PI_4}};
    MotionGenerator motion_generator(0.5, q_goal);
    std::cout << "WARNING: This example will move the robot! "
              << "Please make sure to have the user stop button at hand!" << std::endl
              << "Press Enter to continue..." << std::endl;
    std::cin.ignore();
    robot.control(motion_generator);
    std::cout << "Finished moving to initial joint configuration." << std::endl;

    // Set additional parameters always before the control loop, NEVER in the control loop!
    // Set collision behavior.
    robot.setCollisionBehavior(
        {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}},
        {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}});

    std::array<double, 7> initial_position;
    double time = 0.0;
    robot.control([ & amp;initial_position, & amp;time](const franka::RobotState & amp; robot_state,
                                             franka::Duration period) -> franka::JointPositions {
      time += period.toSec();

      if (time == 0.0) {
        initial_position = robot_state.q_d;
      }

      double delta_angle = M_PI / 8.0 * (1 - std::cos(M_PI / 2.5 * time));

      franka::JointPositions output = {<!-- -->{initial_position[0], initial_position[1],
                                        initial_position[2], initial_position[3] + delta_angle,
                                        initial_position[4] + delta_angle, initial_position[5],
                                        initial_position[6] + delta_angle}};

      if (time >= 5.0) {
        std::cout << std::endl << "Finished motion, shutting down example" << std::endl;
        return franka::MotionFinished(output);
      }
      return output;
    });
  } catch (const franka::Exception & e) {
    std::cout << e.what() << std::endl;
    return -1;
  }

  return 0;
}

1. Create your own workspace

Type in terminal

mkdir -p position_generator/src
cd position_generator/
catkin_make
code.

After entering VScode, ctrl + shift + B to open a small window, click the small gear on the right of catkin_make:buil, and replace the following program into the {}tasks.json file:

{
// For documentation on the tasks.json format, see
    // https://go.microsoft.com/fwlink/?LinkId=733558
    "version": "2.0.0",
    "tasks": [
        {
            "label": "catkin_make:debug", //Descriptive information representing the prompt
            "type": "shell", //You can choose shell or process, if it is a shell code, it will run a command in the shell, if it is a process, it will run as a process
            "command": "catkin_make",//This is the command we need to run
            "args": [],//If you need to add some suffix after the command, you can write it here, such as -DCATKIN_WHITELIST_PACKAGES="pac1;pac2"
            "group": {"kind":"build","isDefault":true},
            "presentation": {
                "reveal": "always"//Always or silence is optional, representing whether to output information
            },
            "problemMatcher": "$msCompile"
        }
    ]
}

After configuration, ctrl + shift + B will automatically compile. Then create a new function package called “generate_position”. Create the “common.h” header file in the include directory, and create the source files “position.cpp” and “common.cpp” in the src directory. The main function is placed in position.cpp. Friends who are not very good at operating here can refer to the VScode usage tutorial – compiling C/C ++ code under ros. The created folders are as follows:

Write the code of the header file and source file, just copy the following code directly (the following three codes come from the routines examples_common.h examples_common.cpp generate_joint_position_motion.cpp in the libfranka->examples folder respectively)

Note:

The code here is slightly different from the code in the example.

  • Both common.cpp and position.cpp changed #include “examples_common.h” in the routine to #include “generate_position/common.h”

Because our common.h header file is in the include/generate_position directory

  • “common.h” header file code

// 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();
};
  • “common.cpp”code

// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include "generate_position/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] & amp; & amp; 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] & amp; & amp; 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( & amp;joint_positions[0], 7) = (q_start_ + delta_q_d);
  franka::JointPositions output(joint_positions);
  output.motion_finished = motion_finished;
  return output;
}
  • “position.cpp” code
// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include <cmath>
#include <iostream>

#include <franka/exception.h>
#include <franka/robot.h>

#include "generate_position/common.h"

/**
 * @example generate_joint_position_motion.cpp
 * An example showing how to generate a joint position motion.
 *
 * @warning Before executing this example, make sure there is enough space in front of the robot.
 */

int main(int argc, char** argv) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " <robot-hostname>" << std::endl;
    return -1;
  }
  try {
    franka::Robot robot(argv[1]);
    setDefaultBehavior(robot);

    // First move the robot to a suitable joint configuration
    std::array<double, 7> q_goal = {<!-- -->{0, -M_PI_4, 0, -3 * M_PI_4, 0, M_PI_2, M_PI_4}};
    MotionGenerator motion_generator(0.5, q_goal);
    std::cout << "WARNING: This example will move the robot! "
              << "Please make sure to have the user stop button at hand!" << std::endl
              << "Press Enter to continue..." << std::endl;
    std::cin.ignore();
    robot.control(motion_generator);
    std::cout << "Finished moving to initial joint configuration." << std::endl;

    // Set additional parameters always before the control loop, NEVER in the control loop!
    // Set collision behavior.
    robot.setCollisionBehavior(
        {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}}, {<!-- -->{20.0, 20.0, 18.0, 18.0, 16.0, 14.0, 12.0}},
        {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}},
        {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}}, {<!-- -->{20.0, 20.0, 20.0, 25.0, 25.0, 25.0}});

    std::array<double, 7> initial_position;
    double time = 0.0;
    robot.control([ & amp;initial_position, & amp;time](const franka::RobotState & amp; robot_state,
                                             franka::Duration period) -> franka::JointPositions {
      time += period.toSec();

      if (time == 0.0) {
        initial_position = robot_state.q_d;
      }

      double delta_angle = M_PI / 8.0 * (1 - std::cos(M_PI / 2.5 * time));

      franka::JointPositions output = {<!-- -->{initial_position[0], initial_position[1],
                                        initial_position[2], initial_position[3] + delta_angle,
                                        initial_position[4] + delta_angle, initial_position[5],
                                        initial_position[6] + delta_angle}};

      if (time >= 5.0) {
        std::cout << std::endl << "Finished motion, shutting down example" << std::endl;
        return franka::MotionFinished(output);
      }
      return output;
    });
  } catch (const franka::Exception & e) {
    std::cout << e.what() << std::endl;
    return -1;
  }

  return 0;
}

2. Add path to c_cpp_properties.json file

Open the c_cpp_properties.json file under .vscod

In “includePath”, add the path of the header file in the libfranka package, and the other is the path of the header file written by yourself.

(Everyone’s path is different)

Note that there is an English comma at the end of each path, and there is no comma after the last path.

Let’s talk about the path in detail in this section

Because we used #include and #include header files in the code, where are these header files? We open libfranka in vscode, and we can see the header files we reference under include.

Then how to quickly get the path of the header file under include, we right-click include, select Open in Integrated Terminal, and enter in the opened terminal

pwd

You can get the path, and then modify it a little, add /** after the include, which means to include all the files under the include.

The header file path written by yourself can also be obtained in the same way.

3. Write CMakeList file

The CMakeList code is as follows, let’s explain what each part is.

cmake_minimum_required(VERSION 3.0.2)
project(generate_position)


find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
)


catkin_package(

)


include_directories(
include
  ${catkin_INCLUDE_DIRS}
)
include_directories("/usr/include/eigen3") # path of Eigen3 library


add_library(position_lib # Define the name of the library yourself position_lib
  include/${PROJECT_NAME}/common.h # The custom header file called, ${PROJECT_NAME} represents the function package name
  src/common.cpp # Custom source file called
)

add_executable(position src/position.cpp) # position is the name of the mapping node defined by itself, generally the name of the source file
                                            # src/position.cpp is the source file path where the main function is located
find_package(Franka 0.7.0 REQUIRED) # Change 0.7.0 to your own Franka robotic arm version

# add_dependencies must be behind add_library, position_lib must be defined before adding dependencies
add_dependencies(position_lib ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) # The name of the library defined in add_library before add_library
add_dependencies(position ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) # The node name mapped in add_executable position


target_link_libraries(position_lib # The library name defined in add_library before add_library
  ${Franka_LIBRARIES} # Add ${Franka_LIBRARIES} here
  ${catkin_LIBRARIES}
)

target_link_libraries(position # position is the name of the mapping node defined by yourself
  position_lib # The name of the library defined in add_library before add_library
  ${Franka_LIBRARIES}
  ${catkin_LIBRARIES}
)

4. Compile file

start roscore

roscore

Open a new terminal, enter the workspace, and enter in the terminal:

catkin_make -DCMAKE_BUILD_TYPE=Release -DFranka_DIR:PATH=~/catkin_franka/libfranka/build

where PATH= is followed by the path where your libfranka package is compiled

This means that the compilation is successful.

Five. Execution file

Connect the computer to the robotic arm, and then enter the workspace->devel->lib->function package, you can see the executable file, just open the terminal here and run it.

If there is a problem with compilation, saying fatal error: Eigen/Core: No such file or directory, then it is a problem with the Eigen3 library, you can refer to fatal error: Eigen/Core: No such file or directory_Boboyu爱吃小猫博客-CSDN blog

Note: There is a problem that will report an error but is not easy to find, that is, include/generate_position must be on the same line, if generate_position becomes a subdirectory of include, an error will be reported