├── .gitignore
├── 2_RIDE
├── services
│ ├── msg
│ │ ├── SocketInfo.ev
│ │ └── GetSocketPose.op
│ ├── CMakeLists.txt
│ └── src
│ │ └── socket_service.cpp
├── statemachines
│ ├── bundles
│ │ └── ride_tutorial
│ │ │ ├── manifest.json
│ │ │ ├── resources
│ │ │ ├── plug-inserted.svg
│ │ │ ├── logo.svg
│ │ │ ├── input.svg
│ │ │ ├── plug-backup.svg
│ │ │ └── plug.svg
│ │ │ └── sources
│ │ │ ├── convert_safety.lf
│ │ │ ├── plug_in_vision.lf
│ │ │ ├── plug_in.lf
│ │ │ ├── plug_in_app.lf
│ │ │ └── plug_in_wrapper.lf
│ ├── cleanup.sh
│ ├── CMakeLists.txt
│ └── Makefile
├── CMakeLists.txt
├── fig
│ └── plug_in_vision_structure_bundles.png
└── README.md
├── 3_FCI
├── ride_integration
│ ├── statemachines
│ │ ├── bundles
│ │ │ └── fci_tutorial
│ │ │ │ ├── manifest.json
│ │ │ │ ├── resources
│ │ │ │ ├── plug-inserted.svg
│ │ │ │ ├── logo.svg
│ │ │ │ ├── input.svg
│ │ │ │ ├── plug-backup.svg
│ │ │ │ └── plug.svg
│ │ │ │ └── sources
│ │ │ │ └── plug_in_fci.lf
│ │ ├── cleanup.sh
│ │ ├── CMakeLists.txt
│ │ └── Makefile
│ └── services
│ │ ├── msg
│ │ └── PlugIn.op
│ │ ├── CMakeLists.txt
│ │ └── src
│ │ └── fci_service.cpp
├── fig
│ └── fci_service_structure_bundles.png
├── CMakeLists.txt
├── standalone
│ ├── CMakeLists.txt
│ ├── src
│ │ ├── plug_in_controller_standalone.cpp
│ │ └── plug_in_controller.cpp
│ └── include
│ │ └── plug_in_controller.h
└── README.md
├── 4_Learning
├── statemachines
│ ├── bundles
│ │ ├── fci_learning_tutorial
│ │ │ ├── manifest.json
│ │ │ └── resources
│ │ │ │ ├── plug-inserted.svg
│ │ │ │ ├── logo.svg
│ │ │ │ ├── input.svg
│ │ │ │ ├── plug-backup.svg
│ │ │ │ └── plug.svg
│ │ └── ride_learning_tutorial
│ │ │ ├── manifest.json
│ │ │ ├── resources
│ │ │ ├── plug-inserted.svg
│ │ │ ├── logo.svg
│ │ │ ├── input.svg
│ │ │ ├── plug-backup.svg
│ │ │ └── plug.svg
│ │ │ └── sources
│ │ │ └── plug_in_learning_ride.lf
│ ├── cleanup.sh
│ ├── CMakeLists.txt
│ └── Makefile
├── services
│ ├── msg
│ │ ├── GetParams.op
│ │ ├── SetResult.op
│ │ └── Init.op
│ ├── CMakeLists.txt
│ ├── src
│ │ ├── random_search.cpp
│ │ └── learning_service.cpp
│ └── include
│ │ └── random_search.h
├── CMakeLists.txt
├── fig
│ ├── fci_learning_structure_bundles.png
│ └── ride_learning_structure_bundles.png
└── README.md
├── fig
├── overview_system_architecture.png
└── panda_programming_interfaces_gs.png
├── cmake
├── FindEigen3.cmake
└── ClangTools.cmake
├── CMakeLists.txt
├── 1_Desk
└── README.md
├── README.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
--------------------------------------------------------------------------------
/2_RIDE/services/msg/SocketInfo.ev:
--------------------------------------------------------------------------------
1 | {int number_of_sockets;};
2 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/manifest.json:
--------------------------------------------------------------------------------
1 | {"version":"1.0"}
2 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/bundles/fci_tutorial/manifest.json:
--------------------------------------------------------------------------------
1 | {"version":"1.0"}
2 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/fci_learning_tutorial/manifest.json:
--------------------------------------------------------------------------------
1 | {"version":"1.0"}
2 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/ride_learning_tutorial/manifest.json:
--------------------------------------------------------------------------------
1 | {"version":"1.0"}
2 |
--------------------------------------------------------------------------------
/4_Learning/services/msg/GetParams.op:
--------------------------------------------------------------------------------
1 | {} request;
2 | {
3 | []float parameters;
4 | } result;
5 | string error;
6 |
--------------------------------------------------------------------------------
/2_RIDE/services/msg/GetSocketPose.op:
--------------------------------------------------------------------------------
1 | {} request;
2 | {
3 | [16]float socket_pose;
4 | } result;
5 | string error;
6 |
--------------------------------------------------------------------------------
/4_Learning/services/msg/SetResult.op:
--------------------------------------------------------------------------------
1 | {
2 | float cost;
3 | bool success;
4 | } request;
5 | bool result;
6 | string error;
7 |
--------------------------------------------------------------------------------
/fig/overview_system_architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frankarobotics/the_reference_platform/HEAD/fig/overview_system_architecture.png
--------------------------------------------------------------------------------
/fig/panda_programming_interfaces_gs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frankarobotics/the_reference_platform/HEAD/fig/panda_programming_interfaces_gs.png
--------------------------------------------------------------------------------
/3_FCI/fig/fci_service_structure_bundles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frankarobotics/the_reference_platform/HEAD/3_FCI/fig/fci_service_structure_bundles.png
--------------------------------------------------------------------------------
/2_RIDE/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(ride_tutorial)
4 |
5 | add_subdirectory(services)
6 | add_subdirectory(statemachines)
7 |
--------------------------------------------------------------------------------
/2_RIDE/fig/plug_in_vision_structure_bundles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frankarobotics/the_reference_platform/HEAD/2_RIDE/fig/plug_in_vision_structure_bundles.png
--------------------------------------------------------------------------------
/4_Learning/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(learning_tutorial)
4 |
5 | add_subdirectory(services)
6 | add_subdirectory(statemachines)
7 |
--------------------------------------------------------------------------------
/4_Learning/fig/fci_learning_structure_bundles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frankarobotics/the_reference_platform/HEAD/4_Learning/fig/fci_learning_structure_bundles.png
--------------------------------------------------------------------------------
/4_Learning/fig/ride_learning_structure_bundles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frankarobotics/the_reference_platform/HEAD/4_Learning/fig/ride_learning_structure_bundles.png
--------------------------------------------------------------------------------
/4_Learning/services/msg/Init.op:
--------------------------------------------------------------------------------
1 | {
2 | int max_iteration;
3 | int param_count;
4 | []float lower_bounds;
5 | []float upper_bounds;
6 | } request;
7 | {} result;
8 | string error;
9 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/cleanup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BUNDLE_NAME="ride_tutorial"
4 |
5 | GREP_RESULT=$(ride bundle list | grep -i $BUNDLE_NAME)
6 | if [ ! -z "$GREP_RESULT" ]
7 | then
8 | echo "'$BUNDLE_NAME' already installed."
9 | echo "Removing the bundle before installing the new one..."
10 | ride bundle remove $BUNDLE_NAME
11 | fi
12 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/cleanup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BUNDLE_NAME="fci_tutorial"
4 |
5 | GREP_RESULT=$(ride bundle list | grep -i $BUNDLE_NAME)
6 | if [ ! -z "$GREP_RESULT" ]
7 | then
8 | echo "'$BUNDLE_NAME' already installed."
9 | echo "Removing the bundle before installing the new one..."
10 | ride bundle remove $BUNDLE_NAME
11 | fi
12 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/cleanup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BUNDLES="ride_learning_tutorial fci_learning_tutorial"
4 |
5 | for BUNDLE in $BUNDLES; do
6 | GREP_RESULT=$(ride bundle list | grep -i $BUNDLE)
7 | if [ ! -z "$GREP_RESULT" ]
8 | then
9 | echo "'$BUNDLE' already installed."
10 | echo "Removing the bundle before installing the new one..."
11 | ride bundle remove $BUNDLE
12 | fi
13 | done
14 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/services/msg/PlugIn.op:
--------------------------------------------------------------------------------
1 | {
2 | {
3 | float translational_stiffness;
4 | float rotational_stiffness;
5 | float desired_force;
6 | float wiggle_frequency_x;
7 | float wiggle_frequency_y;
8 | float wiggle_amplitude_x;
9 | float wiggle_amplitude_y;
10 | } plug_in_params;
11 | float duration;
12 | []float target_position;
13 | []float tolerance;
14 | } request;
15 | {} result;
16 | string error;
17 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(ride_tutorial_statemachines)
4 |
5 | add_custom_target(
6 | remove_ride_tutorial_statemachines
7 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/
8 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/cleanup.sh
9 | )
10 |
11 | add_custom_target(
12 | ride_tutorial_statemachines
13 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/
14 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/cleanup.sh
15 | COMMAND make
16 | )
17 |
--------------------------------------------------------------------------------
/3_FCI/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(fci_tutorial)
4 |
5 | option(BUILD_FCI_STANDALONE "Build fci standalone tutorial" ON)
6 | option(BUILD_FCI_INTEGRATION "Build fci integration tutorial" ON)
7 |
8 | if(BUILD_FCI_STANDALONE OR BUILD_FCI_INTEGRATION)
9 | add_subdirectory(standalone)
10 | endif()
11 |
12 | if(BUILD_FCI_INTEGRATION)
13 | add_subdirectory(ride_integration/services)
14 | add_subdirectory(ride_integration/statemachines)
15 | endif()
16 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(fci_tutorial_statemachines)
4 |
5 | add_custom_target(
6 | remove_fci_tutorial_statemachines
7 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/
8 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/cleanup.sh
9 | )
10 |
11 | add_custom_target(
12 | fci_tutorial_statemachines
13 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/
14 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/cleanup.sh
15 | COMMAND make
16 | )
17 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(learning_tutorial_statemachines)
4 |
5 | add_custom_target(
6 | remove_learning_tutorial_statemachines
7 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/
8 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/cleanup.sh
9 | )
10 |
11 | add_custom_target(
12 | learning_tutorial_statemachines
13 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/
14 | COMMAND ${CMAKE_CURRENT_LIST_DIR}/cleanup.sh
15 | COMMAND make
16 | )
17 |
--------------------------------------------------------------------------------
/cmake/FindEigen3.cmake:
--------------------------------------------------------------------------------
1 | find_package(Eigen3 CONFIG)
2 | mark_as_advanced(FORCE Eigen3_DIR)
3 |
4 | include(FindPackageHandleStandardArgs)
5 | find_package_handle_standard_args(Eigen3
6 | FOUND_VAR Eigen3_FOUND
7 | REQUIRED_VARS EIGEN3_INCLUDE_DIRS
8 | )
9 |
10 | if(NOT TARGET Eigen3::Eigen3)
11 | add_library(Eigen3::Eigen3 INTERFACE IMPORTED)
12 | set_target_properties(Eigen3::Eigen3 PROPERTIES
13 | INTERFACE_INCLUDE_DIRECTORIES ${EIGEN3_INCLUDE_DIRS}
14 | INTERFACE_COMPILE_DEFINITIONS "${EIGEN3_DEFINITIONS}"
15 | )
16 | endif()
17 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(tutorial)
4 |
5 | set(CMAKE_CXX_STANDARD 14)
6 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
8 |
9 | option(BUILD_RIDE "Build RIDE tutorial" ON)
10 | option(BUILD_FCI "Build FCI tutorial" ON)
11 | option(BUILD_LEARNING "Build LEARNING tutorial" ON)
12 |
13 | if(BUILD_RIDE)
14 | add_subdirectory(2_RIDE)
15 | endif()
16 |
17 | if(BUILD_FCI)
18 | add_subdirectory(3_FCI)
19 | endif()
20 |
21 | if(BUILD_LEARNING)
22 | add_subdirectory(4_Learning)
23 | endif()
24 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/services/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(fci_tutorial_services)
4 |
5 | set(CMAKE_CXX_STANDARD 14)
6 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
8 |
9 | find_package(RaceCom)
10 |
11 | racecom_message_library(
12 | TARGET fci_messages
13 | DIRECTORY msg
14 | STATIC)
15 |
16 | add_executable(fci_service src/fci_service.cpp)
17 | target_include_directories(
18 | fci_service
19 | PRIVATE
20 | ${RACECOM_INCLUDE_DIR}
21 | )
22 | target_link_libraries(
23 | fci_service
24 | PRIVATE
25 | ${RACECOM_LIBRARY}
26 | plug_in_controller
27 | fci_messages
28 | )
29 |
30 | # Tools
31 | include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/ClangTools.cmake OPTIONAL RESULT_VARIABLE CLANG_TOOLS)
32 | if(CLANG_TOOLS)
33 | file(
34 | GLOB_RECURSE
35 | SOURCES
36 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
37 | )
38 | file(
39 | GLOB_RECURSE
40 | HEADERS
41 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h
42 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*/*.h
43 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
44 | )
45 | add_format_target(${PROJECT_NAME} FILES ${SOURCES} ${HEADERS})
46 | add_tidy_target(${PROJECT_NAME} FILES ${SOURCES})
47 | endif()
48 |
--------------------------------------------------------------------------------
/2_RIDE/services/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(ride_tutorial_services)
4 |
5 | set(CMAKE_CXX_STANDARD 14)
6 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
8 |
9 | find_package(RaceCom)
10 |
11 | racecom_message_library(
12 | TARGET ride_tutorial_messages
13 | DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/msg
14 | STATIC)
15 |
16 | add_executable(socket_service ${CMAKE_CURRENT_LIST_DIR}/src/socket_service.cpp)
17 | target_include_directories(
18 | socket_service
19 | PRIVATE
20 | ${RACECOM_INCLUDE_DIR}
21 | )
22 | target_link_libraries(
23 | socket_service
24 | PRIVATE
25 | ${RACECOM_LIBRARY}
26 | ride_tutorial_messages
27 | )
28 |
29 | # Tools
30 | include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/ClangTools.cmake OPTIONAL RESULT_VARIABLE CLANG_TOOLS)
31 | if(CLANG_TOOLS)
32 | file(
33 | GLOB_RECURSE
34 | SOURCES
35 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
36 | )
37 | file(
38 | GLOB_RECURSE
39 | HEADERS
40 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h
41 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*/*.h
42 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
43 | )
44 | add_format_target(${PROJECT_NAME} FILES ${SOURCES} ${HEADERS})
45 | add_tidy_target(${PROJECT_NAME} FILES ${SOURCES})
46 | endif()
47 |
--------------------------------------------------------------------------------
/4_Learning/services/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(learning_tutorial_services)
4 |
5 | set(CMAKE_CXX_STANDARD 14)
6 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
8 |
9 | find_package(RaceCom)
10 |
11 | racecom_message_library(
12 | TARGET learning_messages
13 | DIRECTORY msg
14 | STATIC)
15 |
16 | add_library(optimizer)
17 | target_include_directories(
18 | optimizer
19 | PUBLIC
20 | include/
21 | )
22 | target_sources(
23 | optimizer
24 | PRIVATE
25 | src/random_search.cpp
26 | )
27 |
28 | add_executable(learning_service src/learning_service.cpp)
29 | target_include_directories(
30 | learning_service
31 | PRIVATE
32 | ${RACECOM_INCLUDE_DIR}
33 | )
34 | target_link_libraries(
35 | learning_service
36 | PRIVATE
37 | ${RACECOM_LIBRARY}
38 | optimizer
39 | learning_messages
40 | )
41 |
42 | # Tools
43 | include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/ClangTools.cmake OPTIONAL RESULT_VARIABLE CLANG_TOOLS)
44 | if(CLANG_TOOLS)
45 | file(
46 | GLOB_RECURSE
47 | SOURCES
48 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
49 | )
50 | file(
51 | GLOB_RECURSE
52 | HEADERS
53 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h
54 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*/*.h
55 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
56 | )
57 | add_format_target(${PROJECT_NAME} FILES ${SOURCES} ${HEADERS})
58 | add_tidy_target(${PROJECT_NAME} FILES ${SOURCES})
59 | endif()
60 |
--------------------------------------------------------------------------------
/3_FCI/standalone/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(fci_standalone_tutorial)
4 |
5 | list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_LIST_DIR}/../../cmake)
6 |
7 | set(CMAKE_CXX_STANDARD 14)
8 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
9 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
10 |
11 | find_package(Eigen3 REQUIRED)
12 | find_package(Franka)
13 |
14 | add_library(plug_in_controller)
15 | target_include_directories(
16 | plug_in_controller
17 | PUBLIC
18 | include
19 | ${Franka_INCLUDE_DIRS}
20 | )
21 | target_link_libraries(
22 | plug_in_controller
23 | PRIVATE
24 | Eigen3::Eigen3
25 | ${Franka_LIBRARIES}
26 | )
27 | target_sources(plug_in_controller PRIVATE src/plug_in_controller)
28 |
29 | add_executable(plug_in_controller_standalone src/plug_in_controller_standalone.cpp)
30 | target_link_libraries(plug_in_controller_standalone PRIVATE plug_in_controller)
31 |
32 | # Tools
33 | include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/ClangTools.cmake OPTIONAL RESULT_VARIABLE CLANG_TOOLS)
34 | if(CLANG_TOOLS)
35 | file(
36 | GLOB_RECURSE
37 | SOURCES
38 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
39 | )
40 | file(
41 | GLOB_RECURSE
42 | HEADERS
43 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h
44 | ${CMAKE_CURRENT_SOURCE_DIR}/include/*/*.h
45 | ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
46 | )
47 | add_format_target(${PROJECT_NAME} FILES ${SOURCES} ${HEADERS})
48 | add_tidy_target(${PROJECT_NAME} FILES ${SOURCES})
49 | endif()
50 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/Makefile:
--------------------------------------------------------------------------------
1 | # Configurable variables
2 | SOURCE_DIR ?= bundles
3 | BUILDDIR ?= build
4 | DISTDIR ?= $(BUILDDIR)/dist
5 |
6 | # File handling variables
7 | BUNDLE_LF=$(foreach d,$(SOURCE_DIR), $(wildcard $(d)/*/sources/*.lf))
8 | PARSED_STATEMACHINES=$(foreach f,$(BUNDLE_LF), $(BUILDDIR)/$(f:.lf=.json))
9 |
10 | all: upload-bundles
11 |
12 | BUNDLES=$(shell find $(SOURCE_DIR) -type f -name 'manifest.json' | sed -r 's|/[^/]+$$||' | sed -r 's|^$(SOURCE_DIR)/||' | sort | uniq)
13 | bundles: parse $(DISTDIR)
14 | ride bundle compile $(BUNDLES) -B $(BUILDDIR)/bundles --ignore-dependencies -o $(DISTDIR)/$@.zip
15 | .PHONY: bundles
16 |
17 | upload-bundles: bundles
18 | ride bundle install $(DISTDIR)/bundles.zip
19 |
20 | bundle-%: parse $(DISTDIR)
21 | ride bundle compile $(@:bundle-%=%) -B $(BUILDDIR)/bundles --ignore-dependencies -o $(DISTDIR)/$(patsubst bundle-%,%,$(@)).zip $(patsubst bundle-%,%,$(@))
22 |
23 | clean:
24 | rm -rf $(BUILDDIR) $(DISTDIR)
25 |
26 | parse: $(PARSED_STATEMACHINES)
27 | for dir in $(SOURCE_DIR); do \
28 | mkdir -p $(BUILDDIR)/$$dir; \
29 | bundles=$$(find $$dir -type f -name 'manifest.json' | sed -r 's|/[^/]+$$||' | sort | uniq); \
30 | for bundle in $$bundles; do \
31 | mkdir -p $(BUILDDIR)/$$bundle; \
32 | cp $$bundle/manifest.json $(BUILDDIR)/$$bundle; \
33 | if [ -d $$bundle/resources ]; then cp -r $$bundle/resources $(BUILDDIR)/$$bundle; fi; \
34 | ln -fs sources $(BUILDDIR)/$$bundle/statemachines; \
35 | done; \
36 | done
37 |
38 | $(BUILDDIR)/%.json: %.lf
39 | @mkdir -p $(shell dirname $@)
40 | ride cli parse -o $@ -f $<
41 |
42 | $(DISTDIR):
43 | mkdir -p $(DISTDIR)
44 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/Makefile:
--------------------------------------------------------------------------------
1 | # Configurable variables
2 | SOURCE_DIR ?= bundles
3 | BUILDDIR ?= build
4 | DISTDIR ?= $(BUILDDIR)/dist
5 |
6 | # File handling variables
7 | BUNDLE_LF=$(foreach d,$(SOURCE_DIR), $(wildcard $(d)/*/sources/*.lf))
8 | PARSED_STATEMACHINES=$(foreach f,$(BUNDLE_LF), $(BUILDDIR)/$(f:.lf=.json))
9 |
10 | all: upload-bundles
11 |
12 | BUNDLES=$(shell find $(SOURCE_DIR) -type f -name 'manifest.json' | sed -r 's|/[^/]+$$||' | sed -r 's|^$(SOURCE_DIR)/||' | sort | uniq)
13 | bundles: parse $(DISTDIR)
14 | ride bundle compile $(BUNDLES) -B $(BUILDDIR)/bundles --ignore-dependencies -o $(DISTDIR)/$@.zip
15 | .PHONY: bundles
16 |
17 | upload-bundles: bundles
18 | ride bundle install $(DISTDIR)/bundles.zip
19 |
20 | bundle-%: parse $(DISTDIR)
21 | ride bundle compile $(@:bundle-%=%) -B $(BUILDDIR)/bundles --ignore-dependencies -o $(DISTDIR)/$(patsubst bundle-%,%,$(@)).zip $(patsubst bundle-%,%,$(@))
22 |
23 | clean:
24 | rm -rf $(BUILDDIR) $(DISTDIR)
25 |
26 | parse: $(PARSED_STATEMACHINES)
27 | for dir in $(SOURCE_DIR); do \
28 | mkdir -p $(BUILDDIR)/$$dir; \
29 | bundles=$$(find $$dir -type f -name 'manifest.json' | sed -r 's|/[^/]+$$||' | sort | uniq); \
30 | for bundle in $$bundles; do \
31 | mkdir -p $(BUILDDIR)/$$bundle; \
32 | cp $$bundle/manifest.json $(BUILDDIR)/$$bundle; \
33 | if [ -d $$bundle/resources ]; then cp -r $$bundle/resources $(BUILDDIR)/$$bundle; fi; \
34 | ln -fs sources $(BUILDDIR)/$$bundle/statemachines; \
35 | done; \
36 | done
37 |
38 | $(BUILDDIR)/%.json: %.lf
39 | @mkdir -p $(shell dirname $@)
40 | ride cli parse -o $@ -f $<
41 |
42 | $(DISTDIR):
43 | mkdir -p $(DISTDIR)
44 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/Makefile:
--------------------------------------------------------------------------------
1 | # Configurable variables
2 | SOURCE_DIR ?= bundles
3 | BUILDDIR ?= build
4 | DISTDIR ?= $(BUILDDIR)/dist
5 |
6 | # File handling variables
7 | BUNDLE_LF=$(foreach d,$(SOURCE_DIR), $(wildcard $(d)/*/sources/*.lf))
8 | PARSED_STATEMACHINES=$(foreach f,$(BUNDLE_LF), $(BUILDDIR)/$(f:.lf=.json))
9 |
10 | all: upload-bundles
11 |
12 | BUNDLES=$(shell find $(SOURCE_DIR) -type f -name 'manifest.json' | sed -r 's|/[^/]+$$||' | sed -r 's|^$(SOURCE_DIR)/||' | sort | uniq)
13 | bundles: parse $(DISTDIR)
14 | ride bundle compile $(BUNDLES) -B $(BUILDDIR)/bundles --ignore-dependencies -o $(DISTDIR)/$@.zip
15 | .PHONY: bundles
16 |
17 | upload-bundles: bundles
18 | ride bundle install $(DISTDIR)/bundles.zip
19 |
20 | bundle-%: parse $(DISTDIR)
21 | ride bundle compile $(@:bundle-%=%) -B $(BUILDDIR)/bundles --ignore-dependencies -o $(DISTDIR)/$(patsubst bundle-%,%,$(@)).zip $(patsubst bundle-%,%,$(@))
22 |
23 | clean:
24 | rm -rf $(BUILDDIR) $(DISTDIR)
25 |
26 | parse: $(PARSED_STATEMACHINES)
27 | for dir in $(SOURCE_DIR); do \
28 | mkdir -p $(BUILDDIR)/$$dir; \
29 | bundles=$$(find $$dir -type f -name 'manifest.json' | sed -r 's|/[^/]+$$||' | sort | uniq); \
30 | for bundle in $$bundles; do \
31 | mkdir -p $(BUILDDIR)/$$bundle; \
32 | cp $$bundle/manifest.json $(BUILDDIR)/$$bundle; \
33 | if [ -d $$bundle/resources ]; then cp -r $$bundle/resources $(BUILDDIR)/$$bundle; fi; \
34 | ln -fs sources $(BUILDDIR)/$$bundle/statemachines; \
35 | done; \
36 | done
37 |
38 | $(BUILDDIR)/%.json: %.lf
39 | @mkdir -p $(shell dirname $@)
40 | ride cli parse -o $@ -f $<
41 |
42 | $(DISTDIR):
43 | mkdir -p $(DISTDIR)
44 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/resources/plug-inserted.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/bundles/fci_tutorial/resources/plug-inserted.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/fci_learning_tutorial/resources/plug-inserted.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/ride_learning_tutorial/resources/plug-inserted.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/3_FCI/standalone/src/plug_in_controller_standalone.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | int main(int argc, char** argv) {
9 | // Check whether the required arguments were passed
10 | if (argc != 2) {
11 | std::cerr << "Usage: " << argv[0] << " " << std::endl;
12 | return EXIT_FAILURE;
13 | }
14 | std::cout << "WARNING: Collision thresholds are set to high values. "
15 | << "Make sure you have the user stop at hand!" << std::endl
16 | << "WARNING: Make sure that end effector is in contact with a horizontal rigid surface "
17 | "before starting."
18 | << std::endl
19 | << "Press Enter to continue..." << std::endl;
20 | std::cin.ignore();
21 | try {
22 | {
23 | franka::Robot robot(argv[1]);
24 | robot.setCollisionBehavior({{100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0}},
25 | {{100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0}},
26 | {{100.0, 100.0, 100.0, 100.0, 100.0, 100.0}},
27 | {{100.0, 100.0, 100.0, 100.0, 100.0, 100.0}});
28 | }
29 | // some hardcode parameters
30 | plug_in_controller::Parameters params = {1000.0, 30.0, 3.0, 0.8, 0.5, 0.5, 0.8};
31 | double duration = 5.0; // in [s]
32 | std::array target_position_array = {0., 0., 0.};
33 | std::array tolerance_position_array = {0., 0., 0.};
34 | plug_in_controller::run(argv[1], params, duration, target_position_array,
35 | tolerance_position_array);
36 | } catch (const franka::Exception& ex) {
37 | std::cerr << ex.what() << std::endl;
38 | return EXIT_FAILURE;
39 | }
40 | return EXIT_SUCCESS;
41 | }
42 |
--------------------------------------------------------------------------------
/3_FCI/standalone/include/plug_in_controller.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | namespace plug_in_controller {
5 |
6 | /**
7 | * @brief This struct defines the parameters of the plug in controller.
8 | */
9 | struct Parameters {
10 | /**Stiffness in x,y,z direction of the end effector [N/m], Bounded: [0, 2000] N/m */
11 | double translational_stiffness;
12 | /**Stiffness around x,y,z axis of the end effector [N/rad], Bounded: [0, 300] N/m */
13 | double rotational_stiffness;
14 | /**Forces applied in the end effectors z direction [N] */
15 | double desired_force;
16 | /** Frequency of the wiggle in end effectors x direction [Hz] */
17 | double wiggle_frequency_x;
18 | /** Frequency of the wiggle in end effectors y direction [Hz] */
19 | double wiggle_frequency_y;
20 | /** Amplitude of the wiggle in end effectors x direction [m] */
21 | double wiggle_amplitude_x;
22 | /** Amplitude of the wiggle in end effectors y direction [m] */
23 | double wiggle_amplitude_y;
24 | };
25 |
26 | /**
27 | * @brief This function runs the plug in controller
28 | *
29 | * @param robot_ip IP of the robot
30 | * @param plug_in_param Parameters of the plug in controller
31 | * @param duration Amount of time the plug in controller should run. [s]
32 | * @param target_position_array Array defining the target position. When this position is reached
33 | * within an tolerance the controller stops. [m]
34 | * @param target_tolerance_array Array specifying the maximum tolerance. If the end effector reaches
35 | * the target position within this tolerance the controller stops. [m]
36 | */
37 | void run(const std::string& robot_ip,
38 | const Parameters plug_in_params,
39 | const double duration, // in seconds
40 | const std::array target_position_array,
41 | const std::array target_tolerance_array);
42 | } // namespace plug_in_controller
43 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/resources/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/bundles/fci_tutorial/resources/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/fci_learning_tutorial/resources/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/ride_learning_tutorial/resources/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/4_Learning/services/src/random_search.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "random_search.h"
5 |
6 | RandomSearch::RandomSearch() = default;
7 |
8 | void RandomSearch::init(const Hyperparameters& hyperparams) {
9 | hyperparams_ = hyperparams;
10 | // reset optimizer
11 | current_iteration_ = 0;
12 | samples_.clear();
13 | costs_.clear();
14 | constraints_.clear();
15 | best_sample_.clear();
16 | best_cost_ = std::numeric_limits::max();
17 | best_iter_ = 0;
18 | }
19 |
20 | std::vector RandomSearch::getNextSample() {
21 | std::vector next_sample;
22 | // generate random number between lower and upper_bound for every parameter
23 | next_sample.reserve(hyperparams_.param_count);
24 | for (int i = 0; i < hyperparams_.param_count; i++) {
25 | next_sample.push_back(
26 | generateRandomNumber(hyperparams_.lower_bounds[i], hyperparams_.upper_bounds[i]));
27 | }
28 | // store sample
29 | samples_.push_back(next_sample);
30 | return next_sample;
31 | }
32 |
33 | void RandomSearch::setResult(const double cost, const bool constraint_satisfied) {
34 | costs_.push_back(cost);
35 | constraints_.push_back(constraint_satisfied);
36 | // check if cost is smaller
37 | if (cost < best_cost_ && constraint_satisfied) {
38 | best_sample_ = samples_.back(); // last added element
39 | best_cost_ = cost;
40 | best_iter_ = current_iteration_;
41 | }
42 | // print result of exploration and best parameters
43 | std::cout << "Iteration: " << current_iteration_ << std::endl;
44 | std::cout << "Parameters: ";
45 | for (const auto& item : samples_.back()) {
46 | std::cout << item << ' ';
47 | }
48 | std::cout << "Cost: " << cost << " Success:" << static_cast(constraint_satisfied)
49 | << std::endl;
50 | std::cout << "Best Parameters: ";
51 | for (const auto& item : best_sample_) {
52 | std::cout << item << ' ';
53 | }
54 | std::cout << "Best Cost: " << best_cost_ << std::endl;
55 | std::cout << "=======" << std::endl;
56 |
57 | current_iteration_++;
58 | }
59 |
60 | bool RandomSearch::hasFinished() {
61 | // stop after certain number of iteration
62 | return current_iteration_ >= hyperparams_.max_iterations;
63 | }
64 |
65 | double RandomSearch::generateRandomNumber(const double lower_bound, const double upper_bound) {
66 | std::random_device rd;
67 | std::mt19937 gen(rd());
68 | std::uniform_real_distribution dist(lower_bound, upper_bound);
69 | double random_number = dist(gen);
70 | return random_number;
71 | }
72 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/sources/convert_safety.lf:
--------------------------------------------------------------------------------
1 | convert_safety {
2 | port success result~=nil
3 | port error result==nil
4 |
5 | clientData {
6 | description: @{
7 | This statemachine converts the safety parameters (collision thresholds)
8 | received from timeline to the format required by the state machines.
9 | }@;
10 | }
11 |
12 | parameterType {
13 | {
14 | {
15 | float x;
16 | float y;
17 | float z;
18 | float x_rot;
19 | float y_rot;
20 | float z_rot;
21 | } force_thresholds;
22 | {
23 | float j0;
24 | float j1;
25 | float j2;
26 | float j3;
27 | float j4;
28 | float j5;
29 | float j6;
30 | } torque_thresholds;
31 | } safety;
32 | }
33 |
34 | resultType {
35 | {
36 | [7]float lower_torque_thresholds_nominal;
37 | [7]float upper_torque_thresholds_nominal;
38 | [7]float lower_torque_thresholds_acceleration;
39 | [7]float upper_torque_thresholds_acceleration;
40 | [6]float lower_force_thresholds_nominal;
41 | [6]float upper_force_thresholds_nominal;
42 | [6]float lower_force_thresholds_acceleration;
43 | [6]float upper_force_thresholds_acceleration;
44 | } safety;
45 | }
46 |
47 | entry @{
48 | local result = {}
49 | local torque_thresholds = {
50 | parameter.safety.torque_thresholds.j0, parameter.safety.torque_thresholds.j1,
51 | parameter.safety.torque_thresholds.j2, parameter.safety.torque_thresholds.j3,
52 | parameter.safety.torque_thresholds.j4, parameter.safety.torque_thresholds.j5,
53 | parameter.safety.torque_thresholds.j6
54 | }
55 | local force_thresholds = {
56 | parameter.safety.force_thresholds.x, parameter.safety.force_thresholds.y,
57 | parameter.safety.force_thresholds.z, parameter.safety.force_thresholds.x_rot,
58 | parameter.safety.force_thresholds.y_rot, parameter.safety.force_thresholds.z_rot
59 | }
60 | result.safety = {
61 | lower_torque_thresholds_nominal = torque_thresholds;
62 | upper_torque_thresholds_nominal = torque_thresholds;
63 | lower_torque_thresholds_acceleration = torque_thresholds;
64 | upper_torque_thresholds_acceleration = torque_thresholds;
65 | lower_force_thresholds_nominal = force_thresholds;
66 | upper_force_thresholds_nominal = force_thresholds;
67 | lower_force_thresholds_acceleration = force_thresholds;
68 | upper_force_thresholds_acceleration = force_thresholds;
69 | }
70 | setResult(result)
71 | }@
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/4_Learning/services/include/random_search.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /**
7 | * @brief A structure representing hyperparameters for random search
8 | */
9 | struct Hyperparameters {
10 | /** Maximum number of iterations*/
11 | int max_iterations;
12 | /** Number of parameters being optimized*/
13 | int param_count;
14 | /** Lower boundaries for each parameter being optimized*/
15 | std::vector lower_bounds;
16 | /** Upper boundaries for each parameter being optimized*/
17 | std::vector upper_bounds;
18 | };
19 |
20 | /**
21 | * @brief This class implements random search.
22 | */
23 | class RandomSearch {
24 | public:
25 | RandomSearch();
26 | /**
27 | * @brief This method initializes random search by setting hyperparameters and
28 | * resetting the optimization.
29 | * @param hyperparams Hyperparmeters of random search
30 | */
31 | void init(const Hyperparameters& hyperparams);
32 | /**
33 | * @brief This method returns a vector of random sampled parameters.
34 | * @return parameters for the next experimential trial
35 | */
36 | std::vector getNextSample();
37 | /**
38 | * @brief This method sets a cost value and an indicator if the execution was
39 | * successfull.
40 | * @param cost value of the cost function
41 | * @param contraint_statisfied boolean specifying if the constraint has been satisfied
42 | */
43 | void setResult(const double cost, const bool constraint_satisfied);
44 | /**
45 | * @brief This method returns true when the maximum number of iteration are reached.
46 | */
47 | bool hasFinished();
48 | /**
49 | * @brief This method returns the so far best sample
50 | * @return so far best sample minimizing the cost function and satisfying the constraint
51 | * (successful execution).
52 | */
53 | std::vector getBestSample() const { return best_sample_; };
54 | /**
55 | * @brief This method returns the hyperparameters of random search.
56 | * @return hyperparameters of random search
57 | */
58 | Hyperparameters hyperParameters() const { return hyperparams_; }
59 |
60 | private:
61 | /**
62 | * @brief This method returns a random scalar between lower and upper bound
63 | * @param lower_bound
64 | * @param upper_bound
65 | */
66 | double generateRandomNumber(const double lower_bound, const double upper_bound);
67 |
68 | Hyperparameters hyperparams_;
69 | std::vector> samples_;
70 | std::vector costs_;
71 | std::vector constraints_;
72 | std::vector best_sample_;
73 | double best_cost_;
74 | int current_iteration_;
75 | int best_iter_;
76 | };
77 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/resources/input.svg:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/fci_learning_tutorial/resources/input.svg:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/ride_learning_tutorial/resources/input.svg:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/bundles/fci_tutorial/resources/input.svg:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/services/src/fci_service.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include
10 |
11 | struct FCIParams {
12 | std::string robot_ip;
13 | };
14 |
15 | void plugInCb(CallHandle h, struct PlugIn_request* req, void* hint) {
16 | auto* fci_params = static_cast(hint);
17 |
18 | // required convertion
19 | std::array target_position, tolerance;
20 | std::copy(req->target_position, req->target_position + 3, target_position.begin());
21 | std::copy(req->tolerance, req->tolerance + 3, tolerance.begin());
22 |
23 | plug_in_controller::Parameters plug_in_params = {
24 | req->plug_in_params.translational_stiffness, req->plug_in_params.rotational_stiffness,
25 | req->plug_in_params.desired_force, req->plug_in_params.wiggle_frequency_x,
26 | req->plug_in_params.wiggle_frequency_y, req->plug_in_params.wiggle_amplitude_x,
27 | req->plug_in_params.wiggle_amplitude_y};
28 |
29 | // run the plug in controller with the desired parameters
30 | try {
31 | plug_in_controller::run(fci_params->robot_ip, plug_in_params, req->duration, target_position,
32 | tolerance);
33 |
34 | } catch (const franka::Exception& ex) {
35 | std::cerr << ex.what() << std::endl;
36 | PlugIn_error error = strdup(ex.what());
37 | failOperation(h, &error);
38 | free_PlugIn_request(req);
39 | return;
40 | }
41 | succeedOperation(h, {});
42 | free_PlugIn_request(req);
43 | }
44 |
45 | int main(int argc, char* argv[]) {
46 | if (argc != 4) {
47 | std::cerr << "Usage: " << argv[0]
48 | << "" << std::endl;
49 | return EXIT_FAILURE;
50 | }
51 |
52 | FCIParams fci_params{argv[1]};
53 |
54 | installSigIntHandler();
55 | Service* race_service =
56 | registerService(("tcp://" + std::string(argv[1]) + ":" + std::string(argv[2])).c_str(),
57 | ("tcp://" + std::string(argv[3])).c_str(), "fci");
58 | if (race_service == nullptr) {
59 | std::cerr << "Unable to register RaceCom Service: fci" << std::endl;
60 | return EXIT_FAILURE;
61 | }
62 |
63 | try {
64 | int rc = registerOperation(race_service, "plugIn", PlugInDescriptor(),
65 | reinterpret_cast(plugInCb), &fci_params);
66 | if (rc != 0) {
67 | throw std::runtime_error("Could not register operation 'fci.plugIn'.");
68 | }
69 |
70 | } catch (std::exception const& e) {
71 | std::cerr << e.what() << std::endl;
72 | unregisterService(race_service);
73 | return EXIT_FAILURE;
74 | }
75 |
76 | spin(race_service);
77 |
78 | unregisterService(race_service);
79 |
80 | return EXIT_SUCCESS;
81 | }
82 |
--------------------------------------------------------------------------------
/2_RIDE/services/src/socket_service.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | /** Callback for getSocketPose racecom operation. It returns the socket pose.
11 | */
12 | void getSocketPoseCb(CallHandle h, struct GetSocketPose_request* req, void* /*hint*/) {
13 | /*Assume that a vision component determines the socket pose and returns this to the
14 | plug_in_vision statemachine. However, this example only shows how to connect a RaceCom service
15 | to a RIDE statemachine, the socket pose is hardcoded and no vision is performed here. To
16 | successfully run this demo, the user must manually obtain and copy actual socket information
17 | here. The information consists of the cartesian pose which can be obtained by
18 | moving the robot's end-effector to the socket pose and running following command: `ride service
19 | echo robot sensor_data` in a terminal. The cartesian pose is located in the O_T_EE entry.
20 | */
21 | // pose: column major
22 | std::array socket_pose = {
23 | -0.05183746976227782, 0.9983504730999704, -0.024289019966200448, 0.,
24 | 0.9982147812036288, 0.052514346441068005, 0.028111223837941478, 0.,
25 | 0.02934094053127373, -0.02278888279214321, -0.9993096497231606, 0.,
26 | 0.6370806581504417, 0.06103184325272377, 0.24091866348352692, 1.};
27 |
28 | // Prepare result
29 | // First allocate array for pose
30 | auto* socket_pose_c_array = static_cast(allocateArray(16, sizeof(double)));
31 | // ... then copy content from pose to this allocated C-array
32 | std::copy(socket_pose.begin(), socket_pose.end(), socket_pose_c_array);
33 | GetSocketPose_result result = {socket_pose_c_array};
34 |
35 | // Return result
36 | succeedOperation(h, &result);
37 | free_GetSocketPose_request(req);
38 | }
39 |
40 | int main(int argc, char* argv[]) {
41 | if (argc != 4) {
42 | std::cerr << "Usage: " << argv[0]
43 | << "" << std::endl;
44 | return EXIT_FAILURE;
45 | }
46 |
47 | // Register Service
48 | installSigIntHandler();
49 | Service* race_service =
50 | registerService(("tcp://" + std::string(argv[1]) + ":" + std::string(argv[2])).c_str(),
51 | ("tcp://" + std::string(argv[3])).c_str(), "socket-pose");
52 | if (race_service == nullptr) {
53 | std::cerr << "Could not register service 'socket-pose'." << std::endl;
54 | return EXIT_FAILURE;
55 | }
56 |
57 | Publisher* pub = nullptr;
58 | try {
59 | // Register operation
60 | int rc = registerOperation(race_service, "getSocketPose", GetSocketPoseDescriptor(),
61 | reinterpret_cast(getSocketPoseCb), nullptr);
62 | if (rc != 0) {
63 | throw std::runtime_error("Could not register operation 'socket-pose.getSocketPose'.");
64 | }
65 |
66 | // Register event
67 | pub = registerEvent(race_service, "socket_info", SocketInfoDescriptor());
68 |
69 | if (pub == nullptr) {
70 | throw std::runtime_error("Could not register event 'socket-pose.socket_info'.");
71 | }
72 | } catch (const std::runtime_error& e) {
73 | std::cerr << e.what() << std::endl;
74 | unregisterService(race_service);
75 | return EXIT_FAILURE;
76 | }
77 |
78 | while (!shutdownSignalled()) {
79 | spinOnce(race_service, 1000);
80 | // Determine number of sockets, e.g. by a vision component.
81 | // As we don't do any vision here, the number of sockets is hardcoded.
82 | int number_of_sockets = 1;
83 | // create message
84 | SocketInfo socket_info{.number_of_sockets = number_of_sockets};
85 | publish(pub, &socket_info);
86 | }
87 |
88 | unregisterService(race_service);
89 |
90 | return EXIT_SUCCESS;
91 | }
92 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/resources/plug-backup.svg:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/fci_learning_tutorial/resources/plug-backup.svg:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/ride_learning_tutorial/resources/plug-backup.svg:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/bundles/fci_tutorial/resources/plug-backup.svg:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/resources/plug.svg:
--------------------------------------------------------------------------------
1 |
37 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/bundles/fci_tutorial/resources/plug.svg:
--------------------------------------------------------------------------------
1 |
37 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/fci_learning_tutorial/resources/plug.svg:
--------------------------------------------------------------------------------
1 |
37 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/ride_learning_tutorial/resources/plug.svg:
--------------------------------------------------------------------------------
1 |
37 |
--------------------------------------------------------------------------------
/1_Desk/README.md:
--------------------------------------------------------------------------------
1 | # *Desk*
2 | This tutorial illustrates how to program the *Franka Emika AIR Platform* with *Desk*.
3 |
4 | ## Getting started
5 | Make sure you are connected to the robot, either to the *Arm* base or to *Control*. Then enter the robot ip in a web browser to open *Desk*. Your robot ip will be:
6 |
7 | * `robot.franka.de` if you are connected to the *Arm* base, which is running a DHCP server.
8 | * The already configured shopfloor IP if you are connected to *Control*. If you don't know your shopfloor IP you can always connect your PC to the *Arm* base first and configure it in the Admin menu of *Desk*.
9 |
10 | ## Programming with Desk
11 | This tutorial implements the plug in task using *Desk* as described in Section III-A in the [Tutorial Paper](../README.md#tutorial-paper).
12 | The plug in task is programmed using the following three basic Apps included in the Research Apps bundle:
13 |
14 | * The *Cartesian Motion App* will position the robot in its initial pose.
15 | * The *Move to Contact App* will move the robot towards the socket until it detects a contact.
16 | * The *Lissajous* will regulate a constant force in z-axis and produces a wiggle motion in the `(x,y)` -plane for `n` seconds to insert the plug.
17 |
18 | Drag and drop the three Apps from the App pane to the timeline one after another.
19 |
20 | With *Desk* you can also **download** and **import** your tasks. The `plug-in.task` file included in this folder is a preprogrammed timeline for the task described in the tutorial.
21 | To import it, just drag the `plug-in.task` file and drop it into the task area in *Desk*. Please make sure that *CART MOTION*, *MOVE TO CONTACT* and *LISSAJOUS* apps are already available in desk.
22 |
23 | ## Parameterizing the task
24 | In order to run the plug in task in your custom setup you will have to first parameterize each App. If you imported the task file included in this repo, the parameters will not fit your setup so you will also require a reparameterization. You can do it using the web interface and your mouse but we recommend using the *Pilot* buttons:
25 |
26 | 1. Click on the *Cartesian Motion App* and guide the robot to a position above the hole while the robot is holding the plug between its fingers. Press the O button in the *Pilot* to store poses or the X button to delete them. To set the velocity and acceleration factors you can navigate the menus with the directional pad
27 | ➕ and validate with the ✔ .
28 |
29 | 2. Click on the *Move to Contact App* and guide the robot to the socket pose by inserting the grasped plug into the socket. The context menu of this App is very similar to the previous one.
30 |
31 | 3. Click on the *Lissajous App* and set the wiggle and the force until the plug is successfully inserted. To set the desired wiggle motion and force/torque values you can navigate the menus with the directional pad ➕ and validate with the ✔ . For the wiggle motion you need to configure the number of loops and size of each axis. A good starting point is 3 loops in one axis and 2 in the other one. Try to keep the size as small as possible! Start here with a size of 1 and increase the values carefully if required. In a table-top configuration select the `(x,y)` - plane in which to move.
32 | Moreover you will only need positive force values in the `z` axis. Try to keep the force as small as possible! Start with 5 N and increase the values carefully if needed. Also increase the collision thresholds accordingly to avoid any undesired errors. If after trying high values for the forces the robot is still not able to insert the plug, try teaching again the wiggle motion and/or the poses from the first and the second App.
33 |
34 | ## Running the task
35 |
36 | You can run the task in two different ways:
37 |
38 | 1. **Using *Desk* (web-based interface):**
39 | First open *Desk* in a web browser, activate the external activation device (robot lights should be blue) and press the run button on the side bar.
40 | 2. **Using the `ride-cli` command-line tool:**
41 | You can also use the command-line tool `ride-cli` to start your task from a Linux terminal. To do so, open a terminal and connect to the *Core* by using the following command:
42 |
43 | ```sh
44 | ride login
45 | ```
46 |
47 | To run the task activate the external activation device, stop any execution currently running (e.g. idle state machine) by
48 |
49 | ```sh
50 | ride cli stop
51 | ```
52 |
53 | and then enter
54 |
55 | ```sh
56 | ride cli start 0_plug-in
57 | ```
58 |
--------------------------------------------------------------------------------
/3_FCI/ride_integration/statemachines/bundles/fci_tutorial/sources/plug_in_fci.lf:
--------------------------------------------------------------------------------
1 | plug_in_fci {
2 | port success child("plug_in_controller").port("success")
3 | port error child("move_to_socket_pose").port("error") or
4 | child("set_safety").port("error") or
5 | child("plug_in_controller").port("error")
6 |
7 | clientData{
8 | description: @{
9 | This statemachine inserts a plug into a hole. Therefore it first moves to a socket pose.
10 | Then it runs the plug in controller implemented using fci. It is assumed that the plug is
11 | inserted when the robot pose reaches the hole pose within a tolerance.
12 | }@;
13 | }
14 |
15 | parameterType {
16 | -- parameter for socket motion
17 | {
18 | []{
19 | {[16]float pose;} pose;
20 | bool point2point;
21 | float cartesian_velocity_factor;
22 | float cartesian_acceleration_factor;
23 | float cartesian_deceleration_factor;
24 | float q3;
25 | } poses;
26 | {
27 | [6]int K_x;
28 | } cartesian_impedance;
29 | {
30 | [7]int K_theta;
31 | } joint_impedance;
32 | {
33 | [7]float lower_torque_thresholds_nominal;
34 | [7]float upper_torque_thresholds_nominal;
35 | [7]float lower_torque_thresholds_acceleration;
36 | [7]float upper_torque_thresholds_acceleration;
37 | [6]float lower_force_thresholds_nominal;
38 | [6]float upper_force_thresholds_nominal;
39 | [6]float lower_force_thresholds_acceleration;
40 | [6]float upper_force_thresholds_acceleration;
41 | } safety;
42 | int controller_mode;
43 | } socket_motion;
44 | -- safety parameter for plug_in
45 | {
46 | [7]float lower_torque_thresholds_nominal;
47 | [7]float upper_torque_thresholds_nominal;
48 | [7]float lower_torque_thresholds_acceleration;
49 | [7]float upper_torque_thresholds_acceleration;
50 | [6]float lower_force_thresholds_nominal;
51 | [6]float upper_force_thresholds_nominal;
52 | [6]float lower_force_thresholds_acceleration;
53 | [6]float upper_force_thresholds_acceleration;
54 | } insertion_safety;
55 | -- end effector pose when plug is inserted
56 | [16]float hole_pose;
57 | -- plug in parameter
58 | {
59 | float translational_stiffness;
60 | float rotational_stiffness;
61 | float desired_force;
62 | float wiggle_frequency_x;
63 | float wiggle_frequency_y;
64 | float wiggle_amplitude_x;
65 | float wiggle_amplitude_y;
66 | } plug_in_params;
67 | -- tolerance between the end effector pose
68 | -- and hole pose to check if plug is successfully inserted [m]
69 | [3]float tolerance;
70 | -- maximum insertion time [s]
71 | float duration;
72 | }
73 |
74 | resultType {
75 | string error_cause;
76 | }
77 |
78 | --> move_to_socket_pose <- move_via_with_move_configuration {
79 | port success -> set_safety
80 | } where parameter.socket_motion
81 |
82 | -- set collisions thresholds
83 | set_safety <- set_safety {
84 | port success -> plug_in_controller
85 | } where parameter.insertion_safety
86 |
87 | -- Run the plug_in fci controller. If service returns success, we assume plug has been inserted.
88 | -- Otherwise we assume failure
89 | plug_in_controller {
90 | port success service("fci").operation("plugIn").status == "success"
91 | port error service("fci").operation("plugIn").status == "error"
92 |
93 | parameterType {
94 | {
95 | float translational_stiffness;
96 | float rotational_stiffness;
97 | float desired_force;
98 | float wiggle_frequency_x;
99 | float wiggle_frequency_y;
100 | float wiggle_amplitude_x;
101 | float wiggle_amplitude_y;
102 | } plug_in_params;
103 | float duration;
104 | [3]float tolerance;
105 | [3]float target_position;
106 | }
107 |
108 | resultType {
109 | string error_cause;
110 | }
111 |
112 | entry @{
113 | service("fci").operation("plugIn").call({
114 | plug_in_params = parameter.plug_in_params;
115 | duration = parameter.duration;
116 | tolerance = parameter.tolerance;
117 | target_position = parameter.target_position;
118 | })
119 | }@
120 |
121 | action service("fci").operation("plugIn").status == "error" @{
122 | setResult({error_cause = service("fci").operation("plugIn").error})
123 | }@
124 |
125 | } where {
126 | plug_in_params: parameter.plug_in_params;
127 | duration: parameter.duration;
128 | tolerance: parameter.tolerance;
129 | target_position: [parameter.hole_pose[13], parameter.hole_pose[14], parameter.hole_pose[15]];
130 | }
131 |
132 | -- Check for triggered ports and set result accordingly.
133 |
134 | action child("move_to_socket_pose").port("error") @{
135 | setResult({error_cause = child("move_to_socket_pose").result.error_cause})
136 | }@
137 |
138 | action child("set_safety").port("error") @{
139 | setResult({error_cause = child("set_safety").result.error_cause})
140 | }@
141 |
142 | action child("plug_in_controller").port("error") @{
143 | setResult({error_cause = child("plug_in_controller").result.error_cause})
144 | }@
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/4_Learning/services/src/learning_service.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | /**
12 | * @brief Helper function to convert a c_array to a vector
13 | */
14 | template
15 | std::vector cArrayToVector(int size, T* c_array) {
16 | return std::vector(c_array, c_array + size);
17 | }
18 |
19 | /**
20 | * @brief Helper function to convert a vector to a c_array
21 | */
22 | template
23 | T* vectorToCArray(std::vector vec) {
24 | auto* c_array = static_cast(allocateArray(static_cast(vec.size()), sizeof(double)));
25 | std::copy(vec.begin(), vec.end(), c_array);
26 | return c_array;
27 | }
28 |
29 | /**
30 | * @brief Callback for init racecom operation. It calls init method of random search
31 | * class.
32 | */
33 | void initCb(CallHandle h, struct Init_request* req, void* hint) {
34 | auto* random_search = static_cast(hint);
35 | Hyperparameters hyperparams;
36 | hyperparams.max_iterations = req->max_iteration;
37 | hyperparams.param_count = req->param_count;
38 | hyperparams.lower_bounds = cArrayToVector(req->param_count, req->lower_bounds);
39 | hyperparams.upper_bounds = cArrayToVector(req->param_count, req->upper_bounds);
40 | random_search->init(hyperparams);
41 | succeedOperation(h, {});
42 | free_Init_request(req);
43 | }
44 |
45 | /**
46 | * @brief Callback for get_param racecom operation. It returns an array of
47 | * random sampled parameters by calling getNextSample method of random search class.
48 | */
49 | void getParamsCb(CallHandle h, struct GetParams_request* req, void* hint) {
50 | auto* random_search = static_cast(hint);
51 | std::vector next_sample = random_search->getNextSample();
52 | GetParams_result result{vectorToCArray(next_sample)};
53 | succeedOperation(h, &result);
54 | free_GetParams_request(req);
55 | }
56 |
57 | /**
58 | * @brief Callback for set_result racecom operation. It sets the result of
59 | * the execution for the random search class.
60 | */
61 | void setResultCb(CallHandle h, struct SetResult_request* req, void* hint) {
62 | auto* random_search = static_cast(hint);
63 | random_search->setResult(req->cost, req->success);
64 | bool stop = random_search->hasFinished();
65 | succeedOperation(h, &stop);
66 | free_SetResult_request(req);
67 | }
68 |
69 | /**
70 | * @brief Callback for get_best_params operation. It returns an array of the best
71 | * observed parameters.
72 | */
73 | void getBestParamsCb(CallHandle h, struct GetParams_request* req, void* hint) {
74 | auto* random_search = static_cast(hint);
75 | std::vector best_params = random_search->getBestSample();
76 | if (best_params.empty()) {
77 | GetParams_error error = strdup("App hasn't been learned so far!");
78 | failOperation(h, &error);
79 | free_GetParams_request(req);
80 | return;
81 | }
82 | GetParams_result result{vectorToCArray(best_params)};
83 | succeedOperation(h, &result);
84 | free_GetParams_request(req);
85 | }
86 |
87 | int main(int argc, char* argv[]) {
88 | if (argc != 4) {
89 | std::cerr << "Usage: " << argv[0]
90 | << "" << std::endl;
91 | return EXIT_FAILURE;
92 | }
93 |
94 | RandomSearch random_search;
95 |
96 | // Register Service
97 | installSigIntHandler();
98 | Service* race_service =
99 | registerService(("tcp://" + std::string(argv[1]) + ":" + std::string(argv[2])).c_str(),
100 | ("tcp://" + std::string(argv[3])).c_str(), "learning");
101 | if (race_service == nullptr) {
102 | std::cerr << "Unable to register RaceCom Service: learning" << std::endl;
103 | return EXIT_FAILURE;
104 | }
105 |
106 | // Register all operations
107 | try {
108 | int rc = registerOperation(race_service, "init", InitDescriptor(),
109 | reinterpret_cast(initCb), &random_search);
110 | if (rc != 0) {
111 | throw std::runtime_error("Could not register operation 'learning.init'.");
112 | }
113 |
114 | rc = registerOperation(race_service, "getParams", GetParamsDescriptor(),
115 | reinterpret_cast(getParamsCb), &random_search);
116 | if (rc != 0) {
117 | throw std::runtime_error("Could not register operation 'learning.getParams'.");
118 | }
119 |
120 | rc = registerOperation(race_service, "setResult", SetResultDescriptor(),
121 | reinterpret_cast(setResultCb), &random_search);
122 | if (rc != 0) {
123 | throw std::runtime_error("Could not register operation 'learning.setResult'.");
124 | }
125 |
126 | rc = registerOperation(race_service, "getBestParams", GetParamsDescriptor(),
127 | reinterpret_cast(getBestParamsCb), &random_search);
128 |
129 | if (rc != 0) {
130 | throw std::runtime_error("Could not register operation 'learning.getBestParams'.");
131 | }
132 |
133 | } catch (std::exception const& e) {
134 | std::cerr << e.what() << std::endl;
135 | unregisterService(race_service);
136 | return EXIT_FAILURE;
137 | }
138 |
139 | // Start event loop
140 | spin(race_service);
141 |
142 | unregisterService(race_service);
143 |
144 | return EXIT_SUCCESS;
145 | }
146 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/sources/plug_in_vision.lf:
--------------------------------------------------------------------------------
1 | plug_in_vision {
2 | port success child("plug_in").port("success")
3 | port error child("check_for_sockets").port("error") or
4 | child("get_socket_pose").port("error") or
5 | child("plug_in").port("error")
6 |
7 | clientData {
8 | description: @{
9 | This statemachine implements the vision-based plug-in statemachine as described in the tutorial paper.
10 | It basically shows how to connect a service with a statemachine. Therefore it first subscribes to the
11 | `socket-pose.socket_info` event and checks the number of detected sockets. If the number of sockets
12 | are larger than 0, this statemachine calls the `socket-pose.getSocketPose` operation to get the pose
13 | of the socket and continue with the already implemented plug_in statemachine.
14 | }@;
15 | }
16 |
17 | parameterType {
18 | {
19 | [7]float lower_torque_thresholds_nominal;
20 | [7]float upper_torque_thresholds_nominal;
21 | [7]float lower_torque_thresholds_acceleration;
22 | [7]float upper_torque_thresholds_acceleration;
23 | [6]float lower_force_thresholds_nominal;
24 | [6]float upper_force_thresholds_nominal;
25 | [6]float lower_force_thresholds_acceleration;
26 | [6]float upper_force_thresholds_acceleration;
27 | } safety;
28 | {
29 | -- parameter for wiggle motion
30 | float amplitude_factor_translation;
31 | float amplitude_factor_rotation;
32 | -- stiffness of robot during insertion
33 | float impedance_factor;
34 | -- insertion force in z direction of robot's end-effector frame [N]
35 | float F_z;
36 | } plug_in_params;
37 | -- tolerance between the end effector pose
38 | -- and hole pose to check if plug is successfully inserted [m]
39 | [3]float tolerance;
40 | -- maximum insertion time [s]
41 | float duration;
42 | }
43 |
44 | resultType {
45 | string error_cause;
46 | }
47 |
48 | -- Check number of detected sockets. The amount of detected sockets `number_of_sockets`
49 | -- will be published by the event `sockets-info` of the `socket-pose` service.
50 | -- If `number_of_sockets` is larger than 0 continue to the next step,
51 | -- otherwise fail the plug_in_vision.
52 | --> check_for_sockets {
53 | port success service("socket-pose").event("socket_info")~=nil and
54 | service("socket-pose").event("socket_info").number_of_sockets > 0 -> get_socket_pose
55 | port error service("socket-pose").event("socket_info")~=nil and
56 | service("socket-pose").event("socket_info").number_of_sockets == 0
57 | }
58 |
59 | -- Get the socket pose.
60 | -- It is received from the `getSocketPose` operation of the `socket-pose` operation.
61 | get_socket_pose {
62 | port success service("socket-pose").operation("getSocketPose")
63 | .status == "success" -> get_q3
64 | port error service("socket-pose").operation("getSocketPose").status == "error"
65 |
66 | resultType
67 | {
68 | [16]float socket_pose;
69 | }
70 |
71 | entry @{
72 | service("socket-pose").operation("getSocketPose").call({})
73 | }@
74 |
75 | exit @{
76 | setResult(service("socket-pose").operation("getSocketPose").result)
77 | }@
78 | }
79 |
80 | -- For the socket motion in the plug_in statemachine, a desired angle of joint 3 (q3)
81 | -- for the nullspace is required. This q3 will be set to the current q3.
82 | get_q3 {
83 | port done result~=nil -> compute_hole_pose
84 |
85 | resultType float
86 |
87 | action service("robot").event("sensor_data")~=nil @{
88 | setResult(service("robot").event("sensor_data").q[3])
89 | }@
90 | }
91 |
92 | -- Compute the hole_pose.
93 | -- The hole pose is required to check if plug is inserted.
94 | compute_hole_pose {
95 | port success result~=nil -> plug_in
96 |
97 | parameterType {
98 | [16]float socket_pose;
99 | }
100 |
101 | resultType {
102 | [16]float hole_pose;
103 | }
104 |
105 | entry @{
106 | -- It is assumed that the `hole_pose` is equal to the `socket_pose`
107 | -- minus the plug length in z direction of the base frame.
108 | -- NOTE: If you change the plug, update the plug_length accordingly.
109 | local plug_length = 0.01 -- [m] valid for HDMI plug
110 | local hole_pose = parameter.socket_pose
111 | hole_pose[15] = hole_pose[15] - plug_length
112 | setResult{hole_pose = hole_pose}
113 | }@
114 | } where child("get_socket_pose").result
115 |
116 | -- Plug in the plug
117 | plug_in <- plug_in_wrapper {
118 | } where {
119 | socket_pose: {
120 | pose: child("get_socket_pose").result.socket_pose;
121 | q3: child("get_q3").result;
122 | };
123 | hole_pose: child("compute_hole_pose").result.hole_pose;
124 | plug_in_params: parameter.plug_in_params;
125 | duration: parameter.duration;
126 | tolerance: parameter.tolerance;
127 | safety: parameter.safety;
128 | }
129 |
130 | -- check for triggered ports and set result accordingly
131 |
132 | action child("check_for_sockets").port("error") @{
133 | setResult({error_cause = "Could not detect any socket."})
134 | }@
135 |
136 | action child("get_socket_pose").port("error") @{
137 | setResult({error_cause = "Get_socket_pose: Operation call failed."})
138 | }@
139 |
140 | action child("plug_in").port("error") @{
141 | setResult(child("plug_in").result)
142 | }@
143 |
144 | action child("plug_in").port("success") @{
145 | print("Plug-in-vision ended successfully!")
146 | }@
147 |
148 | } where {
149 | safety: {
150 | lower_torque_thresholds_nominal: [60., 60., 60., 60., 50., 40., 30.];
151 | upper_torque_thresholds_nominal: [60., 60., 60., 60., 50., 40., 30.];
152 | lower_torque_thresholds_acceleration: [60., 60., 60., 60., 50., 40., 30.];
153 | upper_torque_thresholds_acceleration: [60., 60., 60., 60., 50., 40., 30.];
154 | lower_force_thresholds_nominal: [60., 60., 60., 30., 30., 30.];
155 | upper_force_thresholds_nominal: [60., 60., 60., 30., 30., 30.];
156 | lower_force_thresholds_acceleration: [60., 60., 60., 30., 30., 30.];
157 | upper_force_thresholds_acceleration: [60., 60., 60., 30., 30., 30.];
158 | };
159 | plug_in_params: {
160 | amplitude_factor_translation: 1.0;
161 | amplitude_factor_rotation: 1.0;
162 | impedance_factor: 4.0;
163 | F_z: 10.0;
164 | };
165 | duration: 15.0;
166 | tolerance: [0.015, 0.015, 0.001];
167 | }
168 |
--------------------------------------------------------------------------------
/3_FCI/standalone/src/plug_in_controller.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 |
14 | namespace plug_in_controller {
15 |
16 | void run(const std::string& robot_ip,
17 | const Parameters plug_in_params,
18 | const double duration, // in seconds
19 | const std::array target_position_array,
20 | const std::array target_tolerance_array) {
21 | // Parameters for stopping criterion
22 | Eigen::Vector3d target_position(Eigen::Vector3d::Map(target_position_array.data()));
23 | Eigen::Vector3d target_tolerance(Eigen::Vector3d::Map(target_tolerance_array.data()));
24 |
25 | // Compliance parameters
26 | Eigen::MatrixXd cartesian_stiffness(6, 6), cartesian_damping(6, 6);
27 | cartesian_stiffness.setZero();
28 | cartesian_stiffness.topLeftCorner(3, 3)
29 | << plug_in_params.translational_stiffness * Eigen::MatrixXd::Identity(3, 3);
30 | cartesian_stiffness.bottomRightCorner(3, 3)
31 | << plug_in_params.rotational_stiffness * Eigen::MatrixXd::Identity(3, 3);
32 | cartesian_damping.setZero();
33 | cartesian_damping.topLeftCorner(3, 3)
34 | << 2.0 * sqrt(plug_in_params.translational_stiffness) * Eigen::MatrixXd::Identity(3, 3);
35 | cartesian_damping.bottomRightCorner(3, 3)
36 | << 2.0 * sqrt(plug_in_params.rotational_stiffness) * Eigen::MatrixXd::Identity(3, 3);
37 |
38 | // Force parameters
39 | Eigen::VectorXd force_error_integral(6);
40 | // force control P, I gain
41 | double k_p{0.0};
42 | double k_i{1.0};
43 |
44 | franka::Robot robot(robot_ip);
45 | // load the kinematics and dynamics model
46 | franka::Model model = robot.loadModel();
47 |
48 | // initial robot state
49 | franka::RobotState initial_state = robot.readOnce();
50 | Eigen::Map> force_initial(initial_state.O_F_ext_hat_K.data());
51 | force_error_integral.setZero();
52 |
53 | // equilibrium point is the initial position
54 | Eigen::Affine3d initial_transform(Eigen::Matrix4d::Map(initial_state.O_T_EE.data()));
55 | Eigen::Vector3d position_d(initial_transform.translation());
56 | Eigen::Quaterniond orientation_initial(initial_transform.linear());
57 |
58 | double time = 0.0;
59 |
60 | // define callback for the torque control loop
61 | std::function
62 | plug_in_control_callback =
63 | [&](const franka::RobotState& robot_state, franka::Duration period) -> franka::Torques {
64 | time += period.toSec();
65 |
66 | // get state variables
67 | std::array coriolis_array = model.coriolis(robot_state);
68 | std::array jacobian_array =
69 | model.zeroJacobian(franka::Frame::kEndEffector, robot_state);
70 |
71 | // convert to Eigen
72 | Eigen::Map> coriolis(coriolis_array.data());
73 | Eigen::Map> jacobian(jacobian_array.data());
74 | Eigen::Map> force(robot_state.O_F_ext_hat_K.data());
75 | Eigen::Map> dq(robot_state.dq.data());
76 | Eigen::Affine3d transform(Eigen::Matrix4d::Map(robot_state.O_T_EE.data()));
77 | Eigen::Vector3d position(transform.translation());
78 | Eigen::Quaterniond orientation(transform.linear());
79 |
80 | // Compute desired orientation of equilibrium pose and desired forces
81 | // wiggle motion
82 | Eigen::AngleAxisd angle_axis_wiggle_x;
83 | angle_axis_wiggle_x.axis() << 1, 0, 0;
84 | angle_axis_wiggle_x.angle() = sin(2.0 * M_PI * time * plug_in_params.wiggle_frequency_x) *
85 | plug_in_params.wiggle_amplitude_x;
86 | Eigen::AngleAxisd angle_axis_wiggle_y;
87 | angle_axis_wiggle_y.axis() << 0, 1, 0;
88 | angle_axis_wiggle_y.angle() = sin(2.0 * M_PI * time * plug_in_params.wiggle_frequency_y) *
89 | plug_in_params.wiggle_amplitude_y;
90 | Eigen::Quaterniond wiggle_x(angle_axis_wiggle_x);
91 | Eigen::Quaterniond wiggle_y(angle_axis_wiggle_y);
92 | Eigen::Quaterniond orientation_d(wiggle_y * (wiggle_x * orientation_initial));
93 |
94 | // desired forces
95 | Eigen::VectorXd desired_force(6);
96 | desired_force.setZero();
97 | desired_force(2) = -plug_in_params.desired_force;
98 |
99 | // Compute error to desired equilibrium pose
100 | // position error
101 | Eigen::Matrix error;
102 | error.head(3) << position - position_d;
103 |
104 | // orientation error
105 | if (orientation_d.coeffs().dot(orientation.coeffs()) < 0.0) {
106 | orientation.coeffs() << -orientation.coeffs();
107 | }
108 | // "difference" quaternion
109 | Eigen::Quaterniond error_quaternion(orientation.inverse() * orientation_d);
110 | error.tail(3) << error_quaternion.x(), error_quaternion.y(), error_quaternion.z();
111 | // Transform to base frame
112 | error.tail(3) << -transform.linear() * error.tail(3);
113 |
114 | // Compute error to desired force removing initial bias
115 | Eigen::VectorXd force_error;
116 | force_error = desired_force - force + force_initial;
117 | force_error_integral = force_error_integral + period.toSec() * force_error;
118 |
119 | // Compute control
120 | Eigen::VectorXd force_control(6), tau_force(7), tau_cart(7), tau_cmd(7);
121 |
122 | // Force control term
123 | force_control = desired_force + k_p * force_error + k_i * force_error_integral;
124 | force_control << 0, 0, force_control(2), 0, 0, 0;
125 | tau_force = jacobian.transpose() * force_control;
126 |
127 | // Cartesian control term
128 | tau_cart << jacobian.transpose() *
129 | (-cartesian_stiffness * error - cartesian_damping * (jacobian * dq));
130 |
131 | // Commanded torque
132 | tau_cmd << tau_cart + tau_force + coriolis;
133 |
134 | std::array tau_d_array{};
135 | Eigen::VectorXd::Map(&tau_d_array[0], 7) = tau_cmd;
136 |
137 | // check if motion is completed
138 | Eigen::Matrix target_error((target_position - position).cwiseAbs());
139 | bool is_inserted =
140 | (target_error[0] <= target_tolerance[0] && target_error[1] <= target_tolerance[1] &&
141 | target_error[2] <= target_tolerance[2]);
142 | if (time <= duration && is_inserted) {
143 | return franka::MotionFinished(franka::Torques(tau_d_array));
144 | }
145 | if (time > duration) {
146 | throw franka::Exception("Timeout: Plug in controller failed!");
147 | }
148 | return tau_d_array;
149 | };
150 |
151 | robot.control(plug_in_control_callback);
152 | }
153 |
154 | } // namespace plug_in_controller
155 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/sources/plug_in.lf:
--------------------------------------------------------------------------------
1 | plug_in {
2 | port success child("monitor_pose").port("is_inserted")
3 | port error child("move_to_socket_pose").port("error") or
4 | child("set_move_configuration").port("error") or
5 | child("wiggle").port("error") or
6 | child("wiggle").port("success") or
7 | child("press").port("error") or
8 | child("press").port("success")
9 |
10 | clientData {
11 | description: @{
12 | This statemachine inserts a plug into a hole. Therefore it first moves to a socket pose.
13 | Then the robot executes a wiggle motion and presses in the end effector`s z-direction
14 | to insert the plug. We assume that the plug is inserted when
15 | the robot pose reaches the hole pose within an tolerance.
16 | }@;
17 | }
18 |
19 | parameterType {
20 | -- parameter for socket motion
21 | {
22 | []{
23 | {[16]float pose;} pose;
24 | bool point2point;
25 | float cartesian_velocity_factor;
26 | float cartesian_acceleration_factor;
27 | float cartesian_deceleration_factor;
28 | float q3;
29 | } poses;
30 | {
31 | [6]int K_x;
32 | } cartesian_impedance;
33 | {
34 | [7]int K_theta;
35 | } joint_impedance;
36 | {
37 | [7]float lower_torque_thresholds_nominal;
38 | [7]float upper_torque_thresholds_nominal;
39 | [7]float lower_torque_thresholds_acceleration;
40 | [7]float upper_torque_thresholds_acceleration;
41 | [6]float lower_force_thresholds_nominal;
42 | [6]float upper_force_thresholds_nominal;
43 | [6]float lower_force_thresholds_acceleration;
44 | [6]float upper_force_thresholds_acceleration;
45 | } safety;
46 | int controller_mode;
47 | } socket_motion;
48 | -- wiggle motion for insertion
49 | {
50 | [6]{
51 | int type;
52 | float a_0;
53 | [5]float a_n;
54 | [5]float b_n;
55 | float T_fourier;
56 | } profiles;
57 | float duration;
58 | int frame_selection;
59 | float safety_distance_x;
60 | float safety_distance_y;
61 | float safety_distance_z;
62 | float safety_distance_theta;
63 | } wiggle_motion;
64 | -- force profile for insertion
65 | {
66 | [6]float F_d;
67 | float duration;
68 | float safety_distance_x;
69 | float safety_distance_y;
70 | float safety_distance_z;
71 | float safety_distance_theta;
72 | float force_tolerance;
73 | } force;
74 | -- move configuration during the actual insertion: high collision thresholds,
75 | -- low stiffness (depends on high-level parameter impedance_factor)
76 | {
77 | -- cartesian impedance during insertion
78 | {
79 | [6]int K_x;
80 | } cartesian_impedance;
81 | -- joint impedance during insertion, affecting nullspace if a cartesian controller is used
82 | {
83 | [7]int K_theta;
84 | } joint_impedance;
85 | {
86 | [7]float lower_torque_thresholds_nominal;
87 | [7]float upper_torque_thresholds_nominal;
88 | [7]float lower_torque_thresholds_acceleration;
89 | [7]float upper_torque_thresholds_acceleration;
90 | [6]float lower_force_thresholds_nominal;
91 | [6]float upper_force_thresholds_nominal;
92 | [6]float lower_force_thresholds_acceleration;
93 | [6]float upper_force_thresholds_acceleration;
94 | } safety;
95 | } insertion_move_configuration;
96 | -- end effector pose when plug is inserted
97 | [16]float hole_pose;
98 | -- tolerance between the end effector pose
99 | -- and hole pose to check if plug is successfully inserted [m]
100 | [3]float tolerance;
101 | }
102 |
103 | resultType {
104 | string error_cause;
105 | }
106 |
107 | --> move_to_socket_pose <- move_via_with_move_configuration {
108 | port success -> set_move_configuration
109 | } where parameter.socket_motion
110 |
111 | -- set collisions thresholds and stiffness for the actual insertion
112 | set_move_configuration <- set_move_configuration {
113 | port success -> insert
114 | } where parameter.insertion_move_configuration
115 |
116 | -- Execute the wiggle motion, apply force in end effector's z direction
117 | -- and monitor end effector pose in parallel. If hole pose is not reached
118 | -- after a certain amount of time, the plug in state machine failed. Otherwise
119 | -- we assume the plug is inserted and the state machine finishes with success.
120 | barrier insert {
121 | -> wiggle
122 | -> press
123 | -> monitor_pose
124 | }
125 |
126 | -- Execution of the wiggle motion. The robot follows a fourier profile.
127 | wiggle <- move_velocity {
128 | } where parameter.wiggle_motion
129 |
130 | -- Apply force in end effector's z-direction for a certain amount of time.
131 | press <- apply_force {
132 | } where parameter.force
133 |
134 | -- Monitor current end effector pose and check if hole pose is reached within an tolerance.
135 | -- If hole pose is reached we assume that the plug is successfully inserted
136 | monitor_pose {
137 | port is_inserted result~=nil and result
138 |
139 | parameterType{
140 | [16]float target_pose;
141 | [3]float tolerance;
142 | }
143 |
144 | resultType bool
145 |
146 | action service("robot").event("sensor_data") ~= nil @{
147 | local current = service("robot").event("sensor_data").O_T_EE.pose
148 | local x = abs(parameter.target_pose[13] - current[13])
149 | local y = abs(parameter.target_pose[14] - current[14])
150 | local z = abs(parameter.target_pose[15] - current[15])
151 | if x < parameter.tolerance[1] and y < parameter.tolerance[2] and z < parameter.tolerance[3] then
152 | setResult(true)
153 | end
154 | }@
155 | } where {
156 | target_pose: parameter.hole_pose;
157 | tolerance: parameter.tolerance;
158 | }
159 |
160 | -- Check for triggered ports and set result accordingly.
161 |
162 | action child("move_to_socket_pose").port("error") @{
163 | setResult({error_cause = child("move_to_socket_pose").result.error_cause})
164 | }@
165 |
166 | action child("set_move_configuration").port("error") @{
167 | setResult({error_cause = child("set_move_configuration").result.error_cause})
168 | }@
169 |
170 | action child("wiggle").port("error") @{
171 | setResult({error_cause = child("wiggle").result.error_cause})
172 | }@
173 |
174 | action child("press").port("success") @{
175 | setResult({error_cause = "Insertion failed: Timeout."})
176 | }@
177 |
178 | action child("wiggle").port("success") @{
179 | setResult({error_cause = "Insertion failed: Timeout."})
180 | }@
181 |
182 | action child("press").port("error") @{
183 | setResult({error_cause = child("press").result.error_cause})
184 | }@
185 |
186 | }
187 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A Tutorial on the Reference Platform for AI and Robotics (AIR) from Franka Emika
2 |
3 | The Franka Emika Panda Series is the first series of industrial AI-enabled tactile robot platforms. Beyond industrial use, the systems can be seamlessly expanded to fulfill the demands of *research and education* across all robotics and AI disciplines.
4 |
5 | In this repository you will find the accompanying code of the Reference Platform Paper. If this code helps you in your research, please cite.
6 |
7 | In addition, the researchers at Munich School of Robotics and Machine Learning (MSRM) have prepared a series of comprehensive tutorial videos which can be found [here](https://www.youtube.com/playlist?list=PLeVqiD9lLCrlxLJI3Eibi-je2fNqwt1am).
8 |
9 | ## Three programming interfaces
10 |
11 | 
12 |
13 |
14 | The AIR Platfrom provides three different programming interfaces:
15 |
16 | * **Desk**, a high level App-based user interface for easy and fast task programming. *Desk*, together with the Research Apps is conceptualized to be the right interface for high-level programs, such as human-robot interaction studies or for rapid prototyping quick ideas.
17 | * **RIDE**, a command-based programming environment to create high performance robot skills that enables programming custom Apps and integrating external sensors. *RIDE* is the most suitable programming interface for research that does not require per-millisecond reactions, such as task and path planning or perception-based manipulation.
18 | * The **FCI**, a 1 kHz low-level torque and position control interface that exploits the also available Lagrangian dynamic robot model. It is the right
19 | interface to explore new low-level planning and control schemes.
20 |
21 | #### System Architecture
22 |
23 | Before getting started, get familiar with the system architecture shown in the following figure:
24 |
25 | 
26 |
27 | The *Franka Operating System* running on Franka Emika robots has the following main components:
28 |
29 | * *State machines*, which are the basic units of execution in the system. They call operations from services, execute scripts, and may contain other state machines. All Apps as seen in *Desk* are state machines. They are grouped together into *bundles*, which also store related resources such as icons. State machines are programmed in a domain-specific language called *Lingua Franka* and stored in .lf files.
30 |
31 | * *Services* are nodes which run on *Control* (internal services) or any external computer (external services). They communicate with the core via the *race-com* library and can provide operations and publish events periodically. The most relevant service is the *robot service*, which provides all operations to command the robot such as move or applyForce.
32 |
33 | * The *Core* orchestrates the execution of state machines, stores installed bundles and ensures that newly uploaded bundles are consistent with existing ones. It also implements three system interfaces:
34 | * The *Franka Application Interface* (FAI), the interface used by *Desk* to command and monitor the system.
35 | * The *Franka Professional Interface* (FPI), the interface used by *ride-cli* to manipulate, run and install state machines and Apps in the system.
36 | * The *Franka Service Interface* (FSI), the interface used by the *race-com* library to connect external services to the system.
37 |
38 | The *Application Software* represent the access point for users. There are currently two clients in the system:
39 |
40 | * *Desk*, which can start the execution of timelines~(which are sequences of state machines) and trigger other specific functionalities such as opening the brakes or turning the power on or off.
41 | * *ride-cli*, the main App development tool, which provides a command-line interface to start, compile, install, monitor and debug the execution of state machines.
42 |
43 |
44 | Note that the *FCI*, the low-level 1 kHz interface to the robot bypasses the Franka Operating System and represents a direct UDP-based connection to the real-time robot control.
45 |
46 | ## The tutorial
47 | This tutorial showcases the wide range of programming possibilities that the Franka Emika's AIR Platform offers. We strongly recommend to read first the *Reference Platform Paper* before continuing. The tutorial is divided in 4 lessons and built around a plug in task, solved with all three programming interfaces:
48 |
49 | * The [first lesson](1_Desk/), [`1_Desk`](1_Desk/), shows how to quickly prototype the plug in task in *Desk* and how to start tasks from the web interface or the command line tool.
50 | * The [second lesson](2_RIDE/), [`2_RIDE`](2_RIDE/), implements a custom and more capable plug in App with *RIDE*. It then adds an external service simulating the integration of a camera and incorporates it into the App.
51 | * The [third lesson](3_FCI/), [`3_FCI`](3_FCI/), implements a custom plug in controller as a Cartesian impedance controller with an additional term regulating the contact force. It then integrates this custom controller into an App.
52 | * The [fourth lesson](4_Learning/), [`4_Learning`](4_Learning/), implements an on robot learning algorithm that learns the optimal control parameters for the custom App and the custom controller presented in the second and third lesson respectively.
53 |
54 |
55 | ## RIDE CLI
56 | The following table lists some helpful commands that might be used during the tutorial.
57 |
58 | | Command | Description |
59 | |----------------------------------------|---------------------------------------------|
60 | | `ride login ` | Connect to the core. |
61 | | `ride bundle list` | List installed bundles. |
62 | | `ride node list` | List root nodes (installed state machines). |
63 | | `ride service list` | List connected services. |
64 | | `ride execution start ` | Start execution of a state machine. |
65 | | `ride execution stop` | Stop execution gracefully. |
66 | | `ride log` | Display the log message feed from the core. |
67 | | `ride service echo ` | Print service events relayed by the core. |
68 | | `ride execution trace` | Trace execution of a state machine. |
69 |
70 | ## Requirements
71 |
72 | * Ubuntu with PREEMP_RT patch according to [Minimum System and Network Requirements](https://frankaemika.github.io/docs/requirements.html)
73 | * minumum cmake version: cmake >= 3.5
74 | * Robot System version 4.X
75 | * RIDE version 3.X
76 | * RaceCom version 14.X
77 | * libfranka version 0.8.X
78 |
79 | ## Tutorial paper
80 |
81 | **The Franka Emika Robot: A Reference Platform for Robotics Research and Education** paper is located [here](https://ieeexplore.ieee.org/document/9721535).
82 |
83 | ## License
84 |
85 | `the_reference_platform` is licensed under the [Apache 2.0 license][apache-2.0].
86 |
87 | [apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0.html
88 |
--------------------------------------------------------------------------------
/3_FCI/README.md:
--------------------------------------------------------------------------------
1 | # *FCI*
2 | This tutorial shows how to program with the *FCI* as described in the tutorial paper in Section III-C.
3 | The *FCI* offers a 1kHz real-time transparent interface to the robot that allows to run custom controllers on an external computer running Linux with the PREEMPT RT patch.
4 | It can either fully circumvent *Control* or rely on the internal controllers to follow joint or Cartesian space trajectories at 1 kHz specified with the motion interfaces. Specifically, the *FCI* also provides joint position, joint velocity, Cartesian pose and Cartesian velocity interfaces. *libfranka*, the open source C++ library for the client-side of the interface, provides also access to all sensor measurements and the robot model library, which includes the kinematic and Lagrangian dynamic model parameters. This tutorial will show how to implement a standalone custom controller and then how to embed this custom controller into an App.
5 |
6 | **Note:** For more information about the *FCI* check out its [documentation](https://frankaemika.github.io/docs/index.html).
7 |
8 | ## Installation
9 | Make sure that libfranka, *RIDE* and *RaceCom* are installed on your computer. To install libfranka follow these [instructions](https://frankaemika.github.io/docs/installation_linux.html). To install the necessary state machines for the *RIDE* integration, make sure that you are connected to *Control* (The *FCI* will not work if you are connected to the *Arm* base!) and logged into the *Core*:
10 |
11 | ```sh
12 | ride login
13 | ```
14 |
15 | Create your build folder by running the following command on the root of the repository (not in the `3_FCI` folder!):
16 |
17 | ```sh
18 | mkdir build && cd build
19 | ```
20 |
21 | Run cmake with the following flags in order to build the *FCI* tutorial
22 |
23 | ```sh
24 | cmake -DBUILD_RIDE=OFF -DBUILD_FCI=ON -DBUILD_LEARNING=OFF -DCMAKE_BUILD_TYPE=Release -DFranka_DIR= ..
25 | ```
26 |
27 | and finally compile the *FCI* tutorial with
28 |
29 | ```sh
30 | make
31 | ```
32 |
33 | To compile the state machines and Apps and upload them to your robot run
34 |
35 | ```sh
36 | make fci_tutorial_statemachines
37 | ```
38 |
39 | After installing the bundle the `Plug In FCI` App should appear in the app pane. Note that the App is grey shaded as long as the `fci_service` is not running.
40 |
41 | ## Standalone
42 | To program a plug in task with the *FCI* we implement a Cartesian impedance controller with an additional term that regulates the desired contact force. For details on the mathematical background, please check the [Tutorial Paper](../README.md#tutorial-paper). The controller is implemented in `plug_in_controller.cpp/.h` as a reusable function which is used in `plug_in_controller_standalone.cpp` to run the controller. The controller finishes when the `hole_pose` is reached within an certain tolerance and time. Otherwise it throws an `franka_exception` which stop the robot control. For more information on the controller have look at the paper in Section III-C.
43 |
44 | #### Running the custom plug in controller
45 |
46 | To run any *FCI* controller first make sure that your PC is running a real-time kernel and that it's connected to *Control*.
47 |
48 | **Warning:** Make sure that end effector is in contact with a horizontal surface before running this code. The controller regulates the contact force along the z-axis of the robot's end-effector and if no object is holding it, the robot will move down!
49 |
50 | To run the plug in controller go to the root directory of the repository, activate the external activation device and run
51 |
52 | ```sh
53 | ./build/3_FCI/standalone/plug_in_controller_standalone
54 | ```
55 |
56 | **Note:** You can change the control parameters in `plug_in_controller_standalone.cpp` and observe its impact to the behavior of the robot. Make sure you rebuild the plug in controller every time you make a change. The parameters are described in the header `plug_in_controller.h`.
57 |
58 | ## *RIDE* integration
59 | An *FCI* program can be also integrated into a state machine or an App. To do so we will need (1) an external *RIDE* service with an operation that runs the *FCI* controller and (2) a state machine that calls the operation. The following figure illustrates the interplay of interfaces in the system architecture and the involved bundles.
60 |
61 | 
62 |
63 | You can find the corresponding files in the `ride_integration` directory.
64 |
65 | 1. ***FCI* service:**
66 | The implementation of the *RaceCom* service `fci` is located in `services/src/fci_service.cpp`. It has an operation `plugIn` that runs the *FCI* plug in controller described in the standalone section of this tutorial. The operation is defined in the `services/msg/PlugIn.op` file. It specifies an operation that accepts the plug in controller parameters and returns nothing in case of success and a string including the error message in case of any errors.
67 |
68 | 2. **State machine:**
69 | The state machines are very similar to the plug in state machines of the `ride_tutorial` bundle. It also follows the same multi-layered implementation, but in this case the wrapper layer is not necessary because we have to convert less parameters from the context menu. Specifically, the `fci_tutorial` bundle located in the `statemachines/bundles` folder consists of the following state machines:
70 |
71 | 1. *The App layer `plug_in_fci_app.lf`:*
72 | This state machine implements the *context menu* and handles all parameters such as the socket pose, the hole pose and the plug in controller parameters. It then performs the plug in by calling the `plug_in_fci` statemachine.
73 |
74 | 2. *The execution layer `plug_in_fci.lf`:*
75 | This statemachine executes the plug in. It's the same code as in the `plug_in` state machine of the `ride_tutorial` bundle, but instead of the `insert` state, the operation `plugIn` of the `fci` service is called to run the controller.
76 |
77 |
78 | ### Running the Plug in *FCI* App
79 | If you successfully installed the `fci_tutorial` bundle you should see the `Plug In FCI` App (most probably grey shaded) in the App area of *Desk*.
80 | Make sure you are connected to *Control* and do the following:
81 |
82 | 1. Run the `fci` service:
83 | Open a terminal, go to the root of the repository and run
84 |
85 | ```sh
86 | ./build/3_FCI/ride_integration/services/fci_service 11511
87 | ```
88 |
89 | with the following arguments:
90 |
91 | * ``: is IP address of the robot (Your preconfigured shopfloor IP).
92 | * ``: is the network interface used by your computer to reach the robot. You can check your network interfaces with the `ip a` command. If you are using an ethernet adapter, it will be named `enpXXXX`. Wireless adapters are denoted as `wlpXXX`. Ask your system administrator if you can't figure out your network interface.
93 |
94 | If everything went well, the service should be available and will appear listed when you enter the following command
95 |
96 | ```sh
97 | ride cli services
98 | ```
99 |
100 | In addition, the `Plug In FCI` App should now appear colored in Desk. The reason for that is that, for each App, Desk checks if the services that it requires are connected to the robot. If a service is missing, the App will appear grey shaded and will not be executable. In our example, after connecting the `fci` service the App should fulfill all requirements and should appear colored.
101 |
102 | 2. Run the `Plug In FCI` App in Desk:
103 |
104 | 1. Create a new Task
105 |
106 | 2. *Program the task:* Drag the `Plug In FCI` App into the Timeline of the Task
107 |
108 | 3. *Parameterize the task:* Click on the `Plug In FCI` App and follow the instruction to teach the robot.
109 | **Note:** The expert parameters are preconfigured and needn't to be changed. Anyway you can play around with them and observe how the behavior of the robot changes.
110 |
111 | 4. **Optional:** Add a `Cartesian Motion` App before the `Plug in FCI` App to allow for the experiment to be repeated. Teach the `Cartesian Motion` in a way that it unplugs the plug.
112 |
113 | 5. Activate the external activation device and click on the run button to start the task. **This will finally make the robot move!**
114 |
--------------------------------------------------------------------------------
/cmake/ClangTools.cmake:
--------------------------------------------------------------------------------
1 | include(CMakeParseArguments)
2 |
3 | find_program(CLANG_FORMAT_PROG clang-format-6.0 DOC "'clang-format' executable")
4 | if(CLANG_FORMAT_PROG AND NOT TARGET format)
5 | add_custom_target(format)
6 | add_custom_target(check-format)
7 | endif()
8 | find_program(CLANG_TIDY_PROG clang-tidy-6.0 DOC "'clang-tidy' executable")
9 | if(CLANG_TIDY_PROG AND NOT TARGET tidy)
10 | if(NOT CMAKE_EXPORT_COMPILE_COMMANDS)
11 | message(WARNING "Invoke Catkin/CMake with '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON'
12 | to generate compilation database for 'clang-tidy'.")
13 | endif()
14 |
15 | add_custom_target(tidy)
16 | add_custom_target(check-tidy)
17 | endif()
18 | find_program(AUTOPEP8_PROG autopep8 DOC "'autopep8' executable")
19 | if(AUTOPEP8_PROG AND NOT TARGET pep8)
20 | add_custom_target(pep8)
21 | endif()
22 | find_package(Git QUIET)
23 | if(NOT GIT_FOUND)
24 | message(WARNING "Git cannot be found. ")
25 | endif()
26 | find_program(BASENAME_PROG basename DOC "'basename' executable")
27 | if(NOT BASENAME_PROG)
28 | message(WARNING "basename executable cannot be found. ")
29 | endif()
30 | find_program(XARGS_PROG xargs DOC "'xargs' executable")
31 | if(NOT XARGS_PROG)
32 | message(WARNING "xargs executable cannot be found. ")
33 | endif()
34 | find_program(PARALLEL_PROG parallel DOC "'parallel' executable")
35 | if(PARALLEL_PROG)
36 | set(PARALLEL_DELIMITER ":::")
37 | else()
38 | message(WARNING "parallel executable cannot be found. Try with 'sudo apt install parallel'.")
39 | set(PARALLEL_PROG "")
40 | endif()
41 |
42 | # Starting at SEARCH_DIR, look up directory hierarchy for directory containing NAME.
43 | # SEARCH_DIR is a directory. Stop at the first parent directory containing a folder NAME,
44 | # and return the directory in the parent scope in FOUND_DOMINATING_DIR. Return empty if not found.
45 | #
46 | # USAGE:
47 | # locate_dominating_dir(
48 | # NAME
49 | # SEARCH_DIR
50 | # )
51 | #
52 | # ARGUMENTS:
53 | # NAME
54 | # name of the directory to be found
55 | # SEARCH_DIR
56 | # directory path where the search starts
57 | #
58 | # OUTPUT VARIABLES:
59 | # FOUND_DOMINATING_DIR
60 | # path to the directory where NAME is first found
61 | #
62 | # EXAMPLE:
63 | # locate_dominating_dir(
64 | # NAME .git
65 | # SEARCH_URL ${CMAKE_CURRENT_LIST_DIR}
66 | # )
67 | #
68 | function(locate_dominating_dir)
69 | cmake_parse_arguments(ARG "" "NAME;SEARCH_DIR" "" ${ARGN})
70 | if(IS_DIRECTORY ${ARG_SEARCH_DIR}/${ARG_NAME})
71 | set(FOUND_DOMINATING_DIR "${ARG_SEARCH_DIR}" PARENT_SCOPE)
72 | elseif(EXISTS ${ARG_SEARCH_DIR}/..)
73 | locate_dominating_dir(
74 | NAME ${ARG_NAME}
75 | SEARCH_DIR ${ARG_SEARCH_DIR}/..
76 | )
77 | set(FOUND_DOMINATING_DIR ${FOUND_DOMINATING_DIR} PARENT_SCOPE)
78 | endif()
79 | endfunction()
80 |
81 | # Return the git repository name of the calling CMakeLists.txt.
82 | #
83 | # USAGE:
84 | # get_git_repo_name()
85 | #
86 | # OUTPUT VARIABLES:
87 | # GIT_REPO_NAME
88 | # git repository name of the calling CMakeLists.txt
89 | #
90 | function(get_git_repo_name)
91 | if(NOT GIT_FOUND OR NOT BASENAME_PROG OR NOT XARGS_PROG)
92 | return()
93 | endif()
94 | execute_process(
95 | COMMAND ${GIT_EXECUTABLE} config --get remote.origin.url
96 | COMMAND xargs ${BASENAME_PROG} -s .git
97 | WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}"
98 | OUTPUT_VARIABLE GIT_REPO_NAME
99 | RESULT_VARIABLE GIT_GET_NAME_RESULT
100 | ERROR_QUIET
101 | OUTPUT_STRIP_TRAILING_WHITESPACE)
102 | if(NOT GIT_GET_NAME_RESULT EQUAL "0")
103 | unset(GIT_REPO_NAME)
104 | message(WARNING "${CMAKE_CURRENT_LIST_FILE} is probably not inside git repository.")
105 | endif()
106 | set(GIT_REPO_NAME ${GIT_REPO_NAME} PARENT_SCOPE)
107 | endfunction()
108 |
109 | # Use clang-format to format c/c++/Objective-C code.
110 | #
111 | # USAGE:
112 | # add_format_target(_target
113 | # FILES
114 | # )
115 | #
116 | # ARGUMENTS:
117 | # _target
118 | # name of the target
119 | # FILES
120 | # group of paths to header and source files
121 | #
122 | # EXAMPLE:
123 | # add_format_target(${PROJECT_NAME}
124 | # FILES ${SOURCES} ${HEADERS}
125 | # )
126 | #
127 | function(add_format_target _target)
128 | if(NOT CLANG_FORMAT_PROG)
129 | return()
130 | endif()
131 | cmake_parse_arguments(ARG "" "" "FILES" ${ARGN})
132 | locate_dominating_dir(
133 | NAME .git
134 | SEARCH_DIR ${CMAKE_CURRENT_LIST_DIR}
135 | )
136 | if (NOT FOUND_DOMINATING_DIR)
137 | set(WORKING_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
138 | else()
139 | set(WORKING_DIR ${FOUND_DOMINATING_DIR})
140 | endif()
141 |
142 | add_custom_target(format-${_target}
143 | COMMAND ${CLANG_FORMAT_PROG} -i ${ARG_FILES}
144 | WORKING_DIRECTORY ${WORKING_DIR}
145 | COMMENT "Formatting ${_target} source code with clang-format"
146 | VERBATIM
147 | )
148 | add_dependencies(format format-${_target})
149 |
150 | add_custom_target(check-format-${_target}
151 | COMMAND ${CLANG_FORMAT_PROG} -output-replacements-xml ${ARG_FILES} | grep " /dev/null && exit 1 || exit 0
152 | WORKING_DIRECTORY ${WORKING_DIR}
153 | COMMENT "Checking ${_target} code formatting with clang-format"
154 | VERBATIM
155 | )
156 | add_dependencies(check-format check-format-${_target})
157 |
158 | get_git_repo_name()
159 | if(GIT_REPO_NAME)
160 | if(NOT TARGET format-${GIT_REPO_NAME})
161 | add_custom_target(format-${GIT_REPO_NAME})
162 | add_custom_target(check-format-${GIT_REPO_NAME})
163 | endif()
164 | add_dependencies(format-${GIT_REPO_NAME} format-${_target})
165 | add_dependencies(check-format-${GIT_REPO_NAME} check-format-${_target})
166 | endif()
167 |
168 | endfunction()
169 |
170 | # Use clang-tidy to tidy c/c++ code.
171 | #
172 | # USAGE:
173 | # add_tidy_target(_target
174 | # FILES
175 | # DEPENDS
176 | # )
177 | #
178 | # ARGUMENTS:
179 | # _target
180 | # name of the target
181 | # FILES
182 | # group of paths to c/c++ source files
183 | # DEPENDS
184 | # group of dependency targets
185 | #
186 | # EXAMPLE:
187 | # add_tidy_target(${PROJECT_NAME}
188 | # FILES ${SOURCES}
189 | # DEPENDS dep_target_1 dep_target_2
190 | # )
191 | #
192 | function(add_tidy_target _target)
193 | if(NOT CLANG_TIDY_PROG)
194 | return()
195 | endif()
196 |
197 | cmake_parse_arguments(ARG "" "" "FILES;DEPENDS" ${ARGN})
198 | locate_dominating_dir(
199 | NAME .git
200 | SEARCH_DIR ${CMAKE_CURRENT_LIST_DIR})
201 | if (NOT FOUND_DOMINATING_DIR)
202 | set(WORKING_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
203 | else()
204 | set(WORKING_DIR ${FOUND_DOMINATING_DIR})
205 | endif()
206 |
207 | add_custom_target(tidy-${_target}
208 | COMMAND ${PARALLEL_PROG} ${CLANG_TIDY_PROG} -fix -p=${CMAKE_BINARY_DIR} ${PARALLEL_DELIMITER} ${ARG_FILES}
209 | WORKING_DIRECTORY ${WORKING_DIR}
210 | DEPENDS ${ARG_DEPENDS}
211 | COMMENT "Running clang-tidy for ${_target}"
212 | VERBATIM)
213 | add_dependencies(tidy tidy-${_target})
214 |
215 | add_custom_target(check-tidy-${_target}
216 | COMMAND ${PARALLEL_PROG} ${CLANG_TIDY_PROG} -p=${CMAKE_BINARY_DIR} ${PARALLEL_DELIMITER} ${ARG_FILES} | grep . && exit 1 || exit 0
217 | WORKING_DIRECTORY ${WORKING_DIR}
218 | DEPENDS ${ARG_DEPENDS}
219 | COMMENT "Running clang-tidy for ${_target}"
220 | VERBATIM)
221 | add_dependencies(check-tidy check-tidy-${_target})
222 |
223 | get_git_repo_name()
224 | if(GIT_REPO_NAME)
225 | if(NOT TARGET tidy-${GIT_REPO_NAME})
226 | add_custom_target(tidy-${GIT_REPO_NAME})
227 | add_custom_target(check-tidy-${GIT_REPO_NAME})
228 | endif()
229 | add_dependencies(tidy-${GIT_REPO_NAME} tidy-${_target})
230 | add_dependencies(check-tidy-${GIT_REPO_NAME} check-tidy-${_target})
231 | endif()
232 | endfunction()
233 |
234 | # Use autopep8 to format python code with pep8.
235 | #
236 | # USAGE:
237 | # add_pep8_target(_target
238 | # FILES
239 | # )
240 | #
241 | # ARGUMENTS:
242 | # _target
243 | # name of the target
244 | # FILES
245 | # group of paths to python files
246 | #
247 | # EXAMPLE:
248 | # add_pep8_target(${PROJECT_NAME}
249 | # FILES ${PY_SOURCES}
250 | # )
251 | #
252 | function(add_pep8_target _target)
253 | if(NOT AUTOPEP8_PROG)
254 | return()
255 | endif()
256 | cmake_parse_arguments(ARG "" "" "FILES" ${ARGN})
257 |
258 | add_custom_target(pep8-${_target}
259 | COMMAND ${AUTOPEP8_PROG} -i ${ARG_FILES}
260 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
261 | COMMENT "Formatting ${_target} source code with autopep8"
262 | VERBATIM)
263 | add_dependencies(pep8 pep8-${_target})
264 | endfunction()
265 |
--------------------------------------------------------------------------------
/2_RIDE/README.md:
--------------------------------------------------------------------------------
1 | # *RIDE*
2 | This tutorial illustrates how to program with *RIDE*, as described in the [Tutorial Paper](../README.md#tutorial-paper) in Section III-B. It consists of the implementation of the `plug_in` and `plug_in_vision` state machines, their corresponding Apps and services.
3 |
4 | ## Installation
5 | Make sure that *RIDE* and *RaceCom* is installed in your computer, you are connected to the *Arm* base or to *Control* and logged into the *Core*:
6 |
7 | ```sh
8 | ride login
9 | ```
10 |
11 | Create your build folder by running the following command on the root of this repository (not in the `2_RIDE` folder!):
12 |
13 | ```sh
14 | mkdir build && cd build
15 | ```
16 |
17 | Run cmake with the following flags in order to build the RIDE tutorials
18 |
19 | ```sh
20 | cmake -DBUILD_RIDE=ON -DBUILD_FCI=OFF -DBUILD_LEARNING=OFF ..
21 | ```
22 |
23 | and finally compile the *RIDE* tutorial with
24 |
25 | ```sh
26 | make
27 | ```
28 |
29 | To upload the state machines and Apps to the robot run
30 |
31 | ```sh
32 | make ride_tutorial_statemachines
33 | ```
34 |
35 | After installing the bundle the Apps should appear in the app pane. In addition to that, the bundle should appear listed after entering the following command
36 |
37 | ```sh
38 | ride bundle list
39 | ```
40 |
41 | Furthermore the individual state machines and Apps should be listed when you enter
42 |
43 | ```sh
44 | ride node list
45 | ```
46 |
47 | ## Plug in App
48 | The Plug in App is located in the `ride-tutorial` bundle in the `statemachines` directory. Every bundle consists of a `manifest.json` defining the bundle, a resource folder that stores all the icons for the context menu and a source folder, containing the state machines and App files.
49 | The Plug in App of this tutorial is implemented in three different state machine files that can also be seen as layers:
50 |
51 | 1. *The App layer `plug_in_app.lf`:*
52 | This state machine implements the *context menu* where the user can set required parameters in *Desk* like the socket and hole pose. It is also the 'main' execution file: after getting the context menu parameters, it runs the `plug_in_wrapper` state machine which first converts the collected parameters to state machine parameters and then performs the plug insertion.
53 | 2. *The wrapper layer `plug_in_wrapper.lf`:*
54 | This statemachine receives the high-level parameters from the context menus and converts them to the actual parameters required for plug in and then runs the `plug_in` state machine.
55 | 3. *The execution layer `plug_in.lf`:*
56 | This state machine implements the execution of the plug in task given by the following state flow:
57 |
58 | 1. `move_to_socket_pose` - a cartesian motion to the socket_pose with high stiffness for high accuracy and sensitivity
59 | 2. `set_move_configuration` - set collision thresholds and stiffness for the insertion
60 | 3. `insert` - a *barrier* executing the following states in parallel:
61 |
62 | * `wiggle` - execute a wiggle motion in the (x,y) plane
63 | * `press` - apply and regulate a constant force along the z-axis
64 | * `monitor_pose` - monitor the current end-effector pose and check if the hole pose has been reached within a certain tolerance
65 |
66 | This implementation assumes that a plug in is successful when the robot reaches the hole pose within a certain tolerance and maximum time.
67 |
68 | The advantage of such a multi-layer implementation is on the one hand the reusability of individual state machines (e.g. `plug_in` and `plug_in_wrapper`) and the clarity (implementation of context menu, parameter conversion and execution are split in three different files).
69 |
70 |
71 | #### Running the Plug in App
72 | If you installed successfully the `ride_tutorial` the `Plug in` App will appear in the App pane of *Desk*.
73 | To run it follow the subsequent instructions:
74 |
75 | 1. Create a new Task
76 | 2. Program the task: Drag the `Plug in` App into the Timeline of the Task
77 | 3. Parameterize the task: Click on the `Plug in` App and follow the instructions to teach the robot. Note that the context menu that appear in this step are defined in the `plug_in_app.lf` file.
78 | **Note:** The expert parameters are preconfigured and don't need to be changed. However, you can play around with them and see how the behavior of the robot changes.
79 | 4. **Optional:** Add a `Cartesian Motion` App before the `Plug in` App to allow for the experiment to be repeated. Teach the `Cartesian Motion` in a way that it unplugs the plug.
80 | 5. Activate the external activation device and click on the run button to start the task. **This will make the robot move!**
81 |
82 | You can trace the execution in a linux terminal with
83 |
84 | ```sh
85 | ride execution trace
86 | ```
87 |
88 | and check the logs with
89 |
90 | ```sh
91 | ride log
92 | ```
93 |
94 | ## Plug in App with vision
95 | The Plug in App described above is capable of performing an insertion for a socket and hole pose taught with *Desk*. Let us assume now that a computer vision module is estimating the socket pose and we would like to use it in our `Plug in` state machine. To realize this we need: (1) a state machine executing the plug in and (2) a service providing the socket pose.
96 | The following figure illustrates the interplay of interfaces in the system architecture and the involved bundles.
97 |
98 | 
99 |
100 | 1. **Socket-pose service**
101 | The `socket-pose` service is located in `services/src/socket_service.cpp`. It implements the `getSocketPose` operation that returns the socket pose and periodically publishes the `socket-info` event with the number of sockets detected. The operation type is defined in the `GetSocketPose.op` file in `services/msg` directory. It specifies an empty operation call that returns a 16-dimensional float array in case of success and a string in case of an error. Similarly, the event type is defined in the `SocketInfo.ev` file which specifies a message given by an integer.
102 | **Note:** The socket-pose and number of sockets are hardcoded. Please adapt this to your setup.
103 |
104 | 2. **State machine**
105 | The state machine is implemented in the `plug_in_vision.lf` file located in the `ride_tutorial` bundle. It consists of the following state flow:
106 |
107 | 1. `check_for_sockets` - subscribes to the event `socket-info` and checks if any sockets are detected.
108 | 2. `get_socket_pose` - calls the operation `getSocketPose` which returns the pose.
109 | 3. `compute_hole_pose` - computes `hole_pose` from `socket_pose`
110 | 4. `plug_in` - converts parameters and inserts the plug into the detected socket by calling `plug_in_wrapper` from above
111 |
112 | Note that this example focuses on how to connect an external service with a state machine. All hypothetical vision related results such as the socket pose are hardcoded.
113 |
114 |
115 | #### Running the Plug in App with vision
116 | Before running the demo you probably want to adapt the hardcoded pose of the `socket-pose` service to your setup. To do so modify the hardcoded `socket_pose` in `services/src/socket_service.cpp` (l. 22) and compile the service again as described in the installation instructions. To adapt the socket pose to your setup, guide the robot to the desired pose and run the following command in a terminal
117 |
118 | ```sh
119 | ride service echo robot sensor_data
120 | ```
121 |
122 | which echoes all robot sensor data. Copy the output of the `O_T_EE` (end effector pose) and paste it in the `services/src/socket_service.cpp` file (l. 22). After recompiling as described in the installation instructions the service should be providing the new hardcoded pose.
123 |
124 | To run the App do the following:
125 |
126 | 1. Run the service:
127 | Open a terminal, go to the root of the repository and run
128 |
129 | ```sh
130 | ./build/2_RIDE/services/socket_service 11511
131 | ```
132 |
133 | with the following arguments:
134 |
135 | * ``: is IP address of the robot (`robot.franka.de` if you are connected to the base or your preconfigured robot ip otherwise).
136 | * ``: is the network interface used by your computer to reach the robot. You can check your network interfaces with the `ip a` command. If you are using an ethernet adapter, it will be named `enpXXXX`. Wireless adapters are denoted as `wlpXXX`. Ask your system administrator if you can't figure out your network interface.
137 |
138 | After running the service, it should appear listed when you enter the following command
139 |
140 | ```sh
141 | ride service list
142 | ```
143 |
144 | 2. Run the `plug_in_vision` state machine:
145 | To run the `plug_in_vision` state machine first activate the external activation device, stop any execution currently running (e.g. idle state machine) by
146 |
147 | ```sh
148 | ride execution stop
149 | ```
150 | and then
151 |
152 | ```sh
153 | ride execution start plug_in_vision
154 | ```
155 |
156 | **This will make the robot move!**
157 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/sources/plug_in_app.lf:
--------------------------------------------------------------------------------
1 | plug_in_app {
2 | port Success child("plug_in").port("success")
3 | port Error child("plug_in").port("error") or
4 | child("convert").port("error")
5 |
6 | clientData {
7 | type : "app";
8 | name : "Plug In";
9 | color : "hsl(230, 55%, 82%)";
10 | description: @{
11 | This app inserts a plug into a hole. It gets high-level parameter from desk
12 | and calls the plug-in-wrapper for converting parameters and inserting the plug.
13 | }@;
14 | requiredParameters: [
15 | { source: safety; localParameter: safety; }
16 | ];
17 | image: @{
18 |
19 | }@;
20 | contextMenu: @{
21 |
22 |
32 |
33 |
36 |
46 |
47 |
48 |
51 |
52 |
53 |
54 |
56 |
68 |
69 |
72 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
90 |
91 |
92 |
93 |
102 |
103 |
104 |
106 |
111 |
112 |
113 |
121 |
122 |
123 |
124 |
131 |
132 |
133 |
134 |
141 |
142 |
143 |
144 |
151 |
152 |
153 | }@;
154 | }
155 |
156 | parameterType {
157 | -- safety parameters, received from timeline
158 | {
159 | {
160 | float x;
161 | float y;
162 | float z;
163 | float x_rot;
164 | float y_rot;
165 | float z_rot;
166 | } force_thresholds;
167 | {
168 | float j0;
169 | float j1;
170 | float j2;
171 | float j3;
172 | float j4;
173 | float j5;
174 | float j6;
175 | } torque_thresholds;
176 | } safety;
177 | -- pose of socket
178 | {
179 | [16]float pose;
180 | []float joint_angles;
181 | } socket_pose;
182 | -- end effector pose when plug is inserted
183 | {
184 | [16]float pose;
185 | []float joint_angles;
186 | } hole_pose;
187 | {
188 | -- parameter for wiggle motion
189 | float amplitude_factor_translation;
190 | float amplitude_factor_rotation;
191 | -- stiffness of robot during insertion
192 | float impedance_factor;
193 | -- insertion force in z direction of robot's end-effector frame [N]
194 | float F_z;
195 | } plug_in_params;
196 | -- tolerance between the end effector pose
197 | -- and hole pose to check if plug is successfully inserted [m]
198 | [3]float tolerance;
199 | -- maximum insertion time [s]
200 | float duration;
201 | }
202 |
203 | resultType {
204 | string error_cause;
205 | }
206 |
207 | -- convert timeline parameters
208 | --> convert <- convert_safety {
209 | port success -> plug_in
210 | } where {
211 | safety: parameter.safety;
212 | }
213 |
214 | -- plug_in plug
215 | plug_in <- plug_in_wrapper {
216 | } where {
217 | socket_pose: {
218 | pose: parameter.socket_pose.pose;
219 | q3: parameter.socket_pose.joint_angles[3];
220 | };
221 | hole_pose: parameter.hole_pose.pose;
222 | plug_in_params: parameter.plug_in_params;
223 | duration: parameter.duration;
224 | tolerance: parameter.tolerance;
225 | safety: child("convert").result.safety;
226 | }
227 |
228 | action child("plug_in").port("error") @{
229 | setResult(child("plug_in").result)
230 | }@
231 |
232 | } where {
233 | safety: nil;
234 | socket_pose: nil;
235 | hole_pose: nil;
236 | plug_in_params: {
237 | amplitude_factor_translation: 1.0;
238 | amplitude_factor_rotation: 1.0;
239 | impedance_factor: 7.0;
240 | F_z: 7.0;
241 | };
242 | duration: 5.0;
243 | tolerance: [0.015, 0.015, 0.0007];
244 | }
245 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 |
--------------------------------------------------------------------------------
/4_Learning/README.md:
--------------------------------------------------------------------------------
1 | # Learning
2 | This tutorial shows how to apply *on robot* learning with the Franka Emika AIR Platform as described in the [Tutorial Paper](../README.md#tutorial-paper) in Section IV.
3 |
4 | For both the `plug_in` ([RIDE tutorial](../2_RIDE)) and `plug_in_fci` ([FCI tutorial](../3_FCI)) examples, we implement a procedure that explores and optimizes the control parameters that succeed and at the same time minimize the interaction forces.
5 |
6 | ## Installation
7 |
8 | Make sure that libfranka, *RIDE* and *RaceCom* are installed on your computer. To install libfranka follow these [instructions](https://frankaemika.github.io/docs/installation_linux.html). To install the necessary state machines for the *RIDE* integration, make sure that you are connected to *Control* (The *FCI* will not work if you are connected to the *Arm* base!) and logged into the *Core*:
9 |
10 | ```sh
11 | ride login
12 | ```
13 |
14 | The learning tutorial depends on services, state machines and controllers from the *RIDE* and *FCI* tutorials.
15 | To build all of them, run in the repository root (not in the `4_Learning` folder!):
16 |
17 | ```sh
18 | mkdir build && cd build
19 | ```
20 |
21 | Run cmake with the following flags in order to build the learning tutorial
22 |
23 | ```sh
24 | cmake -DBUILD_RIDE=ON -DBUILD_FCI=ON -DBUILD_LEARNING=ON -DCMAKE_BUILD_TYPE=Release -DFranka_Dir= ..
25 | ```
26 |
27 | and compile with
28 |
29 | ```sh
30 | make
31 | ```
32 |
33 | To compile all state machines and Apps and upload them to your robot run
34 |
35 | ```
36 | make ride_tutorial_statemachines &&
37 | make fci_tutorial_statemachines &&
38 | make learning_tutorial_statemachines
39 | ```
40 |
41 | After installing the bundles the Apps should appear in the app pane. Note that the Apps are grey shaded as long as the `fci` and the `learning` service are not running.
42 |
43 | ## Learning service
44 | Regardless of the method used, e.g. reinforcement learning or stochastic optimization, all *on robot* learning methods follow the same underlying operational loop where a learning service generate parameters that are then tested and evaluated on the robot. The result is used by the learning service to generate the next parameter set and this procedure is iterated until convergence.
45 |
46 | The implementation of the `learning` service is located in `services/src/learning_service.cpp`.
47 | In our prototypical implementation, the `learning` service provides the following 4 operations:
48 |
49 | * `init` initializes the learning algorithm. This operation is defined by the `Init.op` and receives the hyperparameters of the learning algorithm.
50 | * `getParams` computes and returns the parameters for the next experimental trial. This operation is defined in the `GetParam.op` file.
51 | * `setResult` receives the cost function value and a boolean representing the insertion success. This operation forwards the result to the learning algorithm. In addition, the operation returns a boolean indicating if the learning process has finished/converged. This operation is defined in the `SetResult.op` file.
52 | * `getBestParams` returns the best set of parameters that has been experienced so far by the learning algorithm. More precisely, the best parameters are the ones that led to a successful execution task and also minimize the interaction forces. This operation is defined by the `GetBestParams.op` file.
53 |
54 | #### Learning algorithm
55 | This tutorial provides the skeleton for the implementation of on robot learning approaches. For the sake of simplicity, the learning algorithm implemented is *random search*, which *randomly* selects parameters, tests and evaluates them.
56 | It is implemented as a C++ class that is located in the `service` directory. The methods of this class are used in the `learning` service to implement the operations. The `learning` service is implemented in such a way that it can learn any state machine or App as it interacts with the state machine through an array of doubles, i.e. it searches for the optimal array of parameters with no prior knowledge at all.
57 | Adapting the implementation to a more capable algorithm should be straightforward.
58 |
59 | ## Plug in learning
60 | This examples shows how to learn the parameters of a state machine, in this case the `plug_in` state machine of the `ride_tutorial` bundle.
61 | The learning loop is implemented in a state machine. The state machine uses the `learning` service for learning and `RIDE` to test and evaluate parameters on the robot.
62 | The following figure illustrates the interplay of interfaces in the system architecture and the involved bundles.
63 |
64 | 
65 |
66 | Specifically, the `ride_learning_tutorial` bundle located in the `statemachine/source` directory is composed by the following state machines:
67 |
68 | * *The App layer `plug_in_learning_ride_app.lf`:*
69 | This state machine implements the *context_menu*, gathers all required parameters from *Desk* and runs the App. It consists of the following two states:
70 |
71 | 1. `learn_or_run`: This state evaluates a boolean that indicates if the user wants to learn or just run the plug in statemachine. The user can toggle this variable in the context menu.
72 |
73 | 2. Depending on the selected mode in the previous step, it will:
74 |
75 | a) **Learning:**
76 | Run the `plug_in_learning_ride` state machine explained below.
77 |
78 | b) **Execution:**
79 | Run the following two states:
80 |
81 | 1. `get_best_params`: This state requests the `getBestParams` operation to get best previously learned parameters currently stored in the `learning` service.
82 |
83 | 2. `plug_in`: This runs the `plug_in_wrapper` state machine from the `ride_tutorial` bundle with the acquired parameters.
84 |
85 | * *The learning layer: `plug_in_learning_ride.lf`:*
86 | This state machine implements the learning loop, given by the following states:
87 |
88 | 1. `init`: The first state initializes the learning algorithm with the hyperparameters using the `init` operation of the `learning` service.
89 | 2. *Learning loop:* The following states will be sequentially executed
90 |
91 | 1. `reset`: This state resets the robot and its environment. It moves the robot to a home pose and clears all errors.
92 | 2. `get_params`: This state request the `getParams` operation for getting the parameters for the next experimental trial.
93 | 3. `explore`: This state runs the plug in App with the parameters from the `get_params` state. It executes the `plug_in_wrapper` state machine of the `ride_tutorial` bundle using these parameters and at the same time the robot state is observed to compute the cost function (here the interaction forces are cumulated).
94 | 4. `set_result`: Regardless of the outcome of the `plug_in_wrapper` state machine, this state returns the result of the cost function and an boolean representing the insertion success by calling the `setResult` operation of the `learning` service. Depending on the operation's response it also decides if the learning should continue or not. If it continues, the state machine jumps to the `reset` state and executes another iteration of the learning loop. Otherwise the learning is done and the state machine finishes with success.
95 |
96 | #### Running the Plug in learning App
97 | If you successfully installed the `ride_learning_tutorial` bundle you should see the `Plug In Learning` App (most probably grey shaded) in the app area of Desk. To
98 |
99 | 1. Run the `learning` service:
100 | Open a terminal and go to the root of the repository and run
101 |
102 | ```sh
103 | ./build/4_Learning/services/learning_service 11511
104 | ```
105 |
106 | with the following arguments:
107 |
108 | * ``: is the IP address of the robot (`robot.franka.de` if you are connected to the *Arm* base or your preconfigured robot ip otherwise).
109 | * ``: is the network interface used by your computer to reach the robot. You can check your network interfaces with the `ip a` command. If you are using an ethernet adapter, it will be named `enpXXXX`. Wireless adapters are denoted as `wlpXXX`. Ask your system administrator if you can't figure out your network interface.
110 |
111 | After running the service, it should appear listed when you enter the following command
112 |
113 | ```sh
114 | ride cli services
115 | ```
116 |
117 | In addition, the `Plug In Learning` App should now be colored in Desk.
118 |
119 | 2. Run the `Plug In Learning` App in Desk:
120 |
121 | 1. Create new Task
122 | 2. *Program the task:* Drag the `Plug In Learning` App into the Timeline of the Task
123 | 3. *Parameterize the task:* Click on the `Plug In Learning` App and follow the instruction to teach the robot.
124 | 4. Learn the task:
125 |
126 | 1. Go to the *Mode* tab and move the slider to *Learning*. Then set the
127 | Hyperparameter of the learning algorithm.
128 | **Note:** The Hyperparameters are already configured. The only thing you should change is the number of iterations. For a short demo, 10 iterations are a good start.
129 | 2. Activate the external activation device and click on the run button.
130 | 5. Run the learned task:
131 | 1. Go to the *Mode* tab and move the slider to *Execution*.
132 | 2. Activate the external activation device and click on the run button.
133 |
134 | ## Plug in *FCI* learning
135 |
136 | In this example we will learn the parameters of a custom FCI controller. It is the same code used in the previous subsection, but instead of exploring the `plug_in_wrapper` state machine of the `ride_tutorial` bundle, the `plug_in_learning_fci` explores the `plug_in_fci` state machine of the `fci_tutorial` bundle. The implementation of this example is collected in the `fci_learning_tutorial` bundle which is located in the `statemachines/source` folder.
137 | 
138 |
139 | #### Running the Plug in *FCI* learning App
140 | If you successfully installed the `fci_learning_tutorial` bundle you should see the `Plug In FCI Learning` App (most probably grey shaded) in the app area of Desk.
141 |
142 | 1. Run the `learning` service as explained in the [*Running the Plug in learning App* subsection](#Running-the-Plug-in-learning-App).
143 | 2. Run the `fci` service:
144 | From your repository root, run:
145 |
146 | ```sh
147 | ./build/3_FCI/ride_integration/services/fci_service 11511
148 | ```
149 |
150 | Now the services are available and you should be able to see them when you enter the following command
151 |
152 | ```sh
153 | ride cli services
154 | ```
155 |
156 | In addition to that, the `Plug In FCI Learning` App should now be colored in Desk.
157 |
158 | 3. Run the `Plug In FCI Learning` App in Desk. It operates the same way as the `Plug In Learning` App described in the [*Running the Plug in learning App* subsection](#Running-the-Plug-in-learning-App)
159 |
--------------------------------------------------------------------------------
/4_Learning/statemachines/bundles/ride_learning_tutorial/sources/plug_in_learning_ride.lf:
--------------------------------------------------------------------------------
1 | plug_in_learning_ride {
2 | port success child("set_result").port("learning_done")
3 | port error child("init").port("error") or
4 | child("reset").port("error") or
5 | child("get_params").port("error") or
6 | child("set_result").port("error")
7 |
8 | clientData{
9 | description: @{
10 | This statemachine learns the parameters of the plug_in app.
11 | The optimizer is implemented in the racecom service 'learning'
12 | and includes operations like init, get_params and set_result.
13 | The statemachine calls the corresponding operations
14 | to init the optimizer, get next parameters to explore and
15 | set the result of the exploration.
16 | }@;
17 | }
18 |
19 | parameterType {
20 | -- safety parameters
21 | {
22 | [7]float lower_torque_thresholds_nominal;
23 | [7]float upper_torque_thresholds_nominal;
24 | [7]float lower_torque_thresholds_acceleration;
25 | [7]float upper_torque_thresholds_acceleration;
26 | [6]float lower_force_thresholds_nominal;
27 | [6]float upper_force_thresholds_nominal;
28 | [6]float lower_force_thresholds_acceleration;
29 | [6]float upper_force_thresholds_acceleration;
30 | } safety;
31 | -- socket pose for plug in
32 | {
33 | [16]float pose;
34 | []float joint_angles;
35 | } socket_pose;
36 | -- end effector pose when plug is inserted
37 | {
38 | [16]float pose;
39 | []float joint_angles;
40 | } hole_pose;
41 | -- start/reset pose
42 | {
43 | [16]float pose;
44 | []float joint_angles;
45 | } home_pose;
46 | -- hyperparameters for optimizer
47 | {
48 | int max_iteration;
49 | int param_count;
50 | []float lower_bounds;
51 | []float upper_bounds;
52 | } hyperparameters;
53 | -- tolerance between the end effector pose
54 | -- and hole pose to check if plug is successfully inserted [m]
55 | [3]float tolerance;
56 | -- maximum insertion time [s]
57 | float duration;
58 | }
59 |
60 | resultType {
61 | string error_cause;
62 | }
63 |
64 | -- Initialize optimizer, e.g.set hyperparameter
65 | --> init {
66 | port success service("learning").operation("init").status == "success" -> reset
67 | port error service("learning").operation("init").status == "error"
68 |
69 | parameterType {
70 | {
71 | int max_iteration;
72 | int param_count;
73 | []float lower_bounds;
74 | []float upper_bounds;
75 | } hyperparameters;
76 | }
77 |
78 | entry @{
79 | service("learning").operation("init").call(parameter.hyperparameters)
80 | }@
81 |
82 | } where {
83 | hyperparameters: parameter.hyperparameters;
84 | }
85 |
86 | -- reset errors and move robot to start pose
87 | reset {
88 | port success child("home").port("success") -> get_params
89 | port error child("reset_errors").port("error") or
90 | child("home").port("error")
91 |
92 | parameterType {
93 | {
94 | [16]float pose;
95 | []float joint_angles;
96 | } home_pose;
97 | -- Safety parameters
98 | {
99 | [7]float lower_torque_thresholds_nominal;
100 | [7]float upper_torque_thresholds_nominal;
101 | [7]float lower_torque_thresholds_acceleration;
102 | [7]float upper_torque_thresholds_acceleration;
103 | [6]float lower_force_thresholds_nominal;
104 | [6]float upper_force_thresholds_nominal;
105 | [6]float lower_force_thresholds_acceleration;
106 | [6]float upper_force_thresholds_acceleration;
107 | } safety;
108 | }
109 |
110 | resultType {
111 | string error_cause;
112 | }
113 |
114 | --> reset_errors <- reset_errors_library {
115 | port success -> home
116 | }
117 |
118 | home <- move_via_with_move_configuration {
119 | } where {
120 | poses: [
121 | {
122 | pose: { pose: parameter.home_pose.pose; };
123 | point2point: true; -- Robot needs to reach this point exactly
124 | cartesian_velocity_factor: 0.1;
125 | cartesian_acceleration_factor: 0.1;
126 | cartesian_deceleration_factor: 0.1;
127 | -- q3 is the robot's third joint
128 | q3: parameter.home_pose.joint_angles[3];
129 | }
130 | ];
131 | controller_mode: 2;
132 | -- high stiffness for high precision
133 | cartesian_impedance: {
134 | K_x: [2000, 2000, 3000, 150, 150, 150];
135 | };
136 | joint_impedance: {
137 | K_theta: [5000, 5000, 4500, 4000, 3000, 2500, 2000];
138 | };
139 | -- collision thresholds
140 | safety: parameter.safety;
141 | }
142 |
143 | action child("reset_errors").port("error") @{
144 | setResult({error_cause = child("reset_errors").result.error_cause;})
145 | }@
146 |
147 | action child("home").port("error") @{
148 | setResult({error_cause = child("home").result.error_cause;})
149 | }@
150 |
151 | } where {
152 | home_pose: parameter.home_pose;
153 | safety: parameter.safety;
154 | }
155 |
156 | get_params {
157 | port success service("learning").operation("getParams").status == "success" -> explore
158 | port error service("learning").operation("getParams").status == "error"
159 |
160 | resultType {
161 | {
162 | float amplitude_factor_translation;
163 | float amplitude_factor_rotation;
164 | float impedance_factor;
165 | float F_z;
166 | } plug_in_params;
167 | }
168 |
169 | entry @{
170 | service("learning").operation("getParams").call({})
171 | }@
172 |
173 | exit @{
174 | local res = service("learning").operation("getParams").result
175 | local plug_in_params = {
176 | amplitude_factor_translation = res.parameters[1],
177 | amplitude_factor_rotation = res.parameters[2],
178 | impedance_factor = res.parameters[3],
179 | F_z = res.parameters[4]
180 | }
181 | setResult({plug_in_params = plug_in_params;})
182 | print("Next Parameters: ")
183 | print(result)
184 | }@
185 | }
186 |
187 | -- explore suggested parameters by running plug_in statemachine
188 | explore {
189 | port done child("plug_in").port("success") or child("plug_in").port("error") -> set_result
190 |
191 | parameterType {
192 | -- Safety parameters
193 | {
194 | [7]float lower_torque_thresholds_nominal;
195 | [7]float upper_torque_thresholds_nominal;
196 | [7]float lower_torque_thresholds_acceleration;
197 | [7]float upper_torque_thresholds_acceleration;
198 | [6]float lower_force_thresholds_nominal;
199 | [6]float upper_force_thresholds_nominal;
200 | [6]float lower_force_thresholds_acceleration;
201 | [6]float upper_force_thresholds_acceleration;
202 | } safety;
203 | {
204 | [16]float pose;
205 | float q3;
206 | } socket_pose;
207 | [16]float hole_pose;
208 | {
209 | -- parameter for wiggle motion
210 | float amplitude_factor_translation;
211 | float amplitude_factor_rotation;
212 | -- stiffness of robot during insertion
213 | float impedance_factor;
214 | -- insertion force in z direction of robot's end-effector frame
215 | float F_z;
216 | } plug_in_params;
217 | -- maximum insertion time
218 | float duration;
219 | -- tolerance between the end effector pose
220 | -- and hole pose to check if plug inserted
221 | [3]float tolerance;
222 | }
223 |
224 | resultType {
225 | float cost;
226 | bool is_inserted;
227 | }
228 |
229 | -- Execute plug_in statemachine and observe the robot_state to compute the cost function
230 | --> barrier plug_in_and_observe {
231 | -> plug_in
232 | -> observe
233 | }
234 |
235 | -- Run the plug_in statemachine
236 | plug_in <- plug_in_wrapper {
237 | } where parameter
238 |
239 | -- observe the robot state and compute parts of the cost function
240 | observe {
241 |
242 | resultType {
243 | float cost;
244 | }
245 |
246 | entry @{
247 | setResult(nil)
248 | }@
249 |
250 | action service("robot").event("sensor_data") ~= nil @{
251 | local F = service("robot").event("sensor_data").O_F_ext_hat_K
252 | local F_norm = 0
253 | -- compute norm
254 | for i = 1,6,1 do
255 | F_norm = F_norm + F[i]*F[i]
256 | end
257 | -- add to history
258 | local res = {}
259 | if result ~= nil then
260 | res.cost = result.cost + F_norm
261 | else
262 | res.cost = F_norm
263 | end
264 | setResult(res)
265 | }@
266 | }
267 |
268 | exit @{
269 | -- set final cost function and flag if plug was successfully inserted
270 | setResult({cost = child("observe").result.cost;
271 | is_inserted = child("plug_in").port("success");})
272 | }@
273 |
274 | } where {
275 | socket_pose: {
276 | pose: parameter.socket_pose.pose;
277 | q3: parameter.socket_pose.joint_angles[3];
278 | };
279 | hole_pose: parameter.hole_pose.pose;
280 | plug_in_params: child("get_params").result.plug_in_params;
281 | duration: parameter.duration;
282 | tolerance: parameter.tolerance;
283 | safety: parameter.safety;
284 | }
285 |
286 | -- Return the cost and success indicator to optimizer
287 | -- Depending on the service`s response the program decides if
288 | -- the robot should to continue learning or not
289 | set_result {
290 | port learning_done service("learning").operation("setResult").status == "success" and
291 | service("learning").operation("setResult").result
292 | port continue service("learning").operation("setResult").status == "success" and
293 | not service("learning").operation("setResult").result -> reset
294 | port error service("learning").operation("setResult").status == "error"
295 |
296 | parameterType {
297 | float cost;
298 | bool is_inserted;
299 | }
300 |
301 | entry @{
302 | print("Result of exploration: ")
303 | print(parameter)
304 | service("learning").operation("setResult").call({cost = parameter.cost;
305 | success = parameter.is_inserted;})
306 | }@
307 | } where child("explore").result
308 |
309 | action child("init").port("error") @{
310 | setResult({error_cause = "Error in init state";})
311 | }@
312 |
313 | action child("reset").port("error") @{
314 | setResult({error_cause = child("reset").result.error_cause;})
315 | }@
316 |
317 | action child("get_params").port("error") @{
318 | setResult({error_cause = "Error in get_params state";})
319 | }@
320 |
321 | action child("set_result").port("error") @{
322 | setResult({error_cause = "Error in set_result state";})
323 | }@
324 |
325 | } where {
326 | hyperparameters: {
327 | max_iteration: 2;
328 | param_count: 4;
329 | lower_bounds: [0.0, 0.0, 1.0, 0.5];
330 | upper_bounds: [3.0, 3.0, 10.0, 25.0];
331 | };
332 | }
333 |
--------------------------------------------------------------------------------
/2_RIDE/statemachines/bundles/ride_tutorial/sources/plug_in_wrapper.lf:
--------------------------------------------------------------------------------
1 | plug_in_wrapper {
2 | port success child("plug_in").port("success")
3 | port error child("plug_in").port("error")
4 |
5 | clientData {
6 | description: @{
7 | This statemachine is a high-level wrapper for plug_in.
8 | It calculates parameters required for plug_in using high-level parameters.
9 | After the conversion the plug_in statemachine is executed.
10 | }@;
11 | }
12 |
13 | parameterType {
14 | -- Safety parameters
15 | {
16 | [7]float lower_torque_thresholds_nominal;
17 | [7]float upper_torque_thresholds_nominal;
18 | [7]float lower_torque_thresholds_acceleration;
19 | [7]float upper_torque_thresholds_acceleration;
20 | [6]float lower_force_thresholds_nominal;
21 | [6]float upper_force_thresholds_nominal;
22 | [6]float lower_force_thresholds_acceleration;
23 | [6]float upper_force_thresholds_acceleration;
24 | } safety;
25 | -- socket pose consisting of pose and q3 (angle of joint 3)
26 | {
27 | [16]float pose;
28 | float q3;
29 | } socket_pose;
30 | -- end effector pose when plug is inserted
31 | [16]float hole_pose;
32 | {
33 | -- parameter for wiggle motion
34 | float amplitude_factor_translation;
35 | float amplitude_factor_rotation;
36 | -- stiffness of robot during insertion
37 | float impedance_factor;
38 | -- insertion force in z direction of robot's end-effector frame [N]
39 | float F_z;
40 | } plug_in_params;
41 | -- tolerance between the end effector pose
42 | -- and hole pose to check if plug is successfully inserted [m]
43 | [3]float tolerance;
44 | -- maximum insertion time [s]
45 | float duration;
46 | }
47 |
48 | resultType {
49 | string error_cause;
50 | }
51 |
52 | entry @{
53 | print("Plug in parameters:")
54 | print(parameter.plug_in_params)
55 | }@
56 |
57 | -- Now, the parameters required for plug in are calculated
58 | --> calculate_parameters {
59 | port done result~=nil -> plug_in
60 |
61 | parameterType {
62 | {
63 | [7]float lower_torque_thresholds_nominal;
64 | [7]float upper_torque_thresholds_nominal;
65 | [7]float lower_torque_thresholds_acceleration;
66 | [7]float upper_torque_thresholds_acceleration;
67 | [6]float lower_force_thresholds_nominal;
68 | [6]float upper_force_thresholds_nominal;
69 | [6]float lower_force_thresholds_acceleration;
70 | [6]float upper_force_thresholds_acceleration;
71 | } safety;
72 | {
73 | [16]float pose;
74 | float q3;
75 | } socket_pose;
76 | [16]float hole_pose;
77 | {
78 | float amplitude_factor_translation;
79 | float amplitude_factor_rotation;
80 | float impedance_factor;
81 | float F_z;
82 | } plug_in_params;
83 | float duration;
84 | [3]float tolerance;
85 | }
86 |
87 | resultType {
88 | -- motion for moving to socket pose
89 | {
90 | []{
91 | {[16]float pose;} pose;
92 | bool point2point;
93 | float cartesian_velocity_factor;
94 | float cartesian_acceleration_factor;
95 | float cartesian_deceleration_factor;
96 | float q3;
97 | } poses;
98 | {
99 | [6]int K_x;
100 | } cartesian_impedance;
101 | {
102 | [7]int K_theta;
103 | } joint_impedance;
104 | {
105 | [7]float lower_torque_thresholds_nominal;
106 | [7]float upper_torque_thresholds_nominal;
107 | [7]float lower_torque_thresholds_acceleration;
108 | [7]float upper_torque_thresholds_acceleration;
109 | [6]float lower_force_thresholds_nominal;
110 | [6]float upper_force_thresholds_nominal;
111 | [6]float lower_force_thresholds_acceleration;
112 | [6]float upper_force_thresholds_acceleration;
113 | } safety;
114 | int controller_mode;
115 | } socket_motion;
116 | -- wiggle motion for insertion
117 | {
118 | [6]{
119 | int type;
120 | float a_0;
121 | [5]float a_n;
122 | [5]float b_n;
123 | float T_fourier;
124 | } profiles;
125 | float duration;
126 | int frame_selection;
127 | float safety_distance_x;
128 | float safety_distance_y;
129 | float safety_distance_z;
130 | float safety_distance_theta;
131 | } wiggle_motion;
132 | -- force profile for insertion
133 | {
134 | [6]float F_d;
135 | float duration;
136 | float safety_distance_x;
137 | float safety_distance_y;
138 | float safety_distance_z;
139 | float safety_distance_theta;
140 | float force_tolerance;
141 | } force;
142 | -- move configuration during the actual insertion: high collision thresholds,
143 | -- low stiffness (depends on high-level parameter impedance_factor)
144 | {
145 | -- cartesian impedance during insertion
146 | {
147 | [6]int K_x;
148 | } cartesian_impedance;
149 | -- joint impedance during insertion, affecting nullspace if a cartesian controller is used
150 | {
151 | [7]int K_theta;
152 | } joint_impedance;
153 | {
154 | [7]float lower_torque_thresholds_nominal;
155 | [7]float upper_torque_thresholds_nominal;
156 | [7]float lower_torque_thresholds_acceleration;
157 | [7]float upper_torque_thresholds_acceleration;
158 | [6]float lower_force_thresholds_nominal;
159 | [6]float upper_force_thresholds_nominal;
160 | [6]float lower_force_thresholds_acceleration;
161 | [6]float upper_force_thresholds_acceleration;
162 | } safety;
163 | } insertion_move_configuration;
164 | -- pose of the hole and tolerance to check if plug is inserted
165 | [16]float hole_pose;
166 | [3]float tolerance;
167 | }
168 |
169 | entry @{
170 | -- define movement to socket pose, accepted by move_via statemachine
171 | local socket_motion = {
172 | poses = {
173 | {
174 | pose = { pose = parameter.socket_pose.pose; },
175 | point2point = true, -- Robot needs to reach this point exactly
176 | cartesian_velocity_factor = 0.1,
177 | cartesian_acceleration_factor = 0.1,
178 | cartesian_deceleration_factor = 0.1,
179 | -- q3 is the robot's third joint
180 | q3 = parameter.socket_pose.q3,
181 | }
182 | },
183 | controller_mode = 2,
184 | -- high stiffness for high precision
185 | cartesian_impedance = {
186 | K_x = {2500, 2500, 1500, 150, 150, 150}
187 | },
188 | joint_impedance = {
189 | K_theta = {4000, 4000, 4000, 3500, 3500, 3000, 3000}
190 | },
191 | -- standard collision thresholds (from idle_statemachine)
192 | safety = parameter.safety;
193 | }
194 |
195 | -- define desired wiggle motion as fourier profile, accepted by move_velocity statemachine
196 | local wiggle_motion = {
197 | -- compute fourier coefficients
198 | profiles = {
199 | {type = 0, a_0 = 0., a_n = {0.01*parameter.plug_in_params.amplitude_factor_translation/4, 0., 0., 0., 0.},
200 | b_n = {0., 0., 0., 0., 0.}, T_fourier = 4.},
201 | {type = 0, a_0 = 0., a_n = {0., 0., 0., 0.03*parameter.plug_in_params.amplitude_factor_translation/2, 0.},
202 | b_n = {0., 0., 0., 0., 0.}, T_fourier = 2.},
203 | {type = 0, a_0 = 0., a_n = {0., 0., 0., 0., 0.},
204 | b_n = {0., 0., 0., 0., 0.}, T_fourier = 1},
205 | {type = 0, a_0 = 0., a_n = {0.1*parameter.plug_in_params.amplitude_factor_rotation/4, 0., 0., 0. , 0.},
206 | b_n = {0., 0., 0., 0., 0.}, T_fourier = 4.},
207 | {type = 0, a_0 = 0., a_n = {0., 0., 0.3*parameter.plug_in_params.amplitude_factor_rotation/2, 0., 0.},
208 | b_n = {0. , 0., 0., 0., 0.}, T_fourier = 2.},
209 | {type = 0, a_0 = 0., a_n = {0., 0., 0.3*parameter.plug_in_params.amplitude_factor_rotation, 0., 0.},
210 | b_n = {0., 0., 0., 0., 0.}, T_fourier = 2.}
211 | },
212 | duration = parameter.duration,
213 | frame_selection = 1,
214 | safety_distance_x = 0.05,
215 | safety_distance_y = 0.05,
216 | safety_distance_z = 0.015,
217 | safety_distance_theta = 0.3
218 | }
219 |
220 | -- define desired force
221 | local force = {
222 | F_d ={0., 0., parameter.plug_in_params.F_z, 0., 0., 0.}; -- desired force
223 | force_tolerance = 0.1;
224 | safety_distance_x = 0.05,
225 | safety_distance_y = 0.05,
226 | safety_distance_z = 0.015,
227 | safety_distance_theta = 0.3,
228 | duration = parameter.duration
229 | }
230 |
231 | -- compute desired cartesian and joint impedance for insertion
232 | local K_x = {math.floor(50*parameter.plug_in_params.impedance_factor), math.floor(50*parameter.plug_in_params.impedance_factor),
233 | math.floor(50*parameter.plug_in_params.impedance_factor), math.floor(5*parameter.plug_in_params.impedance_factor),
234 | math.floor(5*parameter.plug_in_params.impedance_factor), math.floor(5*parameter.plug_in_params.impedance_factor)}
235 | local K_theta = {math.floor(50*parameter.plug_in_params.impedance_factor), math.floor(50*parameter.plug_in_params.impedance_factor),
236 | math.floor(50*parameter.plug_in_params.impedance_factor), math.floor(30*parameter.plug_in_params.impedance_factor),
237 | math.floor(30*parameter.plug_in_params.impedance_factor), math.floor(30*parameter.plug_in_params.impedance_factor),
238 | math.floor(30*parameter.plug_in_params.impedance_factor)}
239 | -- define desired move configuration for insertion: high thresholds, lower stiffness
240 | local insertion_move_configuration = {
241 | cartesian_impedance = {K_x = K_x},
242 | joint_impedance = {K_theta = K_theta},
243 | safety = parameter.safety;
244 | }
245 |
246 | -- set result
247 | local result = {}
248 | result.socket_motion = socket_motion
249 | result.wiggle_motion = wiggle_motion
250 | result.force = force
251 | result.insertion_move_configuration = insertion_move_configuration
252 | result.hole_pose = parameter.hole_pose
253 | result.tolerance = parameter.tolerance
254 | setResult(result)
255 | }@
256 | } where parameter
257 |
258 | -- Execute plug_in statemachine to insert plug into the hole
259 | plug_in <- plug_in {
260 | } where child("calculate_parameters").result
261 |
262 | -- Check for triggered ports and set result accordingly.
263 | action child("plug_in").port("error") @{
264 | setResult(child("plug_in").result)
265 | }@
266 |
267 | } where {
268 | safety: nil;
269 | socket_pose: nil;
270 | hole_pose: nil;
271 | plug_in_params:
272 | {
273 | amplitude_factor_translation: 1.0;
274 | amplitude_factor_rotation: 1.0;
275 | impedance_factor: 7.0;
276 | F_z: 4.0;
277 | };
278 | duration: 5.0;
279 | tolerance: [0.015, 0.015, 0.00025];
280 | }
281 |
--------------------------------------------------------------------------------