Eliminato il supporto a bio-ik, si potrà reinserire in futuro
This commit is contained in:
@@ -1,129 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(bio_ik)
|
||||
|
||||
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
|
||||
message("${PROJECT_NAME}: You did not request a specific build type: Choosing 'Release' for maximum performance")
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS
|
||||
moveit_core
|
||||
moveit_ros_planning
|
||||
pluginlib
|
||||
roscpp
|
||||
tf
|
||||
tf_conversions
|
||||
eigen_conversions
|
||||
)
|
||||
|
||||
find_package(OpenMP)
|
||||
# the specific flag is not yet present in cmake 2.8.12
|
||||
if(OpenMP_CXX_FOUND OR OPENMP_FOUND)
|
||||
message(STATUS "OPENMP FOUND")
|
||||
add_compile_options(${OpenMP_CXX_FLAGS})
|
||||
if(NOT OpenMP_CXX_LIBRARIES)
|
||||
# cmake 2.8.12 does not yet specify the library - assume we might need libgomp
|
||||
set(OpenMP_LIBS gomp)
|
||||
else()
|
||||
set(OpenMP_LIBS ${OpenMP_CXX_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "OPENMP NOT FOUND. You will suffer performance loss.")
|
||||
set(OpenMP_LIBS)
|
||||
endif()
|
||||
|
||||
option(USE_FANN "build the neural-network-based IK solver (experimental)" OFF)
|
||||
if(USE_FANN)
|
||||
find_library(FANN_LIBRARIES NAMES fann)
|
||||
find_path(FANN_INCLUDE_DIRS NAMES fann.h)
|
||||
if(NOT FANN_INCLUDE_DIRS OR NOT FANN_LIBRARIES)
|
||||
message(FATAL_ERROR "Neural network solver requested, but libfann was not found.")
|
||||
else()
|
||||
message("Found libfann: ${FANN_LIBRARIES} / ${FANN_INCLUDE_DIRS}")
|
||||
endif()
|
||||
else()
|
||||
set(FANN_LIBRARIES)
|
||||
set(FANN_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
||||
option(USE_CPPOPTLIB "Include gradient-based solvers from CppNumericalSolvers (bio_ik also provides its own solver)" OFF)
|
||||
if(USE_CPPOPTLIB)
|
||||
find_path(CPPOPTLIB_INCLUDE_DIRS
|
||||
NAMES cppoptlib/solver/bfgssolver.h
|
||||
HINTS ../../CppNumericalSolvers/include)
|
||||
if(NOT CPPOPTLIB_INCLUDE_DIRS)
|
||||
message(FATAL_ERROR "cppoptlib support requested, but the headers could not be found.")
|
||||
else()
|
||||
message("Found cppoptlib: ${CPPOPTLIB_INCLUDE_DIRS}")
|
||||
endif()
|
||||
add_definitions(-DENABLE_CPP_OPTLIB)
|
||||
else()
|
||||
set(CPPOPTLIB_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
||||
catkin_package(
|
||||
INCLUDE_DIRS include
|
||||
LIBRARIES ${PROJECT_NAME}
|
||||
CATKIN_DEPENDS
|
||||
moveit_core
|
||||
moveit_ros_planning
|
||||
pluginlib
|
||||
roscpp
|
||||
tf
|
||||
tf_conversions
|
||||
)
|
||||
|
||||
add_compile_options(-frecord-gcc-switches)
|
||||
|
||||
add_compile_options(-std=c++11)
|
||||
|
||||
add_compile_options($<$<CONFIG:Release>:-O3>)
|
||||
add_compile_options($<$<CONFIG:Release>:-ftree-vectorize>)
|
||||
add_compile_options($<$<CONFIG:Release>:-ffast-math>)
|
||||
|
||||
include_directories(
|
||||
include
|
||||
${FANN_INCLUDE_DIRS}
|
||||
${CPPOPTLIB_INCLUDE_DIRS}
|
||||
${catkin_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
src/goal_types.cpp
|
||||
src/kinematics_plugin.cpp
|
||||
src/problem.cpp
|
||||
|
||||
src/ik_test.cpp
|
||||
src/ik_gradient.cpp
|
||||
src/ik_evolution_1.cpp
|
||||
src/ik_evolution_2.cpp
|
||||
)
|
||||
|
||||
if(USE_FANN)
|
||||
list(APPEND SOURCES src/ik_neural.cpp)
|
||||
endif()
|
||||
|
||||
if(USE_CPPOPTLIB)
|
||||
list(APPEND SOURCES src/ik_cppoptlib.cpp)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME} ${SOURCES})
|
||||
|
||||
set_target_properties(${MOVEIT_LIB_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION})
|
||||
|
||||
add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
${catkin_LIBRARIES}
|
||||
${FANN_LIBRARIES}
|
||||
${OpenMP_LIBS}
|
||||
|
||||
-static-libgcc
|
||||
-static-libstdc++
|
||||
)
|
||||
|
||||
install(DIRECTORY include/${PROJECT_NAME}/ DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION})
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION})
|
||||
|
||||
install(FILES bio_ik_kinematics_description.xml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
@@ -1,393 +0,0 @@
|
||||
# bio-ik
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
You will need ROS version Indigo or newer (wiki.ros.org).
|
||||
The software was developed on Ubuntu Linux 16.04 LTS with ROS Kinetic,
|
||||
but has also been tested on Ubuntu Linux 14.04 LTS with ROS Indigo.
|
||||
Newer versions of ROS should work, but may need some adaptation.
|
||||
See below for version specific instructions.
|
||||
|
||||
* Download the `bio_ik` package and unpack into your catkin workspace.
|
||||
* Run `catkin_make` to compile your workspace:
|
||||
```
|
||||
roscd
|
||||
cd src
|
||||
git clone https://github.com/TAMS-Group/bio_ik.git
|
||||
roscd
|
||||
catkin_make
|
||||
```
|
||||
|
||||
* Configure Moveit to use bio-ik as the kinematics solver (see next section).
|
||||
* Use Moveit to plan and execute motions or use your own code
|
||||
together with `move_group` node to move your robot.
|
||||
* As usual, the public API is specified in the public header files for the `bio_ik` package,
|
||||
located in the `include/bio_ik` subdirectory;
|
||||
the sources including a few private header files are in the `src` subdirectory.
|
||||
|
||||
|
||||
## Basic Usage
|
||||
|
||||
For ease of use and compatibility with existing code,
|
||||
the bio-ik algorithm is encapsulated as a Moveit kinematics plugin.
|
||||
Therefore, bio-ik can be used as a direct replacement of
|
||||
the default Orocos/KDL-based IK solver.
|
||||
Given the name of an end-effector and a 6-DOF target pose,
|
||||
bio-ik will search a valid robot joint configuration that reaches the given target.
|
||||
|
||||
In our tests (see below), both in terms of success rate and solution time,
|
||||
bio-ik regularly outperformed the Orocos [1] solver
|
||||
and is competitive with trac-ik [2].
|
||||
The bio-ik algorithm can also be used for high-DOF system like robot snakes,
|
||||
and it will automatically converge to the best approximate solutions
|
||||
for low-DOF arms where some target poses are not reachable exactly.
|
||||
|
||||
While you can write the Moveit configuration files by hand,
|
||||
the easiest way is to run the Moveit setup assistant for your robot,
|
||||
and then to select bio-ik as the IK solver when configuring the end effectors.
|
||||
Once configured, the solver can be called using the standard Moveit API
|
||||
or used interactively from rviz using the MotionPlanning GUI plugin.
|
||||
|
||||
* Make sure that you have a URDF (or xacro) model for your robot.
|
||||
* Run the moveit setup assistant to create the Moveit configuration files:
|
||||
|
||||
```
|
||||
rosrun moveit_setup_assistant moveit_setup_assistant
|
||||
|
||||
```
|
||||
* The setup assistant automatically searches for all available IK solver plugins
|
||||
in your workspace.
|
||||
Therefore, you can just select select bio-ik as the IK solver
|
||||
from the drop-down list for every end effector and then configure
|
||||
the kinematics parameters, namely the default position accuracy (meters)
|
||||
and the timeout (in seconds). For typical 6-DOF or 7-DOF arms,
|
||||
an accuracy of 0.001 m (or smaller) and a timeout of 1 msec should be ok.
|
||||
More complex robots might need a longer timeout.
|
||||
* Generate the moveit configuration files from the setup assistant.
|
||||
Of course, you can also edit the `config/kinematics.yaml` configuration
|
||||
file with your favorite text editor.
|
||||
For example, a configuration for the PR2 robot might look like this:
|
||||
|
||||
```
|
||||
# example kinematics.yaml for the PR2 robot
|
||||
right_arm:
|
||||
# kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin
|
||||
# kinematics_solver_attempts: 1
|
||||
kinematics_solver: bio_ik/BioIKKinematicsPlugin
|
||||
kinematics_solver_search_resolution: 0.005
|
||||
kinematics_solver_timeout: 0.005
|
||||
kinematics_solver_attempts: 1
|
||||
left_arm:
|
||||
kinematics_solver: bio_ik/BioIKKinematicsPlugin
|
||||
kinematics_solver_search_resolution: 0.005
|
||||
kinematics_solver_timeout: 0.005
|
||||
kinematics_solver_attempts: 1
|
||||
all:
|
||||
kinematics_solver: bio_ik/BioIKKinematicsPlugin
|
||||
kinematics_solver_search_resolution: 0.005
|
||||
kinematics_solver_timeout: 0.02
|
||||
kinematics_solver_attempts: 1
|
||||
|
||||
# optional bio-ik configuration parameters
|
||||
# center_joints_weight: 1
|
||||
# minimal_displacement_weight: 1
|
||||
# avoid_joint_limits_weight: 1
|
||||
```
|
||||
|
||||
|
||||
* For a first test, run the Moveit-created demo launch. Once rviz is running,
|
||||
enable the motion planning plugin, then select one of the end effectors
|
||||
of you robot. Rviz should show an 6-D (position and orientation)
|
||||
interactive marker for the selected end-effector(s).
|
||||
Move the interactive marker and watch bio-ik calculating poses for your robot.
|
||||
|
||||
If you also installed the bio-ik demo (see below), you should be able
|
||||
to run one of the predefined demos:
|
||||
```
|
||||
roslaunch pr2_bioik_moveit demo.launch
|
||||
roslaunch pr2_bioik_moveit valve.launch
|
||||
roslaunch pr2_bioik_moveit dance.launch
|
||||
```
|
||||
|
||||
* You are now ready to use bio-ik from your C/C++ and Python programs,
|
||||
using the standard Moveit API.
|
||||
To explicitly request an IK solution in C++:
|
||||
```
|
||||
robot_model_loader::RobotModelLoader robot_model_loader(robot);
|
||||
|
||||
auto robot_model = robot_model_loader.getModel();
|
||||
auto joint_model_group = robot_model->getJointModelGroup(group);
|
||||
auto tip_names = joint_model_group->getSolverInstance()->getTipFrames();
|
||||
|
||||
kinematics::KinematicsQueryOptions opts;
|
||||
opts.return_approximate_solution = true; // optional
|
||||
|
||||
robot_state::RobotState robot_state_ik(robot_model);
|
||||
|
||||
// traditional "basic" bio-ik usage. The end-effector goal poses
|
||||
// and end-effector link names are passed into the setFromIK()
|
||||
// call. The KinematicsQueryOptions are empty.
|
||||
//
|
||||
bool ok = robot_state_ik.setFromIK(
|
||||
joint_model_group, // joints to be used for IK
|
||||
tip_transforms, // multiple end-effector goal poses
|
||||
tip_names, // names of the end-effector links
|
||||
attempts, timeout, // solver attempts and timeout
|
||||
moveit::core::GroupStateValidityCallbackFn(),
|
||||
opts // mostly empty
|
||||
);
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
For many robot applications, it is essential to specify more than just
|
||||
a single end-effector pose. Typical examples include
|
||||
|
||||
* two-arm manipulation tasks on two-arm robots (e.g. Baxter)
|
||||
* multi end-effector tasks with shared kinematic links
|
||||
* grasping and manipulation tasks with multi-finger hands
|
||||
* full-body motion on humanoid robots
|
||||
* reaching tasks with additional constraints (e.g. shoulder position)
|
||||
* incremental tool motions without robot arm configuration changes
|
||||
* and many more
|
||||
|
||||
In bio-ik, such tasks are specified as a combination of multiple
|
||||
individual *goals*.
|
||||
The algorithm then tries to find a robot configuration
|
||||
that fulfills all given goals simultaneously by minimizing
|
||||
a quadratic error function built from the weighted individual goals.
|
||||
While the current Moveit API does not support multiple-goals tasks directly,
|
||||
it provides the KinematicQueryOptions class.
|
||||
Therefore, bio-ik simply provides a set of predefined motion goals,
|
||||
and a combination of the user-specified goals is passed via Moveit to the IK solver.
|
||||
No API changes are required in Moveit, but using the IK solver now consists
|
||||
passing the weighted goals via the KinematicQueryOptions.
|
||||
The predefined goals include:
|
||||
|
||||
* *PoseGoal*: a full 6-DOF robot pose
|
||||
* *PositionGoal*: a 3-DOF (x,y,z) position
|
||||
* *OrientationGoal*: a 3-DOF orientation, encoded as a quaternion (qx,qy,qz,qw)
|
||||
* *LookAtGoal*: a 3-DOF (x,y,z) position intended as a looking direction
|
||||
for a camera or robot head
|
||||
* *JointGoal*: a set of joint angles, e.g. to specify a
|
||||
* *FunctionGoal*: an arbitrary function of the robot joint values,
|
||||
e.g. to model underactuated joints or mimic joints
|
||||
* and several more
|
||||
|
||||
|
||||
|
||||
To solve a motion problem on your robot, the trick now is to construct
|
||||
a suitable combination of individual goals.
|
||||
|
||||

|
||||
|
||||
In the following example, we want to grasp and then _slowly turn
|
||||
a valve wheel_ with the left and right gripers of the PR2 robot:
|
||||
|
||||
```
|
||||
bio_ik::BioIKKinematicsQueryOptions ik_options;
|
||||
ik_options.replace = true;
|
||||
ik_options.return_approximate_solution = true;
|
||||
|
||||
auto* ll_goal = new bio_ik::PoseGoal();
|
||||
auto* lr_goal = new bio_ik::PoseGoal();
|
||||
auto* rl_goal = new bio_ik::PoseGoal();
|
||||
auto* rr_goal = new bio_ik::PoseGoal();
|
||||
ll_goal->setLinkName("l_gripper_l_finger_tip_link");
|
||||
lr_goal->setLinkName("l_gripper_r_finger_tip_link");
|
||||
rl_goal->setLinkName("r_gripper_l_finger_tip_link");
|
||||
rr_goal->setLinkName("r_gripper_r_finger_tip_link");
|
||||
ik_options.goals.emplace_back(ll_goal);
|
||||
ik_options.goals.emplace_back(lr_goal);
|
||||
ik_options.goals.emplace_back(rl_goal);
|
||||
ik_options.goals.emplace_back(rr_goal);
|
||||
```
|
||||
|
||||
We also set a couple of secondary goals.
|
||||
First, we want that the head of the PR2 looks at the center of the valve.
|
||||
Second, we want to avoid joint-limits on all joints, if possible.
|
||||
Third, we want that IK solutions are as close as possible to the previous
|
||||
joint configuration, meaning small and efficient motions. This is handled
|
||||
by adding the MinimalDisplacementGoal.
|
||||
Fourth, we want to avoid torso lift motions, which are very slow on the PR2.
|
||||
All of this is specified easily:
|
||||
|
||||
```
|
||||
auto* lookat_goal = new bio_ik::LookAtGoal();
|
||||
lookat_goal->setLinkName("sensor_mount_link");
|
||||
ik_options.goals.emplace_back(lookat_goal);
|
||||
|
||||
auto* avoid_joint_limits_goal = new bio_ik::AvoidJointLimitsGoal();
|
||||
ik_options.goals.emplace_back(avoid_joint_limits_goal);
|
||||
|
||||
auto* minimal_displacement_goal = new bio_ik::MinimalDisplacementGoal();
|
||||
ik_options.goals.emplace_back(minimal_displacement_goal);
|
||||
|
||||
auto* torso_goal = new bio_ik::PositionGoal();
|
||||
torso_goal->setLinkName("torso_lift_link");
|
||||
torso_goal->setWeight(1);
|
||||
torso_goal->setPosition(tf::Vector3( -0.05, 0, 1.0 ));
|
||||
ik_options.goals.emplace_back(torso_goal);
|
||||
```
|
||||
|
||||
For the actual turning motion, we calculate a set of required gripper
|
||||
poses in a loop:
|
||||
```
|
||||
for(int i = 0; ; i++) {
|
||||
tf::Vector3 center(0.7, 0, 1);
|
||||
|
||||
double t = i * 0.1;
|
||||
double r = 0.1;
|
||||
double a = sin(t) * 1;
|
||||
double dx = fmin(0.0, cos(t) * -0.1);
|
||||
double dy = cos(a) * r;
|
||||
double dz = sin(a) * r;
|
||||
|
||||
tf::Vector3 dl(dx, +dy, +dz);
|
||||
tf::Vector3 dr(dx, -dy, -dz);
|
||||
tf::Vector3 dg = tf::Vector3(0, cos(a), sin(a)) * (0.025 + fmin(0.025, fmax(0.0, cos(t) * 0.1)));
|
||||
|
||||
ll_goal->setPosition(center + dl + dg);
|
||||
lr_goal->setPosition(center + dl - dg);
|
||||
rl_goal->setPosition(center + dr + dg);
|
||||
rr_goal->setPosition(center + dr - dg);
|
||||
|
||||
double ro = 0;
|
||||
ll_goal->setOrientation(tf::Quaternion(tf::Vector3(1, 0, 0), a + ro));
|
||||
lr_goal->setOrientation(tf::Quaternion(tf::Vector3(1, 0, 0), a + ro));
|
||||
rl_goal->setOrientation(tf::Quaternion(tf::Vector3(1, 0, 0), a + ro));
|
||||
rr_goal->setOrientation(tf::Quaternion(tf::Vector3(1, 0, 0), a + ro));
|
||||
|
||||
lookat_goal->setAxis(tf::Vector3(1, 0, 0));
|
||||
lookat_goal->setTarget(rr_goal->getPosition());
|
||||
|
||||
// "advanced" bio-ik usage. The call parameters for the end-effector
|
||||
// poses and end-effector link names are left empty; instead the
|
||||
// requested goals and weights are passed via the ik_options object.
|
||||
//
|
||||
robot_state.setFromIK(
|
||||
joint_model_group, // active PR2 joints
|
||||
EigenSTL::vector_Affine3d(), // no explicit poses here
|
||||
std::vector<std::string>(), // no end effector links here
|
||||
0, 0.0, // take values from YAML file
|
||||
moveit::core::GroupStateValidityCallbackFn(),
|
||||
ik_options // four gripper goals and secondary goals
|
||||
);
|
||||
|
||||
... // check solution validity and actually move the robot
|
||||
}
|
||||
```
|
||||
|
||||
When you execute the code, the PR2 will reach for the valve wheel
|
||||
and turn it. Every once in a while it can't reach the valve with
|
||||
its current arm configuration and will regrasp the wheel.
|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
The bio-ik solver is based on a memetic algorithm that combines
|
||||
traditional gradient-based search with a hybrid genetic
|
||||
and particle-swarm optimization [3].
|
||||
See [4] for the basic idea and the details of the evolutionary operators
|
||||
and [5] for the description of the algorithm applied to many IK and manipulation tasks.
|
||||
|
||||
Internally, vectors of all robot joint values are used to encode
|
||||
different intermediate solutions (the *genotype* of the genetic algorithm).
|
||||
During the optimization, joint values are always checked against the
|
||||
active lower and upper joint limits, so that only valid robot configurations
|
||||
are generated.
|
||||
|
||||
To calculate the fitness of individuals, the cumulative error over all
|
||||
given individual goals is calculated.
|
||||
Any individual with zero error is an exact solution for the IK problem,
|
||||
while individuals with small error correspond to approximate solutions.
|
||||
|
||||
Individuals are sorted by their fitness, and gradient-based optimization
|
||||
is tried on the best few configuration, resulting in fast convergence
|
||||
and good performance for many problems.
|
||||
If no solution is found from the gradient-based optimization,
|
||||
new individuals are created by a set of mutation and recombination operators,
|
||||
resulting in good search-space exploration.
|
||||
|
||||
|
||||
|
||||
## Example Performance data
|
||||
|
||||
To be written.
|
||||
|
||||
## Running the Self-Tests
|
||||
|
||||
We have tested bio-ik on many different robot arms,
|
||||
both using the tranditional single end-effector API
|
||||
and the advanced multi end-effector API based on the KinematicsQueryOptions.
|
||||
|
||||
One simple selftest consists of generating random valid robot configurations,
|
||||
running forward kinematics to calculate the resulting end-effector pose,
|
||||
and the querying the IK plugin to find a suitable robot joint configuration.
|
||||
Success is then checked by running forrward kinematics again and checking
|
||||
that the end-effector pose for the generated IK solution matches the target pose.
|
||||
This approach can be run easily for thousands or millions of random poses,
|
||||
samples the full workspace of the robot,
|
||||
and allows to quickly generate success-rate and solution-time estimates
|
||||
for the selected IK solver.
|
||||
|
||||
Of course, running the tests requires installing the corresponding robot
|
||||
models and adds a lot of dependencies.
|
||||
Therefore, those tests are not included in the standard bio-ik package,
|
||||
but are packaged separately.
|
||||
|
||||
For convenience, we provide the `pr2_bioik_moveit` package,
|
||||
which also includes a few bio-ik demos for the PR2 service robot.
|
||||
These are kinematics only demos; but of course you can also try
|
||||
running the demos on the real robot (if you have one) or the Gazebo
|
||||
simulator (if you installed Gazebo).
|
||||
|
||||
Simply clone the PR2 description package (inside `pr2_common`)
|
||||
and the `pr2_bioik_moveit` package into your catkin workspace:
|
||||
```
|
||||
roscd
|
||||
cd src
|
||||
git clone https://github.com/PR2/pr2_common.git
|
||||
git clone https://github.com/TAMS/pr2_bioik_moveit.git
|
||||
catkin_make
|
||||
```
|
||||
|
||||
For the FK-IK-FK performance test, please run
|
||||
|
||||
```
|
||||
roslaunch pr2_bioik_moveit env_pr2.launch
|
||||
roslaunch pr2_bioik_moveit test_fk_ik.launch
|
||||
... // wait for test completion and results summary
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## References
|
||||
|
||||
1. Orocos Kinematics and Dynamics, http://www.orocos.org
|
||||
|
||||
2. P. Beeson and B. Ames, *TRAC-IK:
|
||||
An open-source library for improved solving of generic inverse kinematics*,
|
||||
Proceedings of the IEEE RAS Humanoids Conference, Seoul, Korea, November 2015.
|
||||
|
||||
3. Philipp Ruppel, Prformance optimization and implementation
|
||||
of evolutionary inverse kinematics in ROS*,
|
||||
MSc thesis, University of Hamburg, 2017
|
||||
[PDF](https://tams.informatik.uni-hamburg.de/publications/2017/MSc_Philipp_Ruppel.pdf)
|
||||
|
||||
|
||||
4. Sebastian Starke, Norman Hendrich, Jianwei Zhang, *A Memetic
|
||||
Evolutionary Algorithm for Real-Time Articulated Kinematic Motion*,
|
||||
IEEE Intl. Congress on Evolutionary Computation (CEC-2017), p.2437-2479, June 4-8, 2017,
|
||||
San Sebastian, Spain.
|
||||
DOI: [10.1109/CEC.2017.7969605](http://doi.org/10.1109/CEC.2017.7969605)
|
||||
|
||||
5. Sebastian Starke, Norman Hendrich, Dennis Krupke, Jianwei Zhang, *Multi-Objective
|
||||
Evolutionary Optimisation for Inverse Kinematics
|
||||
on Highly Articulated and Humanoid Robots*,
|
||||
IEEE Intl. Conference on Intelligent Robots and Systems (IROS-2017),
|
||||
September 24-28, 2017, Vancouver, Canada
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<library path="lib/libbio_ik">
|
||||
<class name="bio_ik/BioIKKinematicsPlugin" type="bio_ik_kinematics_plugin::BioIKKinematicsPlugin" base_class_type="kinematics::KinematicsBase">
|
||||
</class>
|
||||
</library>
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 115 KiB |
Binary file not shown.
@@ -1,50 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tf/tf.h>
|
||||
|
||||
#include "goal.h"
|
||||
#include "goal_types.h"
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <tf_conversions/tf_kdl.h>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
typedef tf::Quaternion Quaternion;
|
||||
typedef tf::Vector3 Vector3;
|
||||
|
||||
struct Frame
|
||||
{
|
||||
Vector3 pos;
|
||||
double __padding[4 - (sizeof(Vector3) / sizeof(double))];
|
||||
Quaternion rot;
|
||||
inline Frame() {}
|
||||
inline Frame(const tf::Vector3& pos, const tf::Quaternion& rot)
|
||||
: pos(pos)
|
||||
, rot(rot)
|
||||
{
|
||||
}
|
||||
explicit inline Frame(const KDL::Frame& kdl)
|
||||
{
|
||||
pos = tf::Vector3(kdl.p.x(), kdl.p.y(), kdl.p.z());
|
||||
double qx, qy, qz, qw;
|
||||
kdl.M.GetQuaternion(qx, qy, qz, qw);
|
||||
rot = tf::Quaternion(qx, qy, qz, qw);
|
||||
}
|
||||
explicit inline Frame(const geometry_msgs::Pose& msg)
|
||||
{
|
||||
tf::quaternionMsgToTF(msg.orientation, rot);
|
||||
pos = tf::Vector3(msg.position.x, msg.position.y, msg.position.z);
|
||||
}
|
||||
explicit inline Frame(const Eigen::Affine3d& f)
|
||||
{
|
||||
pos = tf::Vector3(f.translation().x(), f.translation().y(), f.translation().z());
|
||||
Eigen::Quaterniond q(f.rotation());
|
||||
rot = tf::Quaternion(q.x(), q.y(), q.z(), q.w());
|
||||
}
|
||||
|
||||
inline const Vector3& getPosition() const { return pos; }
|
||||
inline const Quaternion& getOrientation() const { return rot; }
|
||||
inline void setPosition(const Vector3& p) { pos = p; }
|
||||
inline void setOrientation(const Quaternion& q) { rot = q; }
|
||||
|
||||
private:
|
||||
template <size_t i> struct IdentityFrameTemplate
|
||||
{
|
||||
static const Frame identity_frame;
|
||||
};
|
||||
|
||||
public:
|
||||
static inline const Frame& identity() { return IdentityFrameTemplate<0>::identity_frame; }
|
||||
};
|
||||
|
||||
inline void frameToKDL(const Frame& frame, KDL::Frame& kdl_frame)
|
||||
{
|
||||
KDL::Rotation kdl_rot;
|
||||
KDL::Vector kdl_pos;
|
||||
tf::quaternionTFToKDL(frame.rot, kdl_rot);
|
||||
tf::vectorTFToKDL(frame.pos, kdl_pos);
|
||||
kdl_frame = KDL::Frame(kdl_rot, kdl_pos);
|
||||
}
|
||||
|
||||
template <size_t i> const Frame Frame::IdentityFrameTemplate<i>::identity_frame(Vector3(0, 0, 0), Quaternion(0, 0, 0, 1));
|
||||
|
||||
static std::ostream& operator<<(std::ostream& os, const Frame& f) { return os << "(" << f.pos.x() << "," << f.pos.y() << "," << f.pos.z() << ";" << f.rot.x() << "," << f.rot.y() << "," << f.rot.z() << "," << f.rot.w() << ")"; }
|
||||
|
||||
__attribute__((always_inline)) inline void quat_mul_vec(const tf::Quaternion& q, const tf::Vector3& v, tf::Vector3& r)
|
||||
{
|
||||
double v_x = v.x();
|
||||
double v_y = v.y();
|
||||
double v_z = v.z();
|
||||
|
||||
// if(__builtin_expect(v_x == 0 && v_y == 0 && v_z == 0, 0)) { r = tf::Vector3(0, 0, 0); return; }
|
||||
// if(v_x == 0 && v_y == 0 && v_z == 0) { r = tf::Vector3(0, 0, 0); return; }
|
||||
|
||||
double q_x = q.x();
|
||||
double q_y = q.y();
|
||||
double q_z = q.z();
|
||||
double q_w = q.w();
|
||||
|
||||
if((v_x == 0 && v_y == 0 && v_z == 0) || (q_x == 0 && q_y == 0 && q_z == 0 && q_w == 1))
|
||||
{
|
||||
r = v;
|
||||
return;
|
||||
}
|
||||
// if((v_x + v_y + v_z == 0 && v_x == 0 && v_y == 0) || (q_x + q_y + q_z == 0 && q_x == 0 && q_y == 0 && q_w == 1)) { r = v; return; }
|
||||
// if(q_x == 0 && q_y == 0 && q_z == 0 && q_w == 1) { r = v; return; }
|
||||
|
||||
double t_x = q_y * v_z - q_z * v_y;
|
||||
double t_y = q_z * v_x - q_x * v_z;
|
||||
double t_z = q_x * v_y - q_y * v_x;
|
||||
|
||||
double r_x = q_w * t_x + q_y * t_z - q_z * t_y;
|
||||
double r_y = q_w * t_y + q_z * t_x - q_x * t_z;
|
||||
double r_z = q_w * t_z + q_x * t_y - q_y * t_x;
|
||||
|
||||
r_x += r_x;
|
||||
r_y += r_y;
|
||||
r_z += r_z;
|
||||
|
||||
r_x += v_x;
|
||||
r_y += v_y;
|
||||
r_z += v_z;
|
||||
|
||||
r.setX(r_x);
|
||||
r.setY(r_y);
|
||||
r.setZ(r_z);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void quat_mul_quat(const tf::Quaternion& p, const tf::Quaternion& q, tf::Quaternion& r)
|
||||
{
|
||||
double p_x = p.x();
|
||||
double p_y = p.y();
|
||||
double p_z = p.z();
|
||||
double p_w = p.w();
|
||||
|
||||
double q_x = q.x();
|
||||
double q_y = q.y();
|
||||
double q_z = q.z();
|
||||
double q_w = q.w();
|
||||
|
||||
double r_x = (p_w * q_x + p_x * q_w) + (p_y * q_z - p_z * q_y);
|
||||
double r_y = (p_w * q_y - p_x * q_z) + (p_y * q_w + p_z * q_x);
|
||||
double r_z = (p_w * q_z + p_x * q_y) - (p_y * q_x - p_z * q_w);
|
||||
double r_w = (p_w * q_w - p_x * q_x) - (p_y * q_y + p_z * q_z);
|
||||
|
||||
r.setX(r_x);
|
||||
r.setY(r_y);
|
||||
r.setZ(r_z);
|
||||
r.setW(r_w);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void concat(const Frame& a, const Frame& b, Frame& r)
|
||||
{
|
||||
tf::Vector3 d;
|
||||
quat_mul_vec(a.rot, b.pos, d);
|
||||
r.pos = a.pos + d;
|
||||
quat_mul_quat(a.rot, b.rot, r.rot);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void concat(const Frame& a, const Frame& b, const Frame& c, Frame& r)
|
||||
{
|
||||
Frame tmp;
|
||||
concat(a, b, tmp);
|
||||
concat(tmp, c, r);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void quat_inv(const tf::Quaternion& q, tf::Quaternion& r)
|
||||
{
|
||||
r.setX(-q.x());
|
||||
r.setY(-q.y());
|
||||
r.setZ(-q.z());
|
||||
r.setW(q.w());
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void invert(const Frame& a, Frame& r)
|
||||
{
|
||||
Frame tmp;
|
||||
quat_inv(a.rot, r.rot);
|
||||
quat_mul_vec(r.rot, -a.pos, r.pos);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void change(const Frame& a, const Frame& b, const Frame& c, Frame& r)
|
||||
{
|
||||
Frame tmp;
|
||||
invert(b, tmp);
|
||||
concat(a, tmp, c, r);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline Frame inverse(const Frame& f)
|
||||
{
|
||||
Frame r;
|
||||
invert(f, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline Frame operator*(const Frame& a, const Frame& b)
|
||||
{
|
||||
Frame r;
|
||||
concat(a, b, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline Frame& operator*=(Frame& a, const Frame& b)
|
||||
{
|
||||
a = a * b;
|
||||
return a;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline void normalizeFast(Quaternion& q)
|
||||
{
|
||||
double f = (3.0 - q.length2()) * 0.5;
|
||||
q.setX(q.x() * f);
|
||||
q.setY(q.y() * f);
|
||||
q.setZ(q.z() * f);
|
||||
q.setW(q.w() * f);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline KDL::Twist frameTwist(const Frame& a, const Frame& b)
|
||||
{
|
||||
auto frame = inverse(a) * b;
|
||||
KDL::Twist t;
|
||||
t.vel.x(frame.pos.x());
|
||||
t.vel.y(frame.pos.y());
|
||||
t.vel.z(frame.pos.z());
|
||||
|
||||
double ra = frame.rot.getAngle();
|
||||
// double ra = frame.rot.getAngleShortestPath();
|
||||
if(ra > +M_PI) ra -= 2 * M_PI;
|
||||
// if(ra < -M_PI) ra += 2 * M_PI;
|
||||
|
||||
auto r = frame.rot.getAxis() * ra;
|
||||
t.rot.x(r.x());
|
||||
t.rot.y(r.y());
|
||||
t.rot.z(r.z());
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "frame.h"
|
||||
|
||||
#include <moveit/kinematics_base/kinematics_base.h>
|
||||
|
||||
#include <moveit/robot_model/joint_model_group.h>
|
||||
#include <moveit/robot_model/robot_model.h>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
class RobotInfo;
|
||||
|
||||
class GoalContext
|
||||
{
|
||||
protected:
|
||||
const double* active_variable_positions_;
|
||||
const Frame* tip_link_frames_;
|
||||
std::vector<ssize_t> goal_variable_indices_;
|
||||
std::vector<size_t> goal_link_indices_;
|
||||
bool goal_secondary_;
|
||||
std::vector<std::string> goal_link_names_, goal_variable_names_;
|
||||
double goal_weight_;
|
||||
const moveit::core::JointModelGroup* joint_model_group_;
|
||||
std::vector<size_t> problem_active_variables_;
|
||||
std::vector<size_t> problem_tip_link_indices_;
|
||||
std::vector<double> initial_guess_;
|
||||
std::vector<double> velocity_weights_;
|
||||
const RobotInfo* robot_info_;
|
||||
mutable std::vector<double> temp_vector_;
|
||||
|
||||
public:
|
||||
GoalContext() {}
|
||||
inline const Frame& getLinkFrame(size_t i = 0) const { return tip_link_frames_[goal_link_indices_[i]]; }
|
||||
inline const double getVariablePosition(size_t i = 0) const
|
||||
{
|
||||
auto j = goal_variable_indices_[i];
|
||||
if(j >= 0)
|
||||
return active_variable_positions_[j];
|
||||
else
|
||||
return initial_guess_[-1 - j];
|
||||
}
|
||||
inline const Frame& getProblemLinkFrame(size_t i) const { return tip_link_frames_[i]; }
|
||||
inline size_t getProblemLinkCount() const { return problem_tip_link_indices_.size(); }
|
||||
inline size_t getProblemLinkIndex(size_t i) const { return problem_tip_link_indices_[i]; }
|
||||
inline double getProblemVariablePosition(size_t i) const { return active_variable_positions_[i]; }
|
||||
inline size_t getProblemVariableCount() const { return problem_active_variables_.size(); }
|
||||
inline size_t getProblemVariableIndex(size_t i) const { return problem_active_variables_[i]; }
|
||||
inline double getProblemVariableInitialGuess(size_t i) const { return initial_guess_[problem_active_variables_[i]]; }
|
||||
inline double getProblemVariableWeight(size_t i) const { return velocity_weights_[i]; }
|
||||
inline const RobotInfo& getRobotInfo() const { return *robot_info_; }
|
||||
void addLink(const std::string& name) { goal_link_names_.push_back(name); }
|
||||
void addVariable(const std::string& name) { goal_variable_names_.push_back(name); }
|
||||
void setSecondary(bool secondary) { goal_secondary_ = secondary; }
|
||||
void setWeight(double weight) { goal_weight_ = weight; }
|
||||
const moveit::core::JointModelGroup& getJointModelGroup() const { return *joint_model_group_; }
|
||||
const moveit::core::RobotModel& getRobotModel() const { return joint_model_group_->getParentModel(); }
|
||||
std::vector<double>& getTempVector() const { return temp_vector_; }
|
||||
friend class Problem;
|
||||
};
|
||||
|
||||
class Goal
|
||||
{
|
||||
protected:
|
||||
bool secondary_;
|
||||
double weight_;
|
||||
|
||||
public:
|
||||
Goal()
|
||||
: weight_(1)
|
||||
, secondary_(false)
|
||||
{
|
||||
}
|
||||
virtual ~Goal() {}
|
||||
bool isSecondary() const { return secondary_; }
|
||||
double getWeight() const { return weight_; }
|
||||
void setWeight(double w) { weight_ = w; }
|
||||
virtual void describe(GoalContext& context) const
|
||||
{
|
||||
context.setSecondary(secondary_);
|
||||
context.setWeight(weight_);
|
||||
}
|
||||
virtual double evaluate(const GoalContext& context) const { return 0; }
|
||||
};
|
||||
|
||||
struct BioIKKinematicsQueryOptions : kinematics::KinematicsQueryOptions
|
||||
{
|
||||
std::vector<std::unique_ptr<Goal>> goals;
|
||||
std::vector<std::string> fixed_joints;
|
||||
bool replace;
|
||||
mutable double solution_fitness;
|
||||
BioIKKinematicsQueryOptions();
|
||||
~BioIKKinematicsQueryOptions();
|
||||
};
|
||||
}
|
||||
@@ -1,661 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "goal.h"
|
||||
|
||||
#include "robot_info.h"
|
||||
|
||||
#include <geometric_shapes/shapes.h>
|
||||
|
||||
#include <moveit/collision_detection/collision_robot.h>
|
||||
#include <moveit/collision_detection_fcl/collision_robot_fcl.h>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <geometric_shapes/bodies.h>
|
||||
#include <geometric_shapes/shapes.h>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
class LinkGoalBase : public Goal
|
||||
{
|
||||
std::string link_name_;
|
||||
|
||||
public:
|
||||
LinkGoalBase()
|
||||
{
|
||||
weight_ = 1;
|
||||
link_name_ = "";
|
||||
}
|
||||
LinkGoalBase(const std::string& link_name, double weight)
|
||||
{
|
||||
weight_ = weight;
|
||||
link_name_ = link_name;
|
||||
}
|
||||
virtual void describe(GoalContext& context) const
|
||||
{
|
||||
Goal::describe(context);
|
||||
context.addLink(link_name_);
|
||||
}
|
||||
void setLinkName(const std::string& link_name) { link_name_ = link_name; }
|
||||
const std::string& getLinkName() const { return link_name_; }
|
||||
};
|
||||
|
||||
class PositionGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 position_;
|
||||
|
||||
public:
|
||||
PositionGoal()
|
||||
: position_(0, 0, 0)
|
||||
{
|
||||
}
|
||||
PositionGoal(const std::string& link_name, const tf::Vector3& position, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, position_(position)
|
||||
{
|
||||
}
|
||||
inline const tf::Vector3& getPosition() const { return position_; }
|
||||
inline void setPosition(const tf::Vector3& position) { position_ = position; }
|
||||
virtual double evaluate(const GoalContext& context) const { return context.getLinkFrame().getPosition().distance2(getPosition()); }
|
||||
};
|
||||
|
||||
class OrientationGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Quaternion orientation_;
|
||||
|
||||
public:
|
||||
OrientationGoal()
|
||||
: orientation_(0, 0, 0, 1)
|
||||
{
|
||||
}
|
||||
OrientationGoal(const std::string& link_name, const tf::Quaternion& orientation, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, orientation_(orientation.normalized())
|
||||
{
|
||||
}
|
||||
inline const tf::Quaternion& getOrientation() const { return orientation_; }
|
||||
inline void setOrientation(const tf::Quaternion& orientation) { orientation_ = orientation.normalized(); }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
// return getOrientation().distance2(context.getLinkFrame().getOrientation());
|
||||
// return (getOrientation() - getOrientation().nearest(context.getLinkFrame().getOrientation())).length2();
|
||||
return fmin((getOrientation() - context.getLinkFrame().getOrientation()).length2(), (getOrientation() + context.getLinkFrame().getOrientation()).length2());
|
||||
/*return
|
||||
(getOrientation() - context.getLinkFrame().getOrientation()).length2() *
|
||||
(getOrientation() + context.getLinkFrame().getOrientation()).length2() * 0.5;*/
|
||||
}
|
||||
};
|
||||
|
||||
class PoseGoal : public LinkGoalBase
|
||||
{
|
||||
Frame frame_;
|
||||
double rotation_scale_;
|
||||
|
||||
public:
|
||||
PoseGoal()
|
||||
: rotation_scale_(0.5)
|
||||
, frame_(Frame::identity())
|
||||
{
|
||||
}
|
||||
PoseGoal(const std::string& link_name, const tf::Vector3& position, const tf::Quaternion& orientation, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, frame_(position, orientation.normalized())
|
||||
, rotation_scale_(0.5)
|
||||
{
|
||||
}
|
||||
inline const tf::Vector3& getPosition() const { return frame_.getPosition(); }
|
||||
inline void setPosition(const tf::Vector3& position) { frame_.setPosition(position); }
|
||||
inline const tf::Quaternion& getOrientation() const { return frame_.getOrientation(); }
|
||||
inline void setOrientation(const tf::Quaternion& orientation) { frame_.setOrientation(orientation.normalized()); }
|
||||
inline double getRotationScale() const { return rotation_scale_; }
|
||||
inline void setRotationScale(double rotation_scale) { rotation_scale_ = rotation_scale; }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
double e = 0.0;
|
||||
e += context.getLinkFrame().getPosition().distance2(getPosition());
|
||||
|
||||
/*e +=
|
||||
(getOrientation() - context.getLinkFrame().getOrientation()).length2() *
|
||||
(getOrientation() + context.getLinkFrame().getOrientation()).length2() *
|
||||
(rotation_scale_ * rotation_scale_) * 0.5;*/
|
||||
|
||||
/*double a = getOrientation().angleShortestPath(context.getLinkFrame().getOrientation());
|
||||
e += a * a;
|
||||
return e;*/
|
||||
|
||||
/*e += 1 - getOrientation().dot(context.getLinkFrame().getOrientation());
|
||||
return e;*/
|
||||
|
||||
/*double l = getOrientation().length2() * context.getLinkFrame().getOrientation().length2();
|
||||
//double x = _mm_rsqrt_ss(_mm_set_ss((float)l))[0];
|
||||
double x = 1.0 / l;
|
||||
e += (1 - getOrientation().dot(context.getLinkFrame().getOrientation()) * x) * (rotation_scale_ * rotation_scale_);
|
||||
return e;*/
|
||||
|
||||
e += fmin((getOrientation() - context.getLinkFrame().getOrientation()).length2(), (getOrientation() + context.getLinkFrame().getOrientation()).length2()) * (rotation_scale_ * rotation_scale_);
|
||||
|
||||
// e += (1.0 - getOrientation().dot(context.getLinkFrame().getOrientation())) * (rotation_scale_ * rotation_scale_);
|
||||
|
||||
// e += (getOrientation() - context.getLinkFrame().getOrientation()).length2() * (rotation_scale_ * rotation_scale_);
|
||||
// ROS_ERROR("r %f", (getOrientation() - context.getLinkFrame().getOrientation()).length2());
|
||||
// e += (getOrientation() - getOrientation().nearest(context.getLinkFrame().getOrientation())).length2() * (rotation_scale_ * rotation_scale_);
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
class LookAtGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 axis_;
|
||||
tf::Vector3 target_;
|
||||
|
||||
public:
|
||||
LookAtGoal()
|
||||
: axis_(1, 0, 0)
|
||||
, target_(0, 0, 0)
|
||||
{
|
||||
}
|
||||
LookAtGoal(const std::string& link_name, const tf::Vector3& axis, const tf::Vector3& target, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, axis_(axis)
|
||||
, target_(target)
|
||||
{
|
||||
}
|
||||
const tf::Vector3& getAxis() const { return axis_; }
|
||||
const tf::Vector3& getTarget() const { return target_; }
|
||||
void setAxis(const tf::Vector3& axis) { axis_ = axis.normalized(); }
|
||||
void setTarget(const tf::Vector3& target) { target_ = target; }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& fb = context.getLinkFrame();
|
||||
tf::Vector3 axis;
|
||||
quat_mul_vec(fb.getOrientation(), axis_, axis);
|
||||
return (target_ - fb.getPosition()).normalized().distance2(axis.normalized());
|
||||
// return (target_ - axis * axis.dot(target_ - fb.getPosition())).distance2(fb.getPosition());
|
||||
}
|
||||
};
|
||||
|
||||
class MaxDistanceGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 target;
|
||||
double distance;
|
||||
|
||||
public:
|
||||
MaxDistanceGoal()
|
||||
: target(0, 0, 0)
|
||||
, distance(1)
|
||||
{
|
||||
}
|
||||
MaxDistanceGoal(const std::string& link_name, const tf::Vector3& target, double distance, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, target(target)
|
||||
, distance(distance)
|
||||
{
|
||||
}
|
||||
const tf::Vector3& getTarget() const { return target; }
|
||||
void setTarget(const tf::Vector3& t) { target = t; }
|
||||
double getDistance() const { return distance; }
|
||||
void setDistance(double d) { distance = d; }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& fb = context.getLinkFrame();
|
||||
double d = fmax(0.0, fb.getPosition().distance(target) - distance);
|
||||
return d * d;
|
||||
}
|
||||
};
|
||||
|
||||
class MinDistanceGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 target;
|
||||
double distance;
|
||||
|
||||
public:
|
||||
MinDistanceGoal()
|
||||
: target(0, 0, 0)
|
||||
, distance(1)
|
||||
{
|
||||
}
|
||||
MinDistanceGoal(const std::string& link_name, const tf::Vector3& target, double distance, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, target(target)
|
||||
, distance(distance)
|
||||
{
|
||||
}
|
||||
const tf::Vector3& getTarget() const { return target; }
|
||||
void setTarget(const tf::Vector3& t) { target = t; }
|
||||
double getDistance() const { return distance; }
|
||||
void setDistance(double d) { distance = d; }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& fb = context.getLinkFrame();
|
||||
double d = fmax(0.0, distance - fb.getPosition().distance(target));
|
||||
return d * d;
|
||||
}
|
||||
};
|
||||
|
||||
class LineGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 position;
|
||||
tf::Vector3 direction;
|
||||
|
||||
public:
|
||||
LineGoal()
|
||||
: position(0, 0, 0)
|
||||
, direction(0, 0, 0)
|
||||
{
|
||||
}
|
||||
LineGoal(const std::string& link_name, const tf::Vector3& position, const tf::Vector3& direction, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, position(position)
|
||||
, direction(direction.normalized())
|
||||
{
|
||||
}
|
||||
const tf::Vector3& getPosition() const { return position; }
|
||||
void setPosition(const tf::Vector3& p) { position = p; }
|
||||
const tf::Vector3& getDirection() const { return direction; }
|
||||
void setDirection(const tf::Vector3& d) { direction = d.normalized(); }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& fb = context.getLinkFrame();
|
||||
return position.distance2(fb.getPosition() - direction * direction.dot(fb.getPosition() - position));
|
||||
}
|
||||
};
|
||||
|
||||
class TouchGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 position;
|
||||
tf::Vector3 normal;
|
||||
struct CollisionShape
|
||||
{
|
||||
std::vector<Vector3> vertices;
|
||||
std::vector<fcl::Vec3f> points;
|
||||
std::vector<int> polygons;
|
||||
std::vector<fcl::Vec3f> plane_normals;
|
||||
std::vector<double> plane_dis;
|
||||
collision_detection::FCLGeometryConstPtr geometry;
|
||||
Frame frame;
|
||||
std::vector<std::vector<size_t>> edges;
|
||||
};
|
||||
struct CollisionLink
|
||||
{
|
||||
bool initialized;
|
||||
std::vector<std::shared_ptr<CollisionShape>> shapes;
|
||||
CollisionLink()
|
||||
: initialized(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
struct CollisionModel
|
||||
{
|
||||
std::vector<CollisionLink> collision_links;
|
||||
};
|
||||
mutable CollisionModel* collision_model;
|
||||
mutable const moveit::core::LinkModel* link_model;
|
||||
|
||||
public:
|
||||
TouchGoal()
|
||||
: position(0, 0, 0)
|
||||
, normal(0, 0, 0)
|
||||
{
|
||||
}
|
||||
TouchGoal(const std::string& link_name, const tf::Vector3& position, const tf::Vector3& normal, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, position(position)
|
||||
, normal(normal.normalized())
|
||||
{
|
||||
}
|
||||
virtual void describe(GoalContext& context) const;
|
||||
virtual double evaluate(const GoalContext& context) const;
|
||||
};
|
||||
|
||||
class AvoidJointLimitsGoal : public Goal
|
||||
{
|
||||
public:
|
||||
AvoidJointLimitsGoal(double weight = 1.0, bool secondary = true)
|
||||
{
|
||||
weight_ = weight;
|
||||
secondary_ = secondary;
|
||||
}
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& info = context.getRobotInfo();
|
||||
double sum = 0.0;
|
||||
for(size_t i = 0; i < context.getProblemVariableCount(); i++)
|
||||
{
|
||||
size_t ivar = context.getProblemVariableIndex(i);
|
||||
if(info.getClipMax(ivar) == DBL_MAX) continue;
|
||||
double d = context.getProblemVariablePosition(i) - (info.getMin(ivar) + info.getMax(ivar)) * 0.5;
|
||||
d = fmax(0.0, fabs(d) * 2.0 - info.getSpan(ivar) * 0.5);
|
||||
d *= context.getProblemVariableWeight(i);
|
||||
sum += d * d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
class CenterJointsGoal : public Goal
|
||||
{
|
||||
public:
|
||||
CenterJointsGoal(double weight = 1.0, bool secondary = true)
|
||||
{
|
||||
weight_ = weight;
|
||||
secondary_ = secondary;
|
||||
}
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& info = context.getRobotInfo();
|
||||
double sum = 0.0;
|
||||
for(size_t i = 0; i < context.getProblemVariableCount(); i++)
|
||||
{
|
||||
size_t ivar = context.getProblemVariableIndex(i);
|
||||
if(info.getClipMax(ivar) == DBL_MAX) continue;
|
||||
double d = context.getProblemVariablePosition(i) - (info.getMin(ivar) + info.getMax(ivar)) * 0.5;
|
||||
d *= context.getProblemVariableWeight(i);
|
||||
sum += d * d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
class MinimalDisplacementGoal : public Goal
|
||||
{
|
||||
public:
|
||||
MinimalDisplacementGoal(double weight = 1.0, bool secondary = true)
|
||||
{
|
||||
weight_ = weight;
|
||||
secondary_ = secondary;
|
||||
}
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
double sum = 0.0;
|
||||
for(size_t i = 0; i < context.getProblemVariableCount(); i++)
|
||||
{
|
||||
double d = context.getProblemVariablePosition(i) - context.getProblemVariableInitialGuess(i);
|
||||
d *= context.getProblemVariableWeight(i);
|
||||
sum += d * d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
class JointVariableGoal : public Goal
|
||||
{
|
||||
std::string variable_name;
|
||||
double variable_position;
|
||||
|
||||
public:
|
||||
JointVariableGoal()
|
||||
: variable_position(0)
|
||||
{
|
||||
}
|
||||
JointVariableGoal(const std::string& variable_name, double variable_position, double weight = 1.0, bool secondary = false)
|
||||
: variable_name(variable_name)
|
||||
, variable_position(variable_position)
|
||||
{
|
||||
weight_ = weight;
|
||||
secondary_ = secondary;
|
||||
}
|
||||
double getVariablePosition() const { return variable_position; }
|
||||
void setVariablePosition(double p) { variable_position = p; }
|
||||
const std::string& getVariableName() const { return variable_name; }
|
||||
void setVariableName(const std::string& n) { variable_name = n; }
|
||||
virtual void describe(GoalContext& context) const
|
||||
{
|
||||
Goal::describe(context);
|
||||
context.addVariable(variable_name);
|
||||
}
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
double d = variable_position - context.getVariablePosition();
|
||||
return d * d;
|
||||
}
|
||||
};
|
||||
|
||||
class JointFunctionGoal : public Goal
|
||||
{
|
||||
std::vector<std::string> variable_names;
|
||||
std::function<void(std::vector<double>&)> function;
|
||||
|
||||
public:
|
||||
JointFunctionGoal() {}
|
||||
JointFunctionGoal(const std::vector<std::string>& variable_names, const std::function<void(std::vector<double>&)>& function, double weight = 1.0, bool secondary = false)
|
||||
: variable_names(variable_names)
|
||||
, function(function)
|
||||
{
|
||||
weight_ = weight;
|
||||
secondary_ = secondary;
|
||||
}
|
||||
void setJointVariableNames(const std::vector<std::string>& n) { variable_names = n; }
|
||||
void setJointVariableFunction(const std::function<void(std::vector<double>&)>& f) { function = f; }
|
||||
virtual void describe(GoalContext& context) const
|
||||
{
|
||||
Goal::describe(context);
|
||||
for(auto& variable_name : variable_names)
|
||||
context.addVariable(variable_name);
|
||||
}
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& temp_vector = context.getTempVector();
|
||||
temp_vector.resize(variable_names.size());
|
||||
for(size_t i = 0; i < variable_names.size(); i++)
|
||||
temp_vector[i] = context.getVariablePosition(i);
|
||||
function(temp_vector);
|
||||
double sum = 0.0;
|
||||
for(size_t i = 0; i < variable_names.size(); i++)
|
||||
{
|
||||
double d = temp_vector[i] - context.getVariablePosition(i);
|
||||
sum += d * d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
class BalanceGoal : public Goal
|
||||
{
|
||||
tf::Vector3 target_, axis_;
|
||||
struct BalanceInfo
|
||||
{
|
||||
tf::Vector3 center;
|
||||
double weight;
|
||||
};
|
||||
mutable std::vector<BalanceInfo> balance_infos;
|
||||
|
||||
public:
|
||||
BalanceGoal()
|
||||
: target_(0, 0, 0)
|
||||
, axis_(0, 0, 1)
|
||||
{
|
||||
}
|
||||
BalanceGoal(const tf::Vector3& target, double weight = 1.0)
|
||||
: target_(target)
|
||||
, axis_(0, 0, 1)
|
||||
{
|
||||
weight_ = weight;
|
||||
}
|
||||
const tf::Vector3& getTarget() const { return target_; }
|
||||
const tf::Vector3& getAxis() const { return axis_; }
|
||||
void setTarget(const tf::Vector3& target) { target_ = target; }
|
||||
void setAxis(const tf::Vector3& axis) { axis_ = axis; }
|
||||
virtual void describe(GoalContext& context) const;
|
||||
virtual double evaluate(const GoalContext& context) const;
|
||||
};
|
||||
|
||||
class LinkFunctionGoal : public LinkGoalBase
|
||||
{
|
||||
std::function<double(const tf::Vector3&, const tf::Quaternion&)> function;
|
||||
|
||||
public:
|
||||
LinkFunctionGoal() {}
|
||||
LinkFunctionGoal(const std::string& link_name, const std::function<double(const tf::Vector3&, const tf::Quaternion&)>& function, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, function(function)
|
||||
{
|
||||
}
|
||||
void setLinkFunction(const std::function<double(const tf::Vector3&, const tf::Quaternion&)>& f) { function = f; }
|
||||
virtual double evaluate(const GoalContext& context) const { return function(context.getLinkFrame().getPosition(), context.getLinkFrame().getOrientation()); }
|
||||
};
|
||||
|
||||
class SideGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 axis;
|
||||
tf::Vector3 direction;
|
||||
|
||||
public:
|
||||
SideGoal()
|
||||
: axis(0, 0, 1)
|
||||
, direction(0, 0, 1)
|
||||
{
|
||||
}
|
||||
SideGoal(const std::string& link_name, const tf::Vector3& axis, const tf::Vector3& direction, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, axis(axis)
|
||||
, direction(direction)
|
||||
{
|
||||
}
|
||||
const tf::Vector3& getAxis() const { return axis; }
|
||||
const tf::Vector3& getDirection() const { return direction; }
|
||||
void setAxis(const tf::Vector3& a) { axis = a.normalized(); }
|
||||
void setDirection(const tf::Vector3& d) { direction = d.normalized(); }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& fb = context.getLinkFrame();
|
||||
Vector3 v;
|
||||
quat_mul_vec(fb.getOrientation(), axis, v);
|
||||
double f = fmax(0.0, v.dot(direction));
|
||||
return f * f;
|
||||
}
|
||||
};
|
||||
|
||||
class DirectionGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 axis;
|
||||
tf::Vector3 direction;
|
||||
|
||||
public:
|
||||
DirectionGoal()
|
||||
: axis(0, 0, 1)
|
||||
, direction(0, 0, 1)
|
||||
{
|
||||
}
|
||||
DirectionGoal(const std::string& link_name, const tf::Vector3& axis, const tf::Vector3& direction, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, axis(axis)
|
||||
, direction(direction)
|
||||
{
|
||||
}
|
||||
const tf::Vector3& getAxis() const { return axis; }
|
||||
const tf::Vector3& getDirection() const { return direction; }
|
||||
void setAxis(const tf::Vector3& a) { axis = a.normalized(); }
|
||||
void setDirection(const tf::Vector3& d) { direction = d.normalized(); }
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
auto& fb = context.getLinkFrame();
|
||||
Vector3 v;
|
||||
quat_mul_vec(fb.getOrientation(), axis, v);
|
||||
return v.distance2(direction);
|
||||
}
|
||||
};
|
||||
|
||||
class ConeGoal : public LinkGoalBase
|
||||
{
|
||||
tf::Vector3 position;
|
||||
double position_weight;
|
||||
tf::Vector3 axis;
|
||||
tf::Vector3 direction;
|
||||
double angle;
|
||||
|
||||
public:
|
||||
const tf::Vector3& getPosition() const { return position; }
|
||||
double getPositionWeight() const { return position_weight; }
|
||||
const tf::Vector3& getAxis() const { return axis; }
|
||||
const tf::Vector3& getDirection() const { return direction; }
|
||||
double getAngle() const { return angle; }
|
||||
void setPosition(const tf::Vector3& p) { position = p; }
|
||||
void setPositionWeight(double w) { position_weight = w; }
|
||||
void setAxis(const tf::Vector3& a) { axis = a.normalized(); }
|
||||
void setDirection(const tf::Vector3& d) { direction = d.normalized(); }
|
||||
void setAngle(double a) { angle = a; }
|
||||
ConeGoal()
|
||||
: position(0, 0, 0)
|
||||
, position_weight(0)
|
||||
, axis(0, 0, 1)
|
||||
, direction(0, 0, 1)
|
||||
, angle(0)
|
||||
{
|
||||
}
|
||||
ConeGoal(const std::string& link_name, const tf::Vector3& axis, const tf::Vector3& direction, double angle, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, position(0, 0, 0)
|
||||
, position_weight(0)
|
||||
, axis(axis)
|
||||
, direction(direction)
|
||||
, angle(angle)
|
||||
{
|
||||
}
|
||||
ConeGoal(const std::string& link_name, const tf::Vector3& position, const tf::Vector3& axis, const tf::Vector3& direction, double angle, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, position(position)
|
||||
, position_weight(1)
|
||||
, axis(axis)
|
||||
, direction(direction)
|
||||
, angle(angle)
|
||||
{
|
||||
}
|
||||
ConeGoal(const std::string& link_name, const tf::Vector3& position, double position_weight, const tf::Vector3& axis, const tf::Vector3& direction, double angle, double weight = 1.0)
|
||||
: LinkGoalBase(link_name, weight)
|
||||
, position(position)
|
||||
, position_weight(position_weight)
|
||||
, axis(axis)
|
||||
, direction(direction)
|
||||
, angle(angle)
|
||||
{
|
||||
}
|
||||
virtual double evaluate(const GoalContext& context) const
|
||||
{
|
||||
double sum = 0.0;
|
||||
auto& fb = context.getLinkFrame();
|
||||
Vector3 v;
|
||||
quat_mul_vec(fb.getOrientation(), axis, v);
|
||||
double d = fmax(0.0, v.angle(direction) - angle);
|
||||
sum += d * d;
|
||||
double w = position_weight;
|
||||
sum += w * w * (position - fb.getPosition()).length2();
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <moveit/kinematics_base/kinematics_base.h>
|
||||
#include <moveit/robot_model/robot_model.h>
|
||||
#include <moveit/robot_state/robot_state.h>
|
||||
|
||||
#include <bio_ik/goal.h>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
class RobotInfo
|
||||
{
|
||||
struct VariableInfo
|
||||
{
|
||||
double clip_min, clip_max;
|
||||
double span;
|
||||
double min;
|
||||
double max;
|
||||
double max_velocity, max_velocity_rcp;
|
||||
};
|
||||
std::vector<VariableInfo> variables;
|
||||
std::vector<size_t> activeVariables;
|
||||
std::vector<moveit::core::JointModel::JointType> variable_joint_types;
|
||||
moveit::core::RobotModelConstPtr robot_model;
|
||||
|
||||
__attribute__((always_inline)) static inline double clamp2(double v, double lo, double hi)
|
||||
{
|
||||
if(__builtin_expect(v < lo, 0)) v = lo;
|
||||
if(__builtin_expect(v > hi, 0)) v = hi;
|
||||
return v;
|
||||
}
|
||||
|
||||
public:
|
||||
RobotInfo() {}
|
||||
RobotInfo(moveit::core::RobotModelConstPtr model)
|
||||
: robot_model(model)
|
||||
{
|
||||
for(auto& name : model->getVariableNames())
|
||||
{
|
||||
auto& bounds = model->getVariableBounds(name);
|
||||
|
||||
VariableInfo info;
|
||||
|
||||
bool bounded = bounds.position_bounded_;
|
||||
|
||||
auto* joint_model = model->getJointOfVariable(variables.size());
|
||||
if(auto* revolute = dynamic_cast<const moveit::core::RevoluteJointModel*>(joint_model))
|
||||
|
||||
if(bounds.max_position_ - bounds.min_position_ >= 2 * M_PI * 0.9999) bounded = false;
|
||||
|
||||
info.min = bounds.min_position_;
|
||||
info.max = bounds.max_position_;
|
||||
|
||||
info.clip_min = bounded ? info.min : -DBL_MAX;
|
||||
info.clip_max = bounded ? info.max : +DBL_MAX;
|
||||
|
||||
info.span = info.max - info.min;
|
||||
|
||||
if(!(info.span >= 0 && info.span < FLT_MAX)) info.span = 1;
|
||||
|
||||
info.max_velocity = bounds.max_velocity_;
|
||||
info.max_velocity_rcp = info.max_velocity > 0.0 ? 1.0 / info.max_velocity : 0.0;
|
||||
|
||||
variables.push_back(info);
|
||||
}
|
||||
|
||||
for(size_t ivar = 0; ivar < model->getVariableCount(); ivar++)
|
||||
{
|
||||
variable_joint_types.push_back(model->getJointOfVariable(ivar)->getType());
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
__attribute__((always_inline)) inline double clip(double p, size_t i) const
|
||||
{
|
||||
auto& info = variables[i];
|
||||
return clamp2(p, info.clip_min, info.clip_max);
|
||||
}
|
||||
|
||||
inline double getSpan(size_t i) const { return variables[i].span; }
|
||||
inline double getClipMin(size_t i) const { return variables[i].clip_min; }
|
||||
inline double getClipMax(size_t i) const { return variables[i].clip_max; }
|
||||
inline double getMin(size_t i) const { return variables[i].min; }
|
||||
inline double getMax(size_t i) const { return variables[i].max; }
|
||||
|
||||
inline bool isRevolute(size_t variable_index) const { return variable_joint_types[variable_index] == moveit::core::JointModel::REVOLUTE; }
|
||||
inline bool isPrismatic(size_t variable_index) const { return variable_joint_types[variable_index] == moveit::core::JointModel::PRISMATIC; }
|
||||
inline double getMaxVelocity(size_t i) const { return variables[i].max_velocity; }
|
||||
inline double getMaxVelocityRcp(size_t i) const { return variables[i].max_velocity_rcp; }
|
||||
};
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
|
||||
<name>bio_ik</name>
|
||||
<version>1.0.0</version>
|
||||
<description>BioIK for ROS</description>
|
||||
<license>BSD</license>
|
||||
|
||||
<author email="0ruppel@informatik.uni-hamburg.de">Philipp Sebastian Ruppel</author>
|
||||
|
||||
<maintainer email="0ruppel@informatik.uni-hamburg.de">Philipp Sebastian Ruppel</maintainer>
|
||||
<maintainer email="me@v4hn.de">Michael Goerner</maintainer>
|
||||
|
||||
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
|
||||
<build_depend>moveit_core</build_depend>
|
||||
<run_depend>moveit_core</run_depend>
|
||||
|
||||
<build_depend>pluginlib</build_depend>
|
||||
<run_depend>pluginlib</run_depend>
|
||||
|
||||
<build_depend>eigen</build_depend>
|
||||
<run_depend>eigen</run_depend>
|
||||
|
||||
<build_depend>moveit_ros_planning</build_depend>
|
||||
<run_depend>moveit_ros_planning</run_depend>
|
||||
|
||||
<build_depend>roscpp</build_depend>
|
||||
<run_depend>roscpp</run_depend>
|
||||
|
||||
<build_depend>tf</build_depend>
|
||||
<run_depend>tf</run_depend>
|
||||
|
||||
<build_depend>tf_conversions</build_depend>
|
||||
<run_depend>tf_conversions</run_depend>
|
||||
|
||||
<build_depend>eigen_conversions</build_depend>
|
||||
<run_depend>eigen_conversions</run_depend>
|
||||
|
||||
<test_depend>rosunit</test_depend>
|
||||
|
||||
<export>
|
||||
<moveit_core plugin="${prefix}/bio_ik_kinematics_description.xml"/>
|
||||
</export>
|
||||
|
||||
</package>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include <bio_ik/goal_types.h>
|
||||
|
||||
#include <geometric_shapes/bodies.h>
|
||||
#include <geometric_shapes/shapes.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
void TouchGoal::describe(GoalContext& context) const
|
||||
{
|
||||
LinkGoalBase::describe(context);
|
||||
auto* robot_model = &context.getRobotModel();
|
||||
{
|
||||
static std::map<const moveit::core::RobotModel*, CollisionModel*> collision_cache;
|
||||
if(collision_cache.find(robot_model) == collision_cache.end()) collision_cache[&context.getRobotModel()] = new CollisionModel();
|
||||
collision_model = collision_cache[robot_model];
|
||||
collision_model->collision_links.resize(robot_model->getLinkModelCount());
|
||||
}
|
||||
link_model = robot_model->getLinkModel(this->getLinkName());
|
||||
size_t link_index = link_model->getLinkIndex();
|
||||
auto touch_goal_normal = normal.normalized();
|
||||
// auto fbrot = fb.rot.normalized();
|
||||
auto& collision_link = collision_model->collision_links[link_index];
|
||||
if(!collision_link.initialized)
|
||||
{
|
||||
collision_link.initialized = true;
|
||||
collision_link.shapes.resize(link_model->getShapes().size());
|
||||
for(size_t shape_index = 0; shape_index < link_model->getShapes().size(); shape_index++)
|
||||
{
|
||||
collision_link.shapes[shape_index] = std::make_shared<CollisionShape>();
|
||||
auto& s = *collision_link.shapes[shape_index];
|
||||
s.frame = Frame(link_model->getCollisionOriginTransforms()[shape_index]);
|
||||
auto* shape = link_model->getShapes()[shape_index].get();
|
||||
// LOG(link_model->getName(), shape_index, link_model->getShapes().size(), typeid(*shape).name());
|
||||
if(auto* mesh = dynamic_cast<const shapes::Mesh*>(shape))
|
||||
{
|
||||
struct : bodies::ConvexMesh
|
||||
{
|
||||
std::vector<fcl::Vec3f> points;
|
||||
std::vector<int> polygons;
|
||||
std::vector<fcl::Vec3f> plane_normals;
|
||||
std::vector<double> plane_dis;
|
||||
void init(const shapes::Shape* shape)
|
||||
{
|
||||
type_ = shapes::MESH;
|
||||
scaled_vertices_ = 0;
|
||||
{
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
setDimensions(shape);
|
||||
}
|
||||
for(auto& v : mesh_data_->vertices_)
|
||||
points.emplace_back(v.x(), v.y(), v.z());
|
||||
for(size_t triangle_index = 0; triangle_index < mesh_data_->triangles_.size() / 3; triangle_index++)
|
||||
{
|
||||
polygons.push_back(3);
|
||||
polygons.push_back(mesh_data_->triangles_[triangle_index * 3 + 0]);
|
||||
polygons.push_back(mesh_data_->triangles_[triangle_index * 3 + 1]);
|
||||
polygons.push_back(mesh_data_->triangles_[triangle_index * 3 + 2]);
|
||||
}
|
||||
for(size_t triangle_index = 0; triangle_index < mesh_data_->triangles_.size() / 3; triangle_index++)
|
||||
{
|
||||
auto plane_index = mesh_data_->plane_for_triangle_[triangle_index];
|
||||
auto plane = mesh_data_->planes_[plane_index];
|
||||
plane_normals.emplace_back(plane.x(), plane.y(), plane.z());
|
||||
plane_dis.push_back(plane.w());
|
||||
}
|
||||
}
|
||||
} convex;
|
||||
convex.init(mesh);
|
||||
s.points = convex.points;
|
||||
s.polygons = convex.polygons;
|
||||
s.plane_normals = convex.plane_normals;
|
||||
s.plane_dis = convex.plane_dis;
|
||||
|
||||
// auto* fcl = new fcl::Convex(s.plane_normals.data(), s.plane_dis.data(), s.plane_normals.size(), s.points.data(), s.points.size(), s.polygons.data());
|
||||
|
||||
// workaround for fcl::Convex initialization bug
|
||||
auto* fcl = (fcl::Convex*)::operator new(sizeof(fcl::Convex));
|
||||
fcl->num_points = s.points.size();
|
||||
fcl = new(fcl) fcl::Convex(s.plane_normals.data(), s.plane_dis.data(), s.plane_normals.size(), s.points.data(), s.points.size(), s.polygons.data());
|
||||
|
||||
s.geometry = decltype(s.geometry)(new collision_detection::FCLGeometry(fcl, link_model, shape_index));
|
||||
s.edges.resize(s.points.size());
|
||||
std::vector<std::unordered_set<size_t>> edge_sets(s.points.size());
|
||||
for(size_t edge_index = 0; edge_index < fcl->num_edges; edge_index++)
|
||||
{
|
||||
auto edge = fcl->edges[edge_index];
|
||||
if(edge_sets[edge.first].find(edge.second) == edge_sets[edge.first].end())
|
||||
{
|
||||
edge_sets[edge.first].insert(edge.second);
|
||||
s.edges[edge.first].push_back(edge.second);
|
||||
}
|
||||
if(edge_sets[edge.second].find(edge.first) == edge_sets[edge.second].end())
|
||||
{
|
||||
edge_sets[edge.second].insert(edge.first);
|
||||
s.edges[edge.second].push_back(edge.first);
|
||||
}
|
||||
}
|
||||
for(auto& p : s.points)
|
||||
s.vertices.emplace_back(p[0], p[1], p[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.geometry = collision_detection::createCollisionGeometry(link_model->getShapes()[shape_index], link_model, shape_index);
|
||||
}
|
||||
// LOG("b");
|
||||
}
|
||||
// getchar();
|
||||
}
|
||||
}
|
||||
|
||||
double TouchGoal::evaluate(const GoalContext& context) const
|
||||
{
|
||||
double dmin = DBL_MAX;
|
||||
context.getTempVector().resize(1);
|
||||
auto& last_collision_vertex = context.getTempVector()[0];
|
||||
auto& fb = context.getLinkFrame();
|
||||
size_t link_index = link_model->getLinkIndex();
|
||||
auto& collision_link = collision_model->collision_links[link_index];
|
||||
for(size_t shape_index = 0; shape_index < link_model->getShapes().size(); shape_index++)
|
||||
{
|
||||
if(!collision_link.shapes[shape_index]->geometry) continue;
|
||||
auto* shape = link_model->getShapes()[shape_index].get();
|
||||
// LOG(shape_index, typeid(*shape).name());
|
||||
if(auto* mesh = dynamic_cast<const shapes::Mesh*>(shape))
|
||||
{
|
||||
auto& s = collision_link.shapes[shape_index];
|
||||
double d = DBL_MAX;
|
||||
auto goal_normal = normal;
|
||||
quat_mul_vec(fb.rot.inverse(), goal_normal, goal_normal);
|
||||
quat_mul_vec(s->frame.rot.inverse(), goal_normal, goal_normal);
|
||||
/*{
|
||||
size_t array_index = 0;
|
||||
for(size_t vertex_index = 0; vertex_index < mesh->vertex_count; vertex_index++)
|
||||
{
|
||||
double dot_x = mesh->vertices[array_index++] * goal_normal.x();
|
||||
double dot_y = mesh->vertices[array_index++] * goal_normal.y();
|
||||
double dot_z = mesh->vertices[array_index++] * goal_normal.z();
|
||||
double e = dot_x + dot_y + dot_z;
|
||||
if(e < d) d = e;
|
||||
}
|
||||
}*/
|
||||
if(mesh->vertex_count > 0)
|
||||
{
|
||||
size_t vertex_index = last_collision_vertex;
|
||||
double vertex_dot_normal = goal_normal.dot(s->vertices[vertex_index]);
|
||||
// size_t loops = 0;
|
||||
while(true)
|
||||
{
|
||||
bool repeat = false;
|
||||
for(auto vertex_index_2 : s->edges[vertex_index])
|
||||
{
|
||||
auto vertex_dot_normal_2 = goal_normal.dot(s->vertices[vertex_index_2]);
|
||||
if(vertex_dot_normal_2 < vertex_dot_normal)
|
||||
{
|
||||
vertex_index = vertex_index_2;
|
||||
vertex_dot_normal = vertex_dot_normal_2;
|
||||
repeat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!repeat) break;
|
||||
// loops++;
|
||||
}
|
||||
// LOG_VAR(loops);
|
||||
d = vertex_dot_normal;
|
||||
last_collision_vertex = vertex_index;
|
||||
}
|
||||
d -= normal.dot(position - fb.pos);
|
||||
// ROS_INFO("touch goal");
|
||||
if(d < dmin) dmin = d;
|
||||
}
|
||||
else
|
||||
{
|
||||
double offset = 10000;
|
||||
static fcl::Sphere shape1(offset);
|
||||
fcl::DistanceRequest request;
|
||||
fcl::DistanceResult result;
|
||||
auto pos1 = position - normal * offset * 2;
|
||||
auto* shape2 = collision_link.shapes[shape_index]->geometry->collision_geometry_.get();
|
||||
auto frame2 = Frame(fb.pos, fb.rot.normalized()) * collision_link.shapes[shape_index]->frame;
|
||||
double d = fcl::distance(&shape1, fcl::Transform3f(fcl::Vec3f(pos1.x(), pos1.y(), pos1.z())), shape2, fcl::Transform3f(fcl::Quaternion3f(frame2.rot.w(), frame2.rot.x(), frame2.rot.y(), frame2.rot.z()), fcl::Vec3f(frame2.pos.x(), frame2.pos.y(), frame2.pos.z())), request, result);
|
||||
d -= offset;
|
||||
if(d < dmin) dmin = d;
|
||||
}
|
||||
}
|
||||
return dmin * dmin;
|
||||
}
|
||||
|
||||
void BalanceGoal::describe(GoalContext& context) const
|
||||
{
|
||||
Goal::describe(context);
|
||||
balance_infos.clear();
|
||||
double total = 0.0;
|
||||
for(auto& link_name : context.getRobotModel().getLinkModelNames())
|
||||
{
|
||||
auto link_urdf = context.getRobotModel().getURDF()->getLink(link_name);
|
||||
if(!link_urdf) continue;
|
||||
if(!link_urdf->inertial) continue;
|
||||
const auto& center_urdf = link_urdf->inertial->origin.position;
|
||||
tf::Vector3 center(center_urdf.x, center_urdf.y, center_urdf.z);
|
||||
double mass = link_urdf->inertial->mass;
|
||||
if(!(mass > 0)) continue;
|
||||
balance_infos.emplace_back();
|
||||
balance_infos.back().center = center;
|
||||
balance_infos.back().weight = mass;
|
||||
total += mass;
|
||||
context.addLink(link_name);
|
||||
}
|
||||
for(auto& b : balance_infos)
|
||||
{
|
||||
b.weight /= total;
|
||||
}
|
||||
}
|
||||
|
||||
double BalanceGoal::evaluate(const GoalContext& context) const
|
||||
{
|
||||
tf::Vector3 center = tf::Vector3(0, 0, 0);
|
||||
for(size_t i = 0; i < balance_infos.size(); i++)
|
||||
{
|
||||
auto& info = balance_infos[i];
|
||||
auto& frame = context.getLinkFrame(i);
|
||||
auto c = info.center;
|
||||
quat_mul_vec(frame.rot, c, c);
|
||||
c += frame.pos;
|
||||
center += c * info.weight;
|
||||
}
|
||||
center -= target_;
|
||||
center -= axis_ * axis_.dot(center);
|
||||
return center.length2();
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bio_ik/goal.h>
|
||||
|
||||
#include "forward_kinematics.h"
|
||||
#include "problem.h"
|
||||
#include "utils.h"
|
||||
#include <bio_ik/robot_info.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
struct Random
|
||||
{
|
||||
// std::mt19937 rng;
|
||||
std::minstd_rand rng;
|
||||
// std::ranlux24 rng;
|
||||
// std::knuth_b rng;
|
||||
// std::default_random_engine rng;
|
||||
|
||||
inline double random() { return std::uniform_real_distribution<double>(0, 1)(rng); }
|
||||
|
||||
inline std::size_t random_index(std::size_t s) { return std::uniform_int_distribution<size_t>(0, s - 1)(rng); }
|
||||
|
||||
std::normal_distribution<double> normal_distribution;
|
||||
inline double random_gauss() { return normal_distribution(rng); }
|
||||
|
||||
inline double random(double min, double max) { return random() * (max - min) + min; }
|
||||
|
||||
template <class e> inline e& random_element(std::vector<e>& l) { return l[random_index(l.size())]; }
|
||||
|
||||
template <class e> inline const e& random_element(const std::vector<e>& l) { return l[random_index(l.size())]; }
|
||||
|
||||
XORShift64 _xorshift;
|
||||
inline size_t fast_random_index(size_t mod) { return _xorshift() % mod; }
|
||||
template <class T> inline const T& fast_random_element(const std::vector<T>& v) { return v[fast_random_index(v.size())]; }
|
||||
|
||||
static const size_t random_buffer_size = 1024 * 1024 * 8;
|
||||
|
||||
const double* make_random_buffer()
|
||||
{
|
||||
static std::vector<double> buf;
|
||||
buf.resize(random_buffer_size);
|
||||
for(auto& r : buf)
|
||||
r = random();
|
||||
return buf.data();
|
||||
}
|
||||
const double* random_buffer;
|
||||
size_t random_buffer_index;
|
||||
inline double fast_random()
|
||||
{
|
||||
double r = random_buffer[random_buffer_index & (random_buffer_size - 1)];
|
||||
random_buffer_index++;
|
||||
return r;
|
||||
}
|
||||
|
||||
const double* make_random_gauss_buffer()
|
||||
{
|
||||
// LOG("make_random_gauss_buffer");
|
||||
static std::vector<double> buf;
|
||||
buf.resize(random_buffer_size);
|
||||
for(auto& r : buf)
|
||||
r = random_gauss();
|
||||
return buf.data();
|
||||
}
|
||||
const double* random_gauss_buffer;
|
||||
size_t random_gauss_index;
|
||||
inline double fast_random_gauss()
|
||||
{
|
||||
double r = random_gauss_buffer[random_gauss_index & (random_buffer_size - 1)];
|
||||
random_gauss_index++;
|
||||
return r;
|
||||
}
|
||||
inline const double* fast_random_gauss_n(size_t n)
|
||||
{
|
||||
size_t i = random_gauss_index;
|
||||
random_gauss_index += n;
|
||||
if(random_gauss_index >= random_buffer_size) i = 0, random_gauss_index = n;
|
||||
return random_gauss_buffer + i;
|
||||
}
|
||||
|
||||
Random()
|
||||
: rng(std::random_device()())
|
||||
{
|
||||
random_buffer = make_random_buffer();
|
||||
random_buffer_index = _xorshift();
|
||||
random_gauss_buffer = make_random_gauss_buffer();
|
||||
random_gauss_index = _xorshift();
|
||||
}
|
||||
};
|
||||
|
||||
struct IKBase : Random
|
||||
{
|
||||
IKParams params;
|
||||
RobotFK model;
|
||||
RobotInfo modelInfo;
|
||||
int thread_index;
|
||||
Problem problem;
|
||||
std::vector<Frame> null_tip_frames;
|
||||
volatile int canceled;
|
||||
|
||||
virtual void step() = 0;
|
||||
|
||||
virtual const std::vector<double>& getSolution() const = 0;
|
||||
|
||||
virtual void setParams(const IKParams& p) {}
|
||||
|
||||
IKBase(const IKParams& p)
|
||||
: model(p.robot_model)
|
||||
, modelInfo(p.robot_model)
|
||||
, params(p)
|
||||
{
|
||||
setParams(p);
|
||||
}
|
||||
virtual ~IKBase() {}
|
||||
|
||||
virtual void initialize(const Problem& problem)
|
||||
{
|
||||
this->problem = problem;
|
||||
this->problem.initialize2();
|
||||
model.initialize(problem.tip_link_indices);
|
||||
// active_variables = problem.active_variables;
|
||||
null_tip_frames.resize(problem.tip_link_indices.size());
|
||||
}
|
||||
|
||||
double computeSecondaryFitnessActiveVariables(const double* active_variable_positions) { return problem.computeGoalFitness(problem.secondary_goals, null_tip_frames.data(), active_variable_positions); }
|
||||
|
||||
double computeSecondaryFitnessAllVariables(const std::vector<double>& variable_positions) { return computeSecondaryFitnessActiveVariables(extractActiveVariables(variable_positions)); }
|
||||
|
||||
double computeFitnessActiveVariables(const std::vector<Frame>& tip_frames, const double* active_variable_positions) { return problem.computeGoalFitness(problem.goals, tip_frames.data(), active_variable_positions); }
|
||||
|
||||
double computeFitnessActiveVariables(const aligned_vector<Frame>& tip_frames, const double* active_variable_positions) { return problem.computeGoalFitness(problem.goals, tip_frames.data(), active_variable_positions); }
|
||||
|
||||
double computeCombinedFitnessActiveVariables(const std::vector<Frame>& tip_frames, const double* active_variable_positions)
|
||||
{
|
||||
double ret = 0.0;
|
||||
ret += problem.computeGoalFitness(problem.goals, tip_frames.data(), active_variable_positions);
|
||||
ret += problem.computeGoalFitness(problem.secondary_goals, null_tip_frames.data(), active_variable_positions);
|
||||
return ret;
|
||||
}
|
||||
|
||||
double computeCombinedFitnessActiveVariables(const aligned_vector<Frame>& tip_frames, const double* active_variable_positions)
|
||||
{
|
||||
double ret = 0.0;
|
||||
ret += problem.computeGoalFitness(problem.goals, tip_frames.data(), active_variable_positions);
|
||||
ret += problem.computeGoalFitness(problem.secondary_goals, null_tip_frames.data(), active_variable_positions);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool checkSolutionActiveVariables(const std::vector<Frame>& tip_frames, const double* active_variable_positions) { return problem.checkSolutionActiveVariables(tip_frames, active_variable_positions); }
|
||||
|
||||
bool checkSolution(const std::vector<double>& variable_positions, const std::vector<Frame>& tips) { return checkSolutionActiveVariables(tips, extractActiveVariables(variable_positions)); }
|
||||
|
||||
std::vector<double> temp_active_variable_positions;
|
||||
|
||||
double* extractActiveVariables(const std::vector<double>& variable_positions)
|
||||
{
|
||||
temp_active_variable_positions.resize(problem.active_variables.size());
|
||||
for(size_t i = 0; i < temp_active_variable_positions.size(); i++)
|
||||
temp_active_variable_positions[i] = variable_positions[problem.active_variables[i]];
|
||||
return temp_active_variable_positions.data();
|
||||
}
|
||||
|
||||
double computeFitness(const std::vector<double>& variable_positions, const std::vector<Frame>& tip_frames) { return computeFitnessActiveVariables(tip_frames, extractActiveVariables(variable_positions)); }
|
||||
|
||||
double computeFitness(const std::vector<double>& variable_positions)
|
||||
{
|
||||
model.applyConfiguration(variable_positions);
|
||||
return computeFitness(variable_positions, model.getTipFrames());
|
||||
}
|
||||
|
||||
virtual size_t concurrency() const { return 1; }
|
||||
};
|
||||
|
||||
typedef IKBase IKSolver;
|
||||
|
||||
typedef Factory<IKSolver, const IKParams&> IKFactory;
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
#include "cppoptlib/boundedproblem.h"
|
||||
#include "cppoptlib/meta.h"
|
||||
#include "cppoptlib/problem.h"
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
/*
|
||||
struct IKOptLibProblem : cppoptlib::Problem<double>
|
||||
{
|
||||
IKBase* ik;
|
||||
std::vector<double> fk_values;
|
||||
IKOptLibProblem(IKBase* ik) : ik(ik)
|
||||
{
|
||||
}
|
||||
void initialize()
|
||||
{
|
||||
// set all variables to initial guess, including inactive ones
|
||||
fk_values = ik->problem.initial_guess;
|
||||
}
|
||||
double value(const TVector& x)
|
||||
{
|
||||
// fill in active variables and compute fitness
|
||||
for(size_t i = 0; i < ik->problem.active_variables.size(); i++) fk_values[ik->problem.active_variables[i]] = x[i];
|
||||
return ik->computeFitness(fk_values);
|
||||
}
|
||||
bool callback(const cppoptlib::Criteria<double>& state, const TVector& x)
|
||||
{
|
||||
// check ik timeout
|
||||
return ros::WallTime::now().toSec() < ik->problem.timeout;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
// problem description for cppoptlib
|
||||
struct IKOptLibProblem : cppoptlib::BoundedProblem<double>
|
||||
{
|
||||
IKBase* ik;
|
||||
std::vector<double> fk_values;
|
||||
IKOptLibProblem(IKBase* ik)
|
||||
: cppoptlib::BoundedProblem<double>(TVector(ik->problem.active_variables.size()), TVector(ik->problem.active_variables.size()))
|
||||
, ik(ik)
|
||||
{
|
||||
// init bounds
|
||||
for(size_t i = 0; i < ik->problem.active_variables.size(); i++)
|
||||
{
|
||||
m_lowerBound[i] = ik->modelInfo.getMin(ik->problem.active_variables[i]);
|
||||
m_upperBound[i] = ik->modelInfo.getMax(ik->problem.active_variables[i]);
|
||||
// m_lowerBound[i] = fmax(m_lowerBound[i], -100);
|
||||
// m_upperBound[i] = fmin(m_upperBound[i], 100);
|
||||
}
|
||||
}
|
||||
void initialize()
|
||||
{
|
||||
// set all variables to initial guess, including inactive ones
|
||||
fk_values = ik->problem.initial_guess;
|
||||
}
|
||||
double value(const TVector& x)
|
||||
{
|
||||
// fill in active variables and compute fitness
|
||||
for(size_t i = 0; i < ik->problem.active_variables.size(); i++)
|
||||
fk_values[ik->problem.active_variables[i]] = x[i];
|
||||
// for(size_t i = 0; i < ik->active_variables.size(); i++) LOG(i, x[i]); LOG("");
|
||||
// for(size_t i = 0; i < ik->active_variables.size(); i++) std::cerr << ((void*)*(uint64_t*)&x[i]) << " "; std::cerr << std::endl;
|
||||
// size_t h = 0; for(size_t i = 0; i < ik->active_variables.size(); i++) h ^= (std::hash<double>()(x[i]) << i); LOG((void*)h);
|
||||
return ik->computeFitness(fk_values);
|
||||
}
|
||||
bool callback(const cppoptlib::Criteria<double>& state, const TVector& x)
|
||||
{
|
||||
// check ik timeout
|
||||
return ros::WallTime::now().toSec() < ik->problem.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
// ik solver using cppoptlib
|
||||
template <class SOLVER, int reset_if_stuck, int threads> struct IKOptLib : IKBase
|
||||
{
|
||||
// current solution
|
||||
std::vector<double> solution, best_solution, temp;
|
||||
|
||||
// cppoptlib solver
|
||||
std::shared_ptr<SOLVER> solver;
|
||||
|
||||
// cppoptlib problem description
|
||||
IKOptLibProblem f;
|
||||
|
||||
// reset flag
|
||||
bool reset;
|
||||
|
||||
// stop criteria
|
||||
cppoptlib::Criteria<double> crit;
|
||||
|
||||
IKOptLib(const IKParams& p)
|
||||
: IKBase(p)
|
||||
, f(this)
|
||||
{
|
||||
}
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
IKBase::initialize(problem);
|
||||
|
||||
// set initial guess
|
||||
solution = problem.initial_guess;
|
||||
|
||||
// randomize if more than 1 thread
|
||||
reset = false;
|
||||
if(thread_index > 0) reset = true;
|
||||
|
||||
// init best solution
|
||||
best_solution = solution;
|
||||
|
||||
// initialize cppoptlib problem description
|
||||
f = IKOptLibProblem(this);
|
||||
f.initialize();
|
||||
|
||||
// init stop criteria (timeout will be handled explicitly)
|
||||
crit = cppoptlib::Criteria<double>::defaults();
|
||||
// crit.iterations = SIZE_MAX;
|
||||
crit.gradNorm = 1e-10;
|
||||
// p.node_handle.param("optlib_stop", crit.gradNorm, crit.gradNorm);
|
||||
|
||||
if(!solver) solver = std::make_shared<SOLVER>();
|
||||
}
|
||||
|
||||
const std::vector<double>& getSolution() const { return best_solution; }
|
||||
|
||||
void step()
|
||||
{
|
||||
// set stop criteria
|
||||
solver->setStopCriteria(crit);
|
||||
|
||||
// random reset if stuck (and if random resets are enabled)
|
||||
if(reset)
|
||||
{
|
||||
// LOG("RESET");
|
||||
reset = false;
|
||||
for(auto& vi : problem.active_variables)
|
||||
solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
|
||||
}
|
||||
|
||||
// set to current positions
|
||||
temp = solution;
|
||||
typename SOLVER::TVector x(problem.active_variables.size());
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
x[i] = temp[problem.active_variables[i]];
|
||||
|
||||
// solve
|
||||
solver->minimize(f, x);
|
||||
|
||||
// get results
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
temp[problem.active_variables[i]] = x[i];
|
||||
|
||||
// update solution
|
||||
if(computeFitness(temp) < computeFitness(solution))
|
||||
{
|
||||
solution = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(reset_if_stuck) reset = true;
|
||||
}
|
||||
|
||||
// update best solution
|
||||
if(computeFitness(solution) < computeFitness(best_solution))
|
||||
{
|
||||
best_solution = solution;
|
||||
}
|
||||
}
|
||||
|
||||
size_t concurrency() const { return threads; }
|
||||
};
|
||||
}
|
||||
|
||||
static std::string mkoptname(std::string name, int reset, int threads)
|
||||
{
|
||||
name = "optlib_" + name;
|
||||
if(reset) name += "_r";
|
||||
if(threads > 1) name += "_" + std::to_string(threads);
|
||||
return name;
|
||||
}
|
||||
|
||||
#define IKCPPOPTX(n, t, reset, threads) static bio_ik::IKFactory::Class<bio_ik::IKOptLib<cppoptlib::t<bio_ik::IKOptLibProblem>, reset, threads>> ik_optlib_##t##reset##threads(mkoptname(n, reset, threads));
|
||||
|
||||
//#define IKCPPOPT(n, t) static bio_ik::IKFactory::Class<bio_ik::IKOptLib<cppoptlib::t<bio_ik::IKOptLibProblem>>> ik_optlib_##t(n)
|
||||
|
||||
#define IKCPPOPT(n, t) \
|
||||
IKCPPOPTX(n, t, 0, 1) \
|
||||
IKCPPOPTX(n, t, 0, 2) \
|
||||
IKCPPOPTX(n, t, 0, 4) \
|
||||
IKCPPOPTX(n, t, 1, 1) \
|
||||
IKCPPOPTX(n, t, 1, 2) \
|
||||
IKCPPOPTX(n, t, 1, 4)
|
||||
|
||||
#include "cppoptlib/solver/bfgssolver.h"
|
||||
IKCPPOPT("bfgs", BfgsSolver);
|
||||
|
||||
//#include "cppoptlib/solver/cmaesbsolver.h"
|
||||
// IKCPPOPT("cmaesb", CMAesBSolver);
|
||||
|
||||
//#include "cppoptlib/solver/cmaessolver.h"
|
||||
// IKCPPOPT("cmaes", CMAesSolver);
|
||||
|
||||
#include "cppoptlib/solver/conjugatedgradientdescentsolver.h"
|
||||
IKCPPOPT("cgd", ConjugatedGradientDescentSolver);
|
||||
|
||||
#include "cppoptlib/solver/gradientdescentsolver.h"
|
||||
IKCPPOPT("gd", GradientDescentSolver);
|
||||
|
||||
#include "cppoptlib/solver/lbfgsbsolver.h"
|
||||
IKCPPOPT("lbfgsb", LbfgsbSolver);
|
||||
|
||||
#include "cppoptlib/solver/lbfgssolver.h"
|
||||
IKCPPOPT("lbfgs", LbfgsSolver);
|
||||
|
||||
#include "cppoptlib/solver/neldermeadsolver.h"
|
||||
IKCPPOPT("nm", NelderMeadSolver);
|
||||
|
||||
#include "cppoptlib/solver/newtondescentsolver.h"
|
||||
IKCPPOPT("nd", NewtonDescentSolver);
|
||||
@@ -1,561 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
struct IKEvolution1 : IKBase
|
||||
{
|
||||
struct Individual
|
||||
{
|
||||
std::vector<double> genes;
|
||||
std::vector<double> gradients;
|
||||
double extinction;
|
||||
double fitness;
|
||||
};
|
||||
|
||||
class HeuristicErrorTree
|
||||
{
|
||||
size_t variable_count, tip_count;
|
||||
std::vector<double> table;
|
||||
std::vector<double> chain_lengths;
|
||||
std::vector<std::vector<double>> chain_lengths_2;
|
||||
|
||||
public:
|
||||
HeuristicErrorTree() {}
|
||||
HeuristicErrorTree(moveit::core::RobotModelConstPtr robot_model, const std::vector<std::string>& tip_names)
|
||||
{
|
||||
tip_count = tip_names.size();
|
||||
variable_count = robot_model->getVariableCount();
|
||||
table.resize(tip_count * variable_count);
|
||||
for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
|
||||
{
|
||||
auto& tip_name = tip_names[tip_index];
|
||||
for(auto* link_model = robot_model->getLinkModel(tip_name); link_model; link_model = link_model->getParentLinkModel())
|
||||
{
|
||||
auto* joint_model = link_model->getParentJointModel();
|
||||
size_t v1 = joint_model->getFirstVariableIndex();
|
||||
size_t vn = joint_model->getVariableCount();
|
||||
for(size_t variable_index = v1; variable_index < v1 + vn; variable_index++)
|
||||
table[variable_index * tip_count + tip_index] = 1;
|
||||
}
|
||||
}
|
||||
for(size_t variable_index = 0; variable_index < variable_count; variable_index++)
|
||||
{
|
||||
double sum = 0;
|
||||
for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
|
||||
sum += table[variable_index * tip_count + tip_index];
|
||||
if(sum > 0)
|
||||
for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
|
||||
table[variable_index * tip_count + tip_index] /= sum;
|
||||
}
|
||||
|
||||
chain_lengths.resize(tip_count);
|
||||
for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
|
||||
{
|
||||
auto& tip_name = tip_names[tip_index];
|
||||
double chain_length = 0;
|
||||
for(auto* link_model = robot_model->getLinkModel(tip_name); link_model; link_model = link_model->getParentLinkModel())
|
||||
{
|
||||
chain_length += Frame(link_model->getJointOriginTransform()).pos.length();
|
||||
}
|
||||
chain_lengths[tip_index] = chain_length;
|
||||
}
|
||||
|
||||
chain_lengths_2.resize(tip_count);
|
||||
for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
|
||||
{
|
||||
auto& tip_name = tip_names[tip_index];
|
||||
double chain_length = 0;
|
||||
chain_lengths_2[tip_index].resize(variable_count, 0.0);
|
||||
for(auto* link_model = robot_model->getLinkModel(tip_name); link_model; link_model = link_model->getParentLinkModel())
|
||||
{
|
||||
|
||||
auto* joint_model = link_model->getParentJointModel();
|
||||
int vmin = joint_model->getFirstVariableIndex();
|
||||
int vmax = vmin + joint_model->getVariableCount();
|
||||
for(int vi = vmin; vi < vmax; vi++)
|
||||
chain_lengths_2[tip_index][vi] = chain_length;
|
||||
chain_length += Frame(link_model->getJointOriginTransform()).pos.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
inline double getInfluence(size_t variable_index, size_t tip_index) const { return table[variable_index * tip_count + tip_index]; }
|
||||
inline double getChainLength(size_t tip_index) const { return chain_lengths[tip_index]; }
|
||||
inline double getJointVariableChainLength(size_t tip_index, size_t variable_index) const { return chain_lengths_2[tip_index][variable_index]; }
|
||||
};
|
||||
|
||||
HeuristicErrorTree heuristicErrorTree;
|
||||
std::vector<double> solution;
|
||||
std::vector<Individual> population;
|
||||
int populationSize, eliteCount;
|
||||
std::vector<Individual*> tempPool;
|
||||
std::vector<Individual> tempOffspring;
|
||||
std::vector<double> initialGuess;
|
||||
|
||||
bool opt_no_wipeout;
|
||||
|
||||
bool linear_fitness;
|
||||
|
||||
void setParams(const IKParams& p)
|
||||
{
|
||||
opt_no_wipeout = p.opt_no_wipeout;
|
||||
populationSize = p.population_size;
|
||||
eliteCount = p.elite_count;
|
||||
linear_fitness = p.linear_fitness;
|
||||
}
|
||||
|
||||
bool in_final_adjustment_loop;
|
||||
|
||||
template <class t> inline t select(const std::vector<t>& v)
|
||||
{
|
||||
// FNPROFILER();
|
||||
linear_int_distribution<size_t> d(v.size());
|
||||
size_t index = d(rng);
|
||||
return v[index];
|
||||
}
|
||||
|
||||
inline double clip(double v, size_t i) { return modelInfo.clip(v, i); }
|
||||
|
||||
inline double getMutationStrength(size_t i, const Individual& parentA, const Individual& parentB)
|
||||
{
|
||||
double extinction = 0.5 * (parentA.extinction + parentB.extinction);
|
||||
double span = modelInfo.getSpan(i);
|
||||
return span * extinction;
|
||||
}
|
||||
|
||||
double computeAngularScale(size_t tip_index, const Frame& tip_frame)
|
||||
{
|
||||
double angular_scale = sqrt(heuristicErrorTree.getChainLength(tip_index) * tip_frame.pos.length()) / M_PI;
|
||||
return angular_scale;
|
||||
// return 1;
|
||||
/*double angular_scale = sqrt(heuristicErrorTree.getChainLength(tip_index) * tip_frame.pos.length()) / M_PI;
|
||||
//double angular_scale = heuristicErrorTree.getChainLength(tip_index) * (1.0 / M_PI);
|
||||
if(opt_angular_scale_full_circle) angular_scale *= 0.5;
|
||||
return angular_scale;*/
|
||||
}
|
||||
|
||||
double getHeuristicError(size_t variable_index, bool balanced)
|
||||
{
|
||||
// return 1;
|
||||
|
||||
double heuristic_error = 0;
|
||||
// for(int tip_index = 0; tip_index < tipObjectives.size(); tip_index++)
|
||||
for(int tip_index = 0; tip_index < problem.goals.size(); tip_index++)
|
||||
{
|
||||
double influence = heuristicErrorTree.getInfluence(variable_index, tip_index);
|
||||
if(influence == 0) continue;
|
||||
|
||||
// const auto& ta = tipObjectives[tip_index];
|
||||
const auto& ta = problem.goals[tip_index].frame;
|
||||
const auto& tb = model.getTipFrame(tip_index);
|
||||
|
||||
double length = heuristicErrorTree.getJointVariableChainLength(tip_index, variable_index);
|
||||
|
||||
// LOG_ALWAYS("a", heuristicErrorTree.getJointVariableChainLength(tip_index, variable_index));
|
||||
|
||||
// double length = model.getJointVariableFrame(variable_index).pos.distance(model.getTipFrame(tip_index).pos); if(length <= 0.000000001) length = 0;
|
||||
|
||||
// LOG_ALWAYS("b", length);
|
||||
|
||||
if(modelInfo.isPrismatic(variable_index))
|
||||
{
|
||||
// heuristic_error += ta.pos.distance(tb.pos) * influence;
|
||||
// if(length) heuristic_error += ta.rot.angle(tb.rot) * length * influence;
|
||||
|
||||
if(length)
|
||||
{
|
||||
heuristic_error += ta.pos.distance(tb.pos) * influence * 0.5;
|
||||
heuristic_error += ta.rot.angle(tb.rot) * length * influence * 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
heuristic_error += ta.pos.distance(tb.pos) * influence;
|
||||
}
|
||||
}
|
||||
|
||||
if(modelInfo.isRevolute(variable_index))
|
||||
{
|
||||
// if(length) heuristic_error += ta.pos.distance(tb.pos) / length * influence;
|
||||
// heuristic_error += ta.rot.angle(tb.rot) * influence;
|
||||
|
||||
if(length)
|
||||
{
|
||||
heuristic_error += ta.pos.distance(tb.pos) / length * influence * 0.5;
|
||||
heuristic_error += ta.rot.angle(tb.rot) * influence * 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
heuristic_error += ta.rot.angle(tb.rot) * influence;
|
||||
}
|
||||
|
||||
// double d = 0.0;
|
||||
// if(length) d = std::max(d, ta.pos.distance(tb.pos) / length);
|
||||
// d = std::max(d, ta.rot.angle(tb.rot));
|
||||
// heuristic_error += d * influence;
|
||||
}
|
||||
}
|
||||
// heuristic_error *= 0.5;
|
||||
// LOG_ALWAYS(heuristic_error);
|
||||
return heuristic_error;
|
||||
}
|
||||
|
||||
bool in_adjustment_2, in_get_solution_fitness;
|
||||
|
||||
void reroll(Individual& offspring)
|
||||
{
|
||||
FNPROFILER();
|
||||
// for(size_t i = 0; i < offspring.genes.size(); i++)
|
||||
for(auto i : problem.active_variables)
|
||||
{
|
||||
offspring.genes[i] = random(modelInfo.getMin(i), modelInfo.getMax(i));
|
||||
|
||||
offspring.genes[i] = mix(offspring.genes[i], (modelInfo.getMin(i) + modelInfo.getMax(i)) * 0.5, random(0.0, 0.1));
|
||||
|
||||
offspring.gradients[i] = 0;
|
||||
}
|
||||
offspring.fitness = computeFitness(offspring.genes, false);
|
||||
}
|
||||
|
||||
double computeFitness(const std::vector<double>& genes, bool balanced)
|
||||
{
|
||||
if(linear_fitness)
|
||||
{
|
||||
model.applyConfiguration(genes);
|
||||
double fitness_sum = 0.0;
|
||||
for(size_t goal_index = 0; goal_index < problem.goals.size(); goal_index++)
|
||||
{
|
||||
const auto& ta = problem.goals[goal_index].frame;
|
||||
const auto& tb = model.getTipFrame(problem.goals[goal_index].tip_index);
|
||||
|
||||
double tdist = ta.pos.distance(tb.pos) / computeAngularScale(problem.goals[goal_index].tip_index, ta);
|
||||
double rdist = ta.rot.angle(tb.rot);
|
||||
|
||||
fitness_sum += mix(tdist, rdist, (balanced || in_final_adjustment_loop) ? 0.5 : random());
|
||||
}
|
||||
return fitness_sum;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IKBase::computeFitness(genes);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkWipeout()
|
||||
{
|
||||
FNPROFILER();
|
||||
auto& genes = population[0].genes;
|
||||
// for(size_t i = 0; i < genes.size(); i++)
|
||||
for(auto i : problem.active_variables)
|
||||
{
|
||||
double v0 = genes[i];
|
||||
double fitness = computeFitness(genes, true);
|
||||
double heuristicError = getHeuristicError(i, true);
|
||||
// double heuristicError = 0.001;
|
||||
genes[i] = modelInfo.clip(v0 + random(0, heuristicError), i);
|
||||
double incFitness = computeFitness(genes, true);
|
||||
genes[i] = modelInfo.clip(v0 - random(0, heuristicError), i);
|
||||
double decFitness = computeFitness(genes, true);
|
||||
genes[i] = v0;
|
||||
if(incFitness < fitness || decFitness < fitness)
|
||||
{
|
||||
// LOG("no wipeout");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// LOG("wipeout 1");
|
||||
return true;
|
||||
}
|
||||
|
||||
void computeExtinctions()
|
||||
{
|
||||
double min = population.front().fitness;
|
||||
double max = population.back().fitness;
|
||||
for(size_t i = 0; i < populationSize; i++)
|
||||
{
|
||||
double grading = (double)i / (double)(populationSize - 1);
|
||||
population[i].extinction = (population[i].fitness + min * (grading - 1)) / max;
|
||||
}
|
||||
}
|
||||
|
||||
bool tryUpdateSolution()
|
||||
{
|
||||
FNPROFILER();
|
||||
double solutionFitness = computeFitness(solution, true);
|
||||
double candidateFitness = computeFitness(population[0].genes, true);
|
||||
// LOG_VAR(solutionFitness);
|
||||
// LOG_VAR(candidateFitness);
|
||||
if(candidateFitness < solutionFitness)
|
||||
{
|
||||
solution = population[0].genes;
|
||||
// solution = initialGuess;
|
||||
// for(auto i : problem.active_variables)
|
||||
// solution[i] = population[0].genes[i];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double getMutationProbability(const Individual& parentA, const Individual& parentB)
|
||||
{
|
||||
double extinction = 0.5 * (parentA.extinction + parentB.extinction);
|
||||
double inverse = 1.0 / parentA.genes.size();
|
||||
return extinction * (1.0 - inverse) + inverse;
|
||||
}
|
||||
|
||||
void sortByFitness()
|
||||
{
|
||||
FNPROFILER();
|
||||
sort(population.begin(), population.end(), [](const Individual& a, const Individual& b) { return a.fitness < b.fitness; });
|
||||
}
|
||||
|
||||
double bounce(double v, int i)
|
||||
{
|
||||
double c = clip(v, i);
|
||||
v = c - (v - c) * 2;
|
||||
// v = c + c - v;
|
||||
v = clip(v, i);
|
||||
return v;
|
||||
}
|
||||
|
||||
void reproduce(Individual& offspring, const Individual& parentA, const Individual& parentB, const Individual& prototype)
|
||||
{
|
||||
FNPROFILER();
|
||||
for(size_t i = 0; i < offspring.genes.size(); i++)
|
||||
// for(auto i : problem.active_variables)
|
||||
{
|
||||
offspring.genes[i] = mix(parentA.genes[i], parentB.genes[i], random());
|
||||
offspring.genes[i] += parentA.gradients[i] * random();
|
||||
offspring.genes[i] += parentB.gradients[i] * random();
|
||||
|
||||
double storage = offspring.genes[i];
|
||||
|
||||
if(random() < getMutationProbability(parentA, parentB)) offspring.genes[i] += random(-1, 1) * getMutationStrength(i, parentA, parentB);
|
||||
// offspring.genes[i] += normal_random() * getMutationStrength(i, parentA, parentB);
|
||||
|
||||
offspring.genes[i] += mix(random() * (0.5 * (parentA.genes[i] + parentB.genes[i]) - offspring.genes[i]), random() * (prototype.genes[i] - offspring.genes[i]), random());
|
||||
|
||||
// offspring.genes[i] = clip(offspring.genes[i], i);
|
||||
|
||||
// offspring.genes[i] += fabs(offspring.genes[i] - storage) * offspring.genes[i] - (modelInfo.getMin(i) + modelInfo.getMax(i)) * 0.5;
|
||||
|
||||
// offspring.genes[i] = mix(offspring.genes[i], (modelInfo.getMin(i) + modelInfo.getMax(i)) * 0.5, random() * 0.1 * fabs(offspring.genes[i] - storage) / modelInfo.getSpan(i));
|
||||
|
||||
offspring.genes[i] = clip(offspring.genes[i], i);
|
||||
|
||||
// offspring.genes[i] = bounce(offspring.genes[i], i);
|
||||
|
||||
offspring.gradients[i] = random() * offspring.gradients[i] + offspring.genes[i] - storage;
|
||||
}
|
||||
|
||||
offspring.fitness = computeFitness(offspring.genes, false);
|
||||
}
|
||||
|
||||
void exploit(Individual& individual)
|
||||
{
|
||||
FNPROFILER();
|
||||
|
||||
double fitness_sum = 0;
|
||||
|
||||
// model.incrementalBegin(individual.genes);
|
||||
|
||||
for(auto i : problem.active_variables)
|
||||
{
|
||||
double fitness = computeFitness(individual.genes, true);
|
||||
|
||||
double heuristicError = getHeuristicError(i, true);
|
||||
double v_0 = individual.genes[i];
|
||||
|
||||
double v_inc = clip(v_0 + random(0, heuristicError), i);
|
||||
double v_dec = clip(v_0 - random(0, heuristicError), i);
|
||||
|
||||
individual.genes[i] = v_inc;
|
||||
double inc_fitness = computeFitness(individual.genes, true);
|
||||
individual.genes[i] = v_dec;
|
||||
double dec_fitness = computeFitness(individual.genes, true);
|
||||
|
||||
if(inc_fitness < fitness && inc_fitness <= dec_fitness)
|
||||
{
|
||||
individual.genes[i] = v_inc;
|
||||
individual.gradients[i] = v_0 * random() + v_inc - v_0;
|
||||
fitness_sum += inc_fitness;
|
||||
}
|
||||
else if(dec_fitness < fitness && dec_fitness <= inc_fitness)
|
||||
{
|
||||
individual.genes[i] = v_dec;
|
||||
individual.gradients[i] = v_0 * random() + v_dec - v_0;
|
||||
fitness_sum += dec_fitness;
|
||||
}
|
||||
else
|
||||
{
|
||||
individual.genes[i] = v_0;
|
||||
fitness_sum += fitness;
|
||||
}
|
||||
}
|
||||
|
||||
// model.incrementalEnd();
|
||||
|
||||
individual.fitness = fitness_sum / individual.genes.size();
|
||||
}
|
||||
|
||||
IKEvolution1(const IKParams& p)
|
||||
: IKBase(p)
|
||||
, populationSize(12)
|
||||
, eliteCount(4)
|
||||
, in_final_adjustment_loop(false)
|
||||
, in_adjustment_2(false)
|
||||
, in_get_solution_fitness(false)
|
||||
{
|
||||
setParams(p);
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
initialGuess = problem.initial_guess;
|
||||
solution = initialGuess;
|
||||
|
||||
population.resize(populationSize);
|
||||
|
||||
{
|
||||
auto& p = population[0];
|
||||
p.genes = solution;
|
||||
p.gradients.clear();
|
||||
p.gradients.resize(p.genes.size(), 0);
|
||||
p.fitness = computeFitness(p.genes, false);
|
||||
}
|
||||
|
||||
for(int i = 1; i < populationSize; i++)
|
||||
{
|
||||
auto& p = population[i];
|
||||
p.genes = solution;
|
||||
p.gradients.clear();
|
||||
p.gradients.resize(p.genes.size(), 0);
|
||||
reroll(p);
|
||||
}
|
||||
|
||||
sortByFitness();
|
||||
computeExtinctions();
|
||||
}
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
IKBase::initialize(problem);
|
||||
|
||||
std::vector<std::string> tips;
|
||||
for(auto tip_link_index : problem.tip_link_indices)
|
||||
tips.push_back(params.robot_model->getLinkModelNames()[tip_link_index]);
|
||||
heuristicErrorTree = HeuristicErrorTree(params.robot_model, tips);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
const std::vector<double>& getSolution() const { return solution; }
|
||||
|
||||
double getSolutionFitness()
|
||||
{
|
||||
in_get_solution_fitness = true;
|
||||
double f = computeFitness(solution, true);
|
||||
in_get_solution_fitness = false;
|
||||
return f;
|
||||
}
|
||||
|
||||
const std::vector<Frame>& getSolutionTipFrames()
|
||||
{
|
||||
model.applyConfiguration(solution);
|
||||
return model.getTipFrames();
|
||||
}
|
||||
|
||||
bool evolve()
|
||||
{
|
||||
FNPROFILER();
|
||||
|
||||
auto& offspring = tempOffspring;
|
||||
offspring = population;
|
||||
|
||||
for(size_t i = 0; i < eliteCount; i++)
|
||||
{
|
||||
offspring[i] = population[i];
|
||||
exploit(offspring[i]);
|
||||
}
|
||||
|
||||
auto& pool = tempPool;
|
||||
pool.resize(populationSize);
|
||||
iota(pool.begin(), pool.end(), &population[0]);
|
||||
|
||||
for(size_t i = eliteCount; i < populationSize; i++)
|
||||
{
|
||||
if(pool.size() > 0)
|
||||
{
|
||||
auto& parentA = *select(pool);
|
||||
auto& parentB = *select(pool);
|
||||
auto& prototype = *select(pool);
|
||||
reproduce(offspring[i], parentA, parentB, prototype);
|
||||
if(offspring[i].fitness < parentA.fitness) pool.erase(remove(pool.begin(), pool.end(), &parentA), pool.end());
|
||||
if(offspring[i].fitness < parentB.fitness) pool.erase(remove(pool.begin(), pool.end(), &parentB), pool.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
reroll(offspring[i]);
|
||||
}
|
||||
}
|
||||
|
||||
population = offspring;
|
||||
|
||||
sortByFitness();
|
||||
|
||||
computeExtinctions();
|
||||
|
||||
if(tryUpdateSolution()) return true;
|
||||
if(opt_no_wipeout) return false;
|
||||
if(!checkWipeout()) return false;
|
||||
|
||||
init();
|
||||
|
||||
return tryUpdateSolution();
|
||||
}
|
||||
|
||||
void step()
|
||||
{
|
||||
in_adjustment_2 = false;
|
||||
evolve();
|
||||
}
|
||||
|
||||
virtual size_t concurrency() const { return 4; }
|
||||
};
|
||||
|
||||
static IKFactory::Class<IKEvolution1> cIKEvolution1("bio1");
|
||||
|
||||
}
|
||||
@@ -1,659 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
#ifdef ENABLE_CPP_OPTLIB
|
||||
#include "cppoptlib/solver/lbfgssolver.h"
|
||||
#endif
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
// fast evolutionary inverse kinematics
|
||||
template <int memetic> struct IKEvolution2 : IKBase
|
||||
{
|
||||
struct Individual
|
||||
{
|
||||
aligned_vector<double> genes;
|
||||
aligned_vector<double> gradients;
|
||||
double fitness;
|
||||
};
|
||||
|
||||
struct Species
|
||||
{
|
||||
std::vector<Individual> individuals;
|
||||
double fitness;
|
||||
bool improved;
|
||||
};
|
||||
|
||||
std::vector<double> initial_guess;
|
||||
std::vector<double> solution;
|
||||
std::vector<double> temp_joint_variables;
|
||||
double solution_fitness;
|
||||
std::vector<Species> species;
|
||||
std::vector<Individual> children;
|
||||
std::vector<aligned_vector<Frame>> phenotypes, phenotypes2, phenotypes3;
|
||||
std::vector<size_t> child_indices;
|
||||
std::vector<double*> genotypes;
|
||||
std::vector<Frame> phenotype;
|
||||
std::vector<size_t> quaternion_genes;
|
||||
aligned_vector<double> genes_min, genes_max, genes_span;
|
||||
aligned_vector<double> gradient, temp;
|
||||
|
||||
IKEvolution2(const IKParams& p)
|
||||
: IKBase(p)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CPP_OPTLIB
|
||||
struct OptlibProblem : cppoptlib::Problem<double>
|
||||
{
|
||||
IKEvolution2* ik;
|
||||
OptlibProblem(IKEvolution2* ik)
|
||||
: ik(ik)
|
||||
{
|
||||
}
|
||||
double value(const TVector& x)
|
||||
{
|
||||
const double* genes = x.data();
|
||||
ik->model.computeApproximateMutations(1, &genes, ik->phenotypes);
|
||||
return ik->computeCombinedFitnessActiveVariables(ik->phenotypes[0], genes);
|
||||
}
|
||||
};
|
||||
typedef cppoptlib::LbfgsSolver<OptlibProblem> OptlibSolver;
|
||||
std::shared_ptr<OptlibSolver> optlib_solver;
|
||||
std::shared_ptr<OptlibProblem> optlib_problem;
|
||||
typename OptlibSolver::TVector optlib_vector;
|
||||
#endif
|
||||
|
||||
void genesToJointVariables(const Individual& individual, std::vector<double>& variables)
|
||||
{
|
||||
auto& genes = individual.genes;
|
||||
variables.resize(params.robot_model->getVariableCount());
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
variables[problem.active_variables[i]] = genes[i];
|
||||
}
|
||||
|
||||
const std::vector<double>& getSolution() const { return solution; }
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
BLOCKPROFILER("initialization");
|
||||
|
||||
IKBase::initialize(problem);
|
||||
|
||||
// init list of quaternion joint genes to be normalized during each mutation
|
||||
quaternion_genes.clear();
|
||||
for(size_t igene = 0; igene < problem.active_variables.size(); igene++)
|
||||
{
|
||||
size_t ivar = problem.active_variables[igene];
|
||||
auto* joint_model = params.robot_model->getJointOfVariable(ivar);
|
||||
if(joint_model->getFirstVariableIndex() + 3 != ivar) continue;
|
||||
if(joint_model->getType() != moveit::core::JointModel::FLOATING) continue;
|
||||
quaternion_genes.push_back(igene);
|
||||
}
|
||||
|
||||
// set solution to initial guess
|
||||
initial_guess = problem.initial_guess;
|
||||
solution = initial_guess;
|
||||
solution_fitness = computeFitness(solution);
|
||||
|
||||
// init temporary buffer with positions of inactive joints
|
||||
temp_joint_variables = initial_guess;
|
||||
|
||||
// params
|
||||
size_t population_size = 2;
|
||||
size_t child_count = 16;
|
||||
|
||||
// initialize population on current island
|
||||
species.resize(2);
|
||||
for(auto& s : species)
|
||||
{
|
||||
// create individuals
|
||||
s.individuals.resize(population_size);
|
||||
|
||||
// initialize first individual
|
||||
{
|
||||
auto& v = s.individuals[0];
|
||||
|
||||
// init genes
|
||||
v.genes.resize(problem.active_variables.size());
|
||||
// if(thread_index == 0) // on first island?
|
||||
// if(thread_index % 2 == 0) // on every second island...
|
||||
if(1)
|
||||
{
|
||||
// set to initial_guess
|
||||
for(size_t i = 0; i < v.genes.size(); i++)
|
||||
v.genes[i] = initial_guess[problem.active_variables[i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize populations on other islands randomly
|
||||
for(size_t i = 0; i < v.genes.size(); i++)
|
||||
v.genes[i] = random(modelInfo.getMin(problem.active_variables[i]), modelInfo.getMax(problem.active_variables[i]));
|
||||
}
|
||||
|
||||
// set gradients to zero
|
||||
v.gradients.clear();
|
||||
v.gradients.resize(problem.active_variables.size(), 0);
|
||||
}
|
||||
|
||||
// clone first individual
|
||||
for(size_t i = 1; i < s.individuals.size(); i++)
|
||||
{
|
||||
s.individuals[i].genes = s.individuals[0].genes;
|
||||
s.individuals[i].gradients = s.individuals[0].gradients;
|
||||
}
|
||||
}
|
||||
|
||||
// space for child population
|
||||
children.resize(population_size + child_count);
|
||||
for(auto& child : children)
|
||||
{
|
||||
child.genes.resize(problem.active_variables.size());
|
||||
child.gradients.resize(problem.active_variables.size());
|
||||
}
|
||||
|
||||
// init gene infos
|
||||
// if(genes_min.empty())
|
||||
{
|
||||
genes_min.resize(problem.active_variables.size());
|
||||
genes_max.resize(problem.active_variables.size());
|
||||
genes_span.resize(problem.active_variables.size());
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
{
|
||||
genes_min[i] = modelInfo.getClipMin(problem.active_variables[i]);
|
||||
genes_max[i] = modelInfo.getClipMax(problem.active_variables[i]);
|
||||
genes_span[i] = modelInfo.getSpan(problem.active_variables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// init chain mutation masks
|
||||
chain_mutation_masks.resize(chain_mutation_mask_count);
|
||||
for(auto& chain_mutation_mask : chain_mutation_masks)
|
||||
{
|
||||
temp_mutation_chain.clear();
|
||||
if(problem.tip_link_indices.size() > 1)
|
||||
{
|
||||
for(auto* chain_link = params.robot_model->getLinkModel(random_element(problem.tip_link_indices)); chain_link; chain_link = chain_link->getParentLinkModel())
|
||||
temp_mutation_chain.push_back(chain_link);
|
||||
temp_mutation_chain.resize(random_index(temp_mutation_chain.size()) + 1);
|
||||
}
|
||||
|
||||
temp_chain_mutation_var_mask.resize(params.robot_model->getVariableCount());
|
||||
for(auto& m : temp_chain_mutation_var_mask) m = 0;
|
||||
for(auto* chain_link : temp_mutation_chain)
|
||||
{
|
||||
auto* chain_joint = chain_link->getParentJointModel();
|
||||
for(size_t ivar = chain_joint->getFirstVariableIndex(); ivar < chain_joint->getFirstVariableIndex() + chain_joint->getVariableCount(); ivar++)
|
||||
temp_chain_mutation_var_mask[ivar] = 1;
|
||||
}
|
||||
|
||||
chain_mutation_mask.resize(problem.active_variables.size());
|
||||
for(size_t igene = 0; igene < problem.active_variables.size(); igene++)
|
||||
chain_mutation_mask[igene] = temp_chain_mutation_var_mask[problem.active_variables[igene]];
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
const size_t chain_mutation_mask_count = 256;
|
||||
std::vector<std::vector<int>> chain_mutation_masks;
|
||||
std::vector<const moveit::core::LinkModel*> temp_mutation_chain;
|
||||
std::vector<int> temp_chain_mutation_var_mask;
|
||||
*/
|
||||
|
||||
// aligned_vector<double> rmask;
|
||||
|
||||
// create offspring and mutate
|
||||
__attribute__((hot)) __attribute__((noinline))
|
||||
//__attribute__((target_clones("avx2", "avx", "sse2", "default")))
|
||||
//__attribute__((target("avx")))
|
||||
void
|
||||
reproduce(const std::vector<Individual>& population)
|
||||
{
|
||||
const auto __attribute__((aligned(32)))* __restrict__ genes_span = this->genes_span.data();
|
||||
const auto __attribute__((aligned(32)))* __restrict__ genes_min = this->genes_min.data();
|
||||
const auto __attribute__((aligned(32)))* __restrict__ genes_max = this->genes_max.data();
|
||||
|
||||
auto gene_count = children[0].genes.size();
|
||||
|
||||
size_t s = (children.size() - population.size()) * gene_count + children.size() * 4 + 4;
|
||||
|
||||
auto* __restrict__ rr = fast_random_gauss_n(s);
|
||||
rr = (const double*)(((size_t)rr + 3) / 4 * 4);
|
||||
|
||||
/*rmask.resize(s);
|
||||
for(auto& m : rmask) m = fast_random() < 0.1 ? 1.0 : 0.0;
|
||||
double* dm = rmask.data();*/
|
||||
|
||||
for(size_t child_index = population.size(); child_index < children.size(); child_index++)
|
||||
{
|
||||
double mutation_rate = (1 << fast_random_index(16)) * (1.0 / (1 << 23));
|
||||
auto& parent = population[0];
|
||||
auto& parent2 = population[1];
|
||||
double fmix = (child_index % 2 == 0) * 0.2;
|
||||
double gradient_factor = child_index % 3;
|
||||
|
||||
auto __attribute__((aligned(32)))* __restrict__ parent_genes = parent.genes.data();
|
||||
auto __attribute__((aligned(32)))* __restrict__ parent_gradients = parent.gradients.data();
|
||||
|
||||
auto __attribute__((aligned(32)))* __restrict__ parent2_genes = parent2.genes.data();
|
||||
auto __attribute__((aligned(32)))* __restrict__ parent2_gradients = parent2.gradients.data();
|
||||
|
||||
auto& child = children[child_index];
|
||||
|
||||
auto __attribute__((aligned(32)))* __restrict__ child_genes = child.genes.data();
|
||||
auto __attribute__((aligned(32)))* __restrict__ child_gradients = child.gradients.data();
|
||||
|
||||
#pragma unroll
|
||||
#pragma omp simd aligned(genes_span : 32), aligned(genes_min : 32), aligned(genes_max : 32), aligned(parent_genes : 32), aligned(parent_gradients : 32), aligned(parent2_genes : 32), aligned(parent2_gradients : 32), aligned(child_genes : 32), aligned(child_gradients : 32) aligned(rr : 32)
|
||||
for(size_t gene_index = 0; gene_index < gene_count; gene_index++)
|
||||
{
|
||||
// double mutation_rate = (1 << fast_random_index(16)) * (1.0 / (1 << 23));
|
||||
|
||||
double r = rr[gene_index];
|
||||
// r *= dm[gene_index];
|
||||
double f = mutation_rate * genes_span[gene_index];
|
||||
double gene = parent_genes[gene_index];
|
||||
double parent_gene = gene;
|
||||
gene += r * f;
|
||||
double parent_gradient = mix(parent_gradients[gene_index], parent2_gradients[gene_index], fmix);
|
||||
double gradient = parent_gradient * gradient_factor;
|
||||
gene += gradient;
|
||||
gene = clamp(gene, genes_min[gene_index], genes_max[gene_index]);
|
||||
child_genes[gene_index] = gene;
|
||||
child_gradients[gene_index] = mix(parent_gradient, gene - parent_gene, 0.3);
|
||||
}
|
||||
rr += (gene_count + 3) / 4 * 4;
|
||||
// dm += (gene_count + 3) / 4 * 4;
|
||||
|
||||
/*if(problem.tip_link_indices.size() > 1)
|
||||
{
|
||||
if(fast_random() < 0.5)
|
||||
{
|
||||
auto& mask = chain_mutation_masks[fast_random_index(chain_mutation_mask_count)];
|
||||
for(size_t gene_index = 0; gene_index < gene_count; gene_index++)
|
||||
{
|
||||
if(!mask[gene_index])
|
||||
{
|
||||
child_genes[gene_index] = parent_genes[gene_index];
|
||||
child_gradients[gene_index] = parent_gradients[gene_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
for(auto quaternion_gene_index : quaternion_genes)
|
||||
{
|
||||
auto& qpos = (*(Quaternion*)&(children[child_index].genes[quaternion_gene_index]));
|
||||
normalizeFast(qpos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void step()
|
||||
{
|
||||
FNPROFILER();
|
||||
|
||||
for(size_t ispecies = 0; ispecies < species.size(); ispecies++)
|
||||
{
|
||||
auto& species = this->species[ispecies];
|
||||
auto& population = species.individuals;
|
||||
|
||||
{
|
||||
BLOCKPROFILER("evolution");
|
||||
|
||||
// initialize forward kinematics approximator
|
||||
genesToJointVariables(species.individuals[0], temp_joint_variables);
|
||||
{
|
||||
BLOCKPROFILER("fk");
|
||||
model.applyConfiguration(temp_joint_variables);
|
||||
model.initializeMutationApproximator(problem.active_variables);
|
||||
}
|
||||
|
||||
// run evolution for a few generations
|
||||
size_t generation_count = 16;
|
||||
if(memetic) generation_count = 8;
|
||||
for(size_t generation = 0; generation < generation_count; generation++)
|
||||
{
|
||||
// BLOCKPROFILER("evolution");
|
||||
|
||||
if(canceled) break;
|
||||
|
||||
// reproduction
|
||||
{
|
||||
BLOCKPROFILER("reproduction");
|
||||
reproduce(population);
|
||||
}
|
||||
|
||||
size_t child_count = children.size();
|
||||
|
||||
// pre-selection by secondary objectives
|
||||
if(problem.secondary_goals.size())
|
||||
{
|
||||
BLOCKPROFILER("pre-selection");
|
||||
child_count = random_index(children.size() - population.size() - 1) + 1 + population.size();
|
||||
for(size_t child_index = population.size(); child_index < children.size(); child_index++)
|
||||
{
|
||||
children[child_index].fitness = computeSecondaryFitnessActiveVariables(children[child_index].genes.data());
|
||||
}
|
||||
{
|
||||
BLOCKPROFILER("pre-selection sort");
|
||||
std::sort(children.begin() + population.size(), children.end(), [](const Individual& a, const Individual& b) { return a.fitness < b.fitness; });
|
||||
}
|
||||
}
|
||||
|
||||
// keep parents
|
||||
{
|
||||
BLOCKPROFILER("keep alive");
|
||||
for(size_t i = 0; i < population.size(); i++)
|
||||
{
|
||||
children[i].genes = population[i].genes;
|
||||
children[i].gradients = population[i].gradients;
|
||||
}
|
||||
}
|
||||
|
||||
// genotype-phenotype mapping
|
||||
{
|
||||
BLOCKPROFILER("phenotype");
|
||||
size_t gene_count = children[0].genes.size();
|
||||
genotypes.resize(child_count);
|
||||
for(size_t i = 0; i < child_count; i++)
|
||||
genotypes[i] = children[i].genes.data();
|
||||
model.computeApproximateMutations(child_count, genotypes.data(), phenotypes);
|
||||
}
|
||||
|
||||
// fitness
|
||||
{
|
||||
BLOCKPROFILER("fitness");
|
||||
for(size_t child_index = 0; child_index < child_count; child_index++)
|
||||
{
|
||||
children[child_index].fitness = computeFitnessActiveVariables(phenotypes[child_index], genotypes[child_index]);
|
||||
}
|
||||
}
|
||||
|
||||
// selection
|
||||
{
|
||||
BLOCKPROFILER("selection");
|
||||
child_indices.resize(child_count);
|
||||
for(size_t i = 0; i < child_count; i++)
|
||||
child_indices[i] = i;
|
||||
for(size_t i = 0; i < population.size(); i++)
|
||||
{
|
||||
size_t jmin = i;
|
||||
double fmin = children[child_indices[i]].fitness;
|
||||
for(size_t j = i + 1; j < child_count; j++)
|
||||
{
|
||||
double f = children[child_indices[j]].fitness;
|
||||
if(f < fmin) jmin = j, fmin = f;
|
||||
}
|
||||
std::swap(child_indices[i], child_indices[jmin]);
|
||||
}
|
||||
for(size_t i = 0; i < population.size(); i++)
|
||||
{
|
||||
std::swap(population[i].genes, children[child_indices[i]].genes);
|
||||
std::swap(population[i].gradients, children[child_indices[i]].gradients);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// memetic optimization
|
||||
{
|
||||
BLOCKPROFILER("memetics");
|
||||
|
||||
if(memetic == 'q' || memetic == 'l')
|
||||
{
|
||||
|
||||
// init
|
||||
auto& individual = population[0];
|
||||
gradient.resize(problem.active_variables.size());
|
||||
if(genotypes.empty()) genotypes.emplace_back();
|
||||
phenotypes2.resize(1);
|
||||
phenotypes3.resize(1);
|
||||
|
||||
// differentiation step size
|
||||
double dp = 0.0000001;
|
||||
if(fast_random() < 0.5) dp = -dp;
|
||||
|
||||
for(size_t generation = 0; generation < 8; generation++)
|
||||
// for(size_t generation = 0; generation < 32; generation++)
|
||||
{
|
||||
|
||||
if(canceled) break;
|
||||
|
||||
// compute gradient
|
||||
temp = individual.genes;
|
||||
genotypes[0] = temp.data();
|
||||
model.computeApproximateMutations(1, genotypes.data(), phenotypes2);
|
||||
double f2p = computeFitnessActiveVariables(phenotypes2[0], genotypes[0]);
|
||||
double fa = f2p + computeSecondaryFitnessActiveVariables(genotypes[0]);
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
{
|
||||
double* pp = &(genotypes[0][i]);
|
||||
genotypes[0][i] = individual.genes[i] + dp;
|
||||
model.computeApproximateMutation1(problem.active_variables[i], +dp, phenotypes2[0], phenotypes3[0]);
|
||||
double fb = computeCombinedFitnessActiveVariables(phenotypes3[0], genotypes[0]);
|
||||
genotypes[0][i] = individual.genes[i];
|
||||
double d = fb - fa;
|
||||
gradient[i] = d;
|
||||
}
|
||||
|
||||
// normalize gradient
|
||||
double sum = dp * dp;
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
sum += fabs(gradient[i]);
|
||||
double f = 1.0 / sum * dp;
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
gradient[i] *= f;
|
||||
|
||||
// sample support points for line search
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
genotypes[0][i] = individual.genes[i] - gradient[i];
|
||||
model.computeApproximateMutations(1, genotypes.data(), phenotypes3);
|
||||
double f1 = computeCombinedFitnessActiveVariables(phenotypes3[0], genotypes[0]);
|
||||
|
||||
double f2 = fa;
|
||||
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
genotypes[0][i] = individual.genes[i] + gradient[i];
|
||||
model.computeApproximateMutations(1, genotypes.data(), phenotypes3);
|
||||
double f3 = computeCombinedFitnessActiveVariables(phenotypes3[0], genotypes[0]);
|
||||
|
||||
// quadratic step size
|
||||
if(memetic == 'q')
|
||||
{
|
||||
|
||||
// compute step size
|
||||
double v1 = (f2 - f1); // f / j
|
||||
double v2 = (f3 - f2); // f / j
|
||||
double v = (v1 + v2) * 0.5; // f / j
|
||||
double a = (v1 - v2); // f / j^2
|
||||
double step_size = v / a; // (f / j) / (f / j^2) = f / j / f * j * j = j
|
||||
|
||||
// double v1 = (f2 - f1) / dp;
|
||||
// double v2 = (f3 - f2) / dp;
|
||||
// double v = (v1 + v2) * 0.5;
|
||||
// double a = (v2 - v1) / dp;
|
||||
// // v * x + a * x * x = 0;
|
||||
// // v = - a * x
|
||||
// // - v / a = x
|
||||
// // x = -v / a;
|
||||
// double step_size = -v / a / dp;
|
||||
|
||||
// for(double f : { 1.0, 0.5, 0.25 })
|
||||
{
|
||||
|
||||
double f = 1.0;
|
||||
|
||||
// move by step size along gradient and compute fitness
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
genotypes[0][i] = modelInfo.clip(individual.genes[i] + gradient[i] * step_size * f, problem.active_variables[i]);
|
||||
model.computeApproximateMutations(1, genotypes.data(), phenotypes2);
|
||||
double f4p = computeFitnessActiveVariables(phenotypes2[0], genotypes[0]);
|
||||
|
||||
// accept new position if better
|
||||
if(f4p < f2p)
|
||||
{
|
||||
individual.genes = temp;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// break;
|
||||
}
|
||||
|
||||
// linear step size
|
||||
if(memetic == 'l')
|
||||
{
|
||||
|
||||
// compute step size
|
||||
double cost_diff = (f3 - f1) * 0.5; // f / j
|
||||
double step_size = f2 / cost_diff; // f / (f / j) = j
|
||||
|
||||
// move by step size along gradient and compute fitness
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
temp[i] = modelInfo.clip(individual.genes[i] - gradient[i] * step_size, problem.active_variables[i]);
|
||||
model.computeApproximateMutations(1, genotypes.data(), phenotypes2);
|
||||
double f4p = computeFitnessActiveVariables(phenotypes2[0], genotypes[0]);
|
||||
|
||||
// accept new position if better
|
||||
if(f4p < f2p)
|
||||
{
|
||||
individual.genes = temp;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CPP_OPTLIB
|
||||
// cppoptlib::LbfgsSolver memetic test
|
||||
if(memetic == 'o')
|
||||
{
|
||||
auto& individual = population[0];
|
||||
|
||||
// create cppoptlib solver and cppoptlib problem, if not yet existing
|
||||
if(!optlib_solver)
|
||||
{
|
||||
optlib_solver = std::make_shared<OptlibSolver>();
|
||||
cppoptlib::Criteria<double> crit;
|
||||
crit.iterations = 4;
|
||||
optlib_solver->setStopCriteria(crit);
|
||||
optlib_problem = std::make_shared<OptlibProblem>(this);
|
||||
}
|
||||
|
||||
// init starting point
|
||||
optlib_vector.resize(problem.active_variables.size());
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
optlib_vector[i] = individual.genes[i];
|
||||
|
||||
// solve
|
||||
optlib_solver->minimize(*optlib_problem, optlib_vector);
|
||||
|
||||
// get result
|
||||
for(size_t i = 0; i < problem.active_variables.size(); i++)
|
||||
individual.genes[i] = modelInfo.clip(optlib_vector[i], problem.active_variables[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
BLOCKPROFILER("species");
|
||||
|
||||
// compute species fitness
|
||||
for(auto& species : this->species)
|
||||
{
|
||||
genesToJointVariables(species.individuals[0], temp_joint_variables);
|
||||
double fitness = computeFitness(temp_joint_variables);
|
||||
species.improved = (fitness != species.fitness);
|
||||
species.fitness = fitness;
|
||||
}
|
||||
|
||||
// sort species by fitness
|
||||
std::sort(species.begin(), species.end(), [](const Species& a, const Species& b) { return a.fitness < b.fitness; });
|
||||
|
||||
// wipeouts
|
||||
for(size_t species_index = 1; species_index < species.size(); species_index++)
|
||||
{
|
||||
if(fast_random() < 0.1 || !species[species_index].improved)
|
||||
// if(fast_random() < 0.05 || !species[species_index].improved)
|
||||
{
|
||||
{
|
||||
auto& individual = species[species_index].individuals[0];
|
||||
|
||||
for(size_t i = 0; i < individual.genes.size(); i++)
|
||||
individual.genes[i] = random(modelInfo.getMin(problem.active_variables[i]), modelInfo.getMax(problem.active_variables[i]));
|
||||
|
||||
for(auto& v : individual.gradients)
|
||||
v = 0;
|
||||
}
|
||||
for(size_t i = 0; i < species[species_index].individuals.size(); i++)
|
||||
species[species_index].individuals[i] = species[species_index].individuals[0];
|
||||
}
|
||||
}
|
||||
|
||||
// update solution
|
||||
if(species[0].fitness < solution_fitness)
|
||||
{
|
||||
genesToJointVariables(species[0].individuals[0], solution);
|
||||
solution_fitness = species[0].fitness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// number of islands
|
||||
virtual size_t concurrency() const { return 4; }
|
||||
};
|
||||
|
||||
static IKFactory::Class<IKEvolution2<0>> bio2("bio2");
|
||||
static IKFactory::Class<IKEvolution2<'q'>> bio2_memetic("bio2_memetic");
|
||||
static IKFactory::Class<IKEvolution2<'l'>> bio2_memetic_l("bio2_memetic_l");
|
||||
|
||||
#ifdef ENABLE_CPP_OPTLIB
|
||||
static IKFactory::Class<IKEvolution2<'o'>> bio2_memetic_0("bio2_memetic_lbfgs");
|
||||
#endif
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
// pseudoinverse jacobian solver
|
||||
// (mainly for testing RobotFK_Jacobian::computeJacobian)
|
||||
template <class BASE> struct IKJacobianBase : BASE
|
||||
{
|
||||
using BASE::modelInfo;
|
||||
using BASE::model;
|
||||
using BASE::params;
|
||||
using BASE::computeFitness;
|
||||
using BASE::problem;
|
||||
|
||||
std::vector<Frame> tipObjectives;
|
||||
|
||||
Eigen::VectorXd tip_diffs;
|
||||
Eigen::VectorXd joint_diffs;
|
||||
Eigen::MatrixXd jacobian;
|
||||
std::vector<Frame> tip_frames_temp;
|
||||
|
||||
IKJacobianBase(const IKParams& p)
|
||||
: BASE(p)
|
||||
{
|
||||
}
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
BASE::initialize(problem);
|
||||
tipObjectives.resize(problem.tip_link_indices.size());
|
||||
for(auto& goal : problem.goals)
|
||||
tipObjectives[goal.tip_index] = goal.frame;
|
||||
}
|
||||
|
||||
void optimizeJacobian(std::vector<double>& solution)
|
||||
{
|
||||
FNPROFILER();
|
||||
|
||||
int tip_count = problem.tip_link_indices.size();
|
||||
tip_diffs.resize(tip_count * 6);
|
||||
joint_diffs.resize(problem.active_variables.size());
|
||||
|
||||
// compute fk
|
||||
model.applyConfiguration(solution);
|
||||
|
||||
double translational_scale = 1;
|
||||
double rotational_scale = 1;
|
||||
|
||||
// compute goal diffs
|
||||
tip_frames_temp = model.getTipFrames();
|
||||
for(int itip = 0; itip < tip_count; itip++)
|
||||
{
|
||||
auto twist = frameTwist(tip_frames_temp[itip], tipObjectives[itip]);
|
||||
tip_diffs(itip * 6 + 0) = twist.vel.x() * translational_scale;
|
||||
tip_diffs(itip * 6 + 1) = twist.vel.y() * translational_scale;
|
||||
tip_diffs(itip * 6 + 2) = twist.vel.z() * translational_scale;
|
||||
tip_diffs(itip * 6 + 3) = twist.rot.x() * rotational_scale;
|
||||
tip_diffs(itip * 6 + 4) = twist.rot.y() * rotational_scale;
|
||||
tip_diffs(itip * 6 + 5) = twist.rot.z() * rotational_scale;
|
||||
}
|
||||
|
||||
// compute jacobian
|
||||
{
|
||||
model.computeJacobian(problem.active_variables, jacobian);
|
||||
int icol = 0;
|
||||
for(auto ivar : problem.active_variables)
|
||||
{
|
||||
for(size_t itip = 0; itip < tip_count; itip++)
|
||||
{
|
||||
jacobian(itip * 6 + 0, icol) *= translational_scale;
|
||||
jacobian(itip * 6 + 1, icol) *= translational_scale;
|
||||
jacobian(itip * 6 + 2, icol) *= translational_scale;
|
||||
jacobian(itip * 6 + 3, icol) *= rotational_scale;
|
||||
jacobian(itip * 6 + 4, icol) *= rotational_scale;
|
||||
jacobian(itip * 6 + 5, icol) *= rotational_scale;
|
||||
}
|
||||
icol++;
|
||||
}
|
||||
}
|
||||
|
||||
// get pseudoinverse and multiply
|
||||
joint_diffs = jacobian.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(tip_diffs);
|
||||
// joint_diffs = (jacobian.transpose() * jacobian).ldlt().solve(jacobian.transpose() * tip_diffs);
|
||||
|
||||
// apply joint deltas and clip
|
||||
{
|
||||
int icol = 0;
|
||||
for(auto ivar : problem.active_variables)
|
||||
{
|
||||
auto v = solution[ivar] + joint_diffs(icol);
|
||||
if(!std::isfinite(v)) continue;
|
||||
v = modelInfo.clip(v, ivar);
|
||||
solution[ivar] = v;
|
||||
icol++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// simple gradient descent
|
||||
template <int if_stuck, size_t threads> struct IKGradientDescent : IKBase
|
||||
{
|
||||
std::vector<double> solution, best_solution, gradient, temp;
|
||||
bool reset;
|
||||
|
||||
IKGradientDescent(const IKParams& p)
|
||||
: IKBase(p)
|
||||
{
|
||||
}
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
IKBase::initialize(problem);
|
||||
solution = problem.initial_guess;
|
||||
if(thread_index > 0)
|
||||
for(auto& vi : problem.active_variables)
|
||||
solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
|
||||
best_solution = solution;
|
||||
reset = false;
|
||||
}
|
||||
|
||||
const std::vector<double>& getSolution() const { return best_solution; }
|
||||
|
||||
void step()
|
||||
{
|
||||
// random reset if stuck
|
||||
if(reset)
|
||||
{
|
||||
reset = false;
|
||||
for(auto& vi : problem.active_variables)
|
||||
solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
|
||||
}
|
||||
|
||||
// compute gradient direction
|
||||
temp = solution;
|
||||
double jd = 0.0001;
|
||||
gradient.resize(solution.size(), 0);
|
||||
for(auto ivar : problem.active_variables)
|
||||
{
|
||||
temp[ivar] = solution[ivar] - jd;
|
||||
double p1 = computeFitness(temp);
|
||||
|
||||
temp[ivar] = solution[ivar] + jd;
|
||||
double p3 = computeFitness(temp);
|
||||
|
||||
temp[ivar] = solution[ivar];
|
||||
|
||||
gradient[ivar] = p3 - p1;
|
||||
}
|
||||
|
||||
// normalize gradient direction
|
||||
double sum = 0.0001;
|
||||
for(auto ivar : problem.active_variables)
|
||||
sum += fabs(gradient[ivar]);
|
||||
double f = 1.0 / sum * jd;
|
||||
for(auto ivar : problem.active_variables)
|
||||
gradient[ivar] *= f;
|
||||
|
||||
// initialize line search
|
||||
temp = solution;
|
||||
|
||||
for(auto ivar : problem.active_variables)
|
||||
temp[ivar] = solution[ivar] - gradient[ivar];
|
||||
double p1 = computeFitness(temp);
|
||||
|
||||
// for(auto ivar : problem.active_variables) temp[ivar] = solution[ivar];
|
||||
// double p2 = computeFitness(temp);
|
||||
|
||||
for(auto ivar : problem.active_variables)
|
||||
temp[ivar] = solution[ivar] + gradient[ivar];
|
||||
double p3 = computeFitness(temp);
|
||||
|
||||
double p2 = (p1 + p3) * 0.5;
|
||||
|
||||
// linear step size estimation
|
||||
double cost_diff = (p3 - p1) * 0.5;
|
||||
double joint_diff = p2 / cost_diff;
|
||||
|
||||
// apply optimization step
|
||||
// (move along gradient direction by estimated step size)
|
||||
for(auto ivar : problem.active_variables)
|
||||
temp[ivar] = modelInfo.clip(solution[ivar] - gradient[ivar] * joint_diff, ivar);
|
||||
|
||||
if(if_stuck == 'c')
|
||||
{
|
||||
// always accept solution and continue
|
||||
solution = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// has solution improved?
|
||||
if(computeFitness(temp) < computeFitness(solution))
|
||||
{
|
||||
// solution improved -> accept solution
|
||||
solution = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(if_stuck == 'r')
|
||||
{
|
||||
// reset if stuck
|
||||
reset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update best solution
|
||||
if(computeFitness(solution) < computeFitness(best_solution)) best_solution = solution;
|
||||
}
|
||||
|
||||
size_t concurrency() const { return threads; }
|
||||
};
|
||||
|
||||
static IKFactory::Class<IKGradientDescent<' ', 1>> gd("gd");
|
||||
static IKFactory::Class<IKGradientDescent<' ', 2>> gd_2("gd_2");
|
||||
static IKFactory::Class<IKGradientDescent<' ', 4>> gd_4("gd_4");
|
||||
static IKFactory::Class<IKGradientDescent<' ', 8>> gd_8("gd_8");
|
||||
|
||||
static IKFactory::Class<IKGradientDescent<'r', 1>> gd_r("gd_r");
|
||||
static IKFactory::Class<IKGradientDescent<'r', 2>> gd_2_r("gd_r_2");
|
||||
static IKFactory::Class<IKGradientDescent<'r', 4>> gd_4_r("gd_r_4");
|
||||
static IKFactory::Class<IKGradientDescent<'r', 8>> gd_8_r("gd_r_8");
|
||||
|
||||
static IKFactory::Class<IKGradientDescent<'c', 1>> gd_c("gd_c");
|
||||
static IKFactory::Class<IKGradientDescent<'c', 2>> gd_2_c("gd_c_2");
|
||||
static IKFactory::Class<IKGradientDescent<'c', 4>> gd_4_c("gd_c_4");
|
||||
static IKFactory::Class<IKGradientDescent<'c', 8>> gd_8_c("gd_c_8");
|
||||
|
||||
// pseudoinverse jacobian only
|
||||
template <size_t threads> struct IKJacobian : IKJacobianBase<IKBase>
|
||||
{
|
||||
using IKBase::initialize;
|
||||
std::vector<double> solution;
|
||||
IKJacobian(const IKParams& p)
|
||||
: IKJacobianBase<IKBase>(p)
|
||||
{
|
||||
}
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
IKJacobianBase<IKBase>::initialize(problem);
|
||||
solution = problem.initial_guess;
|
||||
if(thread_index > 0)
|
||||
for(auto& vi : problem.active_variables)
|
||||
solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
|
||||
}
|
||||
const std::vector<double>& getSolution() const { return solution; }
|
||||
void step() { optimizeJacobian(solution); }
|
||||
size_t concurrency() const { return threads; }
|
||||
};
|
||||
static IKFactory::Class<IKJacobian<1>> jac("jac");
|
||||
static IKFactory::Class<IKJacobian<2>> jac_2("jac_2");
|
||||
static IKFactory::Class<IKJacobian<4>> jac_4("jac_4");
|
||||
static IKFactory::Class<IKJacobian<8>> jac_8("jac_8");
|
||||
}
|
||||
@@ -1,690 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
#include <fann.h>
|
||||
#include <fann_cpp.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#define LOG_LIST(l) \
|
||||
{ \
|
||||
LOG(#l "[]"); \
|
||||
for(std::size_t i = 0; i < l.size(); i++) \
|
||||
{ \
|
||||
LOG(#l "[" + std::to_string(i) + "]", l[i]); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
/*
|
||||
static inline KDL::Twist toTwist(const Frame& frame)
|
||||
{
|
||||
KDL::Twist t;
|
||||
t.vel.x(frame.pos.x());
|
||||
t.vel.y(frame.pos.y());
|
||||
t.vel.z(frame.pos.z());
|
||||
auto r = frame.rot.getAxis() * frame.rot.getAngle();
|
||||
t.rot.x(r.x());
|
||||
t.rot.y(r.y());
|
||||
t.rot.z(r.z());
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline KDL::Twist frameTwist(const Frame& a, const Frame& b)
|
||||
{
|
||||
auto frame = inverse(a) * b;
|
||||
KDL::Twist t;
|
||||
t.vel.x(frame.pos.x());
|
||||
t.vel.y(frame.pos.y());
|
||||
t.vel.z(frame.pos.z());
|
||||
auto r = frame.rot.getAxis() * frame.rot.getAngle();
|
||||
t.rot.x(r.x());
|
||||
t.rot.y(r.y());
|
||||
t.rot.z(r.z());
|
||||
return t;
|
||||
}
|
||||
*/
|
||||
|
||||
struct IKNeural : IKBase
|
||||
{
|
||||
std::vector<double> solution;
|
||||
FANN::neural_net net;
|
||||
|
||||
std::vector<std::pair<fann_type, fann_type>> input_minmax, output_minmax;
|
||||
|
||||
static void find_minmax(const std::vector<fann_type>& values, std::vector<std::pair<fann_type, fann_type>>& minmax)
|
||||
{
|
||||
for(size_t i = 0; i < minmax.size(); i++)
|
||||
{
|
||||
minmax[i] = std::make_pair(values[i], values[i]);
|
||||
}
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
auto& v = values[i];
|
||||
auto& p = minmax[i % minmax.size()];
|
||||
p.first = std::min(p.first, v);
|
||||
p.second = std::max(p.second, v);
|
||||
}
|
||||
}
|
||||
|
||||
static void normalize(std::vector<fann_type>& values, const std::vector<std::pair<fann_type, fann_type>>& minmax)
|
||||
{
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
auto& v = values[i];
|
||||
auto& p = minmax[i % minmax.size()];
|
||||
v = (v - p.first) / (p.second - p.first);
|
||||
}
|
||||
}
|
||||
|
||||
static void denormalize(std::vector<fann_type>& values, const std::vector<std::pair<fann_type, fann_type>>& minmax)
|
||||
{
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
auto& v = values[i];
|
||||
auto& p = minmax[i % minmax.size()];
|
||||
v = v * (p.second - p.first) + p.first;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int input_count, output_count;
|
||||
|
||||
IKNeural(const IKParams& p)
|
||||
: IKBase(p)
|
||||
{
|
||||
trained = false;
|
||||
}
|
||||
|
||||
bool trained;
|
||||
|
||||
void train()
|
||||
{
|
||||
trained = true;
|
||||
|
||||
input_count = problem.active_variables.size() + problem.tip_link_indices.size() * 6;
|
||||
output_count = problem.active_variables.size();
|
||||
|
||||
LOG_VAR(input_count);
|
||||
LOG_VAR(output_count);
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, input_count, input_count, output_count};
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, input_count + output_count, output_count};
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, input_count + output_count, input_count + output_count, output_count};
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, 100, output_count};
|
||||
|
||||
std::vector<unsigned int> levels = {input_count, 50, output_count};
|
||||
|
||||
net.create_standard_array(levels.size(), levels.data());
|
||||
|
||||
size_t var_count = params.robot_model->getVariableCount();
|
||||
std::vector<double> state1(var_count), state2(var_count);
|
||||
std::vector<Frame> frames1, frames2;
|
||||
|
||||
std::vector<fann_type> inputs, outputs;
|
||||
std::vector<fann_type*> input_pp, output_pp;
|
||||
|
||||
LOG("neuro ik generating training data");
|
||||
|
||||
unsigned int samples = 10000;
|
||||
|
||||
for(size_t iter = 0; iter < samples; iter++)
|
||||
{
|
||||
for(size_t ivar = 0; ivar < var_count; ivar++)
|
||||
{
|
||||
state1[ivar] = random(modelInfo.getMin(ivar), modelInfo.getMax(ivar));
|
||||
state1[ivar] = modelInfo.clip(state1[ivar], ivar);
|
||||
// state2[ivar] = modelInfo.clip(state1[ivar] + random_gauss() * modelInfo.getSpan(ivar), ivar);
|
||||
state2[ivar] = modelInfo.clip(state1[ivar] + random_gauss() * 0.1, ivar);
|
||||
}
|
||||
|
||||
model.applyConfiguration(state1);
|
||||
frames1 = model.getTipFrames();
|
||||
model.applyConfiguration(state2);
|
||||
frames2 = model.getTipFrames();
|
||||
|
||||
for(auto ivar : problem.active_variables)
|
||||
{
|
||||
inputs.push_back(state1[ivar]);
|
||||
outputs.push_back(state2[ivar] - state1[ivar]);
|
||||
}
|
||||
|
||||
for(size_t itip = 0; itip < problem.tip_link_indices.size(); itip++)
|
||||
{
|
||||
double translational_scale = 1.0;
|
||||
double rotational_scale = 1.0;
|
||||
|
||||
// Frame diff = inverse(frames1[itip]) * frames2[itip];
|
||||
// auto twist = toTwist(diff);
|
||||
auto twist = frameTwist(frames1[itip], frames2[itip]);
|
||||
|
||||
inputs.push_back(frames2[itip].pos.x() - frames1[itip].pos.x());
|
||||
inputs.push_back(frames2[itip].pos.y() - frames1[itip].pos.y());
|
||||
inputs.push_back(frames2[itip].pos.z() - frames1[itip].pos.z());
|
||||
|
||||
inputs.push_back(twist.rot.x() * rotational_scale);
|
||||
inputs.push_back(twist.rot.y() * rotational_scale);
|
||||
inputs.push_back(twist.rot.z() * rotational_scale);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& v : inputs)
|
||||
if(!std::isfinite(v)) throw std::runtime_error("NAN");
|
||||
for(auto& v : outputs)
|
||||
if(!std::isfinite(v)) throw std::runtime_error("NAN");
|
||||
|
||||
input_minmax.resize(input_count);
|
||||
output_minmax.resize(output_count);
|
||||
|
||||
find_minmax(inputs, input_minmax);
|
||||
find_minmax(outputs, output_minmax);
|
||||
|
||||
normalize(inputs, input_minmax);
|
||||
normalize(outputs, output_minmax);
|
||||
|
||||
for(size_t iter = 0; iter < samples; iter++)
|
||||
{
|
||||
input_pp.push_back(inputs.data() + iter * input_count);
|
||||
output_pp.push_back(outputs.data() + iter * output_count);
|
||||
}
|
||||
|
||||
LOG("neuro ik training");
|
||||
|
||||
FANN::training_data train;
|
||||
train.set_train_data(samples, input_count, input_pp.data(), output_count, output_pp.data());
|
||||
net.set_callback(
|
||||
[](FANN::neural_net& net, FANN::training_data& train, unsigned int max_epochs, unsigned int epochs_between_reports, float desired_error, unsigned int epochs, void* user_data) {
|
||||
if(epochs % epochs_between_reports != 0) return 0;
|
||||
// LOG("training", epochs, "/", max_epochs, epochs * 100 / max_epochs, "%");
|
||||
LOG("training", epochs, net.get_MSE(), desired_error);
|
||||
return 0;
|
||||
},
|
||||
0);
|
||||
|
||||
net.set_activation_function_hidden(FANN::SIGMOID);
|
||||
net.set_activation_function_output(FANN::SIGMOID);
|
||||
|
||||
net.init_weights(train);
|
||||
|
||||
net.train_on_data(train, 1000, 1, 0.0001);
|
||||
|
||||
fann_type err = net.test_data(train);
|
||||
LOG("neuro ik training error:", err);
|
||||
|
||||
/*std::vector<fann_type> iiv, oov, ttv;
|
||||
for(size_t iter = 0; iter < 10; iter++)
|
||||
{
|
||||
auto* ii = input_pp[iter];
|
||||
auto* oo = net.run(ii);
|
||||
auto* tt = output_pp[iter];
|
||||
iiv.assign(ii, ii + input_count);
|
||||
ttv.assign(tt, tt + output_count);
|
||||
oov.assign(oo, oo + output_count);
|
||||
LOG_LIST(iiv);
|
||||
LOG_LIST(ttv);
|
||||
LOG_LIST(oov);
|
||||
}*/
|
||||
|
||||
LOG("training done");
|
||||
}
|
||||
|
||||
size_t iterations = 0;
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
IKBase::initialize(problem);
|
||||
solution = problem.initial_guess;
|
||||
if(!trained) train();
|
||||
iterations = 0;
|
||||
if(thread_index > 0)
|
||||
for(auto& vi : problem.active_variables)
|
||||
solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
|
||||
}
|
||||
|
||||
const std::vector<double>& getSolution() const { return solution; }
|
||||
|
||||
std::vector<fann_type> inputs, outputs;
|
||||
|
||||
std::vector<Frame> tip_objectives;
|
||||
|
||||
/*void step()
|
||||
{
|
||||
//if(iterations > 1) return;
|
||||
iterations++;
|
||||
|
||||
inputs.clear();
|
||||
for(auto ivar : problem.active_variables)
|
||||
{
|
||||
inputs.push_back(solution[ivar]);
|
||||
}
|
||||
|
||||
tip_objectives.resize(model.getTipFrames().size());
|
||||
for(auto& g : problem.goals)
|
||||
{
|
||||
tip_objectives[g.tip_index] = g.frame;
|
||||
}
|
||||
|
||||
model.applyConfiguration(solution);
|
||||
auto& frames1 = model.getTipFrames();
|
||||
auto& frames2 = tip_objectives;
|
||||
|
||||
double scale = 1.0;
|
||||
|
||||
for(size_t itip = 0; itip < tip_objectives.size(); itip++)
|
||||
{
|
||||
double translational_scale = 1.0;
|
||||
double rotational_scale = 1.0;
|
||||
|
||||
//Frame diff = inverse(frames1[itip]) * frames2[itip];
|
||||
//auto twist = toTwist(diff);
|
||||
auto twist = frameTwist(frames1[itip], frames2[itip]);
|
||||
|
||||
auto dpos = frames2[itip].pos - frames1[itip].pos;
|
||||
auto drot = Vector3(
|
||||
twist.rot.x() * rotational_scale,
|
||||
twist.rot.y() * rotational_scale,
|
||||
twist.rot.z() * rotational_scale
|
||||
);
|
||||
|
||||
scale = 1.0 / (0.0000001 + dpos.length() + drot.length());
|
||||
|
||||
dpos = dpos * scale;
|
||||
drot = drot * scale;
|
||||
|
||||
inputs.push_back(dpos.x());
|
||||
inputs.push_back(dpos.y());
|
||||
inputs.push_back(dpos.z());
|
||||
|
||||
inputs.push_back(drot.x());
|
||||
inputs.push_back(drot.y());
|
||||
inputs.push_back(drot.z());
|
||||
}
|
||||
|
||||
normalize(inputs, input_minmax);
|
||||
|
||||
auto* oo = net.run(inputs.data());
|
||||
|
||||
outputs.assign(oo, oo + output_count);
|
||||
|
||||
denormalize(outputs, output_minmax);
|
||||
|
||||
auto& vv = problem.active_variables;
|
||||
for(size_t iout = 0; iout < vv.size(); iout++)
|
||||
{
|
||||
size_t ivar = vv[iout];
|
||||
solution[ivar] = modelInfo.clip(solution[ivar] + outputs[iout] * 0.1 / scale, ivar);
|
||||
}
|
||||
}*/
|
||||
|
||||
void step()
|
||||
{
|
||||
// if(iterations > 1) return;
|
||||
iterations++;
|
||||
|
||||
inputs.clear();
|
||||
for(auto ivar : problem.active_variables)
|
||||
{
|
||||
inputs.push_back(solution[ivar]);
|
||||
}
|
||||
|
||||
tip_objectives.resize(model.getTipFrames().size());
|
||||
for(auto& g : problem.goals)
|
||||
{
|
||||
tip_objectives[g.tip_index] = g.frame;
|
||||
}
|
||||
|
||||
model.applyConfiguration(solution);
|
||||
auto& frames1 = model.getTipFrames();
|
||||
auto& frames2 = tip_objectives;
|
||||
|
||||
double scale = 1.0;
|
||||
for(size_t itip = 0; itip < tip_objectives.size(); itip++)
|
||||
{
|
||||
double translational_scale = 1.0;
|
||||
double rotational_scale = 1.0;
|
||||
auto twist = frameTwist(frames1[itip], frames2[itip]);
|
||||
auto dpos = frames2[itip].pos - frames1[itip].pos;
|
||||
auto drot = Vector3(twist.rot.x() * rotational_scale, twist.rot.y() * rotational_scale, twist.rot.z() * rotational_scale);
|
||||
|
||||
/*if(iterations % 2)
|
||||
{
|
||||
scale = 1.0 / (0.0000001 + dpos.length());
|
||||
inputs.push_back(dpos.x() * scale);
|
||||
inputs.push_back(dpos.y() * scale);
|
||||
inputs.push_back(dpos.z() * scale);
|
||||
inputs.push_back(0);
|
||||
inputs.push_back(0);
|
||||
inputs.push_back(0);
|
||||
} else {
|
||||
scale = 1.0 / (0.0000001 + drot.length());
|
||||
inputs.push_back(0);
|
||||
inputs.push_back(0);
|
||||
inputs.push_back(0);
|
||||
inputs.push_back(drot.x() * scale);
|
||||
inputs.push_back(drot.y() * scale);
|
||||
inputs.push_back(drot.z() * scale);
|
||||
}*/
|
||||
|
||||
{
|
||||
scale = 1.0 / (0.0000001 + dpos.length() + drot.length());
|
||||
inputs.push_back(dpos.x() * scale);
|
||||
inputs.push_back(dpos.y() * scale);
|
||||
inputs.push_back(dpos.z() * scale);
|
||||
inputs.push_back(drot.x() * scale);
|
||||
inputs.push_back(drot.y() * scale);
|
||||
inputs.push_back(drot.z() * scale);
|
||||
}
|
||||
}
|
||||
normalize(inputs, input_minmax);
|
||||
auto* oo = net.run(inputs.data());
|
||||
outputs.assign(oo, oo + output_count);
|
||||
denormalize(outputs, output_minmax);
|
||||
auto& vv = problem.active_variables;
|
||||
for(size_t iout = 0; iout < vv.size(); iout++)
|
||||
{
|
||||
size_t ivar = vv[iout];
|
||||
solution[ivar] = modelInfo.clip(solution[ivar] + outputs[iout] * 1 / scale, ivar);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static IKFactory::Class<IKNeural> neural("neural");
|
||||
|
||||
struct IKNeural2 : IKBase
|
||||
{
|
||||
std::vector<double> solution;
|
||||
FANN::neural_net net;
|
||||
|
||||
std::vector<std::pair<fann_type, fann_type>> input_minmax, output_minmax;
|
||||
|
||||
/*static void find_minmax(const std::vector<fann_type>& values, std::vector<std::pair<fann_type, fann_type>>& minmax)
|
||||
{
|
||||
for(size_t i = 0; i < minmax.size(); i++)
|
||||
{
|
||||
minmax[i] = std::make_pair(values[i], values[i]);
|
||||
}
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
auto& v = values[i];
|
||||
auto& p = minmax[i % minmax.size()];
|
||||
p.first = std::min(p.first, v);
|
||||
p.second = std::max(p.second, v);
|
||||
}
|
||||
}*/
|
||||
|
||||
static void find_minmax(const std::vector<fann_type>& values, std::vector<std::pair<fann_type, fann_type>>& minmax)
|
||||
{
|
||||
std::vector<double> centers(minmax.size(), 0.0);
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
centers[i % minmax.size()] += values[i] * (1.0 * minmax.size() / values.size());
|
||||
|
||||
std::vector<double> ranges2(minmax.size(), 0.0001);
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
double d = values[i] - centers[i % minmax.size()];
|
||||
d = d * d;
|
||||
ranges2[i % minmax.size()] += d * (1.0 * minmax.size() / values.size());
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < minmax.size(); i++)
|
||||
{
|
||||
auto& p = minmax[i];
|
||||
p.first = centers[i] - sqrt(ranges2[i]);
|
||||
p.second = centers[i] + sqrt(ranges2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void normalize(std::vector<fann_type>& values, const std::vector<std::pair<fann_type, fann_type>>& minmax)
|
||||
{
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
auto& v = values[i];
|
||||
auto& p = minmax[i % minmax.size()];
|
||||
v = (v - p.first) / (p.second - p.first);
|
||||
}
|
||||
}
|
||||
|
||||
static void denormalize(std::vector<fann_type>& values, const std::vector<std::pair<fann_type, fann_type>>& minmax)
|
||||
{
|
||||
for(size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
auto& v = values[i];
|
||||
auto& p = minmax[i % minmax.size()];
|
||||
v = v * (p.second - p.first) + p.first;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int input_count, output_count;
|
||||
|
||||
IKNeural2(const IKParams& p)
|
||||
: IKBase(p)
|
||||
{
|
||||
trained = false;
|
||||
}
|
||||
|
||||
bool trained;
|
||||
|
||||
void train()
|
||||
{
|
||||
trained = true;
|
||||
|
||||
input_count = problem.tip_link_indices.size() * 7;
|
||||
output_count = problem.active_variables.size();
|
||||
|
||||
LOG_VAR(input_count);
|
||||
LOG_VAR(output_count);
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, 100, 100, output_count};
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, input_count, input_count, output_count};
|
||||
|
||||
std::vector<unsigned int> levels = {input_count, input_count + output_count, output_count};
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, input_count + output_count, input_count + output_count, output_count};
|
||||
|
||||
// std::vector<unsigned int> levels = {input_count, output_count};
|
||||
|
||||
net.create_standard_array(levels.size(), levels.data());
|
||||
|
||||
size_t var_count = params.robot_model->getVariableCount();
|
||||
std::vector<double> state = problem.initial_guess;
|
||||
std::vector<Frame> frames;
|
||||
|
||||
std::vector<fann_type> inputs, outputs;
|
||||
std::vector<fann_type*> input_pp, output_pp;
|
||||
|
||||
LOG("neuro ik generating training data");
|
||||
|
||||
unsigned int samples = 10000;
|
||||
|
||||
for(size_t iter = 0; iter < samples; iter++)
|
||||
{
|
||||
for(size_t ivar : problem.active_variables)
|
||||
state[ivar] = random(modelInfo.getMin(ivar), modelInfo.getMax(ivar));
|
||||
|
||||
model.applyConfiguration(state);
|
||||
frames = model.getTipFrames();
|
||||
|
||||
for(auto ivar : problem.active_variables)
|
||||
outputs.push_back(state[ivar]);
|
||||
|
||||
for(size_t itip = 0; itip < problem.tip_link_indices.size(); itip++)
|
||||
{
|
||||
inputs.push_back(frames[itip].pos.x());
|
||||
inputs.push_back(frames[itip].pos.y());
|
||||
inputs.push_back(frames[itip].pos.z());
|
||||
|
||||
auto rot = frames[itip].rot;
|
||||
rot = rot * rot;
|
||||
// rot = tf::Quaternion(0, 0, 0, 1);
|
||||
inputs.push_back(rot.x());
|
||||
inputs.push_back(rot.y());
|
||||
inputs.push_back(rot.z());
|
||||
inputs.push_back(rot.w());
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& v : inputs)
|
||||
if(!std::isfinite(v)) throw std::runtime_error("NAN");
|
||||
for(auto& v : outputs)
|
||||
if(!std::isfinite(v)) throw std::runtime_error("NAN");
|
||||
|
||||
input_minmax.resize(input_count);
|
||||
output_minmax.resize(output_count);
|
||||
|
||||
find_minmax(inputs, input_minmax);
|
||||
find_minmax(outputs, output_minmax);
|
||||
|
||||
normalize(inputs, input_minmax);
|
||||
normalize(outputs, output_minmax);
|
||||
|
||||
for(size_t iter = 0; iter < samples; iter++)
|
||||
{
|
||||
input_pp.push_back(inputs.data() + iter * input_count);
|
||||
output_pp.push_back(outputs.data() + iter * output_count);
|
||||
}
|
||||
|
||||
LOG("neuro ik training");
|
||||
|
||||
FANN::training_data train;
|
||||
train.set_train_data(samples, input_count, input_pp.data(), output_count, output_pp.data());
|
||||
net.set_callback(
|
||||
[](FANN::neural_net& net, FANN::training_data& train, unsigned int max_epochs, unsigned int epochs_between_reports, float desired_error, unsigned int epochs, void* user_data) {
|
||||
if(epochs % epochs_between_reports != 0) return 0;
|
||||
// LOG("training", epochs, "/", max_epochs, epochs * 100 / max_epochs, "%");
|
||||
LOG("training", epochs, net.get_MSE(), desired_error);
|
||||
return 0;
|
||||
},
|
||||
0);
|
||||
|
||||
net.set_activation_function_hidden(FANN::SIGMOID);
|
||||
net.set_activation_function_output(FANN::SIGMOID);
|
||||
|
||||
net.init_weights(train);
|
||||
|
||||
net.train_on_data(train, 100, 1, 0.0001);
|
||||
|
||||
fann_type err = net.test_data(train);
|
||||
LOG("neuro ik training error:", err);
|
||||
|
||||
/*std::vector<fann_type> iiv, oov, ttv;
|
||||
for(size_t iter = 0; iter < 10; iter++)
|
||||
{
|
||||
auto* ii = input_pp[iter];
|
||||
auto* oo = net.run(ii);
|
||||
auto* tt = output_pp[iter];
|
||||
iiv.assign(ii, ii + input_count);
|
||||
ttv.assign(tt, tt + output_count);
|
||||
oov.assign(oo, oo + output_count);
|
||||
LOG_LIST(iiv);
|
||||
LOG_LIST(ttv);
|
||||
LOG_LIST(oov);
|
||||
}*/
|
||||
|
||||
LOG("training done");
|
||||
}
|
||||
|
||||
size_t iterations = 0;
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
IKBase::initialize(problem);
|
||||
solution = problem.initial_guess;
|
||||
if(!trained) train();
|
||||
iterations = 0;
|
||||
}
|
||||
|
||||
const std::vector<double>& getSolution() const { return solution; }
|
||||
|
||||
std::vector<fann_type> inputs, outputs;
|
||||
|
||||
std::vector<Frame> tip_objectives;
|
||||
|
||||
void step()
|
||||
{
|
||||
if(iterations > 1) return;
|
||||
iterations++;
|
||||
|
||||
inputs.clear();
|
||||
|
||||
tip_objectives.resize(model.getTipFrames().size());
|
||||
for(auto& g : problem.goals)
|
||||
{
|
||||
tip_objectives[g.tip_index] = g.frame;
|
||||
}
|
||||
|
||||
auto& frames = tip_objectives;
|
||||
|
||||
for(size_t itip = 0; itip < tip_objectives.size(); itip++)
|
||||
{
|
||||
inputs.push_back(frames[itip].pos.x());
|
||||
inputs.push_back(frames[itip].pos.y());
|
||||
inputs.push_back(frames[itip].pos.z());
|
||||
|
||||
auto rot = frames[itip].rot;
|
||||
rot = rot * rot;
|
||||
// rot = tf::Quaternion(0, 0, 0, 1);
|
||||
inputs.push_back(rot.x());
|
||||
inputs.push_back(rot.y());
|
||||
inputs.push_back(rot.z());
|
||||
inputs.push_back(rot.w());
|
||||
}
|
||||
|
||||
normalize(inputs, input_minmax);
|
||||
|
||||
auto* oo = net.run(inputs.data());
|
||||
|
||||
outputs.assign(oo, oo + output_count);
|
||||
|
||||
denormalize(outputs, output_minmax);
|
||||
|
||||
auto& vv = problem.active_variables;
|
||||
for(size_t iout = 0; iout < vv.size(); iout++)
|
||||
{
|
||||
size_t ivar = vv[iout];
|
||||
solution[ivar] = modelInfo.clip(outputs[iout], ivar);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static IKFactory::Class<IKNeural2> neural2("neural2");
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
#include <boost/thread/barrier.hpp>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
// executes a function in parallel on pre-allocated threads
|
||||
class ParallelExecutor
|
||||
{
|
||||
std::function<void(size_t)> fun;
|
||||
std::vector<std::thread> threads;
|
||||
boost::barrier barrier;
|
||||
volatile bool exit;
|
||||
double best_fitness;
|
||||
|
||||
public:
|
||||
template <class FUN>
|
||||
ParallelExecutor(size_t thread_count, const FUN& f)
|
||||
: exit(false)
|
||||
, threads(thread_count)
|
||||
, fun(f)
|
||||
, barrier(thread_count)
|
||||
{
|
||||
for(size_t i = 1; i < thread_count; i++)
|
||||
{
|
||||
std::thread t([this, i]() {
|
||||
while(true)
|
||||
{
|
||||
barrier.wait();
|
||||
if(exit) break;
|
||||
fun(i);
|
||||
barrier.wait();
|
||||
if(exit) break;
|
||||
}
|
||||
});
|
||||
std::swap(t, threads[i]);
|
||||
}
|
||||
}
|
||||
~ParallelExecutor()
|
||||
{
|
||||
exit = true;
|
||||
barrier.wait();
|
||||
for(auto& t : threads)
|
||||
if(t.joinable()) t.join();
|
||||
}
|
||||
void run()
|
||||
{
|
||||
barrier.wait();
|
||||
fun(0);
|
||||
barrier.wait();
|
||||
}
|
||||
};
|
||||
|
||||
// runs ik on multiple threads until a stop criterion is met
|
||||
struct IKParallel
|
||||
{
|
||||
IKParams params;
|
||||
std::vector<std::unique_ptr<IKSolver>> solvers;
|
||||
std::vector<std::vector<double>> solver_solutions;
|
||||
std::vector<std::vector<double>> solver_temps;
|
||||
std::vector<int> solver_success;
|
||||
std::vector<double> solver_fitness;
|
||||
int thread_count;
|
||||
// std::vector<RobotFK_Fast> fk; // TODO: remove
|
||||
double timeout;
|
||||
bool success;
|
||||
std::atomic<int> finished;
|
||||
std::atomic<uint32_t> iteration_count;
|
||||
std::vector<double> result;
|
||||
std::unique_ptr<ParallelExecutor> par;
|
||||
Problem problem;
|
||||
bool enable_counter;
|
||||
double best_fitness;
|
||||
|
||||
IKParallel(const IKParams& params)
|
||||
: params(params)
|
||||
{
|
||||
// solver class name
|
||||
std::string name = params.solver_class_name;
|
||||
|
||||
enable_counter = params.enable_counter;
|
||||
|
||||
// create solvers
|
||||
solvers.emplace_back(IKFactory::create(name, params));
|
||||
thread_count = solvers.front()->concurrency();
|
||||
if(params.thread_count) {
|
||||
thread_count = params.thread_count;
|
||||
}
|
||||
while(solvers.size() < thread_count)
|
||||
solvers.emplace_back(IKFactory::clone(solvers.front().get()));
|
||||
for(size_t i = 0; i < thread_count; i++)
|
||||
solvers[i]->thread_index = i;
|
||||
|
||||
// while(fk.size() < thread_count) fk.emplace_back(params.robot_model);
|
||||
|
||||
// init buffers
|
||||
solver_solutions.resize(thread_count);
|
||||
solver_temps.resize(thread_count);
|
||||
solver_success.resize(thread_count);
|
||||
solver_fitness.resize(thread_count);
|
||||
|
||||
// create parallel executor
|
||||
par.reset(new ParallelExecutor(thread_count, [this](size_t i) { solverthread(i); }));
|
||||
}
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
this->problem = problem;
|
||||
// for(auto& f : fk) f.initialize(problem.tip_link_indices);
|
||||
}
|
||||
|
||||
private:
|
||||
void solverthread(size_t i)
|
||||
{
|
||||
THREADPROFILER("thread", i);
|
||||
COUNTERPROFILER("solver threads");
|
||||
|
||||
// initialize ik solvers
|
||||
{
|
||||
BLOCKPROFILER("ik solver init");
|
||||
solvers[i]->initialize(problem);
|
||||
}
|
||||
|
||||
// run solver iterations until solution found or timeout
|
||||
for(size_t iteration = 0; (ros::WallTime::now().toSec() < timeout && finished == 0) || (iteration == 0 && i == 0); iteration++)
|
||||
{
|
||||
if(finished) break;
|
||||
|
||||
// run solver for a few steps
|
||||
solvers[i]->step();
|
||||
iteration_count++;
|
||||
for(int it2 = 1; it2 < 4; it2++)
|
||||
if(ros::WallTime::now().toSec() < timeout && finished == 0) solvers[i]->step();
|
||||
|
||||
if(finished) break;
|
||||
|
||||
// get solution and check stop criterion
|
||||
auto& result = solver_temps[i];
|
||||
result = solvers[i]->getSolution();
|
||||
auto& fk = solvers[i]->model;
|
||||
fk.applyConfiguration(result);
|
||||
bool success = solvers[i]->checkSolution(result, fk.getTipFrames());
|
||||
if(success) finished = 1;
|
||||
solver_success[i] = success;
|
||||
solver_solutions[i] = result;
|
||||
solver_fitness[i] = solvers[i]->computeFitness(result, fk.getTipFrames());
|
||||
|
||||
if(success) break;
|
||||
}
|
||||
|
||||
finished = 1;
|
||||
|
||||
for(auto& s : solvers)
|
||||
s->canceled = true;
|
||||
}
|
||||
|
||||
public:
|
||||
void solve()
|
||||
{
|
||||
BLOCKPROFILER("solve mt");
|
||||
|
||||
// prepare
|
||||
iteration_count = 0;
|
||||
result = problem.initial_guess;
|
||||
timeout = problem.timeout;
|
||||
success = false;
|
||||
finished = 0;
|
||||
for(auto& s : solver_solutions)
|
||||
s = problem.initial_guess;
|
||||
for(auto& s : solver_temps)
|
||||
s = problem.initial_guess;
|
||||
for(auto& s : solver_success)
|
||||
s = 0;
|
||||
for(auto& f : solver_fitness)
|
||||
f = DBL_MAX;
|
||||
for(auto& s : solvers)
|
||||
s->canceled = false;
|
||||
|
||||
// run solvers
|
||||
{
|
||||
BLOCKPROFILER("solve mt 2");
|
||||
par->run();
|
||||
}
|
||||
|
||||
size_t best_index = 0;
|
||||
best_fitness = DBL_MAX;
|
||||
|
||||
// if exact primary goal matches have been found ...
|
||||
for(size_t i = 0; i < thread_count; i++)
|
||||
{
|
||||
if(solver_success[i])
|
||||
{
|
||||
double fitness;
|
||||
if(solvers[0]->problem.secondary_goals.empty())
|
||||
{
|
||||
// ... and if no secondary goals have been specified,
|
||||
// select the best result according to primary goals
|
||||
fitness = solver_fitness[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// ... and if secondary goals have been specified,
|
||||
// select the result that best satisfies primary and secondary goals
|
||||
fitness = solver_fitness[i] + solvers[0]->computeSecondaryFitnessAllVariables(solver_solutions[i]);
|
||||
}
|
||||
if(fitness < best_fitness)
|
||||
{
|
||||
best_fitness = fitness;
|
||||
best_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no exact primary goal matches have been found,
|
||||
// select best primary goal approximation
|
||||
if(best_fitness == DBL_MAX)
|
||||
{
|
||||
for(size_t i = 0; i < thread_count; i++)
|
||||
{
|
||||
if(solver_fitness[i] < best_fitness)
|
||||
{
|
||||
best_fitness = solver_fitness[i];
|
||||
best_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(enable_counter)
|
||||
{
|
||||
LOG("iterations", iteration_count);
|
||||
}
|
||||
|
||||
result = solver_solutions[best_index];
|
||||
success = solver_success[best_index];
|
||||
}
|
||||
|
||||
double getSolutionFitness() const { return best_fitness; }
|
||||
|
||||
bool getSuccess() const { return success; }
|
||||
|
||||
const std::vector<double>& getSolution() const { return result; }
|
||||
};
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
struct IKTest : IKBase
|
||||
{
|
||||
|
||||
RobotFK_MoveIt fkref;
|
||||
|
||||
std::vector<double> temp;
|
||||
|
||||
double d_rot_sum, d_pos_sum, d_div;
|
||||
|
||||
IKTest(const IKParams& params)
|
||||
: IKBase(params)
|
||||
, fkref(params.robot_model)
|
||||
{
|
||||
d_rot_sum = d_pos_sum = d_div = 0;
|
||||
}
|
||||
|
||||
/*double tipdiff(const std::vector<Frame>& fa, const std::vector<Frame>& fb)
|
||||
{
|
||||
double diff = 0.0;
|
||||
for(size_t i = 0; i < problem.tip_link_indices.size(); i++)
|
||||
{
|
||||
//LOG_VAR(fa[i]);
|
||||
//LOG_VAR(fb[i]);
|
||||
diff += fa[i].rot.angleShortestPath(fb[i].rot);
|
||||
diff += fa[i].pos.distance(fb[i].pos);
|
||||
}
|
||||
return diff;
|
||||
}*/
|
||||
|
||||
void initialize(const Problem& problem)
|
||||
{
|
||||
IKBase::initialize(problem);
|
||||
|
||||
fkref.initialize(problem.tip_link_indices);
|
||||
model.initialize(problem.tip_link_indices);
|
||||
|
||||
fkref.applyConfiguration(problem.initial_guess);
|
||||
model.applyConfiguration(problem.initial_guess);
|
||||
|
||||
// double diff = tipdiff(fkref.getTipFrames(), model.getTipFrames());
|
||||
// LOG_VAR(diff);
|
||||
|
||||
/*{
|
||||
auto& fa = fkref.getTipFrames();
|
||||
auto& fb = model.getTipFrames();
|
||||
for(size_t i = 0; i < problem.tip_link_indices.size(); i++)
|
||||
{
|
||||
LOG("d rot", i, fa[i].rot.angleShortestPath(fb[i].rot));
|
||||
LOG("d pos", i, fa[i].pos.distance(fb[i].pos));
|
||||
}
|
||||
}*/
|
||||
|
||||
{
|
||||
temp = problem.initial_guess;
|
||||
for(size_t ivar : problem.active_variables)
|
||||
if(modelInfo.isRevolute(ivar) || modelInfo.isPrismatic(ivar)) temp[ivar] = modelInfo.clip(temp[ivar] + random(-0.1, 0.1), ivar);
|
||||
|
||||
fkref.applyConfiguration(temp);
|
||||
auto& fa = fkref.getTipFrames();
|
||||
|
||||
model.applyConfiguration(problem.initial_guess);
|
||||
model.initializeMutationApproximator(problem.active_variables);
|
||||
|
||||
std::vector<aligned_vector<Frame>> fbm;
|
||||
|
||||
std::vector<double> mutation_values;
|
||||
for(size_t ivar : problem.active_variables)
|
||||
mutation_values.push_back(temp[ivar]);
|
||||
const double* mutation_ptr = mutation_values.data();
|
||||
|
||||
model.computeApproximateMutations(1, &mutation_ptr, fbm);
|
||||
|
||||
auto& fb = fbm[0];
|
||||
|
||||
// auto& fb = model.getTipFrames();
|
||||
|
||||
for(size_t i = 0; i < problem.tip_link_indices.size(); i++)
|
||||
{
|
||||
// LOG("d rot", i, fa[i].rot.angleShortestPath(fb[i].rot));
|
||||
// LOG("d pos", i, fa[i].pos.distance(fb[i].pos));
|
||||
|
||||
d_rot_sum += fa[i].rot.angleShortestPath(fb[i].rot);
|
||||
d_pos_sum += fa[i].pos.distance(fb[i].pos);
|
||||
d_div += 1;
|
||||
}
|
||||
}
|
||||
|
||||
LOG("d rot avg", d_rot_sum / d_div);
|
||||
LOG("d pos avg", d_pos_sum / d_div);
|
||||
}
|
||||
|
||||
void step() {}
|
||||
|
||||
const std::vector<double>& getSolution() const { return problem.initial_guess; }
|
||||
};
|
||||
|
||||
static IKFactory::Class<IKTest> test("test");
|
||||
}
|
||||
@@ -1,649 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include <bio_ik/goal.h>
|
||||
|
||||
#include "forward_kinematics.h"
|
||||
#include "ik_base.h"
|
||||
#include "ik_parallel.h"
|
||||
#include "problem.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Dense>
|
||||
#include <Eigen/Geometry>
|
||||
#include <kdl_parser/kdl_parser.hpp>
|
||||
#include <moveit/kinematics_base/kinematics_base.h>
|
||||
#include <moveit/rdf_loader/rdf_loader.h>
|
||||
#include <pluginlib/class_list_macros.h>
|
||||
#include <srdfdom/model.h>
|
||||
#include <urdf/model.h>
|
||||
#include <urdf_model/model.h>
|
||||
|
||||
#include <eigen_conversions/eigen_msg.h>
|
||||
//#include <moveit/common_planning_interface_objects/common_objects.h>
|
||||
#include <moveit/kinematics_base/kinematics_base.h>
|
||||
#include <moveit/robot_model/robot_model.h>
|
||||
#include <moveit/robot_state/robot_state.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include <bio_ik/goal_types.h>
|
||||
|
||||
using namespace bio_ik;
|
||||
|
||||
// implement BioIKKinematicsQueryOptions
|
||||
|
||||
namespace bio_ik {
|
||||
|
||||
std::mutex bioIKKinematicsQueryOptionsMutex;
|
||||
std::unordered_set<const void *> bioIKKinematicsQueryOptionsList;
|
||||
|
||||
BioIKKinematicsQueryOptions::BioIKKinematicsQueryOptions()
|
||||
: replace(false), solution_fitness(0) {
|
||||
std::lock_guard<std::mutex> lock(bioIKKinematicsQueryOptionsMutex);
|
||||
bioIKKinematicsQueryOptionsList.insert(this);
|
||||
}
|
||||
|
||||
BioIKKinematicsQueryOptions::~BioIKKinematicsQueryOptions() {
|
||||
std::lock_guard<std::mutex> lock(bioIKKinematicsQueryOptionsMutex);
|
||||
bioIKKinematicsQueryOptionsList.erase(this);
|
||||
}
|
||||
|
||||
bool isBioIKKinematicsQueryOptions(const void *ptr) {
|
||||
std::lock_guard<std::mutex> lock(bioIKKinematicsQueryOptionsMutex);
|
||||
return bioIKKinematicsQueryOptionsList.find(ptr) !=
|
||||
bioIKKinematicsQueryOptionsList.end();
|
||||
}
|
||||
|
||||
const BioIKKinematicsQueryOptions *
|
||||
toBioIKKinematicsQueryOptions(const void *ptr) {
|
||||
if (isBioIKKinematicsQueryOptions(ptr))
|
||||
return (const BioIKKinematicsQueryOptions *)ptr;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace bio_ik
|
||||
|
||||
// BioIK Kinematics Plugin
|
||||
|
||||
namespace bio_ik_kinematics_plugin {
|
||||
|
||||
// Fallback for older MoveIt versions which don't support lookupParam yet
|
||||
template <class T>
|
||||
static void lookupParam(const std::string ¶m, T &val,
|
||||
const T &default_val) {
|
||||
ros::NodeHandle nodeHandle("~");
|
||||
val = nodeHandle.param(param, default_val);
|
||||
}
|
||||
|
||||
struct BioIKKinematicsPlugin : kinematics::KinematicsBase {
|
||||
std::vector<std::string> joint_names, link_names;
|
||||
moveit::core::RobotModelConstPtr robot_model;
|
||||
const moveit::core::JointModelGroup *joint_model_group;
|
||||
mutable std::unique_ptr<IKParallel> ik;
|
||||
mutable std::vector<double> state, temp;
|
||||
mutable std::unique_ptr<moveit::core::RobotState> temp_state;
|
||||
mutable std::vector<Frame> tipFrames;
|
||||
RobotInfo robot_info;
|
||||
bool enable_profiler;
|
||||
|
||||
BioIKKinematicsPlugin() { enable_profiler = false; }
|
||||
|
||||
virtual const std::vector<std::string> &getJointNames() const {
|
||||
LOG_FNC();
|
||||
return joint_names;
|
||||
}
|
||||
|
||||
virtual const std::vector<std::string> &getLinkNames() const {
|
||||
LOG_FNC();
|
||||
return link_names;
|
||||
}
|
||||
|
||||
virtual bool getPositionFK(const std::vector<std::string> &link_names,
|
||||
const std::vector<double> &joint_angles,
|
||||
std::vector<geometry_msgs::Pose> &poses) const {
|
||||
LOG_FNC();
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool getPositionIK(const geometry_msgs::Pose &ik_pose,
|
||||
const std::vector<double> &ik_seed_state,
|
||||
std::vector<double> &solution,
|
||||
moveit_msgs::MoveItErrorCodes &error_code,
|
||||
const kinematics::KinematicsQueryOptions &options =
|
||||
kinematics::KinematicsQueryOptions()) const {
|
||||
LOG_FNC();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Eigen::Affine3d> tip_reference_frames;
|
||||
|
||||
mutable std::vector<std::unique_ptr<Goal>> default_goals;
|
||||
|
||||
mutable std::vector<const bio_ik::Goal *> all_goals;
|
||||
|
||||
IKParams ikparams;
|
||||
|
||||
mutable Problem problem;
|
||||
|
||||
static moveit::core::RobotModelConstPtr
|
||||
loadRobotModel(const std::string &robot_description) {
|
||||
static std::map<std::string, moveit::core::RobotModelConstPtr>
|
||||
robot_model_cache;
|
||||
static std::mutex cache_mutex;
|
||||
std::lock_guard<std::mutex> lock(cache_mutex);
|
||||
if (robot_model_cache.find(robot_description) == robot_model_cache.end()) {
|
||||
rdf_loader::RDFLoader rdf_loader(robot_description);
|
||||
auto srdf = rdf_loader.getSRDF();
|
||||
auto urdf_model = rdf_loader.getURDF();
|
||||
|
||||
if (!urdf_model || !srdf) {
|
||||
LOG("URDF and SRDF must be loaded for kinematics solver to work.");
|
||||
return nullptr;
|
||||
}
|
||||
robot_model_cache[robot_description] = moveit::core::RobotModelConstPtr(
|
||||
new robot_model::RobotModel(urdf_model, srdf));
|
||||
}
|
||||
return robot_model_cache[robot_description];
|
||||
|
||||
// return
|
||||
// moveit::planning_interface::getSharedRobotModel(robot_description);
|
||||
}
|
||||
|
||||
bool load(std::string robot_description, std::string group_name) {
|
||||
LOG_FNC();
|
||||
|
||||
// LOG_VAR(robot_description);
|
||||
// LOG_VAR(group_name);
|
||||
|
||||
LOG("bio ik init", ros::this_node::getName());
|
||||
|
||||
/*rdf_loader::RDFLoader rdf_loader(robot_description_);
|
||||
auto srdf = rdf_loader.getSRDF();
|
||||
auto urdf_model = rdf_loader.getURDF();
|
||||
|
||||
if(!urdf_model || !srdf)
|
||||
{
|
||||
LOG("URDF and SRDF must be loaded for kinematics solver to work.");
|
||||
return false;
|
||||
}
|
||||
|
||||
robot_model.reset(new robot_model::RobotModel(urdf_model, srdf));*/
|
||||
|
||||
robot_model = loadRobotModel(robot_description);
|
||||
|
||||
joint_model_group = robot_model->getJointModelGroup(group_name);
|
||||
if (!joint_model_group) {
|
||||
LOG("failed to get joint model group");
|
||||
return false;
|
||||
}
|
||||
|
||||
joint_names.clear();
|
||||
|
||||
for (auto *joint_model : joint_model_group->getJointModels())
|
||||
if (joint_model->getName() != base_frame_ &&
|
||||
joint_model->getType() != moveit::core::JointModel::UNKNOWN &&
|
||||
joint_model->getType() != moveit::core::JointModel::FIXED)
|
||||
joint_names.push_back(joint_model->getName());
|
||||
|
||||
auto tips2 = tip_frames_;
|
||||
joint_model_group->getEndEffectorTips(tips2);
|
||||
if (!tips2.empty())
|
||||
tip_frames_ = tips2;
|
||||
|
||||
link_names = tip_frames_;
|
||||
|
||||
// for(auto& n : joint_names) LOG("joint", n);
|
||||
// for(auto& n : link_names) LOG("link", n);
|
||||
|
||||
// bool enable_profiler;
|
||||
lookupParam("profiler", enable_profiler, false);
|
||||
// if(enable_profiler) Profiler::start();
|
||||
|
||||
robot_info = RobotInfo(robot_model);
|
||||
|
||||
ikparams.robot_model = robot_model;
|
||||
ikparams.joint_model_group = joint_model_group;
|
||||
|
||||
// initialize parameters for IKParallel
|
||||
lookupParam("mode", ikparams.solver_class_name,
|
||||
std::string("bio2_memetic"));
|
||||
lookupParam("counter", ikparams.enable_counter, false);
|
||||
lookupParam("threads", ikparams.thread_count, 0);
|
||||
|
||||
// initialize parameters for Problem
|
||||
lookupParam("dpos", ikparams.dpos, DBL_MAX);
|
||||
lookupParam("drot", ikparams.drot, DBL_MAX);
|
||||
lookupParam("dtwist", ikparams.dtwist, 1e-5);
|
||||
|
||||
// initialize parameters for ik_evolution_1
|
||||
lookupParam("no_wipeout", ikparams.opt_no_wipeout, false);
|
||||
lookupParam("population_size", ikparams.population_size, 8);
|
||||
lookupParam("elite_count", ikparams.elite_count, 4);
|
||||
lookupParam("linear_fitness", ikparams.linear_fitness, false);
|
||||
|
||||
temp_state.reset(new moveit::core::RobotState(robot_model));
|
||||
|
||||
ik.reset(new IKParallel(ikparams));
|
||||
|
||||
{
|
||||
|
||||
BLOCKPROFILER("default ik goals");
|
||||
|
||||
default_goals.clear();
|
||||
|
||||
for (size_t i = 0; i < tip_frames_.size(); i++) {
|
||||
PoseGoal *goal = new PoseGoal();
|
||||
|
||||
goal->setLinkName(tip_frames_[i]);
|
||||
|
||||
// LOG_VAR(goal->link_name);
|
||||
|
||||
double rotation_scale = 0.5;
|
||||
|
||||
lookupParam("rotation_scale", rotation_scale, rotation_scale);
|
||||
|
||||
bool position_only_ik = false;
|
||||
lookupParam("position_only_ik", position_only_ik, position_only_ik);
|
||||
if (position_only_ik)
|
||||
rotation_scale = 0;
|
||||
|
||||
goal->setRotationScale(rotation_scale);
|
||||
|
||||
default_goals.emplace_back(goal);
|
||||
}
|
||||
|
||||
{
|
||||
double weight = 0;
|
||||
lookupParam("center_joints_weight", weight, weight);
|
||||
if (weight > 0.0) {
|
||||
auto *avoid_joint_limits_goal = new bio_ik::CenterJointsGoal();
|
||||
avoid_joint_limits_goal->setWeight(weight);
|
||||
default_goals.emplace_back(avoid_joint_limits_goal);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
double weight = 0;
|
||||
lookupParam("avoid_joint_limits_weight", weight, weight);
|
||||
if (weight > 0.0) {
|
||||
auto *avoid_joint_limits_goal = new bio_ik::AvoidJointLimitsGoal();
|
||||
avoid_joint_limits_goal->setWeight(weight);
|
||||
default_goals.emplace_back(avoid_joint_limits_goal);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
double weight = 0;
|
||||
lookupParam("minimal_displacement_weight", weight, weight);
|
||||
if (weight > 0.0) {
|
||||
auto *minimal_displacement_goal =
|
||||
new bio_ik::MinimalDisplacementGoal();
|
||||
minimal_displacement_goal->setWeight(weight);
|
||||
default_goals.emplace_back(minimal_displacement_goal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOG("init ready");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool initialize(const std::string &robot_description,
|
||||
const std::string &group_name,
|
||||
const std::string &base_frame,
|
||||
const std::string &tip_frame,
|
||||
double search_discretization) {
|
||||
LOG_FNC();
|
||||
std::vector<std::string> tip_frames;
|
||||
tip_frames.push_back(tip_frame);
|
||||
initialize(robot_description, group_name, base_frame, tip_frames,
|
||||
search_discretization);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool initialize(const std::string &robot_description,
|
||||
const std::string &group_name,
|
||||
const std::string &base_frame,
|
||||
const std::vector<std::string> &tip_frames,
|
||||
double search_discretization) {
|
||||
LOG_FNC();
|
||||
setValues(robot_description, group_name, base_frame, tip_frames,
|
||||
search_discretization);
|
||||
load(robot_description, group_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
searchPositionIK(const geometry_msgs::Pose &ik_pose,
|
||||
const std::vector<double> &ik_seed_state, double timeout,
|
||||
std::vector<double> &solution,
|
||||
moveit_msgs::MoveItErrorCodes &error_code,
|
||||
const kinematics::KinematicsQueryOptions &options =
|
||||
kinematics::KinematicsQueryOptions()) const {
|
||||
LOG_FNC();
|
||||
return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
|
||||
ik_seed_state, timeout, std::vector<double>(),
|
||||
solution, IKCallbackFn(), error_code, options);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
searchPositionIK(const geometry_msgs::Pose &ik_pose,
|
||||
const std::vector<double> &ik_seed_state, double timeout,
|
||||
const std::vector<double> &consistency_limits,
|
||||
std::vector<double> &solution,
|
||||
moveit_msgs::MoveItErrorCodes &error_code,
|
||||
const kinematics::KinematicsQueryOptions &options =
|
||||
kinematics::KinematicsQueryOptions()) const {
|
||||
LOG_FNC();
|
||||
return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
|
||||
ik_seed_state, timeout, consistency_limits,
|
||||
solution, IKCallbackFn(), error_code, options);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
searchPositionIK(const geometry_msgs::Pose &ik_pose,
|
||||
const std::vector<double> &ik_seed_state, double timeout,
|
||||
std::vector<double> &solution,
|
||||
const IKCallbackFn &solution_callback,
|
||||
moveit_msgs::MoveItErrorCodes &error_code,
|
||||
const kinematics::KinematicsQueryOptions &options =
|
||||
kinematics::KinematicsQueryOptions()) const {
|
||||
LOG_FNC();
|
||||
return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
|
||||
ik_seed_state, timeout, std::vector<double>(),
|
||||
solution, solution_callback, error_code, options);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
searchPositionIK(const geometry_msgs::Pose &ik_pose,
|
||||
const std::vector<double> &ik_seed_state, double timeout,
|
||||
const std::vector<double> &consistency_limits,
|
||||
std::vector<double> &solution,
|
||||
const IKCallbackFn &solution_callback,
|
||||
moveit_msgs::MoveItErrorCodes &error_code,
|
||||
const kinematics::KinematicsQueryOptions &options =
|
||||
kinematics::KinematicsQueryOptions()) const {
|
||||
LOG_FNC();
|
||||
return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
|
||||
ik_seed_state, timeout, consistency_limits,
|
||||
solution, solution_callback, error_code, options);
|
||||
}
|
||||
|
||||
/*struct OptMod : kinematics::KinematicsQueryOptions
|
||||
{
|
||||
int test;
|
||||
};*/
|
||||
|
||||
virtual bool
|
||||
searchPositionIK(const std::vector<geometry_msgs::Pose> &ik_poses,
|
||||
const std::vector<double> &ik_seed_state, double timeout,
|
||||
const std::vector<double> &consistency_limits,
|
||||
std::vector<double> &solution,
|
||||
const IKCallbackFn &solution_callback,
|
||||
moveit_msgs::MoveItErrorCodes &error_code,
|
||||
const kinematics::KinematicsQueryOptions &options =
|
||||
kinematics::KinematicsQueryOptions(),
|
||||
const moveit::core::RobotState *context_state = NULL) const {
|
||||
double t0 = ros::WallTime::now().toSec();
|
||||
|
||||
// timeout = 0.1;
|
||||
|
||||
// LOG("a");
|
||||
|
||||
if (enable_profiler)
|
||||
Profiler::start();
|
||||
|
||||
auto *bio_ik_options = toBioIKKinematicsQueryOptions(&options);
|
||||
|
||||
LOG_FNC();
|
||||
|
||||
FNPROFILER();
|
||||
|
||||
// LOG(typeid(options).name());
|
||||
// LOG(((OptMod*)&options)->test);
|
||||
|
||||
// get variable default positions / context state
|
||||
state.resize(robot_model->getVariableCount());
|
||||
robot_model->getVariableDefaultPositions(state);
|
||||
if (context_state)
|
||||
for (size_t i = 0; i < robot_model->getVariableCount(); i++)
|
||||
state[i] = context_state->getVariablePositions()[i];
|
||||
|
||||
// overwrite used variables with seed state
|
||||
solution = ik_seed_state;
|
||||
{
|
||||
int i = 0;
|
||||
for (auto &joint_name : getJointNames()) {
|
||||
auto *joint_model = robot_model->getJointModel(joint_name);
|
||||
if (!joint_model)
|
||||
continue;
|
||||
for (size_t vi = 0; vi < joint_model->getVariableCount(); vi++)
|
||||
state.at(joint_model->getFirstVariableIndex() + vi) =
|
||||
solution.at(i++);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bio_ik_options || !bio_ik_options->replace) {
|
||||
// transform tips to baseframe
|
||||
tipFrames.clear();
|
||||
for (size_t i = 0; i < ik_poses.size(); i++) {
|
||||
Eigen::Affine3d p, r;
|
||||
tf::poseMsgToEigen(ik_poses[i], p);
|
||||
if (context_state) {
|
||||
r = context_state->getGlobalLinkTransform(getBaseFrame());
|
||||
} else {
|
||||
if (i == 0)
|
||||
temp_state->setToDefaultValues();
|
||||
r = temp_state->getGlobalLinkTransform(getBaseFrame());
|
||||
}
|
||||
tipFrames.emplace_back(r * p);
|
||||
}
|
||||
}
|
||||
|
||||
// init ik
|
||||
|
||||
problem.timeout = t0 + timeout;
|
||||
problem.initial_guess = state;
|
||||
|
||||
// for(auto& v : state) LOG("var", &v - &state.front(), v);
|
||||
|
||||
// problem.tip_objectives = tipFrames;
|
||||
|
||||
/*for(size_t i = 0; i < problem.goals.size(); i++)
|
||||
{
|
||||
problem.goals[i].frame = tipFrames[i];
|
||||
}*/
|
||||
|
||||
// LOG("---");
|
||||
|
||||
/*{
|
||||
BLOCKPROFILER("ik goals");
|
||||
std::vector<std::unique_ptr<Goal>> goals;
|
||||
for(size_t i = 0; i < tip_frames_.size(); i++)
|
||||
{
|
||||
//if(rand() % 2) break;
|
||||
PoseGoal* goal = new PoseGoal();
|
||||
goal->link_name = tip_frames_[i];
|
||||
goal->position = tipFrames[i].pos;
|
||||
goal->orientation = tipFrames[i].rot;
|
||||
goals.emplace_back(goal);
|
||||
//if(rand() % 20) break;
|
||||
}
|
||||
//std::random_shuffle(goals.begin(), goals.end());
|
||||
//LOG_VAR(goals.size());
|
||||
setRequestGoals(problem, goals, ikparams);
|
||||
}*/
|
||||
|
||||
{
|
||||
|
||||
if (!bio_ik_options || !bio_ik_options->replace) {
|
||||
for (size_t i = 0; i < tip_frames_.size(); i++) {
|
||||
auto *goal = (PoseGoal *)default_goals[i].get();
|
||||
goal->setPosition(tipFrames[i].pos);
|
||||
goal->setOrientation(tipFrames[i].rot);
|
||||
}
|
||||
}
|
||||
|
||||
all_goals.clear();
|
||||
|
||||
if (!bio_ik_options || !bio_ik_options->replace)
|
||||
for (auto &goal : default_goals)
|
||||
all_goals.push_back(goal.get());
|
||||
|
||||
if (bio_ik_options)
|
||||
for (auto &goal : bio_ik_options->goals)
|
||||
all_goals.push_back(goal.get());
|
||||
|
||||
{
|
||||
BLOCKPROFILER("problem init");
|
||||
problem.initialize(ikparams.robot_model, ikparams.joint_model_group,
|
||||
ikparams, all_goals, bio_ik_options);
|
||||
// problem.setGoals(default_goals, ikparams);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
BLOCKPROFILER("ik init");
|
||||
ik->initialize(problem);
|
||||
}
|
||||
|
||||
// run ik solver
|
||||
{
|
||||
BLOCKPROFILER("ik_solve");
|
||||
ik->solve();
|
||||
}
|
||||
|
||||
// get solution
|
||||
state = ik->getSolution();
|
||||
|
||||
// wrap angles
|
||||
for (auto ivar : problem.active_variables) {
|
||||
auto v = state[ivar];
|
||||
if (robot_info.isRevolute(ivar) &&
|
||||
robot_model->getMimicJointModels().empty()) {
|
||||
auto r = problem.initial_guess[ivar];
|
||||
auto lo = robot_info.getMin(ivar);
|
||||
auto hi = robot_info.getMax(ivar);
|
||||
|
||||
// move close to initial guess
|
||||
if (r < v - M_PI || r > v + M_PI) {
|
||||
v -= r;
|
||||
v /= (2 * M_PI);
|
||||
v += 0.5;
|
||||
v -= std::floor(v);
|
||||
v -= 0.5;
|
||||
v *= (2 * M_PI);
|
||||
v += r;
|
||||
}
|
||||
|
||||
// wrap at joint limits
|
||||
if (v > hi)
|
||||
v -= std::ceil(std::max(0.0, v - hi) / (2 * M_PI)) * (2 * M_PI);
|
||||
if (v < lo)
|
||||
v += std::ceil(std::max(0.0, lo - v) / (2 * M_PI)) * (2 * M_PI);
|
||||
|
||||
// clamp at edges
|
||||
if (v < lo)
|
||||
v = lo;
|
||||
if (v > hi)
|
||||
v = hi;
|
||||
}
|
||||
state[ivar] = v;
|
||||
}
|
||||
|
||||
// wrap angles
|
||||
robot_model->enforcePositionBounds(state.data());
|
||||
|
||||
// map result to jointgroup variables
|
||||
{
|
||||
solution.clear();
|
||||
for (auto &joint_name : getJointNames()) {
|
||||
auto *joint_model = robot_model->getJointModel(joint_name);
|
||||
if (!joint_model)
|
||||
continue;
|
||||
for (size_t vi = 0; vi < joint_model->getVariableCount(); vi++)
|
||||
solution.push_back(
|
||||
state.at(joint_model->getFirstVariableIndex() + vi));
|
||||
}
|
||||
}
|
||||
|
||||
// set solution fitness
|
||||
if (bio_ik_options) {
|
||||
bio_ik_options->solution_fitness = ik->getSolutionFitness();
|
||||
}
|
||||
|
||||
// return an error if an accurate solution was requested, but no accurate
|
||||
// solution was found
|
||||
if (!ik->getSuccess() && !options.return_approximate_solution) {
|
||||
error_code.val = error_code.NO_IK_SOLUTION;
|
||||
return false;
|
||||
}
|
||||
|
||||
// callback?
|
||||
if (!solution_callback.empty()) {
|
||||
// run callback
|
||||
solution_callback(ik_poses.front(), solution, error_code);
|
||||
|
||||
// return success if callback has accepted the solution
|
||||
return error_code.val == error_code.SUCCESS;
|
||||
} else {
|
||||
// return success
|
||||
error_code.val = error_code.SUCCESS;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool supportsGroup(const moveit::core::JointModelGroup *jmg,
|
||||
std::string *error_text_out = 0) const {
|
||||
LOG_FNC();
|
||||
// LOG_VAR(jmg->getName());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace bio_ik_kinematics_plugin
|
||||
|
||||
// register plugin
|
||||
|
||||
#undef LOG
|
||||
#undef ERROR
|
||||
PLUGINLIB_EXPORT_CLASS(bio_ik_kinematics_plugin::BioIKKinematicsPlugin,
|
||||
kinematics::KinematicsBase);
|
||||
@@ -1,340 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#include "ik_base.h"
|
||||
|
||||
#include <geometric_shapes/bodies.h>
|
||||
#include <geometric_shapes/shapes.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <bio_ik/goal_types.h>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
enum class Problem::GoalType
|
||||
{
|
||||
Unknown,
|
||||
Position,
|
||||
Orientation,
|
||||
Pose,
|
||||
};
|
||||
|
||||
size_t Problem::addTipLink(const moveit::core::LinkModel* link_model)
|
||||
{
|
||||
if(link_tip_indices[link_model->getLinkIndex()] < 0)
|
||||
{
|
||||
link_tip_indices[link_model->getLinkIndex()] = tip_link_indices.size();
|
||||
tip_link_indices.push_back(link_model->getLinkIndex());
|
||||
}
|
||||
return link_tip_indices[link_model->getLinkIndex()];
|
||||
}
|
||||
|
||||
Problem::Problem()
|
||||
: ros_params_initrd(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Problem::initialize(moveit::core::RobotModelConstPtr robot_model, const moveit::core::JointModelGroup* joint_model_group, const IKParams& params, const std::vector<const Goal*>& goals2, const BioIKKinematicsQueryOptions* options)
|
||||
{
|
||||
if(robot_model != this->robot_model)
|
||||
{
|
||||
modelInfo = RobotInfo(robot_model);
|
||||
collision_links.clear();
|
||||
collision_links.resize(robot_model->getLinkModelCount());
|
||||
}
|
||||
|
||||
this->robot_model = robot_model;
|
||||
this->joint_model_group = joint_model_group;
|
||||
this->params = params;
|
||||
|
||||
if(!ros_params_initrd)
|
||||
{
|
||||
ros_params_initrd = true;
|
||||
dpos = params.dpos;
|
||||
drot = params.drot;
|
||||
dtwist = params.dtwist;
|
||||
if(dpos < 0.0 || dpos >= FLT_MAX || !std::isfinite(dpos)) dpos = DBL_MAX;
|
||||
if(drot < 0.0 || drot >= FLT_MAX || !std::isfinite(drot)) drot = DBL_MAX;
|
||||
if(dtwist < 0.0 || dtwist >= FLT_MAX || !std::isfinite(dtwist)) dtwist = DBL_MAX;
|
||||
}
|
||||
|
||||
link_tip_indices.clear();
|
||||
link_tip_indices.resize(robot_model->getLinkModelCount(), -1);
|
||||
tip_link_indices.clear();
|
||||
|
||||
active_variables.clear();
|
||||
auto addActiveVariable = [this, robot_model, joint_model_group, options](const std::string& name) -> ssize_t {
|
||||
if(options)
|
||||
{
|
||||
auto& joint_name = robot_model->getJointOfVariable(name)->getName();
|
||||
for(auto& fixed_joint_name : options->fixed_joints)
|
||||
{
|
||||
if(fixed_joint_name == joint_name)
|
||||
{
|
||||
return (ssize_t)-1 - (ssize_t)robot_model->getVariableIndex(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t i = 0; i < active_variables.size(); i++)
|
||||
if(name == robot_model->getVariableNames()[active_variables[i]]) return i;
|
||||
for(auto& n : joint_model_group->getVariableNames())
|
||||
{
|
||||
if(n == name)
|
||||
{
|
||||
active_variables.push_back(robot_model->getVariableIndex(name));
|
||||
return active_variables.size() - 1;
|
||||
}
|
||||
}
|
||||
ERROR("joint variable not found", name);
|
||||
};
|
||||
|
||||
goals.clear();
|
||||
secondary_goals.clear();
|
||||
for(auto& goal : goals2)
|
||||
{
|
||||
GoalInfo goal_info;
|
||||
|
||||
goal_info.goal = goal;
|
||||
|
||||
goal->describe(goal_info.goal_context);
|
||||
|
||||
for(auto& link_name : goal_info.goal_context.goal_link_names_)
|
||||
{
|
||||
auto* link_model = robot_model->getLinkModel(link_name);
|
||||
if(!link_model) ERROR("link not found", link_name);
|
||||
goal_info.goal_context.goal_link_indices_.push_back(addTipLink(link_model));
|
||||
}
|
||||
|
||||
for(auto& variable_name : goal_info.goal_context.goal_variable_names_)
|
||||
{
|
||||
goal_info.goal_context.goal_variable_indices_.push_back(addActiveVariable(variable_name));
|
||||
}
|
||||
|
||||
goal_info.weight = goal_info.goal_context.goal_weight_;
|
||||
goal_info.weight_sq = goal_info.weight * goal_info.weight;
|
||||
|
||||
goal_info.goal_type = GoalType::Unknown;
|
||||
|
||||
goal_info.frame = Frame::identity();
|
||||
goal_info.tip_index = 0;
|
||||
if(goal_info.goal_context.goal_link_indices_.size()) goal_info.tip_index = goal_info.goal_context.goal_link_indices_[0];
|
||||
|
||||
if(auto* g = dynamic_cast<const PositionGoal*>(goal_info.goal))
|
||||
{
|
||||
goal_info.goal_type = GoalType::Position;
|
||||
goal_info.frame.pos = g->getPosition();
|
||||
}
|
||||
|
||||
if(auto* g = dynamic_cast<const OrientationGoal*>(goal_info.goal))
|
||||
{
|
||||
goal_info.goal_type = GoalType::Orientation;
|
||||
goal_info.frame.rot = g->getOrientation();
|
||||
}
|
||||
|
||||
if(auto* g = dynamic_cast<const PoseGoal*>(goal_info.goal))
|
||||
{
|
||||
goal_info.goal_type = GoalType::Pose;
|
||||
goal_info.frame.pos = g->getPosition();
|
||||
goal_info.frame.rot = g->getOrientation();
|
||||
}
|
||||
|
||||
goal_info.goal_context.joint_model_group_ = joint_model_group;
|
||||
goal_info.goal_context.initial_guess_ = initial_guess;
|
||||
|
||||
if(goal_info.goal_context.goal_secondary_)
|
||||
secondary_goals.push_back(goal_info);
|
||||
else
|
||||
goals.push_back(goal_info);
|
||||
|
||||
// if(goal_info.variable_indices.size() > temp_variables.size()) temp_variables.resize(goal_info.variable_indices.size());
|
||||
|
||||
// if(goal_info.link_indices.size() > temp_frames.size()) temp_frames.resize(goal_info.link_indices.size());
|
||||
}
|
||||
|
||||
// update active variables from active subtree
|
||||
joint_usage.resize(robot_model->getJointModelCount());
|
||||
for(auto& u : joint_usage)
|
||||
u = 0;
|
||||
for(auto tip_index : tip_link_indices)
|
||||
for(auto* link_model = robot_model->getLinkModels()[tip_index]; link_model; link_model = link_model->getParentLinkModel())
|
||||
joint_usage[link_model->getParentJointModel()->getJointIndex()] = 1;
|
||||
if(options)
|
||||
for(auto& fixed_joint_name : options->fixed_joints)
|
||||
joint_usage[robot_model->getJointModel(fixed_joint_name)->getJointIndex()] = 0;
|
||||
for(auto* joint_model : joint_model_group->getActiveJointModels())
|
||||
if(joint_usage[joint_model->getJointIndex()] && !joint_model->getMimic())
|
||||
for(auto& n : joint_model->getVariableNames())
|
||||
addActiveVariable(n);
|
||||
|
||||
// init weights for minimal displacement goals
|
||||
{
|
||||
minimal_displacement_factors.resize(active_variables.size());
|
||||
double s = 0;
|
||||
for(auto ivar : active_variables)
|
||||
s += modelInfo.getMaxVelocityRcp(ivar);
|
||||
if(s > 0)
|
||||
{
|
||||
for(size_t i = 0; i < active_variables.size(); i++)
|
||||
{
|
||||
auto ivar = active_variables[i];
|
||||
minimal_displacement_factors[i] = modelInfo.getMaxVelocityRcp(ivar) / s;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t i = 0; i < active_variables.size(); i++)
|
||||
minimal_displacement_factors[i] = 1.0 / active_variables.size();
|
||||
}
|
||||
}
|
||||
|
||||
initialize2();
|
||||
}
|
||||
|
||||
void Problem::initialize2()
|
||||
{
|
||||
for(auto* gg : {&goals, &secondary_goals})
|
||||
{
|
||||
for(auto& g : *gg)
|
||||
{
|
||||
g.goal_context.problem_active_variables_ = active_variables;
|
||||
g.goal_context.problem_tip_link_indices_ = tip_link_indices;
|
||||
g.goal_context.velocity_weights_ = minimal_displacement_factors;
|
||||
g.goal_context.robot_info_ = &modelInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double Problem::computeGoalFitness(GoalInfo& goal_info, const Frame* tip_frames, const double* active_variable_positions)
|
||||
{
|
||||
goal_info.goal_context.tip_link_frames_ = tip_frames;
|
||||
goal_info.goal_context.active_variable_positions_ = active_variable_positions;
|
||||
return goal_info.goal->evaluate(goal_info.goal_context) * goal_info.weight_sq;
|
||||
}
|
||||
|
||||
double Problem::computeGoalFitness(std::vector<GoalInfo>& goals, const Frame* tip_frames, const double* active_variable_positions)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for(auto& goal : goals)
|
||||
sum += computeGoalFitness(goal, tip_frames, active_variable_positions);
|
||||
return sum;
|
||||
}
|
||||
|
||||
bool Problem::checkSolutionActiveVariables(const std::vector<Frame>& tip_frames, const double* active_variable_positions)
|
||||
{
|
||||
for(auto& goal : goals)
|
||||
{
|
||||
const auto& fa = goal.frame;
|
||||
const auto& fb = tip_frames[goal.tip_index];
|
||||
|
||||
switch(goal.goal_type)
|
||||
{
|
||||
|
||||
case GoalType::Position:
|
||||
{
|
||||
if(dpos != DBL_MAX)
|
||||
{
|
||||
double p_dist = (fb.pos - fa.pos).length();
|
||||
if(!(p_dist <= dpos)) return false;
|
||||
}
|
||||
if(dtwist != DBL_MAX)
|
||||
{
|
||||
KDL::Frame fk_kdl, ik_kdl;
|
||||
frameToKDL(fa, fk_kdl);
|
||||
frameToKDL(fb, ik_kdl);
|
||||
KDL::Twist kdl_diff(fk_kdl.M.Inverse() * KDL::diff(fk_kdl.p, ik_kdl.p), fk_kdl.M.Inverse() * KDL::diff(fk_kdl.M, ik_kdl.M));
|
||||
if(!KDL::Equal(kdl_diff.vel, KDL::Twist::Zero().vel, dtwist)) return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case GoalType::Orientation:
|
||||
{
|
||||
if(drot != DBL_MAX)
|
||||
{
|
||||
double r_dist = fb.rot.angleShortestPath(fa.rot);
|
||||
r_dist = r_dist * 180 / M_PI;
|
||||
if(!(r_dist <= drot)) return false;
|
||||
}
|
||||
if(dtwist != DBL_MAX)
|
||||
{
|
||||
KDL::Frame fk_kdl, ik_kdl;
|
||||
frameToKDL(fa, fk_kdl);
|
||||
frameToKDL(fb, ik_kdl);
|
||||
KDL::Twist kdl_diff(fk_kdl.M.Inverse() * KDL::diff(fk_kdl.p, ik_kdl.p), fk_kdl.M.Inverse() * KDL::diff(fk_kdl.M, ik_kdl.M));
|
||||
if(!KDL::Equal(kdl_diff.rot, KDL::Twist::Zero().rot, dtwist)) return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case GoalType::Pose:
|
||||
{
|
||||
if(dpos != DBL_MAX || drot != DBL_MAX)
|
||||
{
|
||||
double p_dist = (fb.pos - fa.pos).length();
|
||||
double r_dist = fb.rot.angleShortestPath(fa.rot);
|
||||
r_dist = r_dist * 180 / M_PI;
|
||||
if(!(p_dist <= dpos)) return false;
|
||||
if(!(r_dist <= drot)) return false;
|
||||
}
|
||||
if(dtwist != DBL_MAX)
|
||||
{
|
||||
KDL::Frame fk_kdl, ik_kdl;
|
||||
frameToKDL(fa, fk_kdl);
|
||||
frameToKDL(fb, ik_kdl);
|
||||
KDL::Twist kdl_diff(fk_kdl.M.Inverse() * KDL::diff(fk_kdl.p, ik_kdl.p), fk_kdl.M.Inverse() * KDL::diff(fk_kdl.M, ik_kdl.M));
|
||||
if(!KDL::Equal(kdl_diff, KDL::Twist::Zero(), dtwist)) return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
double dmax = DBL_MAX;
|
||||
dmax = std::fmin(dmax, dpos);
|
||||
dmax = std::fmin(dmax, dtwist);
|
||||
double d = computeGoalFitness(goal, tip_frames.data(), active_variable_positions);
|
||||
if(!(d < dmax * dmax)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOG("checkSolutionActiveVariables true");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include <vector>
|
||||
|
||||
#include <bio_ik/robot_info.h>
|
||||
|
||||
#include <geometric_shapes/shapes.h>
|
||||
|
||||
#include <moveit/collision_detection/collision_robot.h>
|
||||
#include <moveit/collision_detection_fcl/collision_robot_fcl.h>
|
||||
|
||||
#include <bio_ik/goal.h>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
class Problem
|
||||
{
|
||||
private:
|
||||
bool ros_params_initrd;
|
||||
std::vector<int> joint_usage;
|
||||
std::vector<ssize_t> link_tip_indices;
|
||||
std::vector<double> minimal_displacement_factors;
|
||||
std::vector<double> joint_transmission_goal_temp, joint_transmission_goal_temp2;
|
||||
moveit::core::RobotModelConstPtr robot_model;
|
||||
const moveit::core::JointModelGroup* joint_model_group;
|
||||
IKParams params;
|
||||
RobotInfo modelInfo;
|
||||
double dpos, drot, dtwist;
|
||||
struct CollisionShape
|
||||
{
|
||||
std::vector<Vector3> vertices;
|
||||
std::vector<fcl::Vec3f> points;
|
||||
std::vector<int> polygons;
|
||||
std::vector<fcl::Vec3f> plane_normals;
|
||||
std::vector<double> plane_dis;
|
||||
collision_detection::FCLGeometryConstPtr geometry;
|
||||
Frame frame;
|
||||
std::vector<std::vector<size_t>> edges;
|
||||
};
|
||||
struct CollisionLink
|
||||
{
|
||||
bool initialized;
|
||||
std::vector<std::shared_ptr<CollisionShape>> shapes;
|
||||
CollisionLink()
|
||||
: initialized(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
std::vector<CollisionLink> collision_links;
|
||||
size_t addTipLink(const moveit::core::LinkModel* link_model);
|
||||
|
||||
public:
|
||||
/*enum class GoalType;
|
||||
struct BalanceGoalInfo
|
||||
{
|
||||
ssize_t tip_index;
|
||||
double mass;
|
||||
Vector3 center;
|
||||
};
|
||||
struct GoalInfo
|
||||
{
|
||||
const Goal* goal;
|
||||
GoalType goal_type;
|
||||
size_t tip_index;
|
||||
double weight;
|
||||
double weight_sq;
|
||||
double rotation_scale;
|
||||
double rotation_scale_sq;
|
||||
Frame frame;
|
||||
tf::Vector3 target;
|
||||
tf::Vector3 direction;
|
||||
tf::Vector3 axis;
|
||||
double distance;
|
||||
ssize_t active_variable_index;
|
||||
double variable_position;
|
||||
std::vector<ssize_t> variable_indices;
|
||||
mutable size_t last_collision_vertex;
|
||||
std::vector<BalanceGoalInfo> balance_goal_infos;
|
||||
};*/
|
||||
enum class GoalType;
|
||||
// std::vector<const Frame*> temp_frames;
|
||||
// std::vector<double> temp_variables;
|
||||
struct GoalInfo
|
||||
{
|
||||
const Goal* goal;
|
||||
double weight_sq;
|
||||
double weight;
|
||||
GoalType goal_type;
|
||||
size_t tip_index;
|
||||
Frame frame;
|
||||
GoalContext goal_context;
|
||||
};
|
||||
double timeout;
|
||||
std::vector<double> initial_guess;
|
||||
std::vector<size_t> active_variables;
|
||||
std::vector<size_t> tip_link_indices;
|
||||
std::vector<GoalInfo> goals;
|
||||
std::vector<GoalInfo> secondary_goals;
|
||||
Problem();
|
||||
void initialize(moveit::core::RobotModelConstPtr robot_model, const moveit::core::JointModelGroup* joint_model_group, const IKParams& params, const std::vector<const Goal*>& goals2, const BioIKKinematicsQueryOptions* options);
|
||||
void initialize2();
|
||||
double computeGoalFitness(GoalInfo& goal, const Frame* tip_frames, const double* active_variable_positions);
|
||||
double computeGoalFitness(std::vector<GoalInfo>& goals, const Frame* tip_frames, const double* active_variable_positions);
|
||||
bool checkSolutionActiveVariables(const std::vector<Frame>& tip_frames, const double* active_variable_positions);
|
||||
};
|
||||
}
|
||||
@@ -1,521 +0,0 @@
|
||||
/*********************************************************************
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2016-2017, Philipp Sebastian Ruppel
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
|
||||
#include <ros/ros.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <tf_conversions/tf_kdl.h>
|
||||
|
||||
#include <XmlRpcException.h>
|
||||
|
||||
//#include <link.h>
|
||||
|
||||
//#include <boost/align/aligned_allocator.hpp>
|
||||
//#include <Eigen/Eigen>
|
||||
|
||||
namespace bio_ik
|
||||
{
|
||||
|
||||
struct IKParams
|
||||
{
|
||||
moveit::core::RobotModelConstPtr robot_model;
|
||||
const moveit::core::JointModelGroup* joint_model_group;
|
||||
|
||||
// IKParallel parameters
|
||||
std::string solver_class_name;
|
||||
bool enable_counter;
|
||||
int thread_count;
|
||||
|
||||
//Problem parameters
|
||||
double dpos;
|
||||
double drot;
|
||||
double dtwist;
|
||||
|
||||
// ik_evolution_1 parameters
|
||||
bool opt_no_wipeout;
|
||||
int population_size;
|
||||
int elite_count;
|
||||
bool linear_fitness;
|
||||
};
|
||||
|
||||
#define ENABLE_LOG
|
||||
|
||||
#define ENABLE_PROFILER
|
||||
|
||||
// logging
|
||||
|
||||
//#define LOG_STREAM (std::cerr << std::fixed)
|
||||
//#define LOG_STREAM (std::cerr << std::scientific)
|
||||
#define LOG_STREAM (std::cerr)
|
||||
|
||||
template <class T> inline void vprint(std::ostream& s, const T& a) { s << a << std::endl; }
|
||||
template <class T, class... AA> inline void vprint(std::ostream& s, const T& a, const AA&... aa)
|
||||
{
|
||||
s << a << " ";
|
||||
vprint(s, aa...);
|
||||
};
|
||||
|
||||
#define LOG2(...) vprint(LOG_STREAM, "ikbio ", __VA_ARGS__)
|
||||
|
||||
#ifdef ENABLE_LOG
|
||||
#define LOG(...) LOG2(__VA_ARGS__)
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
#define LOG_VAR(v) LOG2(#v, (v));
|
||||
|
||||
//#define LOG_FNC() LOG("fun", __func__, __LINE__)
|
||||
#define LOG_FNC()
|
||||
|
||||
// show error and abort
|
||||
// #define ERROR(...) { LOG("ERROR", __VA_ARGS__); exit(-1); }
|
||||
// #define ERROR(a, ...) { LOG(a, __VA_ARGS__); LOG_STREAM.flush(); throw std::runtime_error(a); }
|
||||
#define ERROR(...) \
|
||||
{ \
|
||||
LOG2(__VA_ARGS__); \
|
||||
LOG_STREAM.flush(); \
|
||||
std::stringstream ss; \
|
||||
vprint(ss, __VA_ARGS__); \
|
||||
throw std::runtime_error(ss.str()); \
|
||||
}
|
||||
// #define ERROR(...) { LOG_ALWAYS(__VA_ARGS__); std::raise(SIGINT); }
|
||||
|
||||
// profiler
|
||||
|
||||
#ifdef ENABLE_PROFILER
|
||||
|
||||
// embeddable sampling profiler
|
||||
|
||||
// profiled block or function
|
||||
struct ProfilerBin
|
||||
{
|
||||
const char* volatile name; // name of scope or function, also used as indicator if it is currently being executed
|
||||
std::atomic<int> counter; // only used by CounterScope / COUNTERPROFILER
|
||||
ProfilerBin()
|
||||
: name(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// allocate globally unique profiler buffer via template
|
||||
template <class force_weak_linker_symbol = void> ProfilerBin* getProfilerBuffer()
|
||||
{
|
||||
static std::vector<ProfilerBin> buffer(10000);
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
// reserve profiler buffer segment for current compilation unit
|
||||
template <class force_weak_linker_symbol = void> ProfilerBin* getProfilerSegment()
|
||||
{
|
||||
static size_t index = 0;
|
||||
return getProfilerBuffer() + (index++) * 20;
|
||||
}
|
||||
static ProfilerBin* profiler_segment = getProfilerSegment();
|
||||
|
||||
// identifies currently profiled thread
|
||||
// null if profiler is disabled
|
||||
struct ProfilerInfo
|
||||
{
|
||||
void* stack_begin;
|
||||
void* stack_end;
|
||||
};
|
||||
|
||||
// declare globally unique profiler info via template
|
||||
template <class force_weak_linker_symbol = void> ProfilerInfo& getProfilerInfo()
|
||||
{
|
||||
static ProfilerInfo info;
|
||||
return info;
|
||||
}
|
||||
static ProfilerInfo& profiler_info = getProfilerInfo();
|
||||
|
||||
// profiles a scope or function
|
||||
template <size_t ID> struct ProfilerScope
|
||||
{
|
||||
__attribute__((always_inline)) inline ProfilerScope(const char* name)
|
||||
{
|
||||
if(profiler_info.stack_begin == 0) return;
|
||||
if(this < profiler_info.stack_begin || this > profiler_info.stack_end) return;
|
||||
profiler_segment[ID].name = name;
|
||||
}
|
||||
__attribute__((always_inline)) inline ~ProfilerScope()
|
||||
{
|
||||
if(profiler_info.stack_begin == 0) return;
|
||||
if(this < profiler_info.stack_begin || this > profiler_info.stack_end) return;
|
||||
profiler_segment[ID].name = 0;
|
||||
}
|
||||
};
|
||||
#define FNPROFILER() volatile ProfilerScope<__COUNTER__> _profilerscope(__func__);
|
||||
#define BLOCKPROFILER(name) volatile ProfilerScope<__COUNTER__> _profilerscope(name);
|
||||
|
||||
// per-thread profiling
|
||||
struct ThreadScope
|
||||
{
|
||||
size_t id;
|
||||
__attribute__((always_inline)) inline ThreadScope(const char* name, size_t id)
|
||||
: id(id)
|
||||
{
|
||||
if(profiler_info.stack_begin == 0) return;
|
||||
profiler_segment[id].name = name;
|
||||
}
|
||||
__attribute__((always_inline)) inline ~ThreadScope()
|
||||
{
|
||||
if(profiler_info.stack_begin == 0) return;
|
||||
profiler_segment[id].name = 0;
|
||||
}
|
||||
};
|
||||
#define THREADPROFILER(name, id) \
|
||||
static const char* _threadscope_names[] = {name "0", name "1", name "2", name "3"}; \
|
||||
volatile ThreadScope _threadscope(_threadscope_names[id], __COUNTER__ + id); \
|
||||
(__COUNTER__, __COUNTER__, __COUNTER__, __COUNTER__, __COUNTER__);
|
||||
|
||||
// profiling across multiple threads
|
||||
struct CounterScope
|
||||
{
|
||||
size_t id;
|
||||
__attribute__((always_inline)) inline CounterScope(const char* name, size_t id)
|
||||
: id(id)
|
||||
{
|
||||
if(profiler_info.stack_begin == 0) return;
|
||||
if((profiler_segment[id].counter++) == 0) profiler_segment[id].name = name;
|
||||
}
|
||||
__attribute__((always_inline)) inline ~CounterScope()
|
||||
{
|
||||
if(profiler_info.stack_begin == 0) return;
|
||||
if((--profiler_segment[id].counter) == 0) profiler_segment[id].name = 0;
|
||||
}
|
||||
};
|
||||
#define COUNTERPROFILER(name) volatile CounterScope _counterscope(name, __COUNTER__);
|
||||
|
||||
// starts profiler and periodically writes results to log
|
||||
struct Profiler
|
||||
{
|
||||
std::thread thread;
|
||||
volatile int exit_flag;
|
||||
Profiler()
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
pthread_getattr_np(pthread_self(), &attr);
|
||||
void* stack_addr;
|
||||
size_t stack_size;
|
||||
pthread_attr_getstack(&attr, &stack_addr, &stack_size);
|
||||
profiler_info.stack_begin = stack_addr;
|
||||
profiler_info.stack_end = (char*)stack_addr + stack_size;
|
||||
const size_t maxbin = 1000;
|
||||
static std::mutex mutex;
|
||||
static std::unordered_map<const char*, size_t> samples;
|
||||
exit_flag = 0;
|
||||
std::thread t([this]() {
|
||||
auto* profiler_bins = getProfilerBuffer();
|
||||
while(true)
|
||||
{
|
||||
for(int iter = 0; iter < 100; iter++)
|
||||
{
|
||||
for(int iter = 0; iter < 100; iter++)
|
||||
{
|
||||
int i = rand() % maxbin;
|
||||
const char* p = profiler_bins[i].name;
|
||||
if(p) samples[p]++;
|
||||
}
|
||||
if(exit_flag) break;
|
||||
std::this_thread::sleep_for(std::chrono::duration<size_t, std::micro>(rand() % 1000));
|
||||
}
|
||||
{
|
||||
double thistime = ros::WallTime::now().toSec();
|
||||
static double lasttime = 0.0;
|
||||
if(thistime < lasttime + 1) continue;
|
||||
lasttime = thistime;
|
||||
static std::vector<std::pair<const char*, size_t>> data;
|
||||
data.clear();
|
||||
for(auto& p : samples)
|
||||
data.push_back(p);
|
||||
std::sort(data.begin(), data.end(), [](const std::pair<const char*, size_t>& a, const std::pair<const char*, size_t>& b) { return a.second > b.second; });
|
||||
LOG("");
|
||||
LOG("profiler");
|
||||
for(auto& d : data)
|
||||
{
|
||||
double v = d.second * 100.0 / data[0].second;
|
||||
char s[32];
|
||||
sprintf(s, "%6.2f%%", v);
|
||||
LOG("p", s, d.first);
|
||||
}
|
||||
LOG("");
|
||||
}
|
||||
if(exit_flag) break;
|
||||
}
|
||||
});
|
||||
std::swap(thread, t);
|
||||
}
|
||||
~Profiler()
|
||||
{
|
||||
exit_flag = true;
|
||||
thread.join();
|
||||
}
|
||||
static void start() { static Profiler profiler; }
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#define FNPROFILER()
|
||||
#define BLOCKPROFILER(name)
|
||||
#define THREADPROFILER(name, id)
|
||||
#define COUNTERPROFILER(name)
|
||||
|
||||
struct Profiler
|
||||
{
|
||||
static void start() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
__attribute__((always_inline)) inline double mix(double a, double b, double f) { return a * (1.0 - f) + b * f; }
|
||||
|
||||
__attribute__((always_inline)) inline double clamp(double v, double lo, double hi)
|
||||
{
|
||||
if(v < lo) v = lo;
|
||||
if(v > hi) v = hi;
|
||||
return v;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline double clamp2(double v, double lo, double hi)
|
||||
{
|
||||
if(__builtin_expect(v < lo, 0)) v = lo;
|
||||
if(__builtin_expect(v > hi, 0)) v = hi;
|
||||
return v;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline double smoothstep(float a, float b, float v)
|
||||
{
|
||||
v = clamp((v - a) / (b - a), 0.0, 1.0);
|
||||
return v * v * (3.0 - 2.0 * v);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline double sign(double f)
|
||||
{
|
||||
if(f < 0.0) f = -1.0;
|
||||
if(f > 0.0) f = +1.0;
|
||||
return f;
|
||||
}
|
||||
|
||||
template <class t> class linear_int_distribution
|
||||
{
|
||||
std::uniform_int_distribution<t> base;
|
||||
t n;
|
||||
|
||||
public:
|
||||
inline linear_int_distribution(t vrange)
|
||||
: n(vrange)
|
||||
, base(0, vrange)
|
||||
{
|
||||
}
|
||||
template <class generator> inline t operator()(generator& g)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
t v = base(g) + base(g);
|
||||
if(v < n) return n - v - 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct XORShift64
|
||||
{
|
||||
uint64_t v;
|
||||
|
||||
public:
|
||||
XORShift64()
|
||||
: v(88172645463325252ull)
|
||||
{
|
||||
}
|
||||
__attribute__((always_inline)) inline uint64_t operator()()
|
||||
{
|
||||
v ^= v << 13;
|
||||
v ^= v >> 7;
|
||||
v ^= v << 17;
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
// class factory
|
||||
//
|
||||
// registering a class:
|
||||
// static Factory<Base>::Class<Derived> reg("Derived");
|
||||
//
|
||||
// instantiation:
|
||||
// Base* obj = Factory<Base>::create("Derived");
|
||||
//
|
||||
// cloning and object:
|
||||
// p = Factory<Base>::clone(o);
|
||||
//
|
||||
template <class BASE, class... ARGS> class Factory
|
||||
{
|
||||
typedef BASE* (*Constructor)(ARGS...);
|
||||
struct ClassBase
|
||||
{
|
||||
std::string name;
|
||||
std::type_index type;
|
||||
virtual BASE* create(ARGS... args) const = 0;
|
||||
virtual BASE* clone(const BASE*) const = 0;
|
||||
ClassBase()
|
||||
: type(typeid(void))
|
||||
{
|
||||
}
|
||||
};
|
||||
typedef std::set<ClassBase*> MapType;
|
||||
static MapType& classes()
|
||||
{
|
||||
static MapType ff;
|
||||
return ff;
|
||||
}
|
||||
|
||||
public:
|
||||
template <class DERIVED> struct Class : ClassBase
|
||||
{
|
||||
BASE* create(ARGS... args) const { return new DERIVED(args...); }
|
||||
BASE* clone(const BASE* o) const { return new DERIVED(*(const DERIVED*)o); }
|
||||
Class(const std::string& name)
|
||||
{
|
||||
this->name = name;
|
||||
this->type = typeid(DERIVED);
|
||||
classes().insert(this);
|
||||
}
|
||||
~Class() { classes().erase(this); }
|
||||
};
|
||||
static BASE* create(const std::string& name, ARGS... args)
|
||||
{
|
||||
for(auto* f : classes())
|
||||
if(f->name == name) return f->create(args...);
|
||||
ERROR("class not found", name);
|
||||
}
|
||||
template <class DERIVED> static DERIVED* clone(const DERIVED* o)
|
||||
{
|
||||
for(auto* f : classes())
|
||||
if(f->type == typeid(*o)) return (DERIVED*)f->clone(o);
|
||||
ERROR("class not found", typeid(*o).name());
|
||||
}
|
||||
};
|
||||
|
||||
// Alloctes memory properly aligned for SIMD operations
|
||||
template <class T, size_t A> struct aligned_allocator : public std::allocator<T>
|
||||
{
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef T value_type;
|
||||
T* allocate(size_t s, const void* hint = 0)
|
||||
{
|
||||
void* p;
|
||||
if(posix_memalign(&p, A, sizeof(T) * s + 64)) throw std::bad_alloc();
|
||||
return (T*)p;
|
||||
}
|
||||
void deallocate(T* ptr, size_t s) { free(ptr); }
|
||||
template <class U> struct rebind
|
||||
{
|
||||
typedef aligned_allocator<U, A> other;
|
||||
};
|
||||
};
|
||||
|
||||
// std::vector typedef with proper memory alignment for SIMD operations
|
||||
template <class T> struct aligned_vector : std::vector<T, aligned_allocator<T, 32>>
|
||||
{
|
||||
};
|
||||
|
||||
// Helper class for reading structured data from ROS parameter server
|
||||
class XmlRpcReader
|
||||
{
|
||||
typedef XmlRpc::XmlRpcValue var;
|
||||
var& v;
|
||||
|
||||
public:
|
||||
XmlRpcReader(var& v)
|
||||
: v(v)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
XmlRpcReader at(int i) { return v[i]; }
|
||||
void conv(bool& r) { r = (bool)v; }
|
||||
void conv(double& r) { r = (v.getType() == var::TypeInt) ? ((double)(int)v) : ((double)v); }
|
||||
void conv(tf::Vector3& r)
|
||||
{
|
||||
double x, y, z;
|
||||
at(0).conv(x);
|
||||
at(1).conv(y);
|
||||
at(2).conv(z);
|
||||
r = tf::Vector3(x, y, z);
|
||||
}
|
||||
void conv(tf::Quaternion& r)
|
||||
{
|
||||
double x, y, z, w;
|
||||
at(0).conv(x);
|
||||
at(1).conv(y);
|
||||
at(2).conv(z);
|
||||
at(3).conv(w);
|
||||
r = tf::Quaternion(x, y, z, w).normalized();
|
||||
}
|
||||
void conv(std::string& r) { r = (std::string)v; }
|
||||
|
||||
public:
|
||||
template <class T> void param(const char* key, T& r)
|
||||
{
|
||||
if(!v.hasMember(key)) return;
|
||||
try
|
||||
{
|
||||
XmlRpcReader(v[key]).conv(r);
|
||||
}
|
||||
catch(const XmlRpc::XmlRpcException& e)
|
||||
{
|
||||
LOG(key);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user