initial commit
This commit is contained in:
commit
b2645c0c4c
4
.clang-format
Normal file
4
.clang-format
Normal file
@ -0,0 +1,4 @@
|
||||
BasedOnStyle: Chromium
|
||||
Standard: Cpp11
|
||||
SortIncludes: true
|
||||
ColumnLimit: 100
|
54
.clang-tidy
Normal file
54
.clang-tidy
Normal file
@ -0,0 +1,54 @@
|
||||
Checks: 'cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-optin.cplusplus.VirtualCall,-clang-analyzer-alpha*,google-*,misc-*,readability-*,-readability-function-cognitive-complexity,modernize-*,-modernize-use-trailing-return-type,performance-*,-clang-diagnostic-deprecated-declarations,-modernize-pass-by-value,-clang-diagnostic-reinterpret-base-class,-clang-diagnostic-return-type,-clang-diagnostic-switch,-bugprone-lambda-function-name,-cppcoreguidelines-avoid-magic-numbers, -readability-magic-numbers'
|
||||
HeaderFilterRegex: '^franka_.*'
|
||||
CheckOptions:
|
||||
# Classes, structs, ...
|
||||
- key: readability-identifier-naming.NamespaceCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.StructCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.EnumCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.UnionCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.TypedefCase
|
||||
value: CamelCase
|
||||
|
||||
# Variables, member variables, ...
|
||||
- key: readability-identifier-naming.ParameterCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.VariableCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.MemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.PublicMemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ProtectedMemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.PrivateMemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.PrivateMemberSuffix
|
||||
value: _
|
||||
|
||||
# Functions, methods, ...
|
||||
- key: readability-identifier-naming.FunctionCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.MethodCase
|
||||
value: camelBack
|
||||
|
||||
# Constants
|
||||
- key: readability-identifier-naming.ConstantPrefix
|
||||
value: k
|
||||
- key: readability-identifier-naming.ConstantCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.ConstantMemberPrefix
|
||||
value: ''
|
||||
- key: readability-identifier-naming.ConstantMemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ConstantParameterCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ConstantParameterPrefix
|
||||
value: ''
|
||||
|
||||
|
66
.devcontainer/Dockerfile
Normal file
66
.devcontainer/Dockerfile
Normal file
@ -0,0 +1,66 @@
|
||||
FROM osrf/ros:humble-desktop
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG USER_UID=1001
|
||||
ARG USER_GID=1001
|
||||
ARG USERNAME=user
|
||||
|
||||
WORKDIR /workspaces
|
||||
|
||||
RUN groupadd --gid $USER_GID $USERNAME \
|
||||
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
|
||||
&& mkdir -p -m 0700 /run/user/"${USER_UID}" \
|
||||
&& mkdir -p -m 0700 /run/user/"${USER_UID}"/gdm \
|
||||
&& chown user:user /run/user/"${USER_UID}" \
|
||||
&& chown user:user /workspaces \
|
||||
&& chown user:user /run/user/"${USER_UID}"/gdm \
|
||||
# [Optional] Add sudo support. Omit if you don't need to install software after connecting.
|
||||
&& apt-get update \
|
||||
&& apt-get install -y sudo \
|
||||
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
|
||||
&& chmod 0440 /etc/sudoers.d/$USERNAME
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
ros-humble-hardware-interface \
|
||||
ros-humble-generate-parameter-library \
|
||||
ros-humble-xacro \
|
||||
ros-humble-controller-interface \
|
||||
ros-humble-realtime-tools \
|
||||
ros-humble-ros2-control-test-assets \
|
||||
ros-humble-controller-manager \
|
||||
ros-humble-joint-state-publisher-gui \
|
||||
ros-humble-control-msgs \
|
||||
ros-humble-gazebo-ros \
|
||||
ros-humble-control-toolbox \
|
||||
ros-humble-ackermann-msgs \
|
||||
ros-humble-ament-cmake \
|
||||
ros-humble-ament-cmake-clang-format \
|
||||
ros-humble-moveit \
|
||||
ros-humble-joint-state-broadcaster \
|
||||
libignition-gazebo6-dev \
|
||||
ros-humble-joint-trajectory-controller \
|
||||
libpoco-dev \
|
||||
libeigen3-dev \
|
||||
ament-cmake-clang-tidy \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV XDG_RUNTIME_DIR=/run/user/"${USER_UID}"
|
||||
|
||||
RUN mkdir ~/source_code
|
||||
RUN cd ~/source_code && git clone https://github.com/frankaemika/libfranka.git \
|
||||
&& cd libfranka \
|
||||
&& git switch fr3-develop \
|
||||
&& git submodule init \
|
||||
&& git submodule update \
|
||||
&& mkdir build && cd build \
|
||||
&& cmake -DBUILD_EXAMPLES=OFF -DBUILD_TESTS=OFF .. \
|
||||
&& make franka -j$(nproc) \
|
||||
&& cpack -G DEB \
|
||||
&& sudo dpkg -i libfranka*.deb
|
||||
|
||||
# set the default user to the newly created user
|
||||
USER $USERNAME
|
||||
|
||||
RUN echo 'source "/opt/ros/$ROS_DISTRO/setup.bash"' >>~/.bashrc
|
27
.devcontainer/devcontainer.json
Normal file
27
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,27 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
|
||||
{
|
||||
"name": "ROS2 Humble",
|
||||
"dockerComposeFile": "./docker-compose.yml",
|
||||
"service": "ros2_control",
|
||||
"workspaceFolder": "/workspaces",
|
||||
"remoteUser": "user",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"bungcip.better-toml",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
],
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"bash": {
|
||||
"path": "/bin/bash"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
.devcontainer/docker-compose.yml
Normal file
18
.devcontainer/docker-compose.yml
Normal file
@ -0,0 +1,18 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
ros2_control:
|
||||
build: .
|
||||
network_mode: "host"
|
||||
privileged: true
|
||||
command: /bin/bash
|
||||
tty: true
|
||||
stdin_open: true
|
||||
volumes:
|
||||
- ../:/workspaces/src
|
||||
- /tmp/.X11-unix:/tmp/.X11-unix
|
||||
- $XAUTHORITY:$XAUTHORITY
|
||||
environment:
|
||||
QT_X11_NO_MITSHM: 1
|
||||
DISPLAY: $DISPLAY
|
||||
user: $UID:$UID
|
59
.github/workflows/ci.yml
vendored
Normal file
59
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
jobs:
|
||||
CI:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
mkdir -p ${{github.workspace}}/src
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: src/franka_ros2
|
||||
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
tags: franka_ros2:humble
|
||||
file: Dockerfile
|
||||
push: false
|
||||
|
||||
- name: Build
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: franka_ros2:humble
|
||||
options: -v ${{github.workspace}}/:/ros/
|
||||
run: |
|
||||
cd /ros
|
||||
. /opt/ros/humble/setup.sh
|
||||
colcon build --cmake-args -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCHECK_TIDY=ON
|
||||
|
||||
- name: Test
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: franka_ros2:humble
|
||||
options: -v ${{github.workspace}}/:/ros/
|
||||
run: |
|
||||
cd /ros
|
||||
. /opt/ros/humble/setup.sh
|
||||
. install/setup.sh
|
||||
colcon test
|
||||
colcon test-result
|
||||
|
||||
- name: Upload Tests to Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: build/*/test_results/*/*.xml
|
||||
|
||||
- name: Publish Unit Test Results
|
||||
uses: EnricoMi/publish-unit-test-result-action@v1
|
||||
if: always()
|
||||
with:
|
||||
files: build/*/test_results/*/*.xml
|
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
.idea
|
||||
.vscode
|
||||
*.pyc
|
||||
COLCON_IGNORE
|
||||
AMENT_IGNORE
|
||||
cmake-build-*/
|
||||
build/
|
||||
install/
|
||||
log/
|
29
CHANGELOG.md
Normal file
29
CHANGELOG.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.0 - 2023-07-28
|
||||
|
||||
Requires libfranka >= 0.10.0, required ROS 2 Humble
|
||||
|
||||
* franka\_bringup: franka_robot_state broadcaster added to franka.launch.py.
|
||||
* franka\_example\_controllers: model printing read onyl controller implemented
|
||||
* franka\_robot\_model: semantic component to access robot model parameters.
|
||||
* franka\_msgs: franka robot state msg added
|
||||
* franka\_robot\_state: broadcaster publishes robot state.
|
||||
|
||||
### Added
|
||||
|
||||
* CI tests in Jenkins.
|
||||
* joint\_effort\_trajectory\_controller package that contains a version of the
|
||||
joint\_trajectory\_controller that can use the torque interface.
|
||||
[See this PR](https://github.com/ros-controls/ros2_controllers/pull/225)
|
||||
* franka\_bringup package that contains various launch files to start controller examples or Moveit2.
|
||||
* franka\_moveit\_config package that contains a minimal moveit config to control the robot.
|
||||
* franka\_example\_controllers package that contains some example controllers to use.
|
||||
* franka\_hardware package that contains a plugin to access the robot.
|
||||
* franka\_msgs package that contains common message, service and action type definitions.
|
||||
* franka\_description package that contains all meshes and xacro files.
|
||||
* franka\_gripper package that offers action and service interfaces to use the Franka Hand gripper.
|
||||
|
||||
### Fixed
|
||||
|
||||
* franka\_hardware Fix the mismatched joint state interface type logger error message.
|
72
Dockerfile
Normal file
72
Dockerfile
Normal file
@ -0,0 +1,72 @@
|
||||
FROM ros:humble
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG USER_UID=1001
|
||||
ARG USER_GID=1001
|
||||
ARG USERNAME=user
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
RUN groupadd --gid $USER_GID $USERNAME \
|
||||
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
|
||||
#
|
||||
# [Optional] Add sudo support. Omit if you don't need to install software after connecting.
|
||||
&& apt-get update \
|
||||
&& apt-get install -y sudo \
|
||||
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
|
||||
&& chmod 0440 /etc/sudoers.d/$USERNAME
|
||||
|
||||
RUN apt-get update -y && apt-get install -y --allow-unauthenticated \
|
||||
clang-14 \
|
||||
clang-format-14 \
|
||||
clang-tidy-14 \
|
||||
python3-pip \
|
||||
libpoco-dev \
|
||||
libeigen3-dev \
|
||||
ros-humble-control-msgs \
|
||||
ros-humble-xacro \
|
||||
ros-humble-ament-cmake-clang-format \
|
||||
ros-humble-ament-clang-format \
|
||||
ros-humble-ament-flake8 \
|
||||
ros-humble-ament-cmake-clang-tidy \
|
||||
ros-humble-angles \
|
||||
ros-humble-ros2-control \
|
||||
ros-humble-realtime-tools \
|
||||
ros-humble-control-toolbox \
|
||||
ros-humble-controller-manager \
|
||||
ros-humble-hardware-interface \
|
||||
ros-humble-generate-parameter-library \
|
||||
ros-humble-controller-interface \
|
||||
ros-humble-ros2-control-test-assets \
|
||||
ros-humble-controller-manager \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN python3 -m pip install -U \
|
||||
argcomplete \
|
||||
flake8-blind-except \
|
||||
flake8-builtins \
|
||||
flake8-class-newline \
|
||||
flake8-comprehensions \
|
||||
flake8-deprecated \
|
||||
flake8-docstrings \
|
||||
flake8-import-order \
|
||||
flake8-quotes \
|
||||
pytest-repeat \
|
||||
pytest-rerunfailures \
|
||||
pytest
|
||||
|
||||
RUN mkdir ~/source_code
|
||||
RUN cd ~/source_code && git clone https://github.com/frankaemika/libfranka.git \
|
||||
&& cd libfranka \
|
||||
&& git switch fr3-develop \
|
||||
&& git submodule init \
|
||||
&& git submodule update \
|
||||
&& mkdir build && cd build \
|
||||
&& cmake -DBUILD_EXAMPLES=OFF -DBUILD_TESTS=OFF .. \
|
||||
&& make franka -j$(nproc) \
|
||||
&& cpack -G DEB \
|
||||
&& sudo dpkg -i libfranka*.deb
|
||||
|
||||
# set the default user to the newly created user
|
||||
USER $USERNAME
|
49
Jenkinsfile
vendored
Normal file
49
Jenkinsfile
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
pipeline {
|
||||
agent {
|
||||
dockerfile true
|
||||
}
|
||||
triggers {
|
||||
pollSCM('H/5 * * * *')
|
||||
}
|
||||
stages {
|
||||
stage('Init') {
|
||||
steps {
|
||||
script {
|
||||
notifyBitbucket()
|
||||
}
|
||||
sh 'rm -rf build log install'
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh '''
|
||||
. /opt/ros/humble/setup.sh
|
||||
colcon build --cmake-args -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCHECK_TIDY=ON
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh '''
|
||||
. /opt/ros/humble/setup.sh
|
||||
. install/setup.sh
|
||||
colcon test
|
||||
colcon test-result
|
||||
'''
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit 'build/**/test_results/**/*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
cleanWs()
|
||||
script {
|
||||
notifyBitbucket()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
176
LICENSE
Normal file
176
LICENSE
Normal file
@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
15
NOTICE
Normal file
15
NOTICE
Normal file
@ -0,0 +1,15 @@
|
||||
franka_ros2
|
||||
|
||||
Copyright 2021 Franka Emika GmbH
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# ROS 2 integration for Franka Emika research robots
|
||||
|
||||
[![CI](https://github.com/frankaemika/franka_ros2/actions/workflows/ci.yml/badge.svg)](https://github.com/frankaemika/franka_ros2/actions/workflows/ci.yml)
|
||||
|
||||
See the [Franka Control Interface (FCI) documentation][fci-docs] for more information.
|
||||
|
||||
## License
|
||||
|
||||
All packages of `franka_ros2` are licensed under the [Apache 2.0 license][apache-2.0].
|
||||
|
||||
[apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
[fci-docs]: https://frankaemika.github.io/docs
|
16
franka_bringup/CMakeLists.txt
Normal file
16
franka_bringup/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(franka_bringup)
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
|
||||
install(
|
||||
DIRECTORY config launch
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
endif()
|
||||
|
||||
ament_package()
|
95
franka_bringup/config/controllers.yaml
Normal file
95
franka_bringup/config/controllers.yaml
Normal file
@ -0,0 +1,95 @@
|
||||
controller_manager:
|
||||
ros__parameters:
|
||||
update_rate: 1000 # Hz
|
||||
|
||||
gravity_compensation_example_controller:
|
||||
type: franka_example_controllers/GravityCompensationExampleController
|
||||
|
||||
joint_impedance_example_controller:
|
||||
type: franka_example_controllers/JointImpedanceExampleController
|
||||
|
||||
move_to_start_example_controller:
|
||||
type: franka_example_controllers/MoveToStartExampleController
|
||||
|
||||
model_example_controller:
|
||||
type: franka_example_controllers/ModelExampleController
|
||||
|
||||
joint_trajectory_controller:
|
||||
type: joint_trajectory_controller/JointTrajectoryController
|
||||
|
||||
joint_state_broadcaster:
|
||||
type: joint_state_broadcaster/JointStateBroadcaster
|
||||
|
||||
franka_robot_state_broadcaster:
|
||||
type: franka_robot_state_broadcaster/FrankaRobotStateBroadcaster
|
||||
|
||||
franka_robot_state_broadcaster:
|
||||
ros__parameters:
|
||||
arm_id: panda
|
||||
|
||||
model_example_controller:
|
||||
ros__parameters:
|
||||
arm_id: panda
|
||||
|
||||
joint_trajectory_controller:
|
||||
ros__parameters:
|
||||
joints:
|
||||
- panda_joint1
|
||||
- panda_joint2
|
||||
- panda_joint3
|
||||
- panda_joint4
|
||||
- panda_joint5
|
||||
- panda_joint6
|
||||
- panda_joint7
|
||||
command_interfaces:
|
||||
- effort
|
||||
state_interfaces:
|
||||
- position
|
||||
- velocity
|
||||
gains:
|
||||
panda_joint1: { p: 600., d: 30., i: 0., i_clamp: 1. }
|
||||
panda_joint2: { p: 600., d: 30., i: 0., i_clamp: 1. }
|
||||
panda_joint3: { p: 600., d: 30., i: 0., i_clamp: 1. }
|
||||
panda_joint4: { p: 600., d: 30., i: 0., i_clamp: 1. }
|
||||
panda_joint5: { p: 250., d: 10., i: 0., i_clamp: 1. }
|
||||
panda_joint6: { p: 150., d: 10., i: 0., i_clamp: 1. }
|
||||
panda_joint7: { p: 50., d: 5., i: 0., i_clamp: 1. }
|
||||
|
||||
joint_impedance_example_controller:
|
||||
ros__parameters:
|
||||
arm_id: panda
|
||||
k_gains:
|
||||
- 24.0
|
||||
- 24.0
|
||||
- 24.0
|
||||
- 24.0
|
||||
- 10.0
|
||||
- 6.0
|
||||
- 2.0
|
||||
d_gains:
|
||||
- 2.0
|
||||
- 2.0
|
||||
- 2.0
|
||||
- 1.0
|
||||
- 1.0
|
||||
- 1.0
|
||||
- 0.5
|
||||
move_to_start_example_controller:
|
||||
ros__parameters:
|
||||
arm_id: panda
|
||||
k_gains:
|
||||
- 600.0
|
||||
- 600.0
|
||||
- 600.0
|
||||
- 600.0
|
||||
- 250.0
|
||||
- 150.0
|
||||
- 50.0
|
||||
d_gains:
|
||||
- 30.0
|
||||
- 30.0
|
||||
- 30.0
|
||||
- 30.0
|
||||
- 10.0
|
||||
- 10.0
|
||||
- 5.
|
136
franka_bringup/launch/franka.launch.py
Normal file
136
franka_bringup/launch/franka.launch.py
Normal file
@ -0,0 +1,136 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, Shutdown
|
||||
from launch.conditions import IfCondition, UnlessCondition
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
load_gripper_parameter_name = 'load_gripper'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
fake_sensor_commands_parameter_name = 'fake_sensor_commands'
|
||||
use_rviz_parameter_name = 'use_rviz'
|
||||
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
load_gripper = LaunchConfiguration(load_gripper_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
|
||||
use_rviz = LaunchConfiguration(use_rviz_parameter_name)
|
||||
|
||||
franka_xacro_file = os.path.join(get_package_share_directory('franka_description'), 'robots',
|
||||
'panda_arm.urdf.xacro')
|
||||
robot_description = Command(
|
||||
[FindExecutable(name='xacro'), ' ', franka_xacro_file, ' hand:=', load_gripper,
|
||||
' robot_ip:=', robot_ip, ' use_fake_hardware:=', use_fake_hardware,
|
||||
' fake_sensor_commands:=', fake_sensor_commands])
|
||||
|
||||
rviz_file = os.path.join(get_package_share_directory('franka_description'), 'rviz',
|
||||
'visualize_franka.rviz')
|
||||
|
||||
franka_controllers = PathJoinSubstitution(
|
||||
[
|
||||
FindPackageShare('franka_bringup'),
|
||||
'config',
|
||||
'controllers.yaml',
|
||||
]
|
||||
)
|
||||
|
||||
return LaunchDescription([
|
||||
DeclareLaunchArgument(
|
||||
robot_ip_parameter_name,
|
||||
description='Hostname or IP address of the robot.'),
|
||||
DeclareLaunchArgument(
|
||||
use_rviz_parameter_name,
|
||||
default_value='false',
|
||||
description='Visualize the robot in Rviz'),
|
||||
DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description='Use fake hardware'),
|
||||
DeclareLaunchArgument(
|
||||
fake_sensor_commands_parameter_name,
|
||||
default_value='false',
|
||||
description="Fake sensor commands. Only valid when '{}' is true".format(
|
||||
use_fake_hardware_parameter_name)),
|
||||
DeclareLaunchArgument(
|
||||
load_gripper_parameter_name,
|
||||
default_value='true',
|
||||
description='Use Franka Gripper as an end-effector, otherwise, the robot is loaded '
|
||||
'without an end-effector.'),
|
||||
Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
parameters=[{'robot_description': robot_description}],
|
||||
),
|
||||
Node(
|
||||
package='joint_state_publisher',
|
||||
executable='joint_state_publisher',
|
||||
name='joint_state_publisher',
|
||||
parameters=[
|
||||
{'source_list': ['franka/joint_states', 'panda_gripper/joint_states'],
|
||||
'rate': 30}],
|
||||
),
|
||||
Node(
|
||||
package='controller_manager',
|
||||
executable='ros2_control_node',
|
||||
parameters=[{'robot_description': robot_description}, franka_controllers],
|
||||
remappings=[('joint_states', 'franka/joint_states')],
|
||||
output={
|
||||
'stdout': 'screen',
|
||||
'stderr': 'screen',
|
||||
},
|
||||
on_exit=Shutdown(),
|
||||
),
|
||||
Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['joint_state_broadcaster'],
|
||||
output='screen',
|
||||
),
|
||||
Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['franka_robot_state_broadcaster'],
|
||||
output='screen',
|
||||
condition=UnlessCondition(use_fake_hardware),
|
||||
),
|
||||
IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([PathJoinSubstitution(
|
||||
[FindPackageShare('franka_gripper'), 'launch', 'gripper.launch.py'])]),
|
||||
launch_arguments={robot_ip_parameter_name: robot_ip,
|
||||
use_fake_hardware_parameter_name: use_fake_hardware}.items(),
|
||||
condition=IfCondition(load_gripper)
|
||||
|
||||
),
|
||||
|
||||
Node(package='rviz2',
|
||||
executable='rviz2',
|
||||
name='rviz2',
|
||||
arguments=['--display-config', rviz_file],
|
||||
condition=IfCondition(use_rviz)
|
||||
)
|
||||
|
||||
])
|
@ -0,0 +1,76 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
load_gripper_parameter_name = 'load_gripper'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
fake_sensor_commands_parameter_name = 'fake_sensor_commands'
|
||||
use_rviz_parameter_name = 'use_rviz'
|
||||
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
load_gripper = LaunchConfiguration(load_gripper_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
|
||||
use_rviz = LaunchConfiguration(use_rviz_parameter_name)
|
||||
|
||||
return LaunchDescription([
|
||||
DeclareLaunchArgument(
|
||||
robot_ip_parameter_name,
|
||||
description='Hostname or IP address of the robot.'),
|
||||
DeclareLaunchArgument(
|
||||
use_rviz_parameter_name,
|
||||
default_value='false',
|
||||
description='Visualize the robot in Rviz'),
|
||||
DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description='Use fake hardware'),
|
||||
DeclareLaunchArgument(
|
||||
fake_sensor_commands_parameter_name,
|
||||
default_value='false',
|
||||
description="Fake sensor commands. Only valid when '{}' is true".format(
|
||||
use_fake_hardware_parameter_name)),
|
||||
DeclareLaunchArgument(
|
||||
load_gripper_parameter_name,
|
||||
default_value='true',
|
||||
description='Use Franka Gripper as an end-effector, otherwise, the robot is loaded '
|
||||
'without an end-effector.'),
|
||||
|
||||
IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([PathJoinSubstitution(
|
||||
[FindPackageShare('franka_bringup'), 'launch', 'franka.launch.py'])]),
|
||||
launch_arguments={robot_ip_parameter_name: robot_ip,
|
||||
load_gripper_parameter_name: load_gripper,
|
||||
use_fake_hardware_parameter_name: use_fake_hardware,
|
||||
fake_sensor_commands_parameter_name: fake_sensor_commands,
|
||||
use_rviz_parameter_name: use_rviz
|
||||
}.items(),
|
||||
),
|
||||
|
||||
Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['gravity_compensation_example_controller'],
|
||||
output='screen',
|
||||
),
|
||||
])
|
@ -0,0 +1,77 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
load_gripper_parameter_name = 'load_gripper'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
fake_sensor_commands_parameter_name = 'fake_sensor_commands'
|
||||
use_rviz_parameter_name = 'use_rviz'
|
||||
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
load_gripper = LaunchConfiguration(load_gripper_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
|
||||
use_rviz = LaunchConfiguration(use_rviz_parameter_name)
|
||||
|
||||
return LaunchDescription([
|
||||
DeclareLaunchArgument(
|
||||
robot_ip_parameter_name,
|
||||
description='Hostname or IP address of the robot.'),
|
||||
DeclareLaunchArgument(
|
||||
use_rviz_parameter_name,
|
||||
default_value='false',
|
||||
description='Visualize the robot in Rviz'),
|
||||
DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description='Use fake hardware'),
|
||||
DeclareLaunchArgument(
|
||||
fake_sensor_commands_parameter_name,
|
||||
default_value='false',
|
||||
description="Fake sensor commands. Only valid when '{}' is true".format(
|
||||
use_fake_hardware_parameter_name)),
|
||||
DeclareLaunchArgument(
|
||||
load_gripper_parameter_name,
|
||||
default_value='true',
|
||||
description='Use Franka Gripper as an end-effector, otherwise, the robot is loaded '
|
||||
'without an end-effector.'),
|
||||
|
||||
IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([PathJoinSubstitution(
|
||||
[FindPackageShare('franka_bringup'), 'launch', 'franka.launch.py'])]),
|
||||
launch_arguments={robot_ip_parameter_name: robot_ip,
|
||||
load_gripper_parameter_name: load_gripper,
|
||||
use_fake_hardware_parameter_name: use_fake_hardware,
|
||||
fake_sensor_commands_parameter_name: fake_sensor_commands,
|
||||
use_rviz_parameter_name: use_rviz
|
||||
}.items(),
|
||||
),
|
||||
|
||||
Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['joint_impedance_example_controller'],
|
||||
output='screen',
|
||||
),
|
||||
])
|
77
franka_bringup/launch/model_example_controller.launch.py
Normal file
77
franka_bringup/launch/model_example_controller.launch.py
Normal file
@ -0,0 +1,77 @@
|
||||
# Copyright (c) 2023 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
load_gripper_parameter_name = 'load_gripper'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
fake_sensor_commands_parameter_name = 'fake_sensor_commands'
|
||||
use_rviz_parameter_name = 'use_rviz'
|
||||
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
load_gripper = LaunchConfiguration(load_gripper_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
|
||||
use_rviz = LaunchConfiguration(use_rviz_parameter_name)
|
||||
|
||||
return LaunchDescription([
|
||||
DeclareLaunchArgument(
|
||||
robot_ip_parameter_name,
|
||||
description='Hostname or IP address of the robot.'),
|
||||
DeclareLaunchArgument(
|
||||
use_rviz_parameter_name,
|
||||
default_value='false',
|
||||
description='Visualize the robot in Rviz'),
|
||||
DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description='Use fake hardware'),
|
||||
DeclareLaunchArgument(
|
||||
fake_sensor_commands_parameter_name,
|
||||
default_value='false',
|
||||
description="Fake sensor commands. Only valid when '{}' is true".format(
|
||||
use_fake_hardware_parameter_name)),
|
||||
DeclareLaunchArgument(
|
||||
load_gripper_parameter_name,
|
||||
default_value='true',
|
||||
description='Use Franka Gripper as an end-effector, otherwise, the robot is loaded '
|
||||
'without an end-effector.'),
|
||||
|
||||
IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([PathJoinSubstitution(
|
||||
[FindPackageShare('franka_bringup'), 'launch', 'franka.launch.py'])]),
|
||||
launch_arguments={robot_ip_parameter_name: robot_ip,
|
||||
load_gripper_parameter_name: load_gripper,
|
||||
use_fake_hardware_parameter_name: use_fake_hardware,
|
||||
fake_sensor_commands_parameter_name: fake_sensor_commands,
|
||||
use_rviz_parameter_name: use_rviz
|
||||
}.items(),
|
||||
),
|
||||
|
||||
Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['model_example_controller'],
|
||||
output='screen',
|
||||
),
|
||||
])
|
@ -0,0 +1,90 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
load_gripper_parameter_name = 'load_gripper'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
fake_sensor_commands_parameter_name = 'fake_sensor_commands'
|
||||
use_rviz_parameter_name = 'use_rviz'
|
||||
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
load_gripper = LaunchConfiguration(load_gripper_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
|
||||
use_rviz = LaunchConfiguration(use_rviz_parameter_name)
|
||||
|
||||
return LaunchDescription(
|
||||
[
|
||||
DeclareLaunchArgument(
|
||||
robot_ip_parameter_name, description='Hostname or IP address of the robot.'
|
||||
),
|
||||
DeclareLaunchArgument(
|
||||
use_rviz_parameter_name,
|
||||
default_value='false',
|
||||
description='Visualize the robot in Rviz',
|
||||
),
|
||||
DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description='Use fake hardware',
|
||||
),
|
||||
DeclareLaunchArgument(
|
||||
fake_sensor_commands_parameter_name,
|
||||
default_value='false',
|
||||
description='Fake sensor commands. Only valid when "{}" is true'.format(
|
||||
use_fake_hardware_parameter_name
|
||||
),
|
||||
),
|
||||
DeclareLaunchArgument(
|
||||
load_gripper_parameter_name,
|
||||
default_value='true',
|
||||
description=(
|
||||
'Use Franka Gripper as an end-effector, otherwise, the robot is loaded '
|
||||
'without an end-effector.'
|
||||
),
|
||||
),
|
||||
IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
[
|
||||
PathJoinSubstitution(
|
||||
[FindPackageShare('franka_bringup'), 'launch', 'franka.launch.py']
|
||||
)
|
||||
]
|
||||
),
|
||||
launch_arguments={
|
||||
robot_ip_parameter_name: robot_ip,
|
||||
load_gripper_parameter_name: load_gripper,
|
||||
use_fake_hardware_parameter_name: use_fake_hardware,
|
||||
fake_sensor_commands_parameter_name: fake_sensor_commands,
|
||||
use_rviz_parameter_name: use_rviz,
|
||||
}.items(),
|
||||
),
|
||||
Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['move_to_start_example_controller'],
|
||||
output='screen',
|
||||
),
|
||||
]
|
||||
)
|
28
franka_bringup/package.xml
Normal file
28
franka_bringup/package.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>franka_bringup</name>
|
||||
<version>0.1.0</version>
|
||||
<description>Package with launch files and run-time configurations for using Franka Emika research robots with ros2_control</description>
|
||||
<maintainer email="support@franka.de">Franka Emika GmbH</maintainer>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<exec_depend>franka_description</exec_depend>
|
||||
<exec_depend>franka_hardware</exec_depend>
|
||||
<exec_depend>franka_robot_state_broadcaster</exec_depend>
|
||||
<exec_depend>joint_state_publisher</exec_depend>
|
||||
<exec_depend>robot_state_publisher</exec_depend>
|
||||
<exec_depend>controller_manager</exec_depend>
|
||||
<exec_depend>joint_state_broadcaster</exec_depend>
|
||||
<exec_depend>rviz2</exec_depend>
|
||||
<exec_depend>xacro</exec_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
21
franka_description/CMakeLists.txt
Normal file
21
franka_description/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(franka_description)
|
||||
find_package(ament_cmake REQUIRED)
|
||||
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
find_package(ament_cmake_pytest REQUIRED)
|
||||
ament_add_pytest_test(${PROJECT_NAME}_urdf_tests test/urdf_tests.py)
|
||||
|
||||
endif()
|
||||
|
||||
# Install launch files.
|
||||
install(DIRECTORY
|
||||
launch robots meshes rviz
|
||||
DESTINATION share/${PROJECT_NAME}/
|
||||
)
|
||||
|
||||
ament_package()
|
58
franka_description/launch/visualize_franka.launch.py
Normal file
58
franka_description/launch/visualize_franka.launch.py
Normal file
@ -0,0 +1,58 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.substitutions import Command, FindExecutable, LaunchConfiguration
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
load_gripper_parameter_name = 'load_gripper'
|
||||
load_gripper = LaunchConfiguration(load_gripper_parameter_name)
|
||||
|
||||
franka_xacro_file = os.path.join(get_package_share_directory('franka_description'), 'robots',
|
||||
'panda_arm.urdf.xacro')
|
||||
robot_description = Command(
|
||||
[FindExecutable(name='xacro'), ' ', franka_xacro_file, ' hand:=', load_gripper])
|
||||
|
||||
rviz_file = os.path.join(get_package_share_directory('franka_description'), 'rviz',
|
||||
'visualize_franka.rviz')
|
||||
|
||||
return LaunchDescription([
|
||||
DeclareLaunchArgument(
|
||||
load_gripper_parameter_name,
|
||||
default_value='true',
|
||||
description='Use Franka Gripper as end-effector if true. Robot is loaded without '
|
||||
'end-effector otherwise'),
|
||||
Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
parameters=[{'robot_description': robot_description}],
|
||||
),
|
||||
Node(
|
||||
package='joint_state_publisher_gui',
|
||||
executable='joint_state_publisher_gui',
|
||||
name='joint_state_publisher_gui'
|
||||
),
|
||||
Node(package='rviz2',
|
||||
executable='rviz2',
|
||||
name='rviz2',
|
||||
arguments=['--display-config', rviz_file])
|
||||
])
|
178
franka_description/meshes/visual/finger.dae
Normal file
178
franka_description/meshes/visual/finger.dae
Normal file
File diff suppressed because one or more lines are too long
403
franka_description/meshes/visual/hand.dae
Normal file
403
franka_description/meshes/visual/hand.dae
Normal file
File diff suppressed because one or more lines are too long
928
franka_description/meshes/visual/link0.dae
Normal file
928
franka_description/meshes/visual/link0.dae
Normal file
File diff suppressed because one or more lines are too long
103
franka_description/meshes/visual/link1.dae
Normal file
103
franka_description/meshes/visual/link1.dae
Normal file
File diff suppressed because one or more lines are too long
103
franka_description/meshes/visual/link2.dae
Normal file
103
franka_description/meshes/visual/link2.dae
Normal file
File diff suppressed because one or more lines are too long
328
franka_description/meshes/visual/link3.dae
Normal file
328
franka_description/meshes/visual/link3.dae
Normal file
File diff suppressed because one or more lines are too long
328
franka_description/meshes/visual/link4.dae
Normal file
328
franka_description/meshes/visual/link4.dae
Normal file
File diff suppressed because one or more lines are too long
257
franka_description/meshes/visual/link5.dae
Normal file
257
franka_description/meshes/visual/link5.dae
Normal file
File diff suppressed because one or more lines are too long
1307
franka_description/meshes/visual/link6.dae
Normal file
1307
franka_description/meshes/visual/link6.dae
Normal file
File diff suppressed because one or more lines are too long
632
franka_description/meshes/visual/link7.dae
Normal file
632
franka_description/meshes/visual/link7.dae
Normal file
File diff suppressed because one or more lines are too long
24
franka_description/package.xml
Normal file
24
franka_description/package.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>franka_description</name>
|
||||
<version>0.1.0</version>
|
||||
<description>franka_description contains URDF files and meshes of Franka Emika robots</description>
|
||||
<maintainer email="support@franka.de">Franka Emika GmbH</maintainer>
|
||||
<license>Apache 2.0</license>
|
||||
<author>Franka Emika GmbH</author>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>ament_cmake_pytest</test_depend>
|
||||
|
||||
<exec_depend>xacro</exec_depend>
|
||||
<exec_depend>rviz2</exec_depend>
|
||||
<exec_depend>joint_state_publisher_gui</exec_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
5
franka_description/robots/hand.urdf.xacro
Normal file
5
franka_description/robots/hand.urdf.xacro
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="hand">
|
||||
<xacro:include filename="hand.xacro"/>
|
||||
<xacro:hand ns="panda" safety_distance="0.03"/>
|
||||
</robot>
|
93
franka_description/robots/hand.xacro
Normal file
93
franka_description/robots/hand.xacro
Normal file
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="hand">
|
||||
<!-- safety_distance: Minimum safety distance in [m] by which the collision volumes are expanded and which is enforced during robot motions -->
|
||||
<xacro:macro name="hand" params="connected_to:='' ns:='' rpy:='0 0 0' xyz:='0 0 0' safety_distance:=0">
|
||||
<xacro:unless value="${connected_to == ''}">
|
||||
<joint name="${ns}_hand_joint" type="fixed">
|
||||
<parent link="${connected_to}"/>
|
||||
<child link="${ns}_hand"/>
|
||||
<origin xyz="${xyz}" rpy="${rpy}"/>
|
||||
</joint>
|
||||
</xacro:unless>
|
||||
<link name="${ns}_hand">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://franka_description/meshes/visual/hand.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.04" rpy="0 ${pi/2} ${pi/2}"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.04+safety_distance}" length="0.1"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 -0.05 0.04" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.04+safety_distance}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0.05 0.04" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.04+safety_distance}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.1" rpy="0 ${pi/2} ${pi/2}"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.02+safety_distance}" length="0.1"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 -0.05 0.1" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.02+safety_distance}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0.05 0.1" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.02+safety_distance}"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<!-- Define the hand_tcp frame -->
|
||||
<link name="${ns}_hand_tcp"/>
|
||||
<joint name="${ns}_hand_tcp_joint" type="fixed">
|
||||
<origin xyz="0 0 0.1034" rpy="0 0 0"/>
|
||||
<parent link="${ns}_hand"/>
|
||||
<child link="${ns}_hand_tcp"/>
|
||||
</joint>
|
||||
<link name="${ns}_leftfinger">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://franka_description/meshes/visual/finger.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
</link>
|
||||
<link name="${ns}_rightfinger">
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 ${pi}"/>
|
||||
<geometry>
|
||||
<mesh filename="package://franka_description/meshes/visual/finger.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
</link>
|
||||
<joint name="${ns}_finger_joint1" type="prismatic">
|
||||
<parent link="${ns}_hand"/>
|
||||
<child link="${ns}_leftfinger"/>
|
||||
<origin xyz="0 0 0.0584" rpy="0 0 0"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit effort="20" lower="0.0" upper="0.04" velocity="0.2"/>
|
||||
</joint>
|
||||
<joint name="${ns}_finger_joint2" type="prismatic">
|
||||
<parent link="${ns}_hand"/>
|
||||
<child link="${ns}_rightfinger"/>
|
||||
<origin xyz="0 0 0.0584" rpy="0 0 0"/>
|
||||
<axis xyz="0 -1 0"/>
|
||||
<limit effort="20" lower="0.0" upper="0.04" velocity="0.2"/>
|
||||
<mimic joint="${ns}_finger_joint1"/>
|
||||
</joint>
|
||||
</xacro:macro>
|
||||
</robot>
|
38
franka_description/robots/panda_arm.ros2_control.xacro
Normal file
38
franka_description/robots/panda_arm.ros2_control.xacro
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:macro name="panda_arm_ros2_control" params="ns robot_ip use_fake_hardware:=^|false fake_sensor_commands:=^|false">
|
||||
<ros2_control name="FrankaHardwareInterface" type="system">
|
||||
<hardware>
|
||||
<xacro:if value="${use_fake_hardware}">
|
||||
<plugin>fake_components/GenericSystem</plugin>
|
||||
<param name="fake_sensor_commands">${fake_sensor_commands}</param>
|
||||
<param name="state_following_offset">0.0</param>
|
||||
</xacro:if>
|
||||
<xacro:unless value="${use_fake_hardware}">
|
||||
<plugin>franka_hardware/FrankaHardwareInterface</plugin>
|
||||
<param name="robot_ip">${robot_ip}</param>
|
||||
</xacro:unless>
|
||||
</hardware>
|
||||
|
||||
<xacro:macro name="configure_joint" params="joint_name initial_position">
|
||||
<joint name="${joint_name}">
|
||||
<param name="initial_position">${initial_position}</param>
|
||||
<command_interface name="effort"/>
|
||||
<state_interface name="position"/>
|
||||
<state_interface name="velocity"/>
|
||||
<state_interface name="effort"/>
|
||||
</joint>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:configure_joint joint_name="${ns}_joint1" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="${ns}_joint2" initial_position="${-pi/4}"/>
|
||||
<xacro:configure_joint joint_name="${ns}_joint3" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="${ns}_joint4" initial_position="${-3*pi/4}"/>
|
||||
<xacro:configure_joint joint_name="${ns}_joint5" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="${ns}_joint6" initial_position="${pi/2}"/>
|
||||
<xacro:configure_joint joint_name="${ns}_joint7" initial_position="${pi/4}"/>
|
||||
|
||||
</ros2_control>
|
||||
</xacro:macro>
|
||||
</robot>
|
18
franka_description/robots/panda_arm.urdf.xacro
Normal file
18
franka_description/robots/panda_arm.urdf.xacro
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda">
|
||||
<xacro:arg name="arm_id" default="panda"/> <!-- Name of this panda -->
|
||||
<xacro:arg name="hand" default="false"/> <!-- Should a franka_gripper be mounted at the flange?" -->
|
||||
<xacro:arg name="robot_ip" default=""/> <!-- IP address or hostname of the robot" -->
|
||||
<xacro:arg name="use_fake_hardware" default="false"/>
|
||||
<xacro:arg name="fake_sensor_commands" default="false"/>
|
||||
|
||||
<xacro:include filename="$(find franka_description)/robots/panda_arm.xacro"/>
|
||||
<xacro:panda_arm arm_id="$(arg arm_id)" safety_distance="0.03"/>
|
||||
|
||||
<xacro:if value="$(arg hand)">
|
||||
<xacro:include filename="$(find franka_description)/robots/hand.xacro"/>
|
||||
<xacro:hand ns="$(arg arm_id)" rpy="0 0 ${-pi/4}" connected_to="$(arg arm_id)_link8" safety_distance="0.03"/>
|
||||
</xacro:if>
|
||||
<xacro:include filename="$(find franka_description)/robots/panda_arm.ros2_control.xacro"/>
|
||||
<xacro:panda_arm_ros2_control ns="$(arg arm_id)" robot_ip="$(arg robot_ip)" use_fake_hardware="$(arg use_fake_hardware)" fake_sensor_commands="$(arg fake_sensor_commands)"/>
|
||||
</robot>
|
314
franka_description/robots/panda_arm.xacro
Normal file
314
franka_description/robots/panda_arm.xacro
Normal file
@ -0,0 +1,314 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda">
|
||||
<!-- safety_distance: Minimum safety distance in [m] by which the collision volumes are expanded and which is enforced during robot motions -->
|
||||
<!-- arm_id: Namespace of the panda arm. Serves to differentiate between arms in case of multiple instances. -->
|
||||
<xacro:macro name="panda_arm" params="arm_id:='panda' description_pkg:='franka_description' connected_to:='' xyz:='0 0 0' rpy:='0 0 0' safety_distance:=0">
|
||||
<xacro:unless value="${not connected_to}">
|
||||
<joint name="${arm_id}_joint_${connected_to}" type="fixed">
|
||||
<parent link="${connected_to}"/>
|
||||
<child link="${arm_id}_link0"/>
|
||||
<origin rpy="${rpy}" xyz="${xyz}"/>
|
||||
</joint>
|
||||
</xacro:unless>
|
||||
<link name="${arm_id}_link0">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link0.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.075 0 0.06" rpy="0 ${pi/2} 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.06+safety_distance}" length="0.03" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="-0.06 0 0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="-0.09 0 0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<link name="${arm_id}_link1">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link1.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.1915" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.06+safety_distance}" length="0.2830" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.333" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.05" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="${arm_id}_joint1" type="revolute">
|
||||
<safety_controller k_position="100.0" k_velocity="40.0" soft_lower_limit="-2.8973" soft_upper_limit="2.8973"/>
|
||||
<origin rpy="0 0 0" xyz="0 0 0.333"/>
|
||||
<parent link="${arm_id}_link0"/>
|
||||
<child link="${arm_id}_link1"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="87" lower="-2.8973" upper="2.8973" velocity="2.1750"/>
|
||||
</joint>
|
||||
<link name="${arm_id}_link2">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link2.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.06+safety_distance}" length="0.12" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="${arm_id}_joint2" type="revolute">
|
||||
<safety_controller k_position="100.0" k_velocity="40.0" soft_lower_limit="-1.7628" soft_upper_limit="1.7628"/>
|
||||
<origin rpy="${-pi/2} 0 0" xyz="0 0 0"/>
|
||||
<parent link="${arm_id}_link1"/>
|
||||
<child link="${arm_id}_link2"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="87" lower="-1.7628" upper="1.7628" velocity="2.1750"/>
|
||||
</joint>
|
||||
<link name="${arm_id}_link3">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link3.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.145" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.06+safety_distance}" length="0.15" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.22" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.07" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="${arm_id}_joint3" type="revolute">
|
||||
<safety_controller k_position="100.0" k_velocity="40.0" soft_lower_limit="-2.8973" soft_upper_limit="2.8973"/>
|
||||
<origin rpy="${pi/2} 0 0" xyz="0 -0.316 0"/>
|
||||
<parent link="${arm_id}_link2"/>
|
||||
<child link="${arm_id}_link3"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="87" lower="-2.8973" upper="2.8973" velocity="2.1750"/>
|
||||
</joint>
|
||||
<link name="${arm_id}_link4">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link4.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.06+safety_distance}" length="0.12" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="${arm_id}_joint4" type="revolute">
|
||||
<safety_controller k_position="100.0" k_velocity="40.0" soft_lower_limit="-3.0718" soft_upper_limit="-0.0698"/>
|
||||
<origin rpy="${pi/2} 0 0" xyz="0.0825 0 0"/>
|
||||
<parent link="${arm_id}_link3"/>
|
||||
<child link="${arm_id}_link4"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="87" lower="-3.0718" upper="-0.0698" velocity="2.1750"/>
|
||||
</joint>
|
||||
<link name="${arm_id}_link5">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link5.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.26" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.06+safety_distance}" length="0.1" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.31" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.21" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.06+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0.08 -0.13" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.025+safety_distance}" length="0.14" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0.08 -0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.025+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0.08 -0.20" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.025+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
</link>
|
||||
<joint name="${arm_id}_joint5" type="revolute">
|
||||
<safety_controller k_position="100.0" k_velocity="40.0" soft_lower_limit="-2.8973" soft_upper_limit="2.8973"/>
|
||||
<origin rpy="${-pi/2} 0 0" xyz="-0.0825 0.384 0"/>
|
||||
<parent link="${arm_id}_link4"/>
|
||||
<child link="${arm_id}_link5"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="12" lower="-2.8973" upper="2.8973" velocity="2.6100"/>
|
||||
</joint>
|
||||
<link name="${arm_id}_link6">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link6.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.03" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.05+safety_distance}" length="0.08" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.01" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.05+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.07" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.05+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="${arm_id}_joint6" type="revolute">
|
||||
<safety_controller k_position="100.0" k_velocity="40.0" soft_lower_limit="-0.0175" soft_upper_limit="3.7525"/>
|
||||
<origin rpy="${pi/2} 0 0" xyz="0 0 0"/>
|
||||
<parent link="${arm_id}_link5"/>
|
||||
<child link="${arm_id}_link6"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="12" lower="-0.0175" upper="3.7525" velocity="2.6100"/>
|
||||
</joint>
|
||||
<link name="${arm_id}_link7">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/visual/link7.dae"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.01" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.04+safety_distance}" length="0.14" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.08" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.04+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0 0 -0.06" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.04+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="${arm_id}_joint7" type="revolute">
|
||||
<safety_controller k_position="100.0" k_velocity="40.0" soft_lower_limit="-2.8973" soft_upper_limit="2.8973"/>
|
||||
<origin rpy="${pi/2} 0 0" xyz="0.088 0 0"/>
|
||||
<parent link="${arm_id}_link6"/>
|
||||
<child link="${arm_id}_link7"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="12" lower="-2.8973" upper="2.8973" velocity="2.6100"/>
|
||||
</joint>
|
||||
<link name="${arm_id}_link8">
|
||||
<collision>
|
||||
<origin xyz="0.0424 0.0424 -0.0250" rpy="${pi} ${pi/2} ${pi/2}"/>
|
||||
<geometry>
|
||||
<cylinder radius="${0.03+safety_distance}" length="0.01" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0.0424 0.0424 -0.02" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.03+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<collision>
|
||||
<origin xyz="0.0424 0.0424 -0.03" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="${0.03+safety_distance}" />
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="${arm_id}_joint8" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0 0.107"/>
|
||||
<parent link="${arm_id}_link7"/>
|
||||
<child link="${arm_id}_link8"/>
|
||||
</joint>
|
||||
</xacro:macro>
|
||||
</robot>
|
208
franka_description/rviz/visualize_franka.rviz
Normal file
208
franka_description/rviz/visualize_franka.rviz
Normal file
@ -0,0 +1,208 @@
|
||||
Panels:
|
||||
- Class: rviz_common/Displays
|
||||
Help Height: 78
|
||||
Name: Displays
|
||||
Property Tree Widget:
|
||||
Expanded:
|
||||
- /Global Options1
|
||||
- /Status1
|
||||
- /RobotModel1
|
||||
- /RobotModel1/Description Topic1
|
||||
- /TF1
|
||||
Splitter Ratio: 0.5
|
||||
Tree Height: 814
|
||||
- Class: rviz_common/Selection
|
||||
Name: Selection
|
||||
- Class: rviz_common/Tool Properties
|
||||
Expanded:
|
||||
- /2D Goal Pose1
|
||||
- /Publish Point1
|
||||
Name: Tool Properties
|
||||
Splitter Ratio: 0.5886790156364441
|
||||
- Class: rviz_common/Views
|
||||
Expanded:
|
||||
- /Current View1
|
||||
Name: Views
|
||||
Splitter Ratio: 0.5
|
||||
Visualization Manager:
|
||||
Class: ""
|
||||
Displays:
|
||||
- Alpha: 0.5
|
||||
Cell Size: 1
|
||||
Class: rviz_default_plugins/Grid
|
||||
Color: 160; 160; 164
|
||||
Enabled: true
|
||||
Line Style:
|
||||
Line Width: 0.029999999329447746
|
||||
Value: Lines
|
||||
Name: Grid
|
||||
Normal Cell Count: 0
|
||||
Offset:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Plane: XY
|
||||
Plane Cell Count: 10
|
||||
Reference Frame: <Fixed Frame>
|
||||
Value: true
|
||||
- Alpha: 1
|
||||
Class: rviz_default_plugins/RobotModel
|
||||
Collision Enabled: false
|
||||
Description File: ""
|
||||
Description Source: Topic
|
||||
Description Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /robot_description
|
||||
Enabled: true
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
panda_link0:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link1:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link2:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link3:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link4:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link5:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link6:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link7:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link8:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
Name: RobotModel
|
||||
TF Prefix: ""
|
||||
Update Interval: 0
|
||||
Value: true
|
||||
Visual Enabled: true
|
||||
- Class: rviz_default_plugins/TF
|
||||
Enabled: false
|
||||
Frame Timeout: 15
|
||||
Frames:
|
||||
All Enabled: true
|
||||
Marker Scale: 1
|
||||
Name: TF
|
||||
Show Arrows: true
|
||||
Show Axes: true
|
||||
Show Names: false
|
||||
Tree:
|
||||
{}
|
||||
Update Interval: 0
|
||||
Value: false
|
||||
Enabled: true
|
||||
Global Options:
|
||||
Background Color: 48; 48; 48
|
||||
Fixed Frame: panda_link0
|
||||
Frame Rate: 30
|
||||
Name: root
|
||||
Tools:
|
||||
- Class: rviz_default_plugins/Interact
|
||||
Hide Inactive Objects: true
|
||||
- Class: rviz_default_plugins/MoveCamera
|
||||
- Class: rviz_default_plugins/Select
|
||||
- Class: rviz_default_plugins/FocusCamera
|
||||
- Class: rviz_default_plugins/Measure
|
||||
Line color: 128; 128; 0
|
||||
- Class: rviz_default_plugins/SetInitialPose
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /initialpose
|
||||
- Class: rviz_default_plugins/SetGoal
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /goal_pose
|
||||
- Class: rviz_default_plugins/PublishPoint
|
||||
Single click: true
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /clicked_point
|
||||
Transformation:
|
||||
Current:
|
||||
Class: rviz_default_plugins/TF
|
||||
Value: true
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz_default_plugins/Orbit
|
||||
Distance: 2.737480401992798
|
||||
Enable Stereo Rendering:
|
||||
Stereo Eye Separation: 0.05999999865889549
|
||||
Stereo Focal Distance: 1
|
||||
Swap Stereo Eyes: false
|
||||
Value: false
|
||||
Focal Point:
|
||||
X: 0.005491261370480061
|
||||
Y: -0.07745686918497086
|
||||
Z: 0.32312366366386414
|
||||
Focal Shape Fixed Size: true
|
||||
Focal Shape Size: 0.05000000074505806
|
||||
Invert Z Axis: false
|
||||
Name: Current View
|
||||
Near Clip Distance: 0.009999999776482582
|
||||
Pitch: 0.23539823293685913
|
||||
Target Frame: <Fixed Frame>
|
||||
Value: Orbit (rviz)
|
||||
Yaw: 1.5790610313415527
|
||||
Saved: ~
|
||||
Window Geometry:
|
||||
Displays:
|
||||
collapsed: false
|
||||
Height: 1043
|
||||
Hide Left Dock: false
|
||||
Hide Right Dock: false
|
||||
QMainWindow State: 000000ff00000000fd000000040000000000000156000003b9fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000003b9000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000003b9fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000003b9000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000004c7000003b900000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Selection:
|
||||
collapsed: false
|
||||
Tool Properties:
|
||||
collapsed: false
|
||||
Views:
|
||||
collapsed: false
|
||||
Width: 1848
|
||||
X: 72
|
||||
Y: 0
|
63
franka_description/test/urdf_tests.py
Normal file
63
franka_description/test/urdf_tests.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from os import path
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
import xacro
|
||||
|
||||
panda_xacro_file_name = path.join(get_package_share_directory('franka_description'), 'robots',
|
||||
'panda_arm.urdf.xacro')
|
||||
|
||||
|
||||
def test_load():
|
||||
urdf = xacro.process_file(panda_xacro_file_name).toxml()
|
||||
assert urdf.find('panda_finger_joint') == -1
|
||||
|
||||
|
||||
def test_load_with_gripper():
|
||||
urdf = xacro.process_file(panda_xacro_file_name,
|
||||
mappings={'hand': 'true'}).toxml()
|
||||
assert urdf.find('panda_finger_joint') != -1
|
||||
|
||||
|
||||
def test_load_with_fake_hardware():
|
||||
urdf = xacro.process_file(panda_xacro_file_name,
|
||||
mappings={'use_fake_hardware': 'true'}).toxml()
|
||||
assert urdf.find('fake_components/GenericSystem') != -1
|
||||
|
||||
|
||||
def test_load_with_robot_ip():
|
||||
urdf = xacro.process_file(panda_xacro_file_name,
|
||||
mappings={'robot_ip': 'franka_ip_address'}).toxml()
|
||||
assert urdf.find('franka_ip_address') != -1
|
||||
|
||||
|
||||
def test_load_with_arm_id():
|
||||
urdf = xacro.process_file(panda_xacro_file_name,
|
||||
mappings={'arm_id': 'totally_different_arm'}).toxml()
|
||||
assert urdf.find('totally_different_arm_joint1') != -1
|
||||
|
||||
|
||||
def test_check_interfaces():
|
||||
urdf = xacro.process_file(panda_xacro_file_name).toxml()
|
||||
assert urdf.find('state_interface') != -1
|
||||
assert urdf.find('command_interface') != -1
|
||||
assert urdf.find('position') != -1
|
||||
assert urdf.find('velocity') != -1
|
||||
assert urdf.find('effort') != -1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
138
franka_example_controllers/CMakeLists.txt
Normal file
138
franka_example_controllers/CMakeLists.txt
Normal file
@ -0,0 +1,138 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(franka_example_controllers)
|
||||
|
||||
# Default to C++17
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
option(CHECK_TIDY "Adds clang-tidy tests" OFF)
|
||||
|
||||
# find dependencies
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(controller_interface REQUIRED)
|
||||
find_package(pluginlib REQUIRED)
|
||||
find_package(rclcpp_lifecycle REQUIRED)
|
||||
find_package(hardware_interface REQUIRED)
|
||||
find_package(franka_msgs REQUIRED)
|
||||
find_package(Eigen3 REQUIRED)
|
||||
find_package(franka_semantic_components)
|
||||
find_package(generate_parameter_library)
|
||||
|
||||
add_library(
|
||||
${PROJECT_NAME}
|
||||
SHARED
|
||||
src/gravity_compensation_example_controller.cpp
|
||||
src/joint_impedance_example_controller.cpp
|
||||
src/model_example_controller.cpp
|
||||
src/move_to_start_example_controller.cpp
|
||||
src/motion_generator.cpp)
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PUBLIC
|
||||
include
|
||||
${EIGEN3_INCLUDE_DIRS}
|
||||
)
|
||||
ament_target_dependencies(
|
||||
${PROJECT_NAME}
|
||||
controller_interface
|
||||
hardware_interface
|
||||
pluginlib
|
||||
rclcpp
|
||||
rclcpp_lifecycle
|
||||
franka_semantic_components
|
||||
)
|
||||
|
||||
generate_parameter_library(franka_example_controllers_parameters src/model_example_controller_parameters.yaml)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} franka_example_controllers_parameters)
|
||||
|
||||
pluginlib_export_plugin_description_file(
|
||||
controller_interface franka_example_controllers.xml)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
${PROJECT_NAME}
|
||||
RUNTIME DESTINATION bin
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY include/
|
||||
DESTINATION include
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_cmake_clang_format REQUIRED)
|
||||
find_package(ament_cmake_copyright REQUIRED)
|
||||
find_package(ament_cmake_cppcheck REQUIRED)
|
||||
find_package(ament_cmake_flake8 REQUIRED)
|
||||
find_package(ament_cmake_lint_cmake REQUIRED)
|
||||
find_package(ament_cmake_pep257 REQUIRED)
|
||||
find_package(ament_cmake_xmllint REQUIRED)
|
||||
find_package(ament_cmake_gmock REQUIRED)
|
||||
find_package(controller_manager REQUIRED)
|
||||
find_package(ros2_control_test_assets REQUIRED)
|
||||
|
||||
set(CPP_DIRECTORIES src include)
|
||||
ament_clang_format(CONFIG_FILE ../.clang-format ${CPP_DIRECTORIES})
|
||||
ament_copyright(src ${CPP_DIRECTORIES} package.xml)
|
||||
ament_cppcheck(${CPP_DIRECTORIES})
|
||||
ament_lint_cmake(CMakeLists.txt)
|
||||
ament_flake8()
|
||||
ament_pep257()
|
||||
ament_xmllint()
|
||||
ament_add_gmock(${PROJECT_NAME}_test
|
||||
test/test_load_gravity_compensation_controller.cpp
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME}_test PRIVATE include)
|
||||
ament_target_dependencies(${PROJECT_NAME}_test
|
||||
controller_manager
|
||||
ros2_control_test_assets
|
||||
)
|
||||
|
||||
ament_add_gmock(${PROJECT_NAME}_test_load_move_to_start
|
||||
test/test_load_move_to_start_example_controller.cpp)
|
||||
target_include_directories(${PROJECT_NAME}_test_load_move_to_start PRIVATE include)
|
||||
ament_target_dependencies(${PROJECT_NAME}_test_load_move_to_start
|
||||
controller_manager
|
||||
ros2_control_test_assets
|
||||
)
|
||||
|
||||
|
||||
ament_add_gmock(${PROJECT_NAME}_gravity_test test/test_gravity_compensation_example.cpp)
|
||||
target_include_directories(${PROJECT_NAME}_gravity_test PRIVATE include)
|
||||
target_link_libraries(${PROJECT_NAME}_gravity_test ${PROJECT_NAME})
|
||||
|
||||
ament_add_gmock(${PROJECT_NAME}_move_to_start_test test/test_move_to_start_example_controller.cpp)
|
||||
target_include_directories(${PROJECT_NAME}_move_to_start_test PRIVATE include)
|
||||
target_link_libraries(${PROJECT_NAME}_move_to_start_test ${PROJECT_NAME})
|
||||
|
||||
if(CHECK_TIDY)
|
||||
find_package(ament_cmake_clang_tidy REQUIRED)
|
||||
set(ament_cmake_clang_tidy_CONFIG_FILE ../.clang-tidy)
|
||||
ament_clang_tidy(${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
ament_export_include_directories(
|
||||
include
|
||||
)
|
||||
ament_export_libraries(
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
ament_export_dependencies(
|
||||
controller_interface
|
||||
pluginlib
|
||||
rclcpp
|
||||
rclcpp_lifecycle
|
||||
hardware_interface
|
||||
)
|
||||
ament_package()
|
26
franka_example_controllers/franka_example_controllers.xml
Normal file
26
franka_example_controllers/franka_example_controllers.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<library path="franka_example_controllers">
|
||||
<class name="franka_example_controllers/GravityCompensationExampleController"
|
||||
type="franka_example_controllers::GravityCompensationExampleController" base_class_type="controller_interface::ControllerInterface">
|
||||
<description>
|
||||
The gravity compensation controller only sends zero torques so that the robot does gravity compensation
|
||||
</description>
|
||||
</class>
|
||||
<class name="franka_example_controllers/JointImpedanceExampleController"
|
||||
type="franka_example_controllers::JointImpedanceExampleController" base_class_type="controller_interface::ControllerInterface">
|
||||
<description>
|
||||
The joint impedance example controller moves joint 4 and 5 in a very compliant periodic movement.
|
||||
</description>
|
||||
</class>
|
||||
<class name="franka_example_controllers/MoveToStartExampleController"
|
||||
type="franka_example_controllers::MoveToStartExampleController" base_class_type="controller_interface::ControllerInterface">
|
||||
<description>
|
||||
The move to start example controller moves the robot into default pose.
|
||||
</description>
|
||||
</class>
|
||||
<class name="franka_example_controllers/ModelExampleController"
|
||||
type="franka_example_controllers::ModelExampleController" base_class_type="controller_interface::ControllerInterface">
|
||||
<description>
|
||||
The franka model example controller evaluates and prints the dynamic model of Franka Emika Robots.
|
||||
</description>
|
||||
</class>
|
||||
</library>
|
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <controller_interface/controller_interface.hpp>
|
||||
#include "franka_example_controllers/visibility_control.h"
|
||||
|
||||
#include <rclcpp/duration.hpp>
|
||||
#include <rclcpp/time.hpp>
|
||||
|
||||
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
/**
|
||||
* The gravity compensation controller only sends zero torques so that the robot does gravity
|
||||
* compensation
|
||||
*/
|
||||
class GravityCompensationExampleController : public controller_interface::ControllerInterface {
|
||||
public:
|
||||
FRANKA_EXAMPLE_CONTROLLERS_PUBLIC
|
||||
CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override;
|
||||
|
||||
FRANKA_EXAMPLE_CONTROLLERS_PUBLIC
|
||||
CallbackReturn on_init() override;
|
||||
|
||||
FRANKA_EXAMPLE_CONTROLLERS_PUBLIC
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration command_interface_configuration()
|
||||
const override;
|
||||
|
||||
FRANKA_EXAMPLE_CONTROLLERS_PUBLIC
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration state_interface_configuration()
|
||||
const override;
|
||||
|
||||
FRANKA_EXAMPLE_CONTROLLERS_PUBLIC
|
||||
controller_interface::return_type update(const rclcpp::Time& time,
|
||||
const rclcpp::Duration& period) override;
|
||||
|
||||
private:
|
||||
std::string arm_id_;
|
||||
const int num_joints = 7;
|
||||
};
|
||||
} // namespace franka_example_controllers
|
@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
#include <controller_interface/controller_interface.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
|
||||
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
/**
|
||||
* The joint impedance example controller moves joint 4 and 5 in a very compliant periodic movement.
|
||||
*/
|
||||
class JointImpedanceExampleController : public controller_interface::ControllerInterface {
|
||||
public:
|
||||
using Vector7d = Eigen::Matrix<double, 7, 1>;
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration command_interface_configuration()
|
||||
const override;
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration state_interface_configuration()
|
||||
const override;
|
||||
controller_interface::return_type update(const rclcpp::Time& time,
|
||||
const rclcpp::Duration& period) override;
|
||||
CallbackReturn on_init() override;
|
||||
CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override;
|
||||
CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override;
|
||||
|
||||
private:
|
||||
std::string arm_id_;
|
||||
const int num_joints = 7;
|
||||
Vector7d q_;
|
||||
Vector7d initial_q_;
|
||||
Vector7d dq_;
|
||||
Vector7d dq_filtered_;
|
||||
Vector7d k_gains_;
|
||||
Vector7d d_gains_;
|
||||
rclcpp::Time start_time_;
|
||||
void updateJointStates();
|
||||
};
|
||||
|
||||
} // namespace franka_example_controllers
|
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <controller_interface/controller_interface.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include "franka_semantic_components/franka_robot_model.hpp"
|
||||
|
||||
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
/// The model example controller prints robot model parameters.
|
||||
class ModelExampleController : public controller_interface::ControllerInterface {
|
||||
public:
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration command_interface_configuration()
|
||||
const override;
|
||||
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration state_interface_configuration()
|
||||
const override;
|
||||
|
||||
controller_interface::return_type update(const rclcpp::Time& time,
|
||||
const rclcpp::Duration& period) override;
|
||||
controller_interface::CallbackReturn on_init() override;
|
||||
|
||||
controller_interface::CallbackReturn on_configure(
|
||||
const rclcpp_lifecycle::State& previous_state) override;
|
||||
|
||||
controller_interface::CallbackReturn on_activate(
|
||||
const rclcpp_lifecycle::State& previous_state) override;
|
||||
|
||||
controller_interface::CallbackReturn on_deactivate(
|
||||
const rclcpp_lifecycle::State& previous_state) override;
|
||||
|
||||
private:
|
||||
std::string arm_id_;
|
||||
std::unique_ptr<franka_semantic_components::FrankaRobotModel> franka_robot_model_;
|
||||
|
||||
const std::string k_robot_state_interface_name{"robot_state"};
|
||||
const std::string k_robot_model_interface_name{"robot_model"};
|
||||
};
|
||||
} // namespace franka_example_controllers
|
@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <rclcpp/duration.hpp>
|
||||
/**
|
||||
* An example showing how to generate a joint pose motion to a goal position. Adapted from:
|
||||
* Wisama Khalil and Etienne Dombre. 2002. Modeling, Identification and Control of Robots
|
||||
* (Kogan Page Science Paper edition).
|
||||
*/
|
||||
class MotionGenerator {
|
||||
public:
|
||||
using Vector7d = Eigen::Matrix<double, 7, 1>;
|
||||
/**
|
||||
* Creates a new MotionGenerator instance for a target q.
|
||||
*
|
||||
* @param[in] speed_factor General speed factor in range (0, 1].
|
||||
* @param[in] q_start Start joint positions.
|
||||
* @param[in] q_goal Target joint positions.
|
||||
*/
|
||||
MotionGenerator(double speed_factor, const Vector7d& q_start, const Vector7d& q_goal);
|
||||
|
||||
/**
|
||||
* Sends joint position calculations
|
||||
*
|
||||
* @param[in] robot_state Current state of the robot.
|
||||
* @param[in] trajectory_time Amount of time, that has passed since the start of the trajectory.
|
||||
*
|
||||
* @return Joint positions to use inside a control loop and a boolean indicating whether the
|
||||
* motion is finished.
|
||||
*/
|
||||
std::pair<Vector7d, bool> getDesiredJointPositions(const rclcpp::Duration& trajectory_time);
|
||||
|
||||
private:
|
||||
using Vector7i = Eigen::Matrix<int, 7, 1>;
|
||||
|
||||
bool calculateDesiredValues(double time, Vector7d* delta_q_d) const;
|
||||
void calculateSynchronizedValues();
|
||||
|
||||
static constexpr double kDeltaQMotionFinished = 1e-6;
|
||||
static const int kJoints = 7;
|
||||
|
||||
Vector7d q_start_;
|
||||
Vector7d delta_q_;
|
||||
|
||||
Vector7d dq_max_sync_;
|
||||
Vector7d t_1_sync_;
|
||||
Vector7d t_2_sync_;
|
||||
Vector7d t_f_sync_;
|
||||
Vector7d q_1_;
|
||||
|
||||
double time_ = 0.0;
|
||||
|
||||
Vector7d dq_max_ = (Vector7d() << 2.0, 2.0, 2.0, 2.0, 2.5, 2.5, 2.5).finished(); // in m/s
|
||||
Vector7d ddq_max_start_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished(); // in m/s^2
|
||||
Vector7d ddq_max_goal_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished(); // in m/s^2
|
||||
};
|
@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
#include <controller_interface/controller_interface.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
|
||||
#include "motion_generator.hpp"
|
||||
|
||||
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
/// The move to start example controller moves the robot into default pose.
|
||||
class MoveToStartExampleController : public controller_interface::ControllerInterface {
|
||||
public:
|
||||
using Vector7d = Eigen::Matrix<double, 7, 1>;
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration command_interface_configuration()
|
||||
const override;
|
||||
[[nodiscard]] controller_interface::InterfaceConfiguration state_interface_configuration()
|
||||
const override;
|
||||
controller_interface::return_type update(const rclcpp::Time& time,
|
||||
const rclcpp::Duration& period) override;
|
||||
CallbackReturn on_init() override;
|
||||
CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override;
|
||||
CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override;
|
||||
|
||||
private:
|
||||
std::string arm_id_;
|
||||
const int num_joints = 7;
|
||||
Vector7d q_;
|
||||
Vector7d q_goal_;
|
||||
Vector7d dq_;
|
||||
Vector7d dq_filtered_;
|
||||
Vector7d k_gains_;
|
||||
Vector7d d_gains_;
|
||||
rclcpp::Time start_time_;
|
||||
std::unique_ptr<MotionGenerator> motion_generator_;
|
||||
|
||||
void updateJointStates();
|
||||
};
|
||||
} // namespace franka_example_controllers
|
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* This header must be included by all rclcpp headers which declare symbols
|
||||
* which are defined in the rclcpp library. When not building the rclcpp
|
||||
* library, i.e. when using the headers in other package's code, the contents
|
||||
* of this header change the visibility of certain symbols which the rclcpp
|
||||
* library cannot have, but the consuming code must have inorder to link.
|
||||
*/
|
||||
#ifndef FRANKA_EXAMPLE_CONTROLLERS__VISIBILITY_CONTROL_H_
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS__VISIBILITY_CONTROL_H_
|
||||
|
||||
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
|
||||
// https://gcc.gnu.org/wiki/Visibility
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#ifdef __GNUC__
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_EXPORT __attribute__((dllexport))
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_IMPORT __attribute__((dllimport))
|
||||
#else
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_EXPORT __declspec(dllexport)
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_IMPORT __declspec(dllimport)
|
||||
#endif
|
||||
#ifdef FRANKA_EXAMPLE_CONTROLLERS_BUILDING_DLL
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_PUBLIC FRANKA_EXAMPLE_CONTROLLERS_EXPORT
|
||||
#else
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_PUBLIC FRANKA_EXAMPLE_CONTROLLERS_IMPORT
|
||||
#endif
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_PUBLIC_TYPE FRANKA_EXAMPLE_CONTROLLERS_PUBLIC
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_LOCAL
|
||||
#else
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_EXPORT __attribute__((visibility("default")))
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_IMPORT
|
||||
#if __GNUC__ >= 4
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_PUBLIC __attribute__((visibility("default")))
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_LOCAL __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_PUBLIC
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_LOCAL
|
||||
#endif
|
||||
#define FRANKA_EXAMPLE_CONTROLLERS_PUBLIC_TYPE
|
||||
#endif
|
||||
|
||||
#endif // FRANKA_EXAMPLE_CONTROLLERS__VISIBILITY_CONTROL_H_
|
36
franka_example_controllers/package.xml
Normal file
36
franka_example_controllers/package.xml
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>franka_example_controllers</name>
|
||||
<version>0.1.0</version>
|
||||
<description>franka_example_controllers provides example code for controlling Franka Emika research robots with ros2_control</description>
|
||||
<maintainer email="support@franka.de">Franka Emika GmbH</maintainer>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>rclcpp</depend>
|
||||
<depend>controller_interface</depend>
|
||||
<depend>pluginlib</depend>
|
||||
<depend>rclcpp_lifecycle</depend>
|
||||
<depend>franka_msgs</depend>
|
||||
<depend>franka_semantic_components</depend>
|
||||
|
||||
|
||||
<test_depend>ament_cmake_clang_format</test_depend>
|
||||
<test_depend>ament_cmake_copyright</test_depend>
|
||||
<test_depend>ament_cmake_cppcheck</test_depend>
|
||||
<test_depend>ament_cmake_flake8</test_depend>
|
||||
<test_depend>ament_cmake_lint_cmake</test_depend>
|
||||
<test_depend>ament_cmake_pep257</test_depend>
|
||||
<test_depend>ament_cmake_xmllint</test_depend>
|
||||
<test_depend>ament_cmake_gmock</test_depend>
|
||||
<test_depend>ament_cmake_clang_tidy</test_depend>
|
||||
<test_depend>controller_manager</test_depend>
|
||||
<test_depend>ros2_control_test_assets</test_depend>
|
||||
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <franka_example_controllers/gravity_compensation_example_controller.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
controller_interface::InterfaceConfiguration
|
||||
GravityCompensationExampleController::command_interface_configuration() const {
|
||||
controller_interface::InterfaceConfiguration config;
|
||||
config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
|
||||
|
||||
for (int i = 1; i <= num_joints; ++i) {
|
||||
config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/effort");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
controller_interface::InterfaceConfiguration
|
||||
GravityCompensationExampleController::state_interface_configuration() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
controller_interface::return_type GravityCompensationExampleController::update(
|
||||
const rclcpp::Time& /*time*/,
|
||||
const rclcpp::Duration& /*period*/) {
|
||||
for (auto& command_interface : command_interfaces_) {
|
||||
command_interface.set_value(0);
|
||||
}
|
||||
return controller_interface::return_type::OK;
|
||||
}
|
||||
|
||||
CallbackReturn GravityCompensationExampleController::on_configure(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
arm_id_ = get_node()->get_parameter("arm_id").as_string();
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn GravityCompensationExampleController::on_init() {
|
||||
try {
|
||||
auto_declare<std::string>("arm_id", "panda");
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what());
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
} // namespace franka_example_controllers
|
||||
#include "pluginlib/class_list_macros.hpp"
|
||||
// NOLINTNEXTLINE
|
||||
PLUGINLIB_EXPORT_CLASS(franka_example_controllers::GravityCompensationExampleController,
|
||||
controller_interface::ControllerInterface)
|
@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <franka_example_controllers/joint_impedance_example_controller.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
controller_interface::InterfaceConfiguration
|
||||
JointImpedanceExampleController::command_interface_configuration() const {
|
||||
controller_interface::InterfaceConfiguration config;
|
||||
config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
|
||||
|
||||
for (int i = 1; i <= num_joints; ++i) {
|
||||
config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/effort");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
controller_interface::InterfaceConfiguration
|
||||
JointImpedanceExampleController::state_interface_configuration() const {
|
||||
controller_interface::InterfaceConfiguration config;
|
||||
config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
|
||||
for (int i = 1; i <= num_joints; ++i) {
|
||||
config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/position");
|
||||
config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/velocity");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
controller_interface::return_type JointImpedanceExampleController::update(
|
||||
const rclcpp::Time& /*time*/,
|
||||
const rclcpp::Duration& /*period*/) {
|
||||
updateJointStates();
|
||||
Vector7d q_goal = initial_q_;
|
||||
auto time = this->get_node()->now() - start_time_;
|
||||
double delta_angle = M_PI / 8.0 * (1 - std::cos(M_PI / 2.5 * time.seconds()));
|
||||
q_goal(3) += delta_angle;
|
||||
q_goal(4) += delta_angle;
|
||||
|
||||
const double kAlpha = 0.99;
|
||||
dq_filtered_ = (1 - kAlpha) * dq_filtered_ + kAlpha * dq_;
|
||||
Vector7d tau_d_calculated =
|
||||
k_gains_.cwiseProduct(q_goal - q_) + d_gains_.cwiseProduct(-dq_filtered_);
|
||||
for (int i = 0; i < num_joints; ++i) {
|
||||
command_interfaces_[i].set_value(tau_d_calculated(i));
|
||||
}
|
||||
return controller_interface::return_type::OK;
|
||||
}
|
||||
|
||||
CallbackReturn JointImpedanceExampleController::on_init() {
|
||||
try {
|
||||
auto_declare<std::string>("arm_id", "panda");
|
||||
auto_declare<std::vector<double>>("k_gains", {});
|
||||
auto_declare<std::vector<double>>("d_gains", {});
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what());
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn JointImpedanceExampleController::on_configure(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
arm_id_ = get_node()->get_parameter("arm_id").as_string();
|
||||
auto k_gains = get_node()->get_parameter("k_gains").as_double_array();
|
||||
auto d_gains = get_node()->get_parameter("d_gains").as_double_array();
|
||||
if (k_gains.empty()) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "k_gains parameter not set");
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
if (k_gains.size() != static_cast<uint>(num_joints)) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "k_gains should be of size %d but is of size %ld",
|
||||
num_joints, k_gains.size());
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
if (d_gains.empty()) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "d_gains parameter not set");
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
if (d_gains.size() != static_cast<uint>(num_joints)) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "d_gains should be of size %d but is of size %ld",
|
||||
num_joints, d_gains.size());
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
for (int i = 0; i < num_joints; ++i) {
|
||||
d_gains_(i) = d_gains.at(i);
|
||||
k_gains_(i) = k_gains.at(i);
|
||||
}
|
||||
dq_filtered_.setZero();
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn JointImpedanceExampleController::on_activate(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
updateJointStates();
|
||||
initial_q_ = q_;
|
||||
start_time_ = this->get_node()->now();
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
void JointImpedanceExampleController::updateJointStates() {
|
||||
for (auto i = 0; i < num_joints; ++i) {
|
||||
const auto& position_interface = state_interfaces_.at(2 * i);
|
||||
const auto& velocity_interface = state_interfaces_.at(2 * i + 1);
|
||||
|
||||
assert(position_interface.get_interface_name() == "position");
|
||||
assert(velocity_interface.get_interface_name() == "velocity");
|
||||
|
||||
q_(i) = position_interface.get_value();
|
||||
dq_(i) = velocity_interface.get_value();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace franka_example_controllers
|
||||
#include "pluginlib/class_list_macros.hpp"
|
||||
// NOLINTNEXTLINE
|
||||
PLUGINLIB_EXPORT_CLASS(franka_example_controllers::JointImpedanceExampleController,
|
||||
controller_interface::ControllerInterface)
|
124
franka_example_controllers/src/model_example_controller.cpp
Normal file
124
franka_example_controllers/src/model_example_controller.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <franka_example_controllers/model_example_controller.hpp>
|
||||
|
||||
#include "hardware_interface/types/hardware_interface_return_values.hpp"
|
||||
#include "hardware_interface/types/hardware_interface_type_values.hpp"
|
||||
|
||||
namespace {
|
||||
template <class T, size_t N>
|
||||
std::ostream& operator<<(std::ostream& ostream, const std::array<T, N>& array) {
|
||||
ostream << "[";
|
||||
std::copy(array.cbegin(), array.cend() - 1, std::ostream_iterator<T>(ostream, ","));
|
||||
std::copy(array.cend() - 1, array.cend(), std::ostream_iterator<T>(ostream));
|
||||
ostream << "]";
|
||||
return ostream;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
controller_interface::CallbackReturn ModelExampleController::on_init() {
|
||||
try {
|
||||
if (!get_node()->get_parameter("arm_id", arm_id_)) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "Failed to get arm_id parameter");
|
||||
get_node()->shutdown();
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what());
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
controller_interface::InterfaceConfiguration
|
||||
ModelExampleController::command_interface_configuration() const {
|
||||
return controller_interface::InterfaceConfiguration{
|
||||
controller_interface::interface_configuration_type::NONE};
|
||||
}
|
||||
|
||||
controller_interface::InterfaceConfiguration ModelExampleController::state_interface_configuration()
|
||||
const {
|
||||
controller_interface::InterfaceConfiguration state_interfaces_config;
|
||||
state_interfaces_config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
|
||||
for (const auto& franka_robot_model_name : franka_robot_model_->get_state_interface_names()) {
|
||||
state_interfaces_config.names.push_back(franka_robot_model_name);
|
||||
}
|
||||
return state_interfaces_config;
|
||||
}
|
||||
|
||||
controller_interface::CallbackReturn ModelExampleController::on_configure(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
franka_robot_model_ = std::make_unique<franka_semantic_components::FrankaRobotModel>(
|
||||
franka_semantic_components::FrankaRobotModel(arm_id_ + "/" + k_robot_model_interface_name,
|
||||
arm_id_ + "/" + k_robot_state_interface_name));
|
||||
|
||||
RCLCPP_DEBUG(get_node()->get_logger(), "configured successfully");
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
controller_interface::CallbackReturn ModelExampleController::on_activate(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
franka_robot_model_->assign_loaned_state_interfaces(state_interfaces_);
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
controller_interface::CallbackReturn ModelExampleController::on_deactivate(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
franka_robot_model_->release_interfaces();
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
controller_interface::return_type ModelExampleController::update(
|
||||
const rclcpp::Time& /*time*/,
|
||||
const rclcpp::Duration& /*period*/) {
|
||||
std::array<double, 49> mass = franka_robot_model_->getMassMatrix();
|
||||
std::array<double, 7> coriolis = franka_robot_model_->getCoriolisForceVector();
|
||||
std::array<double, 7> gravity = franka_robot_model_->getGravityForceVector();
|
||||
std::array<double, 16> pose = franka_robot_model_->getPoseMatrix(franka::Frame::kJoint4);
|
||||
std::array<double, 42> joint4_body_jacobian_wrt_joint4 =
|
||||
franka_robot_model_->getBodyJacobian(franka::Frame::kJoint4);
|
||||
std::array<double, 42> endeffector_jacobian_wrt_base =
|
||||
franka_robot_model_->getZeroJacobian(franka::Frame::kEndEffector);
|
||||
|
||||
RCLCPP_INFO_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"-------------------------------------------------------------");
|
||||
RCLCPP_INFO_STREAM_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"mass :" << mass);
|
||||
RCLCPP_INFO_STREAM_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"coriolis :" << coriolis);
|
||||
RCLCPP_INFO_STREAM_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"gravity :" << gravity);
|
||||
RCLCPP_INFO_STREAM_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"joint_pose :" << pose);
|
||||
RCLCPP_INFO_STREAM_THROTTLE(
|
||||
get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"joint4_body_jacobian in joint4 frame :" << joint4_body_jacobian_wrt_joint4);
|
||||
RCLCPP_INFO_STREAM_THROTTLE(
|
||||
get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"end_effector_jacobian in base frame :" << endeffector_jacobian_wrt_base);
|
||||
RCLCPP_INFO_THROTTLE(get_node()->get_logger(), *get_node()->get_clock(), 1000,
|
||||
"-------------------------------------------------------------");
|
||||
|
||||
return controller_interface::return_type::OK;
|
||||
}
|
||||
|
||||
} // namespace franka_example_controllers
|
||||
|
||||
#include "pluginlib/class_list_macros.hpp"
|
||||
// NOLINTNEXTLINE
|
||||
PLUGINLIB_EXPORT_CLASS(franka_example_controllers::ModelExampleController,
|
||||
controller_interface::ControllerInterface)
|
@ -0,0 +1,5 @@
|
||||
model_example_controller:
|
||||
arm_id: {
|
||||
type: string,
|
||||
default_value: "panda",
|
||||
}
|
127
franka_example_controllers/src/motion_generator.cpp
Normal file
127
franka_example_controllers/src/motion_generator.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <franka_example_controllers/motion_generator.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
MotionGenerator::MotionGenerator(double speed_factor,
|
||||
const Vector7d& q_start,
|
||||
const Vector7d& q_goal)
|
||||
: q_start_(q_start) {
|
||||
assert(speed_factor > 0);
|
||||
assert(speed_factor <= 1);
|
||||
delta_q_ = q_goal - q_start;
|
||||
dq_max_ *= speed_factor;
|
||||
ddq_max_start_ *= speed_factor;
|
||||
ddq_max_goal_ *= speed_factor;
|
||||
calculateSynchronizedValues();
|
||||
}
|
||||
|
||||
bool MotionGenerator::calculateDesiredValues(double time, Vector7d* delta_q_d) const {
|
||||
Vector7i sign_delta_q;
|
||||
sign_delta_q << delta_q_.cwiseSign().cast<int>();
|
||||
Vector7d t_d = t_2_sync_ - t_1_sync_;
|
||||
Vector7d delta_t_2_sync = t_f_sync_ - t_2_sync_;
|
||||
std::array<bool, kJoints> joint_motion_finished{};
|
||||
|
||||
for (auto i = 0; i < kJoints; i++) {
|
||||
if (std::abs(delta_q_[i]) < kDeltaQMotionFinished) {
|
||||
(*delta_q_d)[i] = 0;
|
||||
joint_motion_finished.at(i) = true;
|
||||
} else {
|
||||
if (time < t_1_sync_[i]) {
|
||||
(*delta_q_d)[i] = -1.0 / std::pow(t_1_sync_[i], 3.0) * dq_max_sync_[i] * sign_delta_q[i] *
|
||||
(0.5 * time - t_1_sync_[i]) * std::pow(time, 3.0);
|
||||
} else if (time >= t_1_sync_[i] && time < t_2_sync_[i]) {
|
||||
(*delta_q_d)[i] = q_1_[i] + (time - t_1_sync_[i]) * dq_max_sync_[i] * sign_delta_q[i];
|
||||
} else if (time >= t_2_sync_[i] && time < t_f_sync_[i]) {
|
||||
(*delta_q_d)[i] =
|
||||
delta_q_[i] +
|
||||
0.5 *
|
||||
(1.0 / std::pow(delta_t_2_sync[i], 3.0) *
|
||||
(time - t_1_sync_[i] - 2.0 * delta_t_2_sync[i] - t_d[i]) *
|
||||
std::pow((time - t_1_sync_[i] - t_d[i]), 3.0) +
|
||||
(2.0 * time - 2.0 * t_1_sync_[i] - delta_t_2_sync[i] - 2.0 * t_d[i])) *
|
||||
dq_max_sync_[i] * sign_delta_q[i];
|
||||
} else {
|
||||
(*delta_q_d)[i] = delta_q_[i];
|
||||
joint_motion_finished.at(i) = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::all_of(joint_motion_finished.cbegin(), joint_motion_finished.cend(),
|
||||
[](bool each_joint_finished) { return each_joint_finished; });
|
||||
}
|
||||
|
||||
void MotionGenerator::calculateSynchronizedValues() {
|
||||
Vector7d dq_max_reach(dq_max_);
|
||||
Vector7d t_f = Vector7d::Zero();
|
||||
Vector7d delta_t_2 = Vector7d::Zero();
|
||||
Vector7d t_1 = Vector7d::Zero();
|
||||
Vector7d delta_t_2_sync = Vector7d::Zero();
|
||||
Vector7i sign_delta_q;
|
||||
sign_delta_q << delta_q_.cwiseSign().cast<int>();
|
||||
|
||||
for (auto i = 0; i < kJoints; i++) {
|
||||
if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) {
|
||||
if (std::abs(delta_q_[i]) < (3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_start_[i]) +
|
||||
3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_goal_[i]))) {
|
||||
dq_max_reach[i] = std::sqrt(4.0 / 3.0 * delta_q_[i] * sign_delta_q[i] *
|
||||
(ddq_max_start_[i] * ddq_max_goal_[i]) /
|
||||
(ddq_max_start_[i] + ddq_max_goal_[i]));
|
||||
}
|
||||
t_1[i] = 1.5 * dq_max_reach[i] / ddq_max_start_[i];
|
||||
delta_t_2[i] = 1.5 * dq_max_reach[i] / ddq_max_goal_[i];
|
||||
t_f[i] = t_1[i] / 2.0 + delta_t_2[i] / 2.0 + std::abs(delta_q_[i]) / dq_max_reach[i];
|
||||
}
|
||||
}
|
||||
double max_t_f = t_f.maxCoeff();
|
||||
for (auto i = 0; i < kJoints; i++) {
|
||||
if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) {
|
||||
double param_a = 1.5 / 2.0 * (ddq_max_goal_[i] + ddq_max_start_[i]);
|
||||
double param_b = -1.0 * max_t_f * ddq_max_goal_[i] * ddq_max_start_[i];
|
||||
double param_c = std::abs(delta_q_[i]) * ddq_max_goal_[i] * ddq_max_start_[i];
|
||||
double delta = param_b * param_b - 4.0 * param_a * param_c;
|
||||
if (delta < 0.0) {
|
||||
delta = 0.0;
|
||||
}
|
||||
dq_max_sync_[i] = (-1.0 * param_b - std::sqrt(delta)) / (2.0 * param_a);
|
||||
t_1_sync_[i] = 1.5 * dq_max_sync_[i] / ddq_max_start_[i];
|
||||
delta_t_2_sync[i] = 1.5 * dq_max_sync_[i] / ddq_max_goal_[i];
|
||||
t_f_sync_[i] =
|
||||
(t_1_sync_)[i] / 2.0 + delta_t_2_sync[i] / 2.0 + std::abs(delta_q_[i] / dq_max_sync_[i]);
|
||||
t_2_sync_[i] = (t_f_sync_)[i] - delta_t_2_sync[i];
|
||||
q_1_[i] = (dq_max_sync_)[i] * sign_delta_q[i] * (0.5 * (t_1_sync_)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<MotionGenerator::Vector7d, bool> MotionGenerator::getDesiredJointPositions(
|
||||
const rclcpp::Duration& trajectory_time) {
|
||||
time_ = trajectory_time.seconds();
|
||||
|
||||
Vector7d delta_q_d;
|
||||
bool motion_finished = calculateDesiredValues(time_, &delta_q_d);
|
||||
|
||||
std::array<double, kJoints> joint_positions{};
|
||||
Eigen::VectorXd::Map(joint_positions.data(), kJoints) = (q_start_ + delta_q_d);
|
||||
return std::make_pair(q_start_ + delta_q_d, motion_finished);
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <franka_example_controllers/move_to_start_example_controller.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
#include <controller_interface/controller_interface.hpp>
|
||||
|
||||
namespace franka_example_controllers {
|
||||
|
||||
controller_interface::InterfaceConfiguration
|
||||
MoveToStartExampleController::command_interface_configuration() const {
|
||||
controller_interface::InterfaceConfiguration config;
|
||||
config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
|
||||
|
||||
for (int i = 1; i <= num_joints; ++i) {
|
||||
config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/effort");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
controller_interface::InterfaceConfiguration
|
||||
MoveToStartExampleController::state_interface_configuration() const {
|
||||
controller_interface::InterfaceConfiguration config;
|
||||
config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
|
||||
for (int i = 1; i <= num_joints; ++i) {
|
||||
config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/position");
|
||||
config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/velocity");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
controller_interface::return_type MoveToStartExampleController::update(
|
||||
const rclcpp::Time& /*time*/,
|
||||
const rclcpp::Duration& /*period*/) {
|
||||
updateJointStates();
|
||||
auto trajectory_time = this->get_node()->now() - start_time_;
|
||||
auto motion_generator_output = motion_generator_->getDesiredJointPositions(trajectory_time);
|
||||
Vector7d q_desired = motion_generator_output.first;
|
||||
bool finished = motion_generator_output.second;
|
||||
if (not finished) {
|
||||
const double kAlpha = 0.99;
|
||||
dq_filtered_ = (1 - kAlpha) * dq_filtered_ + kAlpha * dq_;
|
||||
Vector7d tau_d_calculated =
|
||||
k_gains_.cwiseProduct(q_desired - q_) + d_gains_.cwiseProduct(-dq_filtered_);
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
command_interfaces_[i].set_value(tau_d_calculated(i));
|
||||
}
|
||||
} else {
|
||||
for (auto& command_interface : command_interfaces_) {
|
||||
command_interface.set_value(0);
|
||||
}
|
||||
this->get_node()->set_parameter({"process_finished", true});
|
||||
}
|
||||
return controller_interface::return_type::OK;
|
||||
}
|
||||
|
||||
CallbackReturn MoveToStartExampleController::on_init() {
|
||||
q_goal_ << 0, -M_PI_4, 0, -3 * M_PI_4, 0, M_PI_2, M_PI_4;
|
||||
try {
|
||||
auto_declare<bool>("process_finished", false);
|
||||
auto_declare<std::string>("arm_id", "panda");
|
||||
auto_declare<std::vector<double>>("k_gains", {});
|
||||
auto_declare<std::vector<double>>("d_gains", {});
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what());
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn MoveToStartExampleController::on_configure(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
arm_id_ = get_node()->get_parameter("arm_id").as_string();
|
||||
auto k_gains = get_node()->get_parameter("k_gains").as_double_array();
|
||||
auto d_gains = get_node()->get_parameter("d_gains").as_double_array();
|
||||
if (k_gains.empty()) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "k_gains parameter not set");
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
if (k_gains.size() != static_cast<uint>(num_joints)) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "k_gains should be of size %d but is of size %ld",
|
||||
num_joints, k_gains.size());
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
if (d_gains.empty()) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "d_gains parameter not set");
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
if (d_gains.size() != static_cast<uint>(num_joints)) {
|
||||
RCLCPP_FATAL(get_node()->get_logger(), "d_gains should be of size %d but is of size %ld",
|
||||
num_joints, d_gains.size());
|
||||
return CallbackReturn::FAILURE;
|
||||
}
|
||||
for (int i = 0; i < num_joints; ++i) {
|
||||
d_gains_(i) = d_gains.at(i);
|
||||
k_gains_(i) = k_gains.at(i);
|
||||
}
|
||||
dq_filtered_.setZero();
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn MoveToStartExampleController::on_activate(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
updateJointStates();
|
||||
motion_generator_ = std::make_unique<MotionGenerator>(0.2, q_, q_goal_);
|
||||
start_time_ = this->get_node()->now();
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
void MoveToStartExampleController::updateJointStates() {
|
||||
for (auto i = 0; i < num_joints; ++i) {
|
||||
const auto& position_interface = state_interfaces_.at(2 * i);
|
||||
const auto& velocity_interface = state_interfaces_.at(2 * i + 1);
|
||||
|
||||
assert(position_interface.get_interface_name() == "position");
|
||||
assert(velocity_interface.get_interface_name() == "velocity");
|
||||
|
||||
q_(i) = position_interface.get_value();
|
||||
dq_(i) = velocity_interface.get_value();
|
||||
}
|
||||
}
|
||||
} // namespace franka_example_controllers
|
||||
#include "pluginlib/class_list_macros.hpp"
|
||||
// NOLINTNEXTLINE
|
||||
PLUGINLIB_EXPORT_CLASS(franka_example_controllers::MoveToStartExampleController,
|
||||
controller_interface::ControllerInterface)
|
@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "franka_example_controllers/gravity_compensation_example_controller.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "hardware_interface/handle.hpp"
|
||||
#include "hardware_interface/types/hardware_interface_type_values.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
|
||||
|
||||
using hardware_interface::CommandInterface;
|
||||
using hardware_interface::HW_IF_EFFORT;
|
||||
using hardware_interface::LoanedCommandInterface;
|
||||
|
||||
class TestGravityCompensationExample : public ::testing::Test {
|
||||
public:
|
||||
static void SetUpTestSuite();
|
||||
static void TearDownTestSuite();
|
||||
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
void SetUpController();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<franka_example_controllers::GravityCompensationExampleController> controller_;
|
||||
|
||||
// dummy joint state values used for tests
|
||||
const std::vector<std::string> joint_names_ = {"joint1", "joint2", "joint3", "joint4",
|
||||
"joint5", "joint6", "joint7"};
|
||||
std::vector<double> joint_commands_ = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
CommandInterface joint_1_pos_cmd_{joint_names_[0], HW_IF_EFFORT, &joint_commands_[0]};
|
||||
CommandInterface joint_2_pos_cmd_{joint_names_[1], HW_IF_EFFORT, &joint_commands_[1]};
|
||||
CommandInterface joint_3_pos_cmd_{joint_names_[2], HW_IF_EFFORT, &joint_commands_[2]};
|
||||
CommandInterface joint_4_pos_cmd_{joint_names_[3], HW_IF_EFFORT, &joint_commands_[3]};
|
||||
CommandInterface joint_5_pos_cmd_{joint_names_[4], HW_IF_EFFORT, &joint_commands_[4]};
|
||||
CommandInterface joint_6_pos_cmd_{joint_names_[5], HW_IF_EFFORT, &joint_commands_[5]};
|
||||
CommandInterface joint_7_pos_cmd_{joint_names_[6], HW_IF_EFFORT, &joint_commands_[6]};
|
||||
};
|
||||
|
||||
void TestGravityCompensationExample::SetUpTestSuite() {
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TestGravityCompensationExample::TearDownTestSuite() {
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void TestGravityCompensationExample::SetUp() {
|
||||
controller_ =
|
||||
std::make_unique<franka_example_controllers::GravityCompensationExampleController>();
|
||||
}
|
||||
|
||||
void TestGravityCompensationExample::TearDown() {
|
||||
controller_.reset(nullptr);
|
||||
}
|
||||
|
||||
void TestGravityCompensationExample::SetUpController() {
|
||||
const auto result = controller_->init("test_gravitiy_compensation_example");
|
||||
ASSERT_EQ(result, controller_interface::return_type::OK);
|
||||
std::vector<LoanedCommandInterface> command_ifs;
|
||||
command_ifs.emplace_back(joint_1_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_2_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_3_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_4_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_5_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_6_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_7_pos_cmd_);
|
||||
|
||||
controller_->assign_interfaces(std::move(command_ifs), {});
|
||||
}
|
||||
|
||||
TEST_F(TestGravityCompensationExample, JointsParameterNotSet) {
|
||||
GTEST_SKIP() << "Skipping joint parameters not set test";
|
||||
SetUpController();
|
||||
|
||||
// TODO(baris) configure must fail, 'joints' parameter not set
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TestGravityCompensationExample, JointsParameterIsEmpty) {
|
||||
GTEST_SKIP() << "Skipping joints parameter is empty test";
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"joints", std::vector<std::string>()});
|
||||
|
||||
// Should return ERROR!!
|
||||
// TODO(baris) params_.joints can't be empty add a check
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TestGravityCompensationExample, given_correct_number_of_joints_configure_returns_success) {
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"joints", joint_names_});
|
||||
|
||||
// configure successful
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
ASSERT_EQ(controller_->on_activate(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
}
|
||||
|
||||
TEST_F(TestGravityCompensationExample, given_joints_and_interface_when_update_expect_zero_values) {
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"joints", joint_names_});
|
||||
|
||||
// configure successful
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
ASSERT_EQ(controller_->on_activate(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
// update successful though no command has been send yet
|
||||
ASSERT_EQ(controller_->update(rclcpp::Time(0), rclcpp::Duration::from_seconds(0.01)),
|
||||
controller_interface::return_type::OK);
|
||||
|
||||
// check joint commands are updated to zero torque value
|
||||
ASSERT_EQ(joint_1_pos_cmd_.get_value(), 0.0);
|
||||
ASSERT_EQ(joint_2_pos_cmd_.get_value(), 0.0);
|
||||
ASSERT_EQ(joint_3_pos_cmd_.get_value(), 0.0);
|
||||
ASSERT_EQ(joint_4_pos_cmd_.get_value(), 0.0);
|
||||
ASSERT_EQ(joint_5_pos_cmd_.get_value(), 0.0);
|
||||
ASSERT_EQ(joint_6_pos_cmd_.get_value(), 0.0);
|
||||
ASSERT_EQ(joint_7_pos_cmd_.get_value(), 0.0);
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
|
||||
#include "controller_manager/controller_manager.hpp"
|
||||
#include "hardware_interface/resource_manager.hpp"
|
||||
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||
#include "ros2_control_test_assets/descriptions.hpp"
|
||||
|
||||
TEST(TestLoadGravityCompensationExampleController, load_controller) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
std::shared_ptr<rclcpp::Executor> executor =
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
|
||||
controller_manager::ControllerManager cm(std::make_unique<hardware_interface::ResourceManager>(
|
||||
ros2_control_test_assets::minimal_robot_urdf),
|
||||
executor, "test_controller_manager");
|
||||
|
||||
auto response =
|
||||
cm.load_controller("test_gravity_compensation_example_controller",
|
||||
"franka_example_controllers/GravityCompensationExampleController");
|
||||
|
||||
ASSERT_NE(response, nullptr);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
|
||||
#include "controller_manager/controller_manager.hpp"
|
||||
#include "hardware_interface/resource_manager.hpp"
|
||||
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||
#include "ros2_control_test_assets/descriptions.hpp"
|
||||
|
||||
TEST(TestLoadMoveToStartExampleController, load_controller) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
std::shared_ptr<rclcpp::Executor> executor =
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
|
||||
controller_manager::ControllerManager cm(std::make_unique<hardware_interface::ResourceManager>(
|
||||
ros2_control_test_assets::minimal_robot_urdf),
|
||||
executor, "test_controller_manager");
|
||||
|
||||
auto response = cm.load_controller("test_move_to_start_example_controller",
|
||||
"franka_example_controllers/MoveToStartExampleController");
|
||||
|
||||
ASSERT_NE(response, nullptr);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "test_move_to_start_example_controller.hpp"
|
||||
|
||||
#include "hardware_interface/handle.hpp"
|
||||
#include "hardware_interface/types/hardware_interface_type_values.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
|
||||
|
||||
const double k_EPS = 1e-5;
|
||||
|
||||
void MoveToStartExampleControllerTest::SetUpTestSuite() {
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void MoveToStartExampleControllerTest::TearDownTestSuite() {
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void MoveToStartExampleControllerTest::SetUp() {
|
||||
controller_ = std::make_unique<franka_example_controllers::MoveToStartExampleController>();
|
||||
}
|
||||
|
||||
void MoveToStartExampleControllerTest::TearDown() {
|
||||
controller_.reset(nullptr);
|
||||
}
|
||||
|
||||
void MoveToStartExampleControllerTest::SetUpController() {
|
||||
const auto result = controller_->init("test_move_to_start_example");
|
||||
ASSERT_EQ(result, controller_interface::return_type::OK);
|
||||
std::vector<LoanedCommandInterface> command_ifs;
|
||||
std::vector<LoanedStateInterface> state_ifs;
|
||||
|
||||
command_ifs.emplace_back(joint_1_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_2_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_3_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_4_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_5_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_6_pos_cmd_);
|
||||
command_ifs.emplace_back(joint_7_pos_cmd_);
|
||||
|
||||
state_ifs.emplace_back(joint_1_pos_state_);
|
||||
state_ifs.emplace_back(joint_1_vel_state_);
|
||||
state_ifs.emplace_back(joint_2_pos_state_);
|
||||
state_ifs.emplace_back(joint_2_vel_state_);
|
||||
state_ifs.emplace_back(joint_3_pos_state_);
|
||||
state_ifs.emplace_back(joint_3_vel_state_);
|
||||
state_ifs.emplace_back(joint_4_pos_state_);
|
||||
state_ifs.emplace_back(joint_4_vel_state_);
|
||||
state_ifs.emplace_back(joint_5_pos_state_);
|
||||
state_ifs.emplace_back(joint_5_vel_state_);
|
||||
state_ifs.emplace_back(joint_6_pos_state_);
|
||||
state_ifs.emplace_back(joint_6_vel_state_);
|
||||
state_ifs.emplace_back(joint_7_pos_state_);
|
||||
state_ifs.emplace_back(joint_7_vel_state_);
|
||||
|
||||
controller_->assign_interfaces(std::move(command_ifs), std::move(state_ifs));
|
||||
}
|
||||
|
||||
TEST_F(MoveToStartExampleControllerTest, controller_gains_not_set_failure) {
|
||||
SetUpController();
|
||||
|
||||
// Failure due to not set K, D gains
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::FAILURE);
|
||||
}
|
||||
|
||||
TEST_F(MoveToStartExampleControllerTest, contoller_gain_empty) {
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"k_gains", std::vector<double>()});
|
||||
|
||||
// Failure due empty k gain parameter
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::FAILURE);
|
||||
}
|
||||
|
||||
TEST_F(MoveToStartExampleControllerTest, contoller_damping_gain_empty) {
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"k_gains", K_gains_});
|
||||
controller_->get_node()->set_parameter({"d_gains", std::vector<double>()});
|
||||
|
||||
// Failure due to empty d gain parameter
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::FAILURE);
|
||||
}
|
||||
|
||||
TEST_F(MoveToStartExampleControllerTest, correct_controller_gains_success) {
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"k_gains", K_gains_});
|
||||
controller_->get_node()->set_parameter({"d_gains", D_gains_});
|
||||
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
}
|
||||
|
||||
TEST_F(MoveToStartExampleControllerTest, correct_setup_on_activate_expect_success) {
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"k_gains", K_gains_});
|
||||
controller_->get_node()->set_parameter({"d_gains", D_gains_});
|
||||
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
ASSERT_EQ(controller_->on_activate(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
}
|
||||
|
||||
TEST_F(MoveToStartExampleControllerTest, correct_setup_on_update_expect_ok) {
|
||||
SetUpController();
|
||||
controller_->get_node()->set_parameter({"k_gains", K_gains_});
|
||||
controller_->get_node()->set_parameter({"d_gains", D_gains_});
|
||||
|
||||
ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
ASSERT_EQ(controller_->on_activate(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
|
||||
|
||||
auto time = rclcpp::Time(0);
|
||||
auto duration = rclcpp::Duration(0, 0);
|
||||
|
||||
ASSERT_EQ(controller_->update(time, duration), controller_interface::return_type::OK);
|
||||
|
||||
EXPECT_NEAR(joint_1_pos_cmd_.get_value(), 0.0, k_EPS);
|
||||
EXPECT_NEAR(joint_2_pos_cmd_.get_value(), 0.0, k_EPS);
|
||||
EXPECT_NEAR(joint_3_pos_cmd_.get_value(), 0.0, k_EPS);
|
||||
EXPECT_NEAR(joint_4_pos_cmd_.get_value(), 0.0, k_EPS);
|
||||
EXPECT_NEAR(joint_5_pos_cmd_.get_value(), 0.0, k_EPS);
|
||||
EXPECT_NEAR(joint_6_pos_cmd_.get_value(), 0.0, k_EPS);
|
||||
EXPECT_NEAR(joint_7_pos_cmd_.get_value(), 0.0, k_EPS);
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
// Copyright 2023 Franka Emika GmbH.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "franka_example_controllers/move_to_start_example_controller.hpp"
|
||||
#include "hardware_interface/handle.hpp"
|
||||
#include "hardware_interface/types/hardware_interface_type_values.hpp"
|
||||
|
||||
using hardware_interface::CommandInterface;
|
||||
using hardware_interface::HW_IF_EFFORT;
|
||||
using hardware_interface::HW_IF_POSITION;
|
||||
using hardware_interface::HW_IF_VELOCITY;
|
||||
using hardware_interface::LoanedCommandInterface;
|
||||
using hardware_interface::LoanedStateInterface;
|
||||
using hardware_interface::StateInterface;
|
||||
|
||||
class MoveToStartExampleControllerTest : public ::testing::Test {
|
||||
public:
|
||||
static void SetUpTestSuite();
|
||||
static void TearDownTestSuite();
|
||||
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
void SetUpController();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<franka_example_controllers::MoveToStartExampleController> controller_;
|
||||
|
||||
// dummy joint state values used for tests
|
||||
const std::vector<std::string> joint_names_ = {"joint1", "joint2", "joint3", "joint4",
|
||||
"joint5", "joint6", "joint7"};
|
||||
std::vector<double> joint_commands_ = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
// The joint goal pose for the move to start controller
|
||||
std::vector<double> joint_q_state_ = {0, -0.785398, 0, -2.35619, 0, 1.5708, 0.785398};
|
||||
std::vector<double> joint_dq_state_ = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
|
||||
std::vector<double> K_gains_ = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
std::vector<double> D_gains_ = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
CommandInterface joint_1_pos_cmd_{joint_names_[0], HW_IF_EFFORT, &joint_commands_[0]};
|
||||
CommandInterface joint_2_pos_cmd_{joint_names_[1], HW_IF_EFFORT, &joint_commands_[1]};
|
||||
CommandInterface joint_3_pos_cmd_{joint_names_[2], HW_IF_EFFORT, &joint_commands_[2]};
|
||||
CommandInterface joint_4_pos_cmd_{joint_names_[3], HW_IF_EFFORT, &joint_commands_[3]};
|
||||
CommandInterface joint_5_pos_cmd_{joint_names_[4], HW_IF_EFFORT, &joint_commands_[4]};
|
||||
CommandInterface joint_6_pos_cmd_{joint_names_[5], HW_IF_EFFORT, &joint_commands_[5]};
|
||||
CommandInterface joint_7_pos_cmd_{joint_names_[6], HW_IF_EFFORT, &joint_commands_[6]};
|
||||
|
||||
// Position state interfaces
|
||||
StateInterface joint_1_pos_state_{joint_names_[0], HW_IF_POSITION, &joint_q_state_[0]};
|
||||
StateInterface joint_2_pos_state_{joint_names_[1], HW_IF_POSITION, &joint_q_state_[1]};
|
||||
StateInterface joint_3_pos_state_{joint_names_[2], HW_IF_POSITION, &joint_q_state_[2]};
|
||||
StateInterface joint_4_pos_state_{joint_names_[3], HW_IF_POSITION, &joint_q_state_[3]};
|
||||
StateInterface joint_5_pos_state_{joint_names_[4], HW_IF_POSITION, &joint_q_state_[4]};
|
||||
StateInterface joint_6_pos_state_{joint_names_[5], HW_IF_POSITION, &joint_q_state_[5]};
|
||||
StateInterface joint_7_pos_state_{joint_names_[6], HW_IF_POSITION, &joint_q_state_[6]};
|
||||
|
||||
// Velocity state interfaces
|
||||
StateInterface joint_1_vel_state_{joint_names_[0], HW_IF_VELOCITY, &joint_dq_state_[0]};
|
||||
StateInterface joint_2_vel_state_{joint_names_[1], HW_IF_VELOCITY, &joint_dq_state_[1]};
|
||||
StateInterface joint_3_vel_state_{joint_names_[2], HW_IF_VELOCITY, &joint_dq_state_[2]};
|
||||
StateInterface joint_4_vel_state_{joint_names_[3], HW_IF_VELOCITY, &joint_dq_state_[3]};
|
||||
StateInterface joint_5_vel_state_{joint_names_[4], HW_IF_VELOCITY, &joint_dq_state_[4]};
|
||||
StateInterface joint_6_vel_state_{joint_names_[5], HW_IF_VELOCITY, &joint_dq_state_[5]};
|
||||
StateInterface joint_7_vel_state_{joint_names_[6], HW_IF_VELOCITY, &joint_dq_state_[6]};
|
||||
};
|
76
franka_gripper/CMakeLists.txt
Normal file
76
franka_gripper/CMakeLists.txt
Normal file
@ -0,0 +1,76 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(franka_gripper)
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(ament_cmake_python REQUIRED)
|
||||
find_package(franka_msgs REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(rclcpp_action REQUIRED)
|
||||
find_package(rclcpp_components REQUIRED)
|
||||
find_package(std_srvs REQUIRED)
|
||||
find_package(sensor_msgs REQUIRED)
|
||||
find_package(control_msgs REQUIRED)
|
||||
|
||||
find_package(Franka REQUIRED)
|
||||
|
||||
add_library(gripper_server SHARED
|
||||
src/gripper_action_server.cpp)
|
||||
target_link_libraries(gripper_server Franka::Franka)
|
||||
target_include_directories(gripper_server PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
ament_target_dependencies(gripper_server
|
||||
franka_msgs
|
||||
rclcpp
|
||||
rclcpp_action
|
||||
rclcpp_components
|
||||
Franka
|
||||
sensor_msgs
|
||||
std_srvs
|
||||
control_msgs)
|
||||
rclcpp_components_register_node(gripper_server PLUGIN "franka_gripper::GripperActionServer" EXECUTABLE franka_gripper_node)
|
||||
install(TARGETS
|
||||
gripper_server
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin)
|
||||
|
||||
install(DIRECTORY
|
||||
launch config
|
||||
DESTINATION share/${PROJECT_NAME}/)
|
||||
|
||||
ament_python_install_package(${PROJECT_NAME})
|
||||
|
||||
install(PROGRAMS
|
||||
scripts/fake_gripper_state_publisher.py
|
||||
DESTINATION lib/${PROJECT_NAME})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_cmake_clang_format REQUIRED)
|
||||
find_package(ament_cmake_copyright REQUIRED)
|
||||
find_package(ament_cmake_cppcheck REQUIRED)
|
||||
find_package(ament_cmake_flake8 REQUIRED)
|
||||
find_package(ament_cmake_lint_cmake REQUIRED)
|
||||
find_package(ament_cmake_pep257 REQUIRED)
|
||||
find_package(ament_cmake_xmllint REQUIRED)
|
||||
|
||||
set(CPP_DIRECTORIES src include)
|
||||
ament_clang_format(CONFIG_FILE ../.clang-format ${CPP_DIRECTORIES})
|
||||
ament_copyright(launch ${CPP_DIRECTORIES} package.xml)
|
||||
ament_cppcheck(${CPP_DIRECTORIES})
|
||||
ament_lint_cmake(CMakeLists.txt)
|
||||
ament_flake8()
|
||||
ament_pep257()
|
||||
ament_xmllint()
|
||||
endif()
|
||||
|
||||
ament_package()
|
8
franka_gripper/config/franka_gripper_node.yaml
Normal file
8
franka_gripper/config/franka_gripper_node.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
panda_gripper:
|
||||
ros__parameters:
|
||||
state_publish_rate: 50 # [Hz]
|
||||
feedback_publish_rate: 30 # [Hz]
|
||||
default_speed: 0.1 # [m/s]
|
||||
default_grasp_epsilon:
|
||||
inner: 0.005 # [m]
|
||||
outer: 0.005 # [m]
|
13
franka_gripper/franka_gripper/__init__.py
Normal file
13
franka_gripper/franka_gripper/__init__.py
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
221
franka_gripper/include/franka_gripper/gripper_action_server.hpp
Normal file
221
franka_gripper/include/franka_gripper/gripper_action_server.hpp
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <franka/exception.h>
|
||||
#include <franka/gripper.h>
|
||||
#include <franka/gripper_state.h>
|
||||
#include <control_msgs/action/gripper_command.hpp>
|
||||
#include <franka_msgs/action/grasp.hpp>
|
||||
#include <franka_msgs/action/homing.hpp>
|
||||
#include <franka_msgs/action/move.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <rclcpp_action/rclcpp_action.hpp>
|
||||
#include <sensor_msgs/msg/joint_state.hpp>
|
||||
#include <std_srvs/srv/trigger.hpp>
|
||||
|
||||
namespace franka_gripper {
|
||||
|
||||
/// checks whether an asynchronous command has finished
|
||||
/// @tparam T the expected return type of the future
|
||||
/// @param t the future which should be checked
|
||||
/// @param future_wait_timeout how long to wait for the result before returning false
|
||||
/// @return whether the asynchronous function has already finished
|
||||
template <typename T>
|
||||
bool resultIsReady(std::future<T>& t, std::chrono::nanoseconds future_wait_timeout) {
|
||||
return t.wait_for(future_wait_timeout) == std::future_status::ready;
|
||||
}
|
||||
|
||||
/// ROS node that offers multiple actions to use the gripper.
|
||||
class GripperActionServer : public rclcpp::Node {
|
||||
public:
|
||||
using Homing = franka_msgs::action::Homing;
|
||||
using GoalHandleHoming = rclcpp_action::ServerGoalHandle<Homing>;
|
||||
|
||||
using Move = franka_msgs::action::Move;
|
||||
using GoalHandleMove = rclcpp_action::ServerGoalHandle<Move>;
|
||||
|
||||
using Grasp = franka_msgs::action::Grasp;
|
||||
using GoalHandleGrasp = rclcpp_action::ServerGoalHandle<Grasp>;
|
||||
|
||||
using GripperCommand = control_msgs::action::GripperCommand;
|
||||
using GoalHandleGripperCommand = rclcpp_action::ServerGoalHandle<GripperCommand>;
|
||||
|
||||
using Trigger = std_srvs::srv::Trigger;
|
||||
|
||||
/// creates an instance of a GripperActionServer
|
||||
/// @param options options for node initialization
|
||||
explicit GripperActionServer(const rclcpp::NodeOptions& options = rclcpp::NodeOptions());
|
||||
|
||||
private:
|
||||
/// describes the different tasks. Each task corresponds to one action server
|
||||
enum class Task { kHoming, kMove, kGrasp, kGripperCommand };
|
||||
|
||||
/// returns a string version of the Task enum
|
||||
static std::string getTaskName(Task task) {
|
||||
switch (task) {
|
||||
case Task::kHoming:
|
||||
return {"Homing"};
|
||||
case Task::kMove:
|
||||
return {"Moving"};
|
||||
case Task::kGrasp:
|
||||
return {"Grasping"};
|
||||
case Task::kGripperCommand:
|
||||
return {"GripperCommand"};
|
||||
default:
|
||||
throw std::invalid_argument("getTaskName is not implemented for this case");
|
||||
}
|
||||
};
|
||||
|
||||
const double k_default_grasp_epsilon = 0.005; // default inner and outer grasp epsilon in meter
|
||||
const double k_default_speed = 0.1; // default gripper speed in m/s
|
||||
const int k_default_state_publish_rate = 30; // default gripper state publish rate
|
||||
const int k_default_feedback_publish_rate = 10; // default action feedback publish rate
|
||||
|
||||
std::unique_ptr<franka::Gripper> gripper_;
|
||||
rclcpp_action::Server<Homing>::SharedPtr homing_server_;
|
||||
rclcpp_action::Server<Move>::SharedPtr move_server_;
|
||||
rclcpp_action::Server<Grasp>::SharedPtr grasp_server_;
|
||||
rclcpp_action::Server<GripperCommand>::SharedPtr gripper_command_server_;
|
||||
rclcpp::Service<Trigger>::SharedPtr stop_service_;
|
||||
std::mutex gripper_state_mutex_;
|
||||
franka::GripperState current_gripper_state_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr joint_states_publisher_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
|
||||
double default_speed_; // default gripper speed parameter value in m/s
|
||||
double default_epsilon_inner_; // default gripper inner epsilon parameter value in m
|
||||
double default_epsilon_outer_; // default gripper outer epsilon parameter value in m
|
||||
std::vector<std::string> joint_names_;
|
||||
std::chrono::nanoseconds future_wait_timeout_{0};
|
||||
|
||||
void publishGripperState();
|
||||
|
||||
/// stops the gripper and writes the result into the response
|
||||
/// @param[out] response will be updated with the success status and error message
|
||||
void stopServiceCallback(const std::shared_ptr<Trigger::Response>& response);
|
||||
|
||||
/// accepts any cancel request
|
||||
rclcpp_action::CancelResponse handleCancel(Task task);
|
||||
|
||||
/// accepts any goal request
|
||||
rclcpp_action::GoalResponse handleGoal(Task task);
|
||||
|
||||
/// performs homing
|
||||
void executeHoming(const std::shared_ptr<GoalHandleHoming>& goal_handle);
|
||||
|
||||
/// performs move
|
||||
void executeMove(const std::shared_ptr<GoalHandleMove>& goal_handle);
|
||||
|
||||
/// performs grasp
|
||||
void executeGrasp(const std::shared_ptr<GoalHandleGrasp>& goal_handle);
|
||||
|
||||
/// Performs the moveit grasp command
|
||||
/// @param goal_handle
|
||||
/// @param command_handler eiter a grasp or move command defined by the onExecuteGripperCommand
|
||||
/// method
|
||||
void executeGripperCommand(const std::shared_ptr<GoalHandleGripperCommand>& goal_handle,
|
||||
const std::function<bool()>& command_handler);
|
||||
|
||||
/// Defines a function for either grasping or moving the gripper, depending on the current gripper
|
||||
/// state and the commanded goal. Then it calls executeGripperCommand to execute that function
|
||||
void onExecuteGripperCommand(const std::shared_ptr<GoalHandleGripperCommand>& goal_handle);
|
||||
|
||||
/// Executes a gripper command
|
||||
/// @tparam T A gripper action message type (Move, Grasp, Homing)
|
||||
/// @param[in] goal_handle The goal handle from the action server
|
||||
/// @param[in] task The type of the Task
|
||||
/// @param[in] command_handler a function that performs the the task. Returns true on success.
|
||||
/// This function is allowed to throw a franka::Exception
|
||||
template <typename T>
|
||||
void executeCommand(const std::shared_ptr<rclcpp_action::ServerGoalHandle<T>>& goal_handle,
|
||||
Task task,
|
||||
const std::function<bool()>& command_handler) {
|
||||
const auto kTaskName = getTaskName(task);
|
||||
RCLCPP_INFO(this->get_logger(), "Gripper %s...", kTaskName.c_str());
|
||||
|
||||
auto command_execution_thread = withResultGenerator<T>(command_handler);
|
||||
|
||||
std::future<std::shared_ptr<typename T::Result>> result_future =
|
||||
std::async(std::launch::async, command_execution_thread);
|
||||
|
||||
while (!resultIsReady(result_future, future_wait_timeout_) && rclcpp::ok()) {
|
||||
if (goal_handle->is_canceling()) {
|
||||
gripper_->stop();
|
||||
auto result = result_future.get();
|
||||
RCLCPP_INFO(get_logger(), "Gripper %s canceled", kTaskName.c_str());
|
||||
goal_handle->canceled(result);
|
||||
return;
|
||||
}
|
||||
publishGripperWidthFeedback(goal_handle);
|
||||
}
|
||||
if (rclcpp::ok()) {
|
||||
const auto kResult = result_future.get();
|
||||
if (kResult->success) {
|
||||
RCLCPP_INFO(get_logger(), "Gripper %s succeeded", kTaskName.c_str());
|
||||
goal_handle->succeed(kResult);
|
||||
} else {
|
||||
RCLCPP_INFO(get_logger(), "Gripper %s failed", kTaskName.c_str());
|
||||
goal_handle->abort(kResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a function that catches exceptions for the gripper command function and returns a
|
||||
/// result
|
||||
/// @tparam T A gripper action message type (Move, Grasp, Homing)
|
||||
/// @param[in] command_handler a function that performs the the task. Returns true on success.
|
||||
/// This function is allowed to throw a franka::Exception
|
||||
/// @return[in] enhanced command_handler that now returns a result an does not throw a
|
||||
/// franka::exception anymore
|
||||
template <typename T>
|
||||
auto withResultGenerator(const std::function<bool()>& command_handler)
|
||||
-> std::function<std::shared_ptr<typename T::Result>()> {
|
||||
return [command_handler]() {
|
||||
auto result = std::make_shared<typename T::Result>();
|
||||
try {
|
||||
result->success = command_handler();
|
||||
} catch (const franka::Exception& e) {
|
||||
result->success = false;
|
||||
result->error = e.what();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/// Publishes the gripper width as feedback for actions
|
||||
/// @tparam T A gripper action message type (Move, Grasp, Homing)
|
||||
/// @param[in] goal_handle The goal handle from the action server
|
||||
template <typename T>
|
||||
void publishGripperWidthFeedback(
|
||||
const std::shared_ptr<rclcpp_action::ServerGoalHandle<T>>& goal_handle) {
|
||||
auto gripper_feedback = std::make_shared<typename T::Feedback>();
|
||||
std::lock_guard<std::mutex> guard(gripper_state_mutex_);
|
||||
gripper_feedback->current_width = current_gripper_state_.width;
|
||||
goal_handle->publish_feedback(gripper_feedback);
|
||||
}
|
||||
|
||||
/// Publishes the gripper width as feedback for the GripperCommand action
|
||||
void publishGripperCommandFeedback(
|
||||
const std::shared_ptr<rclcpp_action::ServerGoalHandle<GripperCommand>>& goal_handle);
|
||||
};
|
||||
} // namespace franka_gripper
|
92
franka_gripper/launch/gripper.launch.py
Normal file
92
franka_gripper/launch/gripper.launch.py
Normal file
@ -0,0 +1,92 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import IfCondition, UnlessCondition
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
arm_parameter_name = 'arm_id'
|
||||
joint_names_parameter_name = 'joint_names'
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
arm_id = LaunchConfiguration(arm_parameter_name)
|
||||
joint_names = LaunchConfiguration(joint_names_parameter_name)
|
||||
|
||||
gripper_config = os.path.join(
|
||||
get_package_share_directory('franka_gripper'), 'config', 'franka_gripper_node.yaml'
|
||||
)
|
||||
|
||||
default_joint_name_postfix = '_finger_joint'
|
||||
arm_default_argument = [
|
||||
'[',
|
||||
arm_id,
|
||||
default_joint_name_postfix,
|
||||
'1',
|
||||
',',
|
||||
arm_id,
|
||||
default_joint_name_postfix,
|
||||
'2',
|
||||
']',
|
||||
]
|
||||
|
||||
return LaunchDescription(
|
||||
[
|
||||
DeclareLaunchArgument(
|
||||
robot_ip_parameter_name, description='Hostname or IP address of the robot.'
|
||||
),
|
||||
DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description=(
|
||||
'Publish fake gripper joint states without connecting to a real gripper'
|
||||
),
|
||||
),
|
||||
DeclareLaunchArgument(
|
||||
arm_parameter_name,
|
||||
default_value='panda',
|
||||
description=(
|
||||
'Name of the arm in the URDF file. This is used to generate the joint '
|
||||
'names of the gripper.'
|
||||
),
|
||||
),
|
||||
DeclareLaunchArgument(
|
||||
joint_names_parameter_name,
|
||||
default_value=arm_default_argument,
|
||||
description='Names of the gripper joints in the URDF',
|
||||
),
|
||||
Node(
|
||||
package='franka_gripper',
|
||||
executable='franka_gripper_node',
|
||||
name=[arm_id, '_gripper'],
|
||||
parameters=[{'robot_ip': robot_ip, 'joint_names': joint_names}, gripper_config],
|
||||
condition=UnlessCondition(use_fake_hardware),
|
||||
),
|
||||
Node(
|
||||
package='franka_gripper',
|
||||
executable='fake_gripper_state_publisher.py',
|
||||
name=[arm_id, '_gripper'],
|
||||
parameters=[{'robot_ip': robot_ip, 'joint_names': joint_names}, gripper_config],
|
||||
condition=IfCondition(use_fake_hardware),
|
||||
),
|
||||
]
|
||||
)
|
34
franka_gripper/package.xml
Normal file
34
franka_gripper/package.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>franka_gripper</name>
|
||||
<version>0.1.0</version>
|
||||
<description>This package implements the franka gripper of type Franka Hand for the use in ROS2</description>
|
||||
<maintainer email="support@franka.de">Franka Emika GmbH</maintainer>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
<buildtool_depend>ament_cmake_python</buildtool_depend>
|
||||
|
||||
<depend>franka_msgs</depend>
|
||||
<depend>rclpy</depend>
|
||||
<depend>rclcpp</depend>
|
||||
<depend>rclcpp_action</depend>
|
||||
<depend>rclcpp_components</depend>
|
||||
<depend>sensor_msgs</depend>
|
||||
<depend>std_srvs</depend>
|
||||
<depend>control_msgs</depend>
|
||||
<depend>libfranka</depend>
|
||||
|
||||
<test_depend>ament_cmake_clang_format</test_depend>
|
||||
<test_depend>ament_cmake_copyright</test_depend>
|
||||
<test_depend>ament_cmake_cppcheck</test_depend>
|
||||
<test_depend>ament_cmake_flake8</test_depend>
|
||||
<test_depend>ament_cmake_lint_cmake</test_depend>
|
||||
<test_depend>ament_cmake_pep257</test_depend>
|
||||
<test_depend>ament_cmake_xmllint</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
58
franka_gripper/scripts/fake_gripper_state_publisher.py
Executable file
58
franka_gripper/scripts/fake_gripper_state_publisher.py
Executable file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import rclpy
|
||||
from rclpy import Parameter
|
||||
from rclpy.node import Node
|
||||
from sensor_msgs.msg import JointState
|
||||
|
||||
|
||||
class FakeGripperStatePublisher(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('fake_gripper_state_publisher')
|
||||
self.publisher_ = self.create_publisher(JointState, '~/joint_states', 1)
|
||||
timer_period = 0.1 # seconds
|
||||
|
||||
self.declare_parameter('joint_names', Parameter.Type.STRING_ARRAY)
|
||||
self.joint_names = (
|
||||
self.get_parameter('joint_names').get_parameter_value().string_array_value
|
||||
)
|
||||
|
||||
assert len(self.joint_names) == 2
|
||||
self.timer = self.create_timer(timer_period, self.publish_state)
|
||||
|
||||
def publish_state(self):
|
||||
joint_states = JointState()
|
||||
joint_states.header.stamp = self.get_clock().now().to_msg()
|
||||
joint_states.name = self.joint_names
|
||||
joint_states.position = [0.035, 0.035]
|
||||
joint_states.velocity = [0.0, 0.0]
|
||||
joint_states.effort = [0.0, 0.0]
|
||||
self.publisher_.publish(joint_states)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
|
||||
state_publisher = FakeGripperStatePublisher()
|
||||
|
||||
rclpy.spin(state_publisher)
|
||||
state_publisher.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
290
franka_gripper/src/gripper_action_server.cpp
Normal file
290
franka_gripper/src/gripper_action_server.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <franka/exception.h>
|
||||
#include <franka/gripper.h>
|
||||
#include <franka/gripper_state.h>
|
||||
#include <control_msgs/action/gripper_command.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <rclcpp_action/rclcpp_action.hpp>
|
||||
#include <rclcpp_components/register_node_macro.hpp>
|
||||
#include <sensor_msgs/msg/joint_state.hpp>
|
||||
#include <std_srvs/srv/trigger.hpp>
|
||||
|
||||
#include <franka_gripper/gripper_action_server.hpp>
|
||||
|
||||
namespace franka_gripper {
|
||||
GripperActionServer::GripperActionServer(const rclcpp::NodeOptions& options)
|
||||
: Node("franka_gripper_node", options) {
|
||||
this->declare_parameter("robot_ip", std::string());
|
||||
this->declare_parameter("default_grasp_epsilon.inner", k_default_grasp_epsilon);
|
||||
this->declare_parameter("default_grasp_epsilon.outer", k_default_grasp_epsilon);
|
||||
this->declare_parameter("default_speed", k_default_speed);
|
||||
this->declare_parameter("joint_names", std::vector<std::string>());
|
||||
this->declare_parameter("state_publish_rate", k_default_state_publish_rate);
|
||||
this->declare_parameter("feedback_publish_rate", k_default_feedback_publish_rate);
|
||||
std::string robot_ip;
|
||||
if (!this->get_parameter<std::string>("robot_ip", robot_ip)) {
|
||||
RCLCPP_FATAL(this->get_logger(), "Parameter 'robot_ip' not set");
|
||||
throw std::invalid_argument("Parameter 'robot_ip' not set");
|
||||
}
|
||||
|
||||
this->default_speed_ = this->get_parameter("default_speed").as_double();
|
||||
this->default_epsilon_inner_ = this->get_parameter("default_grasp_epsilon.inner").as_double();
|
||||
this->default_epsilon_outer_ = this->get_parameter("default_grasp_epsilon.outer").as_double();
|
||||
if (!this->get_parameter("joint_names", this->joint_names_)) {
|
||||
RCLCPP_WARN(this->get_logger(), "Parameter 'joint_names' not set");
|
||||
this->joint_names_ = {"", ""};
|
||||
}
|
||||
|
||||
if (this->joint_names_.size() != 2) {
|
||||
RCLCPP_FATAL(this->get_logger(),
|
||||
"Parameter 'joint_names' needs exactly two arguments, got %ld instead",
|
||||
this->joint_names_.size());
|
||||
throw std::invalid_argument("Parameter 'joint_names' has wrong number of arguments");
|
||||
}
|
||||
|
||||
const double kStatePublishRate =
|
||||
static_cast<double>(this->get_parameter("state_publish_rate").as_int());
|
||||
const double kFeedbackPublishRate =
|
||||
static_cast<double>(this->get_parameter("feedback_publish_rate").as_int());
|
||||
this->future_wait_timeout_ = rclcpp::WallRate(kFeedbackPublishRate).period();
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Trying to establish a connection with the gripper");
|
||||
try {
|
||||
this->gripper_ = std::make_unique<franka::Gripper>(robot_ip);
|
||||
} catch (const franka::Exception& exception) {
|
||||
RCLCPP_FATAL(this->get_logger(), exception.what());
|
||||
throw exception;
|
||||
}
|
||||
RCLCPP_INFO(this->get_logger(), "Connected to gripper");
|
||||
current_gripper_state_ = gripper_->readOnce();
|
||||
const auto kHomingTask = Task::kHoming;
|
||||
this->stop_service_ = // NOLINTNEXTLINE
|
||||
create_service<Trigger>("~/stop",
|
||||
[this](std::shared_ptr<Trigger::Request> /*request*/, // NOLINT
|
||||
std::shared_ptr<Trigger::Response> response) { // NOLINT
|
||||
return stopServiceCallback(std::move(response)); // NOLINT
|
||||
});
|
||||
|
||||
this->homing_server_ = rclcpp_action::create_server<Homing>(
|
||||
this, "~/homing",
|
||||
[this, kHomingTask](auto /*uuid*/, auto /*goal*/) { return handleGoal(kHomingTask); },
|
||||
[this, kHomingTask](const auto& /*goal_handle*/) { return handleCancel(kHomingTask); },
|
||||
[this](const auto& goal_handle) {
|
||||
return std::thread{[goal_handle, this]() { executeHoming(goal_handle); }}.detach();
|
||||
});
|
||||
const auto kMoveTask = Task::kMove;
|
||||
this->move_server_ = rclcpp_action::create_server<Move>(
|
||||
this, "~/move",
|
||||
[this, kMoveTask](auto /*uuid*/, auto /*goal*/) { return handleGoal(kMoveTask); },
|
||||
[this, kMoveTask](const auto& /*goal_handle*/) { return handleCancel(kMoveTask); },
|
||||
[this](const auto& goal_handle) {
|
||||
return std::thread{[goal_handle, this]() { executeMove(goal_handle); }}.detach();
|
||||
});
|
||||
|
||||
const auto kGraspTask = Task::kGrasp;
|
||||
this->grasp_server_ = rclcpp_action::create_server<Grasp>(
|
||||
this, "~/grasp",
|
||||
[this, kGraspTask](auto /*uuid*/, auto /*goal*/) { return handleGoal(kGraspTask); },
|
||||
[this, kGraspTask](const auto& /*goal_handle*/) { return handleCancel(kGraspTask); },
|
||||
[this](const auto& goal_handle) {
|
||||
return std::thread{[goal_handle, this]() { executeGrasp(goal_handle); }}.detach();
|
||||
});
|
||||
|
||||
const auto kGripperCommandTask = Task::kGripperCommand;
|
||||
this->gripper_command_server_ = rclcpp_action::create_server<GripperCommand>(
|
||||
this, "~/gripper_action",
|
||||
[this, kGripperCommandTask](auto /*uuid*/, auto /*goal*/) {
|
||||
return handleGoal(kGripperCommandTask);
|
||||
},
|
||||
[this, kGripperCommandTask](const auto& /*goal_handle*/) {
|
||||
return handleCancel(kGripperCommandTask);
|
||||
},
|
||||
[this](const auto& goal_handle) {
|
||||
return std::thread{[goal_handle, this]() { onExecuteGripperCommand(goal_handle); }}
|
||||
.detach();
|
||||
});
|
||||
|
||||
this->joint_states_publisher_ =
|
||||
this->create_publisher<sensor_msgs::msg::JointState>("~/joint_states", 1);
|
||||
this->timer_ = this->create_wall_timer(rclcpp::WallRate(kStatePublishRate).period(),
|
||||
[this]() { return publishGripperState(); });
|
||||
}
|
||||
|
||||
rclcpp_action::CancelResponse GripperActionServer::handleCancel(Task task) {
|
||||
RCLCPP_INFO(this->get_logger(), "Received request to handleCancel %s", getTaskName(task).c_str());
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
}
|
||||
|
||||
rclcpp_action::GoalResponse GripperActionServer::handleGoal(Task task) {
|
||||
RCLCPP_INFO(this->get_logger(), "Received %s request", getTaskName(task).c_str());
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
}
|
||||
|
||||
void GripperActionServer::executeHoming(const std::shared_ptr<GoalHandleHoming>& goal_handle) {
|
||||
const auto kCommand = [this]() { return gripper_->homing(); };
|
||||
executeCommand(goal_handle, Task::kHoming, kCommand);
|
||||
}
|
||||
|
||||
void GripperActionServer::executeMove(const std::shared_ptr<GoalHandleMove>& goal_handle) {
|
||||
auto command = [goal_handle, this]() {
|
||||
const auto kGoal = goal_handle->get_goal();
|
||||
return gripper_->move(kGoal->width, kGoal->speed);
|
||||
};
|
||||
executeCommand(goal_handle, Task::kMove, command);
|
||||
}
|
||||
|
||||
void GripperActionServer::executeGrasp(const std::shared_ptr<GoalHandleGrasp>& goal_handle) {
|
||||
auto command = [goal_handle, this]() {
|
||||
const auto kGoal = goal_handle->get_goal();
|
||||
return gripper_->grasp(kGoal->width, kGoal->speed, kGoal->force, kGoal->epsilon.inner,
|
||||
kGoal->epsilon.outer);
|
||||
};
|
||||
executeCommand(goal_handle, Task::kGrasp, command);
|
||||
}
|
||||
|
||||
void GripperActionServer::onExecuteGripperCommand(
|
||||
const std::shared_ptr<GoalHandleGripperCommand>& goal_handle) {
|
||||
const auto kGoal = goal_handle->get_goal();
|
||||
const double kTargetWidth = 2 * kGoal->command.position;
|
||||
|
||||
std::unique_lock<std::mutex> guard(gripper_state_mutex_);
|
||||
constexpr double kSamePositionThreshold = 1e-4;
|
||||
auto result = std::make_shared<control_msgs::action::GripperCommand::Result>();
|
||||
const double kCurrentWidth = current_gripper_state_.width;
|
||||
if (kTargetWidth > current_gripper_state_.max_width || kTargetWidth < 0) {
|
||||
RCLCPP_ERROR(this->get_logger(),
|
||||
"GripperServer: Commanding out of range width! max_width = %f command = %f",
|
||||
current_gripper_state_.max_width, kTargetWidth);
|
||||
goal_handle->abort(result);
|
||||
return;
|
||||
}
|
||||
if (std::abs(kTargetWidth - kCurrentWidth) < kSamePositionThreshold) {
|
||||
result->effort = 0;
|
||||
result->position = kCurrentWidth;
|
||||
result->reached_goal = true;
|
||||
result->stalled = false;
|
||||
goal_handle->succeed(result);
|
||||
return;
|
||||
}
|
||||
guard.unlock();
|
||||
auto command = [kTargetWidth, kCurrentWidth, kGoal, this]() {
|
||||
if (kTargetWidth >= kCurrentWidth) {
|
||||
return gripper_->move(kTargetWidth, default_speed_);
|
||||
}
|
||||
return gripper_->grasp(kTargetWidth, default_speed_, kGoal->command.max_effort,
|
||||
default_epsilon_inner_, default_epsilon_outer_);
|
||||
};
|
||||
|
||||
executeGripperCommand(goal_handle, command);
|
||||
}
|
||||
|
||||
void GripperActionServer::executeGripperCommand(
|
||||
const std::shared_ptr<GoalHandleGripperCommand>& goal_handle,
|
||||
const std::function<bool()>& command_handler) {
|
||||
const auto kTaskName = getTaskName(Task::kGripperCommand);
|
||||
RCLCPP_INFO(this->get_logger(), "Gripper %s...", kTaskName.c_str());
|
||||
|
||||
auto command_execution_thread = [command_handler, this]() {
|
||||
auto result = std::make_shared<GripperCommand::Result>();
|
||||
try {
|
||||
result->reached_goal = command_handler();
|
||||
} catch (const franka::Exception& e) {
|
||||
result->reached_goal = false;
|
||||
RCLCPP_ERROR(this->get_logger(), e.what());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
std::future<std::shared_ptr<typename GripperCommand ::Result>> result_future =
|
||||
std::async(std::launch::async, command_execution_thread);
|
||||
|
||||
while (!resultIsReady(result_future, future_wait_timeout_) && rclcpp::ok()) {
|
||||
if (goal_handle->is_canceling()) {
|
||||
gripper_->stop();
|
||||
auto result = result_future.get();
|
||||
RCLCPP_INFO(get_logger(), "Gripper %s canceled", kTaskName.c_str());
|
||||
goal_handle->canceled(result);
|
||||
return;
|
||||
}
|
||||
publishGripperCommandFeedback(goal_handle);
|
||||
}
|
||||
if (rclcpp::ok()) {
|
||||
const auto kResult = result_future.get();
|
||||
std::lock_guard<std::mutex> guard(gripper_state_mutex_);
|
||||
kResult->position = current_gripper_state_.width;
|
||||
kResult->effort = 0.;
|
||||
if (kResult->reached_goal) {
|
||||
RCLCPP_INFO(get_logger(), "Gripper %s succeeded", kTaskName.c_str());
|
||||
goal_handle->succeed(kResult);
|
||||
} else {
|
||||
RCLCPP_INFO(get_logger(), "Gripper %s failed", kTaskName.c_str());
|
||||
goal_handle->abort(kResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GripperActionServer::stopServiceCallback(const std::shared_ptr<Trigger::Response>& response) {
|
||||
RCLCPP_INFO(this->get_logger(), "Stopping gripper_...");
|
||||
auto action_result = withResultGenerator<Homing>([this]() { return gripper_->stop(); })();
|
||||
response->success = action_result->success;
|
||||
response->message = action_result->error;
|
||||
if (response->success) {
|
||||
RCLCPP_INFO(this->get_logger(), "Gripper stopped");
|
||||
} else {
|
||||
RCLCPP_INFO(this->get_logger(), "Gripper could not be stopped");
|
||||
}
|
||||
if (!response->message.empty()) {
|
||||
RCLCPP_ERROR(this->get_logger(), response->message.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void GripperActionServer::publishGripperState() {
|
||||
std::lock_guard<std::mutex> lock(gripper_state_mutex_);
|
||||
try {
|
||||
current_gripper_state_ = gripper_->readOnce();
|
||||
} catch (const franka::Exception& e) {
|
||||
RCLCPP_ERROR(this->get_logger(), e.what());
|
||||
}
|
||||
sensor_msgs::msg::JointState joint_states;
|
||||
joint_states.header.stamp = this->now();
|
||||
joint_states.name.push_back(this->joint_names_[0]);
|
||||
joint_states.name.push_back(this->joint_names_[1]);
|
||||
joint_states.position.push_back(current_gripper_state_.width / 2);
|
||||
joint_states.position.push_back(current_gripper_state_.width / 2);
|
||||
joint_states.velocity.push_back(0.0);
|
||||
joint_states.velocity.push_back(0.0);
|
||||
joint_states.effort.push_back(0.0);
|
||||
joint_states.effort.push_back(0.0);
|
||||
joint_states_publisher_->publish(joint_states);
|
||||
}
|
||||
|
||||
void GripperActionServer::publishGripperCommandFeedback(
|
||||
const std::shared_ptr<rclcpp_action::ServerGoalHandle<GripperCommand>>& goal_handle) {
|
||||
auto gripper_feedback = std::make_shared<GripperCommand::Feedback>();
|
||||
std::lock_guard<std::mutex> guard(gripper_state_mutex_);
|
||||
gripper_feedback->position = current_gripper_state_.width;
|
||||
gripper_feedback->effort = 0.;
|
||||
goal_handle->publish_feedback(gripper_feedback);
|
||||
}
|
||||
} // namespace franka_gripper
|
||||
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(franka_gripper::GripperActionServer) // NOLINT
|
92
franka_hardware/CMakeLists.txt
Normal file
92
franka_hardware/CMakeLists.txt
Normal file
@ -0,0 +1,92 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(franka_hardware)
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
option(CHECK_TIDY "Adds clang-tidy tests" OFF)
|
||||
|
||||
# find dependencies
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(franka_msgs REQUIRED)
|
||||
find_package(hardware_interface REQUIRED)
|
||||
find_package(pluginlib REQUIRED)
|
||||
find_package(Franka REQUIRED)
|
||||
|
||||
|
||||
add_library(franka_hardware
|
||||
SHARED
|
||||
src/franka_hardware_interface.cpp
|
||||
src/robot.cpp)
|
||||
target_include_directories(
|
||||
franka_hardware
|
||||
PRIVATE
|
||||
include
|
||||
)
|
||||
ament_target_dependencies(
|
||||
franka_hardware
|
||||
hardware_interface
|
||||
Franka
|
||||
pluginlib
|
||||
rclcpp
|
||||
franka_msgs
|
||||
)
|
||||
pluginlib_export_plugin_description_file(hardware_interface franka_hardware.xml)
|
||||
|
||||
|
||||
install(
|
||||
TARGETS franka_hardware
|
||||
DESTINATION lib
|
||||
)
|
||||
install(
|
||||
DIRECTORY include/
|
||||
DESTINATION include
|
||||
)
|
||||
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_cmake_clang_format REQUIRED)
|
||||
find_package(ament_cmake_copyright REQUIRED)
|
||||
find_package(ament_cmake_cppcheck REQUIRED)
|
||||
find_package(ament_cmake_flake8 REQUIRED)
|
||||
find_package(ament_cmake_lint_cmake REQUIRED)
|
||||
find_package(ament_cmake_pep257 REQUIRED)
|
||||
find_package(ament_cmake_xmllint REQUIRED)
|
||||
|
||||
set(CPP_DIRECTORIES src include)
|
||||
ament_clang_format(CONFIG_FILE ../.clang-format ${CPP_DIRECTORIES})
|
||||
ament_copyright(${CPP_DIRECTORIES} package.xml)
|
||||
ament_cppcheck(${CPP_DIRECTORIES})
|
||||
ament_lint_cmake(CMakeLists.txt)
|
||||
ament_flake8()
|
||||
ament_pep257()
|
||||
ament_xmllint()
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
if(CHECK_TIDY)
|
||||
find_package(ament_cmake_clang_tidy REQUIRED)
|
||||
set(ament_cmake_clang_tidy_CONFIG_FILE ../.clang-tidy)
|
||||
ament_clang_tidy(${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
ament_export_include_directories(
|
||||
include
|
||||
)
|
||||
ament_export_libraries(
|
||||
franka_hardware
|
||||
)
|
||||
ament_export_dependencies(
|
||||
hardware_interface
|
||||
pluginlib
|
||||
rclcpp
|
||||
)
|
||||
ament_package()
|
10
franka_hardware/franka_hardware.xml
Normal file
10
franka_hardware/franka_hardware.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<library path="franka_hardware">
|
||||
<class name="franka_hardware/FrankaHardwareInterface"
|
||||
type="franka_hardware::FrankaHardwareInterface"
|
||||
base_class_type="hardware_interface::SystemInterface">
|
||||
|
||||
<description>
|
||||
Hardware interface for Franka Emika research robots
|
||||
</description>
|
||||
</class>
|
||||
</library>
|
@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hardware_interface/visibility_control.h>
|
||||
#include <franka_hardware/robot.hpp>
|
||||
|
||||
#include <hardware_interface/hardware_info.hpp>
|
||||
#include <hardware_interface/system_interface.hpp>
|
||||
#include <hardware_interface/types/hardware_interface_return_values.hpp>
|
||||
#include <rclcpp/logger.hpp>
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <rclcpp_lifecycle/state.hpp>
|
||||
|
||||
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
|
||||
|
||||
namespace franka_hardware {
|
||||
|
||||
class FrankaHardwareInterface : public hardware_interface::SystemInterface {
|
||||
public:
|
||||
explicit FrankaHardwareInterface(std::unique_ptr<Robot> robot);
|
||||
FrankaHardwareInterface() = default;
|
||||
|
||||
hardware_interface::return_type prepare_command_mode_switch(
|
||||
const std::vector<std::string>& start_interfaces,
|
||||
const std::vector<std::string>& stop_interfaces) override;
|
||||
hardware_interface::return_type perform_command_mode_switch(
|
||||
const std::vector<std::string>& start_interfaces,
|
||||
const std::vector<std::string>& stop_interfaces) override;
|
||||
std::vector<hardware_interface::StateInterface> export_state_interfaces() override;
|
||||
std::vector<hardware_interface::CommandInterface> export_command_interfaces() override;
|
||||
CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override;
|
||||
CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override;
|
||||
hardware_interface::return_type read(const rclcpp::Time& time,
|
||||
const rclcpp::Duration& period) override;
|
||||
hardware_interface::return_type write(const rclcpp::Time& time,
|
||||
const rclcpp::Duration& period) override;
|
||||
CallbackReturn on_init(const hardware_interface::HardwareInfo& info) override;
|
||||
static const size_t kNumberOfJoints = 7;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Robot> robot_;
|
||||
std::array<double, kNumberOfJoints> hw_commands_{0, 0, 0, 0, 0, 0, 0};
|
||||
std::array<double, kNumberOfJoints> hw_positions_{0, 0, 0, 0, 0, 0, 0};
|
||||
std::array<double, kNumberOfJoints> hw_velocities_{0, 0, 0, 0, 0, 0, 0};
|
||||
std::array<double, kNumberOfJoints> hw_efforts_{0, 0, 0, 0, 0, 0, 0};
|
||||
franka::RobotState hw_franka_robot_state_;
|
||||
franka::RobotState* hw_franka_robot_state_addr_ = &hw_franka_robot_state_;
|
||||
Model* hw_franka_model_ptr_ = nullptr;
|
||||
|
||||
bool effort_interface_claimed_ = false;
|
||||
bool effort_interface_running_ = false;
|
||||
static rclcpp::Logger getLogger();
|
||||
|
||||
const std::string k_robot_name{"panda"};
|
||||
const std::string k_robot_state_interface_name{"robot_state"};
|
||||
const std::string k_robot_model_interface_name{"robot_model"};
|
||||
};
|
||||
} // namespace franka_hardware
|
290
franka_hardware/include/franka_hardware/model.hpp
Normal file
290
franka_hardware/include/franka_hardware/model.hpp
Normal file
@ -0,0 +1,290 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <franka/model.h>
|
||||
|
||||
namespace franka_hardware {
|
||||
|
||||
/**
|
||||
* This class is a thin wrapper around a @ref franka::Model and delegates all calls to
|
||||
* that
|
||||
*/
|
||||
class Model { // NOLINT(cppcoreguidelines-pro-type-member-init,
|
||||
// cppcoreguidelines-special-member-functions)
|
||||
public:
|
||||
/**
|
||||
* Create a new Model instance wrapped around a franka::Model
|
||||
*/
|
||||
explicit Model(franka::Model* model) : model_(model) {}
|
||||
virtual ~Model() = default;
|
||||
|
||||
/**
|
||||
* Gets the 4x4 pose matrix for the given frame in base frame.
|
||||
*
|
||||
* The pose is represented as a 4x4 matrix in column-major format.
|
||||
*
|
||||
* @param[in] frame The desired frame.
|
||||
* @param[in] q Joint position.
|
||||
* @param[in] F_T_EE End effector in flange frame.
|
||||
* @param[in] EE_T_K Stiffness frame K in the end effector frame.
|
||||
*
|
||||
* @return Vectorized 4x4 pose matrix, column-major.
|
||||
*/
|
||||
[[nodiscard]] std::array<double, 16> pose(
|
||||
franka::Frame frame,
|
||||
const std::array<double, 7>& q, // NOLINT(readability-identifier-length)
|
||||
const std::array<double, 16>& F_T_EE, // NOLINT(readability-identifier-naming)
|
||||
const std::array<double, 16>& EE_T_K) // NOLINT(readability-identifier-naming)
|
||||
const {
|
||||
return model_->pose(frame, q, F_T_EE, EE_T_K);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 6x7 Jacobian for the given frame, relative to that frame.
|
||||
*
|
||||
* The Jacobian is represented as a 6x7 matrix in column-major format.
|
||||
*
|
||||
* @param[in] frame The desired frame.
|
||||
* @param[in] q Joint position.
|
||||
* @param[in] F_T_EE End effector in flange frame.
|
||||
* @param[in] EE_T_K Stiffness frame K in the end effector frame.
|
||||
*
|
||||
* @return Vectorized 6x7 Jacobian, column-major.
|
||||
*/
|
||||
[[nodiscard]] std::array<double, 42> bodyJacobian(
|
||||
franka::Frame frame,
|
||||
const std::array<double, 7>& q, // NOLINT(readability-identifier-length)
|
||||
const std::array<double, 16>& F_T_EE, // NOLINT(readability-identifier-naming)
|
||||
const std::array<double, 16>& EE_T_K) // NOLINT(readability-identifier-naming)
|
||||
const {
|
||||
return model_->bodyJacobian(frame, q, F_T_EE, EE_T_K);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 6x7 Jacobian for the given joint relative to the base frame.
|
||||
*
|
||||
* The Jacobian is represented as a 6x7 matrix in column-major format.
|
||||
*
|
||||
* @param[in] frame The desired frame.
|
||||
* @param[in] robot_state State from which the pose should be calculated.
|
||||
*
|
||||
* @return Vectorized 6x7 Jacobian, column-major.
|
||||
*/
|
||||
[[nodiscard]] std::array<double, 42> zeroJacobian(
|
||||
franka::Frame frame,
|
||||
const std::array<double, 7>& q, // NOLINT(readability-identifier-length)
|
||||
const std::array<double, 16>& F_T_EE, // NOLINT(readability-identifier-naming)
|
||||
const std::array<double, 16>& EE_T_K) // NOLINT(readability-identifier-naming)
|
||||
const {
|
||||
return model_->zeroJacobian(frame, q, F_T_EE, EE_T_K);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the 7x7 mass matrix. Unit: \f$[kg \times m^2]\f$.
|
||||
*
|
||||
* @param[in] q Joint position.
|
||||
* @param[in] I_total Inertia of the attached total load including end effector, relative to
|
||||
* center of mass, given as vectorized 3x3 column-major matrix. Unit: \f$[kg \times m^2]\f$.
|
||||
* @param[in] m_total Weight of the attached total load including end effector.
|
||||
* Unit: \f$[kg]\f$.
|
||||
* @param[in] F_x_Ctotal Translation from flange to center of mass of the attached total load.
|
||||
* Unit: \f$[m]\f$.
|
||||
*
|
||||
* @return Vectorized 7x7 mass matrix, column-major.
|
||||
*/
|
||||
[[nodiscard]] std::array<double, 49> mass(
|
||||
const std::array<double, 7>& q, // NOLINT(readability-identifier-length)
|
||||
const std::array<double, 9>& I_total, // NOLINT(readability-identifier-naming)
|
||||
double m_total,
|
||||
const std::array<double, 3>& F_x_Ctotal) // NOLINT(readability-identifier-naming)
|
||||
const noexcept {
|
||||
return model_->mass(q, I_total, m_total, F_x_Ctotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the Coriolis force vector (state-space equation): \f$ c= C \times
|
||||
* dq\f$, in \f$[Nm]\f$.
|
||||
*
|
||||
* @param[in] q Joint position.
|
||||
* @param[in] dq Joint velocity.
|
||||
* @param[in] I_total Inertia of the attached total load including end effector, relative to
|
||||
* center of mass, given as vectorized 3x3 column-major matrix. Unit: \f$[kg \times m^2]\f$.
|
||||
* @param[in] m_total Weight of the attached total load including end effector.
|
||||
* Unit: \f$[kg]\f$.
|
||||
* @param[in] F_x_Ctotal Translation from flange to center of mass of the attached total load.
|
||||
* Unit: \f$[m]\f$.
|
||||
*
|
||||
* @return Coriolis force vector.
|
||||
*/
|
||||
[[nodiscard]] std::array<double, 7> coriolis(
|
||||
const std::array<double, 7>& q, // NOLINT(readability-identifier-length)
|
||||
const std::array<double, 7>& dq, // NOLINT(readability-identifier-length)
|
||||
const std::array<double, 9>& I_total, // NOLINT(readability-identifier-naming)
|
||||
double m_total,
|
||||
const std::array<double, 3>& F_x_Ctotal) // NOLINT(readability-identifier-naming)
|
||||
const noexcept {
|
||||
return model_->coriolis(q, dq, I_total, m_total, F_x_Ctotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the gravity vector. Unit: \f$[Nm]\f$.
|
||||
*
|
||||
* @param[in] q Joint position.
|
||||
* @param[in] m_total Weight of the attached total load including end effector.
|
||||
* Unit: \f$[kg]\f$.
|
||||
* @param[in] F_x_Ctotal Translation from flange to center of mass of the attached total load.
|
||||
* Unit: \f$[m]\f$.
|
||||
* @param[in] gravity_earth Earth's gravity vector. Unit: \f$\frac{m}{s^2}\f$.
|
||||
*
|
||||
* @return Gravity vector.
|
||||
*/
|
||||
[[nodiscard]] std::array<double, 7> gravity(
|
||||
const std::array<double, 7>& q, // NOLINT(readability-identifier-length)
|
||||
double m_total,
|
||||
const std::array<double, 3>& F_x_Ctotal, // NOLINT(readability-identifier-naming)
|
||||
const std::array<double, 3>& gravity_earth) const noexcept {
|
||||
return model_->gravity(q, m_total, F_x_Ctotal, gravity_earth);
|
||||
}
|
||||
/**
|
||||
* Gets the 4x4 pose matrix for the given frame in base frame.
|
||||
*
|
||||
* The pose is represented as a 4x4 matrix in column-major format.
|
||||
*
|
||||
* @param[in] frame The desired frame.
|
||||
* @param[in] robot_state State from which the pose should be calculated.
|
||||
*
|
||||
* @return Vectorized 4x4 pose matrix, column-major.
|
||||
*/
|
||||
[[nodiscard]] virtual std::array<double, 16> pose(franka::Frame frame,
|
||||
const franka::RobotState& robot_state) const {
|
||||
return pose(frame, robot_state.q, robot_state.F_T_EE, robot_state.EE_T_K);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 6x7 Jacobian for the given frame, relative to that frame.
|
||||
*
|
||||
* The Jacobian is represented as a 6x7 matrix in column-major format.
|
||||
*
|
||||
* @param[in] frame The desired frame.
|
||||
* @param[in] robot_state State from which the pose should be calculated.
|
||||
*
|
||||
* @return Vectorized 6x7 Jacobian, column-major.
|
||||
*/
|
||||
[[nodiscard]] virtual std::array<double, 42> bodyJacobian(
|
||||
franka::Frame frame,
|
||||
const franka::RobotState& robot_state) const {
|
||||
return bodyJacobian(frame, robot_state.q, robot_state.F_T_EE, robot_state.EE_T_K);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 6x7 Jacobian for the given joint relative to the base frame.
|
||||
*
|
||||
* The Jacobian is represented as a 6x7 matrix in column-major format.
|
||||
*
|
||||
* @param[in] frame The desired frame.
|
||||
* @param[in] robot_state State from which the pose should be calculated.
|
||||
*
|
||||
* @return Vectorized 6x7 Jacobian, column-major.
|
||||
*/
|
||||
[[nodiscard]] virtual std::array<double, 42> zeroJacobian(
|
||||
franka::Frame frame,
|
||||
const franka::RobotState& robot_state) const {
|
||||
return zeroJacobian(frame, robot_state.q, robot_state.F_T_EE, robot_state.EE_T_K);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the 7x7 mass matrix. Unit: \f$[kg \times m^2]\f$.
|
||||
*
|
||||
* @param[in] robot_state State from which the pose should be calculated.
|
||||
*
|
||||
* @return Vectorized 7x7 mass matrix, column-major.
|
||||
*/
|
||||
[[nodiscard]] virtual std::array<double, 49> mass(const franka::RobotState& robot_state) const {
|
||||
return mass(robot_state.q, robot_state.I_total, robot_state.m_total, robot_state.F_x_Ctotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the Coriolis force vector (state-space equation): \f$ c= C \times
|
||||
* dq\f$, in \f$[Nm]\f$.
|
||||
*
|
||||
* @param[in] robot_state State from which the Coriolis force vector should be calculated.
|
||||
*
|
||||
* @return Coriolis force vector.
|
||||
*/
|
||||
[[nodiscard]] virtual std::array<double, 7> coriolis(
|
||||
const franka::RobotState& robot_state) const {
|
||||
return coriolis(robot_state.q, robot_state.dq, robot_state.I_total, robot_state.m_total,
|
||||
robot_state.F_x_Ctotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the gravity vector. Unit: \f$[Nm]\f$. Assumes default gravity vector of -9.81 m/s^2
|
||||
*
|
||||
* @param[in] q Joint position.
|
||||
* @param[in] m_total Weight of the attached total load including end effector.
|
||||
* Unit: \f$[kg]\f$.
|
||||
* @param[in] F_x_Ctotal Translation from flange to center of mass of the attached total load.
|
||||
* Unit: \f$[m]\f$.
|
||||
*
|
||||
* @return Gravity vector.
|
||||
*/
|
||||
[[nodiscard]] std::array<double, 7> gravity(
|
||||
const std::array<double, 7>& q, // NOLINT(readability-identifier-length)
|
||||
double m_total,
|
||||
const std::array<double, 3>& F_x_Ctotal // NOLINT(readability-identifier-naming)
|
||||
) const {
|
||||
return gravity(q, m_total, F_x_Ctotal, {0, 0, -9.81});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the gravity vector. Unit: \f$[Nm]\f$.
|
||||
*
|
||||
* @param[in] robot_state State from which the gravity vector should be calculated.
|
||||
* @param[in] gravity_earth Earth's gravity vector. Unit: \f$\frac{m}{s^2}\f$.
|
||||
*
|
||||
* @return Gravity vector.
|
||||
*/
|
||||
[[nodiscard]] virtual std::array<double, 7> gravity(
|
||||
const franka::RobotState& robot_state,
|
||||
const std::array<double, 3>& gravity_earth) const {
|
||||
return gravity(robot_state.q, robot_state.m_total, robot_state.F_x_Ctotal, gravity_earth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the gravity vector. Unit: \f$[Nm]\f$. Assumes default gravity vector of -9.81 m/s^2
|
||||
*
|
||||
* @param[in] robot_state State from which the gravity vector should be calculated.
|
||||
*
|
||||
* @return Gravity vector.
|
||||
*/
|
||||
[[nodiscard]] virtual std::array<double, 7> gravity(const franka::RobotState& robot_state) const {
|
||||
#ifdef ENABLE_BASE_ACCELERATION
|
||||
return gravity(robot_state.q, robot_state.m_total, robot_state.F_x_Ctotal, robot_state.O_ddP_O);
|
||||
#else
|
||||
return gravity(robot_state.q, robot_state.m_total, robot_state.F_x_Ctotal, {0, 0, -9.81});
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
Model() = default;
|
||||
|
||||
private:
|
||||
franka::Model* model_;
|
||||
};
|
||||
|
||||
} // namespace franka_hardware
|
104
franka_hardware/include/franka_hardware/robot.hpp
Normal file
104
franka_hardware/include/franka_hardware/robot.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <franka/model.h>
|
||||
#include <franka/robot.h>
|
||||
#include <franka_hardware/model.hpp>
|
||||
#include <rclcpp/logger.hpp>
|
||||
|
||||
namespace franka_hardware {
|
||||
|
||||
class Robot {
|
||||
public:
|
||||
/**
|
||||
* Connects to the robot. This method can block for up to one minute if the robot is not
|
||||
* responding. An exception will be thrown if the connection cannot be established.
|
||||
*
|
||||
* @param[in] robot_ip IP address or hostname of the robot.
|
||||
* @param[im] logger ROS Logger to print eventual warnings.
|
||||
*/
|
||||
explicit Robot(const std::string& robot_ip, const rclcpp::Logger& logger);
|
||||
Robot(const Robot&) = delete;
|
||||
Robot& operator=(const Robot& other) = delete;
|
||||
Robot& operator=(Robot&& other) = delete;
|
||||
Robot(Robot&& other) = delete;
|
||||
|
||||
/// Stops the currently running loop and closes the connection with the robot.
|
||||
virtual ~Robot();
|
||||
|
||||
/**
|
||||
* Starts a torque control loop. Before using this method make sure that no other
|
||||
* control or reading loop is currently active.
|
||||
*/
|
||||
virtual void initializeTorqueControl();
|
||||
|
||||
/**
|
||||
* Starts a reading loop of the robot state. Before using this method make sure that no other
|
||||
* control or reading loop is currently active.
|
||||
*/
|
||||
virtual void initializeContinuousReading();
|
||||
|
||||
/// stops the control or reading loop of the robot.
|
||||
virtual void stopRobot();
|
||||
|
||||
/**
|
||||
* Get the current robot state in a thread-safe way.
|
||||
* @return current robot state.
|
||||
*/
|
||||
virtual franka::RobotState read();
|
||||
|
||||
/**
|
||||
* Return pointer to the franka robot model object .
|
||||
* @return pointer to the current robot model.
|
||||
*/
|
||||
virtual franka_hardware::Model* getModel();
|
||||
|
||||
/**
|
||||
* Sends new desired torque commands to the control loop in a thread-safe way.
|
||||
* The robot will use these torques until a different set of torques are commanded.
|
||||
* @param[in] efforts torque command for each joint.
|
||||
*/
|
||||
virtual void write(const std::array<double, 7>& efforts);
|
||||
|
||||
/// @return true if there is no control or reading loop running.
|
||||
[[nodiscard]] virtual bool isStopped() const;
|
||||
|
||||
protected:
|
||||
Robot() = default;
|
||||
|
||||
private:
|
||||
std::unique_ptr<std::thread> control_thread_;
|
||||
std::unique_ptr<franka::Robot> robot_;
|
||||
std::unique_ptr<franka::Model> model_;
|
||||
std::unique_ptr<Model> franka_hardware_model_;
|
||||
|
||||
std::mutex read_mutex_;
|
||||
std::mutex write_mutex_;
|
||||
std::atomic_bool finish_{false};
|
||||
bool stopped_ = true;
|
||||
franka::RobotState current_state_;
|
||||
|
||||
std::array<double, 7> tau_command_{};
|
||||
};
|
||||
} // namespace franka_hardware
|
30
franka_hardware/package.xml
Normal file
30
franka_hardware/package.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>franka_hardware</name>
|
||||
<version>0.1.0</version>
|
||||
<description>franka_hardware provides hardware interfaces for using Franka Emika research robots with ros2_control</description>
|
||||
<maintainer email="support@franka.de">Franka Emika GmbH</maintainer>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>rclcpp</depend>
|
||||
<depend>franka_msgs</depend>
|
||||
<depend>hardware_interface</depend>
|
||||
<depend>pluginlib</depend>
|
||||
|
||||
<test_depend>ament_cmake_clang_format</test_depend>
|
||||
<test_depend>ament_cmake_copyright</test_depend>
|
||||
<test_depend>ament_cmake_cppcheck</test_depend>
|
||||
<test_depend>ament_cmake_flake8</test_depend>
|
||||
<test_depend>ament_cmake_lint_cmake</test_depend>
|
||||
<test_depend>ament_cmake_pep257</test_depend>
|
||||
<test_depend>ament_cmake_xmllint</test_depend>
|
||||
<test_depend>ament_cmake_gmock</test_depend>
|
||||
<test_depend>ament_cmake_clang_tidy</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
231
franka_hardware/src/franka_hardware_interface.cpp
Normal file
231
franka_hardware/src/franka_hardware_interface.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <franka_hardware/franka_hardware_interface.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
|
||||
#include <franka/exception.h>
|
||||
#include <hardware_interface/handle.hpp>
|
||||
#include <hardware_interface/hardware_info.hpp>
|
||||
#include <hardware_interface/system_interface.hpp>
|
||||
#include <hardware_interface/types/hardware_interface_return_values.hpp>
|
||||
#include <hardware_interface/types/hardware_interface_type_values.hpp>
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
|
||||
namespace franka_hardware {
|
||||
|
||||
using StateInterface = hardware_interface::StateInterface;
|
||||
using CommandInterface = hardware_interface::CommandInterface;
|
||||
|
||||
FrankaHardwareInterface::FrankaHardwareInterface(std::unique_ptr<Robot> robot)
|
||||
: robot_{std::move(robot)} {}
|
||||
|
||||
std::vector<StateInterface> FrankaHardwareInterface::export_state_interfaces() {
|
||||
std::vector<StateInterface> state_interfaces;
|
||||
for (auto i = 0U; i < info_.joints.size(); i++) {
|
||||
state_interfaces.emplace_back(StateInterface(
|
||||
info_.joints[i].name, hardware_interface::HW_IF_POSITION, &hw_positions_.at(i)));
|
||||
state_interfaces.emplace_back(StateInterface(
|
||||
info_.joints[i].name, hardware_interface::HW_IF_VELOCITY, &hw_velocities_.at(i)));
|
||||
state_interfaces.emplace_back(
|
||||
StateInterface(info_.joints[i].name, hardware_interface::HW_IF_EFFORT, &hw_efforts_.at(i)));
|
||||
}
|
||||
|
||||
state_interfaces.emplace_back(StateInterface(
|
||||
k_robot_name, k_robot_state_interface_name,
|
||||
reinterpret_cast<double*>( // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
&hw_franka_robot_state_addr_)));
|
||||
state_interfaces.emplace_back(StateInterface(
|
||||
k_robot_name, k_robot_model_interface_name,
|
||||
reinterpret_cast<double*>( // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
&hw_franka_model_ptr_)));
|
||||
return state_interfaces;
|
||||
}
|
||||
|
||||
std::vector<CommandInterface> FrankaHardwareInterface::export_command_interfaces() {
|
||||
std::vector<CommandInterface> command_interfaces;
|
||||
command_interfaces.reserve(info_.joints.size());
|
||||
for (auto i = 0U; i < info_.joints.size(); i++) {
|
||||
command_interfaces.emplace_back(CommandInterface(
|
||||
info_.joints[i].name, hardware_interface::HW_IF_EFFORT, &hw_commands_.at(i)));
|
||||
}
|
||||
return command_interfaces;
|
||||
}
|
||||
|
||||
CallbackReturn FrankaHardwareInterface::on_activate(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
robot_->initializeContinuousReading();
|
||||
hw_commands_.fill(0);
|
||||
read(rclcpp::Time(0),
|
||||
rclcpp::Duration(0, 0)); // makes sure that the robot state is properly initialized.
|
||||
RCLCPP_INFO(getLogger(), "Started");
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn FrankaHardwareInterface::on_deactivate(
|
||||
const rclcpp_lifecycle::State& /*previous_state*/) {
|
||||
RCLCPP_INFO(getLogger(), "trying to Stop...");
|
||||
robot_->stopRobot();
|
||||
RCLCPP_INFO(getLogger(), "Stopped");
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
hardware_interface::return_type FrankaHardwareInterface::read(const rclcpp::Time& /*time*/,
|
||||
const rclcpp::Duration& /*period*/) {
|
||||
if (hw_franka_model_ptr_ == nullptr) {
|
||||
hw_franka_model_ptr_ = robot_->getModel();
|
||||
}
|
||||
hw_franka_robot_state_ = robot_->read();
|
||||
hw_positions_ = hw_franka_robot_state_.q;
|
||||
hw_velocities_ = hw_franka_robot_state_.dq;
|
||||
hw_efforts_ = hw_franka_robot_state_.tau_J;
|
||||
return hardware_interface::return_type::OK;
|
||||
}
|
||||
|
||||
hardware_interface::return_type FrankaHardwareInterface::write(const rclcpp::Time& /*time*/,
|
||||
const rclcpp::Duration& /*period*/) {
|
||||
if (std::any_of(hw_commands_.begin(), hw_commands_.end(),
|
||||
[](double hw_command) { return !std::isfinite(hw_command); })) {
|
||||
return hardware_interface::return_type::ERROR;
|
||||
}
|
||||
|
||||
robot_->write(hw_commands_);
|
||||
return hardware_interface::return_type::OK;
|
||||
}
|
||||
|
||||
CallbackReturn FrankaHardwareInterface::on_init(const hardware_interface::HardwareInfo& info) {
|
||||
if (hardware_interface::SystemInterface::on_init(info) != CallbackReturn::SUCCESS) {
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
if (info_.joints.size() != kNumberOfJoints) {
|
||||
RCLCPP_FATAL(getLogger(), "Got %ld joints. Expected %ld.", info_.joints.size(),
|
||||
kNumberOfJoints);
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
|
||||
for (const auto& joint : info_.joints) {
|
||||
if (joint.command_interfaces.size() != 1) {
|
||||
RCLCPP_FATAL(getLogger(), "Joint '%s' has %zu command interfaces found. 1 expected.",
|
||||
joint.name.c_str(), joint.command_interfaces.size());
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
if (joint.command_interfaces[0].name != hardware_interface::HW_IF_EFFORT) {
|
||||
RCLCPP_FATAL(getLogger(), "Joint '%s' has unexpected command interface '%s'. Expected '%s'",
|
||||
joint.name.c_str(), joint.command_interfaces[0].name.c_str(),
|
||||
hardware_interface::HW_IF_EFFORT);
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
if (joint.state_interfaces.size() != 3) {
|
||||
RCLCPP_FATAL(getLogger(), "Joint '%s' has %zu state interfaces found. 3 expected.",
|
||||
joint.name.c_str(), joint.state_interfaces.size());
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
if (joint.state_interfaces[0].name != hardware_interface::HW_IF_POSITION) {
|
||||
RCLCPP_FATAL(getLogger(), "Joint '%s' has unexpected state interface '%s'. Expected '%s'",
|
||||
joint.name.c_str(), joint.state_interfaces[0].name.c_str(),
|
||||
hardware_interface::HW_IF_POSITION);
|
||||
}
|
||||
if (joint.state_interfaces[1].name != hardware_interface::HW_IF_VELOCITY) {
|
||||
RCLCPP_FATAL(getLogger(), "Joint '%s' has unexpected state interface '%s'. Expected '%s'",
|
||||
joint.name.c_str(), joint.state_interfaces[1].name.c_str(),
|
||||
hardware_interface::HW_IF_VELOCITY);
|
||||
}
|
||||
if (joint.state_interfaces[2].name != hardware_interface::HW_IF_EFFORT) {
|
||||
RCLCPP_FATAL(getLogger(), "Joint '%s' has unexpected state interface '%s'. Expected '%s'",
|
||||
joint.name.c_str(), joint.state_interfaces[2].name.c_str(),
|
||||
hardware_interface::HW_IF_EFFORT);
|
||||
}
|
||||
}
|
||||
if (!robot_) {
|
||||
std::string robot_ip;
|
||||
try {
|
||||
robot_ip = info_.hardware_parameters.at("robot_ip");
|
||||
} catch (const std::out_of_range& ex) {
|
||||
RCLCPP_FATAL(getLogger(), "Parameter 'robot_ip' is not set");
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
try {
|
||||
RCLCPP_INFO(getLogger(), "Connecting to robot at \"%s\" ...", robot_ip.c_str());
|
||||
robot_ = std::make_unique<Robot>(robot_ip, getLogger());
|
||||
} catch (const franka::Exception& e) {
|
||||
RCLCPP_FATAL(getLogger(), "Could not connect to robot");
|
||||
RCLCPP_FATAL(getLogger(), "%s", e.what());
|
||||
return CallbackReturn::ERROR;
|
||||
}
|
||||
RCLCPP_INFO(getLogger(), "Successfully connected to robot");
|
||||
}
|
||||
return CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
rclcpp::Logger FrankaHardwareInterface::getLogger() {
|
||||
return rclcpp::get_logger("FrankaHardwareInterface");
|
||||
}
|
||||
|
||||
hardware_interface::return_type FrankaHardwareInterface::perform_command_mode_switch(
|
||||
const std::vector<std::string>& /*start_interfaces*/,
|
||||
const std::vector<std::string>& /*stop_interfaces*/) {
|
||||
if (!effort_interface_running_ && effort_interface_claimed_) {
|
||||
robot_->stopRobot();
|
||||
robot_->initializeTorqueControl();
|
||||
effort_interface_running_ = true;
|
||||
} else if (effort_interface_running_ && !effort_interface_claimed_) {
|
||||
robot_->stopRobot();
|
||||
robot_->initializeContinuousReading();
|
||||
effort_interface_running_ = false;
|
||||
}
|
||||
return hardware_interface::return_type::OK;
|
||||
}
|
||||
|
||||
hardware_interface::return_type FrankaHardwareInterface::prepare_command_mode_switch(
|
||||
const std::vector<std::string>& start_interfaces,
|
||||
const std::vector<std::string>& stop_interfaces) {
|
||||
auto is_effort_interface = [](const std::string& interface) {
|
||||
return interface.find(hardware_interface::HW_IF_EFFORT) != std::string::npos;
|
||||
};
|
||||
|
||||
int64_t num_stop_effort_interfaces =
|
||||
std::count_if(stop_interfaces.begin(), stop_interfaces.end(), is_effort_interface);
|
||||
if (num_stop_effort_interfaces == kNumberOfJoints) {
|
||||
effort_interface_claimed_ = false;
|
||||
} else if (num_stop_effort_interfaces != 0) {
|
||||
RCLCPP_FATAL(this->getLogger(), "Expected %ld effort interfaces to stop, but got %ld instead.",
|
||||
kNumberOfJoints, num_stop_effort_interfaces);
|
||||
std::string error_string = "Invalid number of effort interfaces to stop. Expected ";
|
||||
error_string += std::to_string(kNumberOfJoints);
|
||||
throw std::invalid_argument(error_string);
|
||||
}
|
||||
|
||||
int64_t num_start_effort_interfaces =
|
||||
std::count_if(start_interfaces.begin(), start_interfaces.end(), is_effort_interface);
|
||||
if (num_start_effort_interfaces == kNumberOfJoints) {
|
||||
effort_interface_claimed_ = true;
|
||||
} else if (num_start_effort_interfaces != 0) {
|
||||
RCLCPP_FATAL(this->getLogger(), "Expected %ld effort interfaces to start, but got %ld instead.",
|
||||
kNumberOfJoints, num_start_effort_interfaces);
|
||||
std::string error_string = "Invalid number of effort interfaces to start. Expected ";
|
||||
error_string += std::to_string(kNumberOfJoints);
|
||||
throw std::invalid_argument(error_string);
|
||||
}
|
||||
return hardware_interface::return_type::OK;
|
||||
}
|
||||
} // namespace franka_hardware
|
||||
|
||||
#include "pluginlib/class_list_macros.hpp"
|
||||
// NOLINTNEXTLINE
|
||||
PLUGINLIB_EXPORT_CLASS(franka_hardware::FrankaHardwareInterface,
|
||||
hardware_interface::SystemInterface)
|
104
franka_hardware/src/robot.cpp
Normal file
104
franka_hardware/src/robot.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2021 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <franka_hardware/robot.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
|
||||
#include <franka/control_tools.h>
|
||||
#include <rclcpp/logging.hpp>
|
||||
|
||||
namespace franka_hardware {
|
||||
|
||||
Robot::Robot(const std::string& robot_ip, const rclcpp::Logger& logger) {
|
||||
tau_command_.fill(0.);
|
||||
franka::RealtimeConfig rt_config = franka::RealtimeConfig::kEnforce;
|
||||
if (!franka::hasRealtimeKernel()) {
|
||||
rt_config = franka::RealtimeConfig::kIgnore;
|
||||
RCLCPP_WARN(
|
||||
logger,
|
||||
"You are not using a real-time kernel. Using a real-time kernel is strongly recommended!");
|
||||
}
|
||||
robot_ = std::make_unique<franka::Robot>(robot_ip, rt_config);
|
||||
model_ = std::make_unique<franka::Model>(robot_->loadModel());
|
||||
franka_hardware_model_ = std::make_unique<Model>(model_.get());
|
||||
}
|
||||
|
||||
void Robot::write(const std::array<double, 7>& efforts) {
|
||||
std::lock_guard<std::mutex> lock(write_mutex_);
|
||||
tau_command_ = efforts;
|
||||
}
|
||||
|
||||
franka::RobotState Robot::read() {
|
||||
std::lock_guard<std::mutex> lock(read_mutex_);
|
||||
return {current_state_};
|
||||
}
|
||||
|
||||
franka_hardware::Model* Robot::getModel() {
|
||||
return franka_hardware_model_.get();
|
||||
}
|
||||
|
||||
void Robot::stopRobot() {
|
||||
if (!stopped_) {
|
||||
finish_ = true;
|
||||
control_thread_->join();
|
||||
finish_ = false;
|
||||
stopped_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Robot::initializeTorqueControl() {
|
||||
assert(isStopped());
|
||||
stopped_ = false;
|
||||
const auto kTorqueControl = [this]() {
|
||||
robot_->control(
|
||||
[this](const franka::RobotState& state, const franka::Duration& /*period*/) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(read_mutex_);
|
||||
current_state_ = state;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(write_mutex_);
|
||||
franka::Torques out(tau_command_);
|
||||
out.motion_finished = finish_;
|
||||
return out;
|
||||
},
|
||||
true, franka::kMaxCutoffFrequency);
|
||||
};
|
||||
control_thread_ = std::make_unique<std::thread>(kTorqueControl);
|
||||
}
|
||||
|
||||
void Robot::initializeContinuousReading() {
|
||||
assert(isStopped());
|
||||
stopped_ = false;
|
||||
const auto kReading = [this]() {
|
||||
robot_->read([this](const franka::RobotState& state) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(read_mutex_);
|
||||
current_state_ = state;
|
||||
}
|
||||
return !finish_;
|
||||
});
|
||||
};
|
||||
control_thread_ = std::make_unique<std::thread>(kReading);
|
||||
}
|
||||
|
||||
Robot::~Robot() {
|
||||
stopRobot();
|
||||
}
|
||||
|
||||
bool Robot::isStopped() const {
|
||||
return stopped_;
|
||||
}
|
||||
} // namespace franka_hardware
|
6
franka_hardware/test/CMakeLists.txt
Normal file
6
franka_hardware/test/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
find_package(ament_cmake_gmock REQUIRED)
|
||||
|
||||
ament_add_gmock(${PROJECT_NAME}_test franka_hardware_interface_test.cpp)
|
||||
target_include_directories(${PROJECT_NAME}_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}_test ${PROJECT_NAME})
|
400
franka_hardware/test/franka_hardware_interface_test.cpp
Normal file
400
franka_hardware/test/franka_hardware_interface_test.cpp
Normal file
@ -0,0 +1,400 @@
|
||||
// Copyright (c) 2023 Franka Emika GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <exception>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
|
||||
#include <franka_hardware/franka_hardware_interface.hpp>
|
||||
#include <franka_hardware/model.hpp>
|
||||
#include <franka_hardware/robot.hpp>
|
||||
|
||||
#include <hardware_interface/hardware_info.hpp>
|
||||
#include <hardware_interface/types/hardware_interface_return_values.hpp>
|
||||
#include <hardware_interface/types/hardware_interface_type_values.hpp>
|
||||
|
||||
const std::string k_position_controller{"position"};
|
||||
const std::string k_velocity_controller{"velocity"};
|
||||
const std::string k_effort_controller{"effort"};
|
||||
const std::string k_joint_name{"joint"};
|
||||
const size_t k_number_of_joints{7};
|
||||
const double k_EPS{1e-5};
|
||||
|
||||
class MockModel : public franka_hardware::Model {};
|
||||
|
||||
class MockRobot : public franka_hardware::Robot {
|
||||
public:
|
||||
MOCK_METHOD(void, initializeContinuousReading, (), (override));
|
||||
MOCK_METHOD(void, stopRobot, (), (override));
|
||||
MOCK_METHOD(void, initializeTorqueControl, (), (override));
|
||||
MOCK_METHOD(franka::RobotState, read, (), (override));
|
||||
MOCK_METHOD(MockModel*, getModel, (), (override));
|
||||
};
|
||||
|
||||
auto createHardwareInfo() -> hardware_interface::HardwareInfo {
|
||||
hardware_interface::HardwareInfo info;
|
||||
std::unordered_map<std::string, std::string> hw_params;
|
||||
hw_params["robot_ip"] = "dummy_ip";
|
||||
|
||||
info.hardware_parameters = hw_params;
|
||||
hardware_interface::InterfaceInfo command_interface, effort_state_interface,
|
||||
position_state_interface, velocity_state_interface;
|
||||
|
||||
effort_state_interface.name = hardware_interface::HW_IF_EFFORT;
|
||||
position_state_interface.name = hardware_interface::HW_IF_POSITION;
|
||||
velocity_state_interface.name = hardware_interface::HW_IF_VELOCITY;
|
||||
|
||||
std::vector<hardware_interface::InterfaceInfo> state_interfaces = {
|
||||
position_state_interface, velocity_state_interface, effort_state_interface};
|
||||
|
||||
for (auto i = 0U; i < k_number_of_joints; i++) {
|
||||
hardware_interface::ComponentInfo joint;
|
||||
|
||||
joint.name = k_joint_name + std::to_string(i + 1);
|
||||
|
||||
command_interface.name = k_effort_controller;
|
||||
|
||||
joint.command_interfaces.push_back(command_interface);
|
||||
joint.state_interfaces = state_interfaces;
|
||||
|
||||
info.joints.push_back(joint);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest, when_on_init_called_expect_success) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
const hardware_interface::HardwareInfo info = createHardwareInfo();
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
auto return_type = franka_hardware_interface.on_init(info);
|
||||
|
||||
EXPECT_EQ(return_type,
|
||||
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS);
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest, given_that_the_robot_interfaces_set_when_read_called_return_ok) {
|
||||
franka::RobotState robot_state;
|
||||
MockModel mock_model;
|
||||
MockModel* model_address = &mock_model;
|
||||
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state));
|
||||
EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address));
|
||||
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
auto time = rclcpp::Time(0, 0);
|
||||
auto duration = rclcpp::Duration(0, 0);
|
||||
auto return_type = franka_hardware_interface.read(time, duration);
|
||||
EXPECT_EQ(return_type, hardware_interface::return_type::OK);
|
||||
}
|
||||
|
||||
TEST(
|
||||
FrankaHardwareInterfaceTest,
|
||||
given_that_the_robot_interfaces_are_set_when_call_export_state_return_zero_values_and_correct_interface_names) {
|
||||
franka::RobotState robot_state;
|
||||
const size_t state_interface_size =
|
||||
23; // position, effort and velocity states for every joint + robot state and model
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
MockModel mock_model;
|
||||
MockModel* model_address = &mock_model;
|
||||
|
||||
EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address));
|
||||
EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state));
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
auto time = rclcpp::Time(0);
|
||||
auto duration = rclcpp::Duration(0, 0);
|
||||
auto return_type = franka_hardware_interface.read(time, duration);
|
||||
EXPECT_EQ(return_type, hardware_interface::return_type::OK);
|
||||
auto states = franka_hardware_interface.export_state_interfaces();
|
||||
size_t joint_index = 0;
|
||||
|
||||
// Get all the states except the last two reserved for robot state
|
||||
for (size_t i = 0; i < states.size() - 2; i++) {
|
||||
if (i % 3 == 0) {
|
||||
joint_index++;
|
||||
}
|
||||
const std::string joint_name = k_joint_name + std::to_string(joint_index);
|
||||
if (i % 3 == 0) {
|
||||
EXPECT_EQ(states[i].get_name(), joint_name + "/" + k_position_controller);
|
||||
} else if (i % 3 == 1) {
|
||||
EXPECT_EQ(states[i].get_name(), joint_name + "/" + k_velocity_controller);
|
||||
} else {
|
||||
EXPECT_EQ(states[i].get_name(), joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
EXPECT_EQ(states[i].get_value(), 0.0);
|
||||
}
|
||||
|
||||
EXPECT_EQ(states.size(), state_interface_size);
|
||||
}
|
||||
|
||||
TEST(
|
||||
FrankaHardwareInterfaceTest,
|
||||
given_that_the_robot_interfaces_are_set_when_call_export_state_interface_robot_model_interface_exists) {
|
||||
franka::RobotState robot_state;
|
||||
const size_t state_interface_size =
|
||||
23; // position, effort and velocity states for every joint + robot state and model
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
|
||||
MockModel mock_model;
|
||||
MockModel* model_address = &mock_model;
|
||||
|
||||
EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state));
|
||||
EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address));
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
auto time = rclcpp::Time(0);
|
||||
auto duration = rclcpp::Duration(0, 0);
|
||||
auto return_type = franka_hardware_interface.read(time, duration);
|
||||
EXPECT_EQ(return_type, hardware_interface::return_type::OK);
|
||||
auto states = franka_hardware_interface.export_state_interfaces();
|
||||
EXPECT_EQ(states[state_interface_size - 1].get_name(),
|
||||
"panda/robot_model"); // Last state interface is the robot model state
|
||||
EXPECT_NEAR(states[state_interface_size - 1].get_value(),
|
||||
*reinterpret_cast<double*>(&model_address),
|
||||
k_EPS); // testing that the casted mock_model ptr
|
||||
// is correctly pushed to state interface
|
||||
}
|
||||
|
||||
TEST(
|
||||
FrankaHardwareInterfaceTest,
|
||||
given_that_the_robot_interfaces_are_set_when_call_export_state_interface_robot_state_interface_exists) {
|
||||
const size_t state_interface_size =
|
||||
23; // position, effort and velocity states for every joint + robot state and model
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
|
||||
franka::RobotState robot_state;
|
||||
franka::RobotState* robot_state_address = &robot_state;
|
||||
|
||||
MockModel mock_model;
|
||||
MockModel* model_address = &mock_model;
|
||||
|
||||
EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state));
|
||||
EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address));
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
auto time = rclcpp::Time(0);
|
||||
auto duration = rclcpp::Duration(0, 0);
|
||||
auto return_type = franka_hardware_interface.read(time, duration);
|
||||
EXPECT_EQ(return_type, hardware_interface::return_type::OK);
|
||||
auto states = franka_hardware_interface.export_state_interfaces();
|
||||
EXPECT_EQ(states[state_interface_size - 2].get_name(),
|
||||
"panda/robot_state"); // Last state interface is the robot model state
|
||||
EXPECT_NEAR(states[state_interface_size - 2].get_value(),
|
||||
*reinterpret_cast<double*>(&robot_state_address),
|
||||
k_EPS); // testing that the casted robot state ptr
|
||||
// is correctly pushed to state interface
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest,
|
||||
when_prepare_command_mode_interface_for_stop_effort_interfaces_expect_ok) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
std::vector<std::string> stop_interface;
|
||||
|
||||
for (size_t i = 0; i < hardware_info.joints.size(); i++) {
|
||||
const std::string joint_name = k_joint_name + std::to_string(i);
|
||||
stop_interface.push_back(joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
std::vector<std::string> start_interface = {};
|
||||
EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
}
|
||||
|
||||
TEST(
|
||||
FrankaHardwareInterfaceTest,
|
||||
when_prepare_command_mode_interface_is_called_with_invalid_start_interface_number_expect_throw) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
std::vector<std::string> stop_interface;
|
||||
|
||||
for (size_t i = 0; i < hardware_info.joints.size(); i++) {
|
||||
const std::string joint_name = k_joint_name + std::to_string(i);
|
||||
stop_interface.push_back(joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
std::vector<std::string> start_interface = {"fr3_joint1/effort"};
|
||||
EXPECT_THROW(
|
||||
franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest,
|
||||
when_prepare_command_mode_interface_for_start_effort_interfaces_expect_ok) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
std::vector<std::string> start_interface;
|
||||
|
||||
for (size_t i = 0; i < hardware_info.joints.size(); i++) {
|
||||
const std::string joint_name = k_joint_name + std::to_string(i);
|
||||
start_interface.push_back(joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
|
||||
std::vector<std::string> stop_interface = {};
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
}
|
||||
|
||||
TEST(
|
||||
FrankaHardwareInterfaceTest,
|
||||
when_prepare_command_mode_interface_is_called_with_invalid_stop_interface_number_expect_throw) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
std::vector<std::string> start_interface;
|
||||
|
||||
for (size_t i = 0; i < hardware_info.joints.size(); i++) {
|
||||
const std::string joint_name = k_joint_name + std::to_string(i);
|
||||
start_interface.push_back(joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
|
||||
std::vector<std::string> stop_interface = {"fr3_joint1/effort"};
|
||||
|
||||
EXPECT_THROW(
|
||||
franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareIntefaceTest, when_write_called_expect_ok) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
|
||||
const auto time = rclcpp::Time(0, 0);
|
||||
const auto duration = rclcpp::Duration(0, 0);
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.write(time, duration), hardware_interface::return_type::OK);
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest, when_on_activate_called_expect_success) {
|
||||
franka::RobotState robot_state;
|
||||
|
||||
MockModel mock_model;
|
||||
MockModel* model_address = &mock_model;
|
||||
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state));
|
||||
EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address));
|
||||
|
||||
EXPECT_CALL(*mock_robot, initializeContinuousReading());
|
||||
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.on_activate(rclcpp_lifecycle::State()),
|
||||
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS);
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest, when_on_deactivate_called_expect_success) {
|
||||
franka::RobotState robot_state;
|
||||
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
EXPECT_CALL(*mock_robot, stopRobot());
|
||||
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.on_deactivate(rclcpp_lifecycle::State()),
|
||||
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS);
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest,
|
||||
given_start_effort_interface_prepared_when_perform_comamnd_mode_switch_called_expect_ok) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
EXPECT_CALL(*mock_robot, stopRobot());
|
||||
EXPECT_CALL(*mock_robot, initializeTorqueControl());
|
||||
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
std::vector<std::string> start_interface;
|
||||
|
||||
for (size_t i = 0; i < hardware_info.joints.size(); i++) {
|
||||
const std::string joint_name = k_joint_name + std::to_string(i);
|
||||
start_interface.push_back(joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
|
||||
std::vector<std::string> stop_interface = {};
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.perform_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
}
|
||||
|
||||
TEST(FrankaHardwareInterfaceTest,
|
||||
given_that_effort_control_started_perform_command_mode_switch_stop_expect_ok) {
|
||||
auto mock_robot = std::make_unique<MockRobot>();
|
||||
EXPECT_CALL(*mock_robot, stopRobot()).Times(2).WillRepeatedly(testing::Return());
|
||||
EXPECT_CALL(*mock_robot, initializeTorqueControl());
|
||||
EXPECT_CALL(*mock_robot, initializeContinuousReading());
|
||||
|
||||
franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot));
|
||||
|
||||
const auto hardware_info = createHardwareInfo();
|
||||
franka_hardware_interface.on_init(hardware_info);
|
||||
std::vector<std::string> start_interface;
|
||||
|
||||
for (size_t i = 0; i < hardware_info.joints.size(); i++) {
|
||||
const std::string joint_name = k_joint_name + std::to_string(i);
|
||||
start_interface.push_back(joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
|
||||
std::vector<std::string> stop_interface = {};
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.perform_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
|
||||
for (size_t i = 0; i < hardware_info.joints.size(); i++) {
|
||||
const std::string joint_name = k_joint_name + std::to_string(i);
|
||||
stop_interface.push_back(joint_name + "/" + k_effort_controller);
|
||||
}
|
||||
|
||||
start_interface.clear();
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
|
||||
EXPECT_EQ(franka_hardware_interface.perform_command_mode_switch(start_interface, stop_interface),
|
||||
hardware_interface::return_type::OK);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
19
franka_moveit_config/CMakeLists.txt
Normal file
19
franka_moveit_config/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(franka_moveit_config)
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
|
||||
install(
|
||||
DIRECTORY config launch srdf rviz
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
find_package(ament_cmake_pytest REQUIRED)
|
||||
ament_add_pytest_test(${PROJECT_NAME}_srdf_tests test/srdf_tests.py)
|
||||
endif()
|
||||
|
||||
ament_package()
|
4
franka_moveit_config/config/kinematics.yaml
Normal file
4
franka_moveit_config/config/kinematics.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
panda_arm:
|
||||
kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin
|
||||
kinematics_solver_search_resolution: 0.005
|
||||
kinematics_solver_timeout: 0.05
|
152
franka_moveit_config/config/ompl_planning.yaml
Normal file
152
franka_moveit_config/config/ompl_planning.yaml
Normal file
@ -0,0 +1,152 @@
|
||||
planner_configs:
|
||||
SBLkConfigDefault:
|
||||
type: geometric::SBL
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
ESTkConfigDefault:
|
||||
type: geometric::EST
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0 setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05
|
||||
LBKPIECEkConfigDefault:
|
||||
type: geometric::LBKPIECE
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
border_fraction: 0.9 # Fraction of time focused on boarder default: 0.9
|
||||
min_valid_path_fraction: 0.5 # Accept partially valid moves above fraction. default: 0.5
|
||||
BKPIECEkConfigDefault:
|
||||
type: geometric::BKPIECE
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
border_fraction: 0.9 # Fraction of time focused on boarder default: 0.9
|
||||
failed_expansion_score_factor: 0.5 # When extending motion fails, scale score by factor. default: 0.5
|
||||
min_valid_path_fraction: 0.5 # Accept partially valid moves above fraction. default: 0.5
|
||||
KPIECEkConfigDefault:
|
||||
type: geometric::KPIECE
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05
|
||||
border_fraction: 0.9 # Fraction of time focused on boarder default: 0.9 (0.0,1.]
|
||||
failed_expansion_score_factor: 0.5 # When extending motion fails, scale score by factor. default: 0.5
|
||||
min_valid_path_fraction: 0.5 # Accept partially valid moves above fraction. default: 0.5
|
||||
RRTkConfigDefault:
|
||||
type: geometric::RRT
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability? default: 0.05
|
||||
RRTConnectkConfigDefault:
|
||||
type: geometric::RRTConnect
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
RRTstarkConfigDefault:
|
||||
type: geometric::RRTstar
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability? default: 0.05
|
||||
delay_collision_checking: 1 # Stop collision checking as soon as C-free parent found. default 1
|
||||
TRRTkConfigDefault:
|
||||
type: geometric::TRRT
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability? default: 0.05
|
||||
max_states_failed: 10 # when to start increasing temp. default: 10
|
||||
temp_change_factor: 2.0 # how much to increase or decrease temp. default: 2.0
|
||||
min_temperature: 10e-10 # lower limit of temp change. default: 10e-10
|
||||
init_temperature: 10e-6 # initial temperature. default: 10e-6
|
||||
frountier_threshold: 0.0 # dist new state to nearest neighbor to disqualify as frontier. default: 0.0 set in setup()
|
||||
frountierNodeRatio: 0.1 # 1/10, or 1 nonfrontier for every 10 frontier. default: 0.1
|
||||
k_constant: 0.0 # value used to normalize expresssion. default: 0.0 set in setup()
|
||||
PRMkConfigDefault:
|
||||
type: geometric::PRM
|
||||
max_nearest_neighbors: 10 # use k nearest neighbors. default: 10
|
||||
PRMstarkConfigDefault:
|
||||
type: geometric::PRMstar
|
||||
FMTkConfigDefault:
|
||||
type: geometric::FMT
|
||||
num_samples: 1000 # number of states that the planner should sample. default: 1000
|
||||
radius_multiplier: 1.1 # multiplier used for the nearest neighbors search radius. default: 1.1
|
||||
nearest_k: 1 # use Knearest strategy. default: 1
|
||||
cache_cc: 1 # use collision checking cache. default: 1
|
||||
heuristics: 0 # activate cost to go heuristics. default: 0
|
||||
extended_fmt: 1 # activate the extended FMT*: adding new samples if planner does not finish successfully. default: 1
|
||||
BFMTkConfigDefault:
|
||||
type: geometric::BFMT
|
||||
num_samples: 1000 # number of states that the planner should sample. default: 1000
|
||||
radius_multiplier: 1.0 # multiplier used for the nearest neighbors search radius. default: 1.0
|
||||
nearest_k: 1 # use the Knearest strategy. default: 1
|
||||
balanced: 0 # exploration strategy: balanced true expands one tree every iteration. False will select the tree with lowest maximum cost to go. default: 1
|
||||
optimality: 1 # termination strategy: optimality true finishes when the best possible path is found. Otherwise, the algorithm will finish when the first feasible path is found. default: 1
|
||||
heuristics: 1 # activates cost to go heuristics. default: 1
|
||||
cache_cc: 1 # use the collision checking cache. default: 1
|
||||
extended_fmt: 1 # Activates the extended FMT*: adding new samples if planner does not finish successfully. default: 1
|
||||
PDSTkConfigDefault:
|
||||
type: geometric::PDST
|
||||
STRIDEkConfigDefault:
|
||||
type: geometric::STRIDE
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05
|
||||
use_projected_distance: 0 # whether nearest neighbors are computed based on distances in a projection of the state rather distances in the state space itself. default: 0
|
||||
degree: 16 # desired degree of a node in the Geometric Near-neightbor Access Tree (GNAT). default: 16
|
||||
max_degree: 18 # max degree of a node in the GNAT. default: 12
|
||||
min_degree: 12 # min degree of a node in the GNAT. default: 12
|
||||
max_pts_per_leaf: 6 # max points per leaf in the GNAT. default: 6
|
||||
estimated_dimension: 0.0 # estimated dimension of the free space. default: 0.0
|
||||
min_valid_path_fraction: 0.2 # Accept partially valid moves above fraction. default: 0.2
|
||||
BiTRRTkConfigDefault:
|
||||
type: geometric::BiTRRT
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
temp_change_factor: 0.1 # how much to increase or decrease temp. default: 0.1
|
||||
init_temperature: 100 # initial temperature. default: 100
|
||||
frountier_threshold: 0.0 # dist new state to nearest neighbor to disqualify as frontier. default: 0.0 set in setup()
|
||||
frountier_node_ratio: 0.1 # 1/10, or 1 nonfrontier for every 10 frontier. default: 0.1
|
||||
cost_threshold: 1e300 # the cost threshold. Any motion cost that is not better will not be expanded. default: inf
|
||||
LBTRRTkConfigDefault:
|
||||
type: geometric::LBTRRT
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05
|
||||
epsilon: 0.4 # optimality approximation factor. default: 0.4
|
||||
BiESTkConfigDefault:
|
||||
type: geometric::BiEST
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
ProjESTkConfigDefault:
|
||||
type: geometric::ProjEST
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05
|
||||
LazyPRMkConfigDefault:
|
||||
type: geometric::LazyPRM
|
||||
range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup()
|
||||
LazyPRMstarkConfigDefault:
|
||||
type: geometric::LazyPRMstar
|
||||
SPARSkConfigDefault:
|
||||
type: geometric::SPARS
|
||||
stretch_factor: 3.0 # roadmap spanner stretch factor. multiplicative upper bound on path quality. It does not make sense to make this parameter more than 3. default: 3.0
|
||||
sparse_delta_fraction: 0.25 # delta fraction for connection distance. This value represents the visibility range of sparse samples. default: 0.25
|
||||
dense_delta_fraction: 0.001 # delta fraction for interface detection. default: 0.001
|
||||
max_failures: 1000 # maximum consecutive failure limit. default: 1000
|
||||
SPARStwokConfigDefault:
|
||||
type: geometric::SPARStwo
|
||||
stretch_factor: 3.0 # roadmap spanner stretch factor. multiplicative upper bound on path quality. It does not make sense to make this parameter more than 3. default: 3.0
|
||||
sparse_delta_fraction: 0.25 # delta fraction for connection distance. This value represents the visibility range of sparse samples. default: 0.25
|
||||
dense_delta_fraction: 0.001 # delta fraction for interface detection. default: 0.001
|
||||
max_failures: 5000 # maximum consecutive failure limit. default: 5000
|
||||
TrajOptDefault:
|
||||
type: geometric::TrajOpt
|
||||
|
||||
panda_arm:
|
||||
planner_configs:
|
||||
- SBLkConfigDefault
|
||||
- ESTkConfigDefault
|
||||
- LBKPIECEkConfigDefault
|
||||
- BKPIECEkConfigDefault
|
||||
- KPIECEkConfigDefault
|
||||
- RRTkConfigDefault
|
||||
- RRTConnectkConfigDefault
|
||||
- RRTstarkConfigDefault
|
||||
- TRRTkConfigDefault
|
||||
- PRMkConfigDefault
|
||||
- PRMstarkConfigDefault
|
||||
- FMTkConfigDefault
|
||||
- BFMTkConfigDefault
|
||||
- PDSTkConfigDefault
|
||||
- STRIDEkConfigDefault
|
||||
- BiTRRTkConfigDefault
|
||||
- LBTRRTkConfigDefault
|
||||
- BiESTkConfigDefault
|
||||
- ProjESTkConfigDefault
|
||||
- LazyPRMkConfigDefault
|
||||
- LazyPRMstarkConfigDefault
|
||||
- SPARSkConfigDefault
|
||||
- SPARStwokConfigDefault
|
||||
- TrajOptDefault
|
||||
|
15
franka_moveit_config/config/panda_controllers.yaml
Normal file
15
franka_moveit_config/config/panda_controllers.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
controller_names:
|
||||
- panda_arm_controller
|
||||
|
||||
panda_arm_controller:
|
||||
action_ns: follow_joint_trajectory
|
||||
type: FollowJointTrajectory
|
||||
default: true
|
||||
joints:
|
||||
- panda_joint1
|
||||
- panda_joint2
|
||||
- panda_joint3
|
||||
- panda_joint4
|
||||
- panda_joint5
|
||||
- panda_joint6
|
||||
- panda_joint7
|
40
franka_moveit_config/config/panda_ros_controllers.yaml
Normal file
40
franka_moveit_config/config/panda_ros_controllers.yaml
Normal file
@ -0,0 +1,40 @@
|
||||
controller_manager:
|
||||
ros__parameters:
|
||||
update_rate: 1000 # Hz
|
||||
|
||||
panda_arm_controller:
|
||||
type: joint_trajectory_controller/JointTrajectoryController
|
||||
|
||||
joint_state_broadcaster:
|
||||
type: joint_state_broadcaster/JointStateBroadcaster
|
||||
|
||||
franka_robot_state_broadcaster:
|
||||
type: franka_robot_state_broadcaster/FrankaRobotStateBroadcaster
|
||||
|
||||
franka_robot_state_broadcaster:
|
||||
ros__parameters:
|
||||
arm_id: panda
|
||||
|
||||
panda_arm_controller:
|
||||
ros__parameters:
|
||||
command_interfaces:
|
||||
- effort
|
||||
state_interfaces:
|
||||
- position
|
||||
- velocity
|
||||
joints:
|
||||
- panda_joint1
|
||||
- panda_joint2
|
||||
- panda_joint3
|
||||
- panda_joint4
|
||||
- panda_joint5
|
||||
- panda_joint6
|
||||
- panda_joint7
|
||||
gains:
|
||||
panda_joint1: { p: 600., d: 30., i: 0., i_clamp: 1., ff_velocity_scale: 1.}
|
||||
panda_joint2: { p: 600., d: 30., i: 0., i_clamp: 1., ff_velocity_scale: 1. }
|
||||
panda_joint3: { p: 600., d: 30., i: 0., i_clamp: 1., ff_velocity_scale: 1. }
|
||||
panda_joint4: { p: 600., d: 30., i: 0., i_clamp: 1., ff_velocity_scale: 1. }
|
||||
panda_joint5: { p: 250., d: 10., i: 0., i_clamp: 1., ff_velocity_scale: 1. }
|
||||
panda_joint6: { p: 150., d: 10., i: 0., i_clamp: 1., ff_velocity_scale: 1. }
|
||||
panda_joint7: { p: 50., d: 5., i: 0., i_clamp: 1., ff_velocity_scale: 1. }
|
244
franka_moveit_config/launch/moveit.launch.py
Normal file
244
franka_moveit_config/launch/moveit.launch.py
Normal file
@ -0,0 +1,244 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file is an adapted version of
|
||||
# https://github.com/ros-planning/moveit_resources/blob/ca3f7930c630581b5504f3b22c40b4f82ee6369d/panda_moveit_config/launch/demo.launch.py
|
||||
|
||||
import os
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import (DeclareLaunchArgument, ExecuteProcess, IncludeLaunchDescription,
|
||||
Shutdown)
|
||||
from launch.conditions import UnlessCondition
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
import yaml
|
||||
|
||||
|
||||
def load_yaml(package_name, file_path):
|
||||
package_path = get_package_share_directory(package_name)
|
||||
absolute_file_path = os.path.join(package_path, file_path)
|
||||
|
||||
try:
|
||||
with open(absolute_file_path, 'r') as file:
|
||||
return yaml.safe_load(file)
|
||||
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
|
||||
return None
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
fake_sensor_commands_parameter_name = 'fake_sensor_commands'
|
||||
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
|
||||
|
||||
# Command-line arguments
|
||||
|
||||
db_arg = DeclareLaunchArgument(
|
||||
'db', default_value='False', description='Database flag'
|
||||
)
|
||||
|
||||
# planning_context
|
||||
franka_xacro_file = os.path.join(get_package_share_directory('franka_description'), 'robots',
|
||||
'panda_arm.urdf.xacro')
|
||||
robot_description_config = Command(
|
||||
[FindExecutable(name='xacro'), ' ', franka_xacro_file, ' hand:=true',
|
||||
' robot_ip:=', robot_ip, ' use_fake_hardware:=', use_fake_hardware,
|
||||
' fake_sensor_commands:=', fake_sensor_commands])
|
||||
|
||||
robot_description = {'robot_description': robot_description_config}
|
||||
|
||||
franka_semantic_xacro_file = os.path.join(get_package_share_directory('franka_moveit_config'),
|
||||
'srdf',
|
||||
'panda_arm.srdf.xacro')
|
||||
robot_description_semantic_config = Command(
|
||||
[FindExecutable(name='xacro'), ' ', franka_semantic_xacro_file, ' hand:=true']
|
||||
)
|
||||
robot_description_semantic = {
|
||||
'robot_description_semantic': robot_description_semantic_config
|
||||
}
|
||||
|
||||
kinematics_yaml = load_yaml(
|
||||
'franka_moveit_config', 'config/kinematics.yaml'
|
||||
)
|
||||
|
||||
# Planning Functionality
|
||||
ompl_planning_pipeline_config = {
|
||||
'move_group': {
|
||||
'planning_plugin': 'ompl_interface/OMPLPlanner',
|
||||
'request_adapters': 'default_planner_request_adapters/AddTimeOptimalParameterization '
|
||||
'default_planner_request_adapters/ResolveConstraintFrames '
|
||||
'default_planner_request_adapters/FixWorkspaceBounds '
|
||||
'default_planner_request_adapters/FixStartStateBounds '
|
||||
'default_planner_request_adapters/FixStartStateCollision '
|
||||
'default_planner_request_adapters/FixStartStatePathConstraints',
|
||||
'start_state_max_bounds_error': 0.1,
|
||||
}
|
||||
}
|
||||
ompl_planning_yaml = load_yaml(
|
||||
'franka_moveit_config', 'config/ompl_planning.yaml'
|
||||
)
|
||||
ompl_planning_pipeline_config['move_group'].update(ompl_planning_yaml)
|
||||
|
||||
# Trajectory Execution Functionality
|
||||
moveit_simple_controllers_yaml = load_yaml(
|
||||
'franka_moveit_config', 'config/panda_controllers.yaml'
|
||||
)
|
||||
moveit_controllers = {
|
||||
'moveit_simple_controller_manager': moveit_simple_controllers_yaml,
|
||||
'moveit_controller_manager': 'moveit_simple_controller_manager'
|
||||
'/MoveItSimpleControllerManager',
|
||||
}
|
||||
|
||||
trajectory_execution = {
|
||||
'moveit_manage_controllers': True,
|
||||
'trajectory_execution.allowed_execution_duration_scaling': 1.2,
|
||||
'trajectory_execution.allowed_goal_duration_margin': 0.5,
|
||||
'trajectory_execution.allowed_start_tolerance': 0.01,
|
||||
}
|
||||
|
||||
planning_scene_monitor_parameters = {
|
||||
'publish_planning_scene': True,
|
||||
'publish_geometry_updates': True,
|
||||
'publish_state_updates': True,
|
||||
'publish_transforms_updates': True,
|
||||
}
|
||||
|
||||
# Start the actual move_group node/action server
|
||||
run_move_group_node = Node(
|
||||
package='moveit_ros_move_group',
|
||||
executable='move_group',
|
||||
output='screen',
|
||||
parameters=[
|
||||
robot_description,
|
||||
robot_description_semantic,
|
||||
kinematics_yaml,
|
||||
ompl_planning_pipeline_config,
|
||||
trajectory_execution,
|
||||
moveit_controllers,
|
||||
planning_scene_monitor_parameters,
|
||||
],
|
||||
)
|
||||
|
||||
# RViz
|
||||
rviz_base = os.path.join(get_package_share_directory('franka_moveit_config'), 'rviz')
|
||||
rviz_full_config = os.path.join(rviz_base, 'moveit.rviz')
|
||||
|
||||
rviz_node = Node(
|
||||
package='rviz2',
|
||||
executable='rviz2',
|
||||
name='rviz2',
|
||||
output='log',
|
||||
arguments=['-d', rviz_full_config],
|
||||
parameters=[
|
||||
robot_description,
|
||||
robot_description_semantic,
|
||||
ompl_planning_pipeline_config,
|
||||
kinematics_yaml,
|
||||
],
|
||||
)
|
||||
|
||||
# Publish TF
|
||||
robot_state_publisher = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='both',
|
||||
parameters=[robot_description],
|
||||
)
|
||||
|
||||
ros2_controllers_path = os.path.join(
|
||||
get_package_share_directory('franka_moveit_config'),
|
||||
'config',
|
||||
'panda_ros_controllers.yaml',
|
||||
)
|
||||
ros2_control_node = Node(
|
||||
package='controller_manager',
|
||||
executable='ros2_control_node',
|
||||
parameters=[robot_description, ros2_controllers_path],
|
||||
remappings=[('joint_states', 'franka/joint_states')],
|
||||
output={
|
||||
'stdout': 'screen',
|
||||
'stderr': 'screen',
|
||||
},
|
||||
on_exit=Shutdown(),
|
||||
)
|
||||
|
||||
# Load controllers
|
||||
load_controllers = []
|
||||
for controller in ['panda_arm_controller', 'joint_state_broadcaster']:
|
||||
load_controllers += [
|
||||
ExecuteProcess(
|
||||
cmd=['ros2 run controller_manager spawner {}'.format(controller)],
|
||||
shell=True,
|
||||
output='screen',
|
||||
)
|
||||
]
|
||||
|
||||
joint_state_publisher = Node(
|
||||
package='joint_state_publisher',
|
||||
executable='joint_state_publisher',
|
||||
name='joint_state_publisher',
|
||||
parameters=[
|
||||
{'source_list': ['franka/joint_states', 'panda_gripper/joint_states'], 'rate': 30}],
|
||||
)
|
||||
|
||||
franka_robot_state_broadcaster = Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['franka_robot_state_broadcaster'],
|
||||
output='screen',
|
||||
condition=UnlessCondition(use_fake_hardware),
|
||||
)
|
||||
|
||||
robot_arg = DeclareLaunchArgument(
|
||||
robot_ip_parameter_name,
|
||||
description='Hostname or IP address of the robot.')
|
||||
|
||||
use_fake_hardware_arg = DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description='Use fake hardware')
|
||||
fake_sensor_commands_arg = DeclareLaunchArgument(
|
||||
fake_sensor_commands_parameter_name,
|
||||
default_value='false',
|
||||
description="Fake sensor commands. Only valid when '{}' is true".format(
|
||||
use_fake_hardware_parameter_name))
|
||||
gripper_launch_file = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([PathJoinSubstitution(
|
||||
[FindPackageShare('franka_gripper'), 'launch', 'gripper.launch.py'])]),
|
||||
launch_arguments={'robot_ip': robot_ip,
|
||||
use_fake_hardware_parameter_name: use_fake_hardware}.items(),
|
||||
)
|
||||
return LaunchDescription(
|
||||
[robot_arg,
|
||||
use_fake_hardware_arg,
|
||||
fake_sensor_commands_arg,
|
||||
db_arg,
|
||||
rviz_node,
|
||||
robot_state_publisher,
|
||||
run_move_group_node,
|
||||
ros2_control_node,
|
||||
joint_state_publisher,
|
||||
franka_robot_state_broadcaster,
|
||||
gripper_launch_file
|
||||
]
|
||||
+ load_controllers
|
||||
)
|
237
franka_moveit_config/launch/moveit_without_hand.launch.py
Normal file
237
franka_moveit_config/launch/moveit_without_hand.launch.py
Normal file
@ -0,0 +1,237 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file is an adapted version of
|
||||
# https://github.com/ros-planning/moveit_resources/blob/ca3f7930c630581b5504f3b22c40b4f82ee6369d/panda_moveit_config/launch/demo.launch.py
|
||||
|
||||
import os
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import (DeclareLaunchArgument, ExecuteProcess, IncludeLaunchDescription,
|
||||
Shutdown)
|
||||
from launch.conditions import UnlessCondition
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
|
||||
from launch_ros.actions import Node
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
import yaml
|
||||
|
||||
|
||||
def load_yaml(package_name, file_path):
|
||||
package_path = get_package_share_directory(package_name)
|
||||
absolute_file_path = os.path.join(package_path, file_path)
|
||||
|
||||
try:
|
||||
with open(absolute_file_path, 'r') as file:
|
||||
return yaml.safe_load(file)
|
||||
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
|
||||
return None
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
robot_ip_parameter_name = 'robot_ip'
|
||||
use_fake_hardware_parameter_name = 'use_fake_hardware'
|
||||
fake_sensor_commands_parameter_name = 'fake_sensor_commands'
|
||||
|
||||
robot_ip = LaunchConfiguration(robot_ip_parameter_name)
|
||||
use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name)
|
||||
fake_sensor_commands = LaunchConfiguration(fake_sensor_commands_parameter_name)
|
||||
|
||||
# Command-line arguments
|
||||
|
||||
db_arg = DeclareLaunchArgument(
|
||||
'db', default_value='False', description='Database flag'
|
||||
)
|
||||
|
||||
# planning_context
|
||||
franka_xacro_file = os.path.join(get_package_share_directory('franka_description'), 'robots',
|
||||
'panda_arm.urdf.xacro')
|
||||
robot_description_config = Command(
|
||||
[FindExecutable(name='xacro'), ' ', franka_xacro_file, ' hand:=false',
|
||||
' robot_ip:=', robot_ip, ' use_fake_hardware:=', use_fake_hardware,
|
||||
' fake_sensor_commands:=', fake_sensor_commands])
|
||||
|
||||
robot_description = {'robot_description': robot_description_config}
|
||||
|
||||
franka_semantic_xacro_file = os.path.join(get_package_share_directory('franka_moveit_config'),
|
||||
'srdf',
|
||||
'panda_arm.srdf.xacro')
|
||||
robot_description_semantic_config = Command(
|
||||
[FindExecutable(name='xacro'), ' ', franka_semantic_xacro_file, ' hand:=false']
|
||||
)
|
||||
robot_description_semantic = {
|
||||
'robot_description_semantic': robot_description_semantic_config
|
||||
}
|
||||
|
||||
kinematics_yaml = load_yaml(
|
||||
'franka_moveit_config', 'config/kinematics.yaml'
|
||||
)
|
||||
|
||||
# Planning Functionality
|
||||
ompl_planning_pipeline_config = {
|
||||
'move_group': {
|
||||
'planning_plugin': 'ompl_interface/OMPLPlanner',
|
||||
'request_adapters': 'default_planner_request_adapters/AddTimeOptimalParameterization '
|
||||
'default_planner_request_adapters/ResolveConstraintFrames '
|
||||
'default_planner_request_adapters/FixWorkspaceBounds '
|
||||
'default_planner_request_adapters/FixStartStateBounds '
|
||||
'default_planner_request_adapters/FixStartStateCollision '
|
||||
'default_planner_request_adapters/FixStartStatePathConstraints',
|
||||
'start_state_max_bounds_error': 0.1,
|
||||
}
|
||||
}
|
||||
ompl_planning_yaml = load_yaml(
|
||||
'franka_moveit_config', 'config/ompl_planning.yaml'
|
||||
)
|
||||
ompl_planning_pipeline_config['move_group'].update(ompl_planning_yaml)
|
||||
|
||||
# Trajectory Execution Functionality
|
||||
moveit_simple_controllers_yaml = load_yaml(
|
||||
'franka_moveit_config', 'config/panda_controllers.yaml'
|
||||
)
|
||||
moveit_controllers = {
|
||||
'moveit_simple_controller_manager': moveit_simple_controllers_yaml,
|
||||
'moveit_controller_manager': 'moveit_simple_controller_manager'
|
||||
'/MoveItSimpleControllerManager',
|
||||
}
|
||||
|
||||
trajectory_execution = {
|
||||
'moveit_manage_controllers': True,
|
||||
'trajectory_execution.allowed_execution_duration_scaling': 1.2,
|
||||
'trajectory_execution.allowed_goal_duration_margin': 0.5,
|
||||
'trajectory_execution.allowed_start_tolerance': 0.01,
|
||||
}
|
||||
|
||||
planning_scene_monitor_parameters = {
|
||||
'publish_planning_scene': True,
|
||||
'publish_geometry_updates': True,
|
||||
'publish_state_updates': True,
|
||||
'publish_transforms_updates': True,
|
||||
}
|
||||
|
||||
# Start the actual move_group node/action server
|
||||
run_move_group_node = Node(
|
||||
package='moveit_ros_move_group',
|
||||
executable='move_group',
|
||||
output='screen',
|
||||
parameters=[
|
||||
robot_description,
|
||||
robot_description_semantic,
|
||||
kinematics_yaml,
|
||||
ompl_planning_pipeline_config,
|
||||
trajectory_execution,
|
||||
moveit_controllers,
|
||||
planning_scene_monitor_parameters,
|
||||
],
|
||||
)
|
||||
|
||||
# RViz
|
||||
rviz_base = os.path.join(get_package_share_directory('franka_moveit_config'), 'rviz')
|
||||
rviz_full_config = os.path.join(rviz_base, 'moveit.rviz')
|
||||
|
||||
rviz_node = Node(
|
||||
package='rviz2',
|
||||
executable='rviz2',
|
||||
name='rviz2',
|
||||
output='log',
|
||||
arguments=['-d', rviz_full_config],
|
||||
parameters=[
|
||||
robot_description,
|
||||
robot_description_semantic,
|
||||
ompl_planning_pipeline_config,
|
||||
kinematics_yaml,
|
||||
],
|
||||
)
|
||||
|
||||
# Publish TF
|
||||
robot_state_publisher = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='both',
|
||||
parameters=[robot_description],
|
||||
)
|
||||
|
||||
ros2_controllers_path = os.path.join(
|
||||
get_package_share_directory('franka_moveit_config'),
|
||||
'config',
|
||||
'panda_ros_controllers.yaml',
|
||||
)
|
||||
ros2_control_node = Node(
|
||||
package='controller_manager',
|
||||
executable='ros2_control_node',
|
||||
parameters=[robot_description, ros2_controllers_path],
|
||||
remappings=[('joint_states', 'franka/joint_states')],
|
||||
output={
|
||||
'stdout': 'screen',
|
||||
'stderr': 'screen',
|
||||
},
|
||||
on_exit=Shutdown(),
|
||||
)
|
||||
|
||||
# Load controllers
|
||||
load_controllers = []
|
||||
for controller in ['panda_arm_controller', 'joint_state_broadcaster']:
|
||||
load_controllers += [
|
||||
ExecuteProcess(
|
||||
cmd=['ros2 run controller_manager spawner {}'.format(controller)],
|
||||
shell=True,
|
||||
output='screen',
|
||||
)
|
||||
]
|
||||
|
||||
joint_state_publisher = Node(
|
||||
package='joint_state_publisher',
|
||||
executable='joint_state_publisher',
|
||||
name='joint_state_publisher',
|
||||
parameters=[
|
||||
{'source_list': ['franka/joint_states'], 'rate': 30}],
|
||||
)
|
||||
|
||||
franka_robot_state_broadcaster = Node(
|
||||
package='controller_manager',
|
||||
executable='spawner',
|
||||
arguments=['franka_robot_state_broadcaster'],
|
||||
output='screen',
|
||||
condition=UnlessCondition(use_fake_hardware),
|
||||
)
|
||||
|
||||
robot_arg = DeclareLaunchArgument(
|
||||
robot_ip_parameter_name,
|
||||
description='Hostname or IP address of the robot.')
|
||||
|
||||
use_fake_hardware_arg = DeclareLaunchArgument(
|
||||
use_fake_hardware_parameter_name,
|
||||
default_value='false',
|
||||
description='Use fake hardware')
|
||||
fake_sensor_commands_arg = DeclareLaunchArgument(
|
||||
fake_sensor_commands_parameter_name,
|
||||
default_value='false',
|
||||
description="Fake sensor commands. Only valid when '{}' is true".format(
|
||||
use_fake_hardware_parameter_name))
|
||||
return LaunchDescription(
|
||||
[robot_arg,
|
||||
use_fake_hardware_arg,
|
||||
fake_sensor_commands_arg,
|
||||
db_arg,
|
||||
rviz_node,
|
||||
robot_state_publisher,
|
||||
run_move_group_node,
|
||||
ros2_control_node,
|
||||
joint_state_publisher,
|
||||
franka_robot_state_broadcaster
|
||||
]
|
||||
+ load_controllers
|
||||
)
|
33
franka_moveit_config/package.xml
Normal file
33
franka_moveit_config/package.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>franka_moveit_config</name>
|
||||
<version>0.1.0</version>
|
||||
<description>Contains Moveit2 configuration files for Franka Emika research robots</description>
|
||||
<maintainer email="support@franka.de">Franka Emika GmbH</maintainer>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<exec_depend>franka_description</exec_depend>
|
||||
<exec_depend>franka_hardware</exec_depend>
|
||||
<exec_depend>franka_gripper</exec_depend>
|
||||
<exec_depend>joint_state_publisher</exec_depend>
|
||||
<exec_depend>robot_state_publisher</exec_depend>
|
||||
<exec_depend>joint_state_broadcaster</exec_depend>
|
||||
<exec_depend>controller_manager</exec_depend>
|
||||
<exec_depend>moveit_ros_move_group</exec_depend>
|
||||
<exec_depend>moveit_kinematics</exec_depend>
|
||||
<exec_depend>moveit_planners_ompl</exec_depend>
|
||||
<exec_depend>moveit_ros_visualization</exec_depend>
|
||||
<exec_depend>rviz2</exec_depend>
|
||||
<exec_depend>xacro</exec_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>ament_cmake_pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
359
franka_moveit_config/rviz/moveit.rviz
Normal file
359
franka_moveit_config/rviz/moveit.rviz
Normal file
@ -0,0 +1,359 @@
|
||||
Panels:
|
||||
- Class: rviz_common/Displays
|
||||
Help Height: 78
|
||||
Name: Displays
|
||||
Property Tree Widget:
|
||||
Expanded: ~
|
||||
Splitter Ratio: 0.5
|
||||
Tree Height: 814
|
||||
- Class: rviz_common/Selection
|
||||
Name: Selection
|
||||
- Class: rviz_common/Tool Properties
|
||||
Expanded:
|
||||
- /2D Goal Pose1
|
||||
- /Publish Point1
|
||||
Name: Tool Properties
|
||||
Splitter Ratio: 0.5886790156364441
|
||||
- Class: rviz_common/Views
|
||||
Expanded:
|
||||
- /Current View1
|
||||
Name: Views
|
||||
Splitter Ratio: 0.5
|
||||
Visualization Manager:
|
||||
Class: ""
|
||||
Displays:
|
||||
- Alpha: 0.5
|
||||
Cell Size: 1
|
||||
Class: rviz_default_plugins/Grid
|
||||
Color: 160; 160; 164
|
||||
Enabled: true
|
||||
Line Style:
|
||||
Line Width: 0.029999999329447746
|
||||
Value: Lines
|
||||
Name: Grid
|
||||
Normal Cell Count: 0
|
||||
Offset:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Plane: XY
|
||||
Plane Cell Count: 10
|
||||
Reference Frame: <Fixed Frame>
|
||||
Value: true
|
||||
- Class: rviz_default_plugins/MarkerArray
|
||||
Enabled: true
|
||||
Name: MarkerArray
|
||||
Namespaces:
|
||||
{}
|
||||
Topic:
|
||||
Depth: 100
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /rviz_visual_tools
|
||||
Value: true
|
||||
- Class: moveit_rviz_plugin/Trajectory
|
||||
Color Enabled: false
|
||||
Enabled: true
|
||||
Interrupt Display: false
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
panda_link0:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link1:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link2:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link3:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link4:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link5:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link6:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link7:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link8:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
Loop Animation: false
|
||||
Name: Trajectory
|
||||
Robot Alpha: 0.5
|
||||
Robot Color: 150; 50; 150
|
||||
Robot Description: robot_description
|
||||
Show Robot Collision: false
|
||||
Show Robot Visual: true
|
||||
Show Trail: false
|
||||
State Display Time: 0.05 s
|
||||
Trail Step Size: 1
|
||||
Trajectory Topic: /display_planned_path
|
||||
Value: true
|
||||
- Class: moveit_rviz_plugin/PlanningScene
|
||||
Enabled: true
|
||||
Move Group Namespace: ""
|
||||
Name: PlanningScene
|
||||
Planning Scene Topic: /monitored_planning_scene
|
||||
Robot Description: robot_description
|
||||
Scene Geometry:
|
||||
Scene Alpha: 0.8999999761581421
|
||||
Scene Color: 50; 230; 50
|
||||
Scene Display Time: 0.009999999776482582
|
||||
Show Scene Geometry: true
|
||||
Voxel Coloring: Z-Axis
|
||||
Voxel Rendering: Occupied Voxels
|
||||
Scene Robot:
|
||||
Attached Body Color: 150; 50; 150
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
panda_link0:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link1:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link2:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link3:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link4:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link5:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link6:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link7:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
panda_link8:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
Robot Alpha: 1
|
||||
Show Robot Collision: false
|
||||
Show Robot Visual: true
|
||||
Value: true
|
||||
- Acceleration_Scaling_Factor: 0.1
|
||||
Class: moveit_rviz_plugin/MotionPlanning
|
||||
Enabled: false
|
||||
Move Group Namespace: ""
|
||||
MoveIt_Allow_Approximate_IK: false
|
||||
MoveIt_Allow_External_Program: false
|
||||
MoveIt_Allow_Replanning: false
|
||||
MoveIt_Allow_Sensor_Positioning: false
|
||||
MoveIt_Planning_Attempts: 10
|
||||
MoveIt_Planning_Time: 5
|
||||
MoveIt_Use_Cartesian_Path: false
|
||||
MoveIt_Use_Constraint_Aware_IK: false
|
||||
MoveIt_Warehouse_Host: 127.0.0.1
|
||||
MoveIt_Warehouse_Port: 33829
|
||||
MoveIt_Workspace:
|
||||
Center:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Size:
|
||||
X: 2
|
||||
Y: 2
|
||||
Z: 2
|
||||
Name: MotionPlanning
|
||||
Planned Path:
|
||||
Color Enabled: false
|
||||
Interrupt Display: false
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
Loop Animation: false
|
||||
Robot Alpha: 0.5
|
||||
Robot Color: 150; 50; 150
|
||||
Show Robot Collision: false
|
||||
Show Robot Visual: true
|
||||
Show Trail: false
|
||||
State Display Time: 0.05 s
|
||||
Trail Step Size: 1
|
||||
Trajectory Topic: /display_planned_path
|
||||
Planning Metrics:
|
||||
Payload: 1
|
||||
Show Joint Torques: false
|
||||
Show Manipulability: false
|
||||
Show Manipulability Index: false
|
||||
Show Weight Limit: false
|
||||
TextHeight: 0.07999999821186066
|
||||
Planning Request:
|
||||
Colliding Link Color: 255; 0; 0
|
||||
Goal State Alpha: 1
|
||||
Goal State Color: 250; 128; 0
|
||||
Interactive Marker Size: 0
|
||||
Joint Violation Color: 255; 0; 255
|
||||
Planning Group: panda_manipulator
|
||||
Query Goal State: true
|
||||
Query Start State: false
|
||||
Show Workspace: false
|
||||
Start State Alpha: 1
|
||||
Start State Color: 0; 255; 0
|
||||
Planning Scene Topic: /monitored_planning_scene
|
||||
Robot Description: robot_description
|
||||
Scene Geometry:
|
||||
Scene Alpha: 0.8999999761581421
|
||||
Scene Color: 50; 230; 50
|
||||
Scene Display Time: 0.009999999776482582
|
||||
Show Scene Geometry: true
|
||||
Voxel Coloring: Z-Axis
|
||||
Voxel Rendering: Occupied Voxels
|
||||
Scene Robot:
|
||||
Attached Body Color: 150; 50; 150
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
Robot Alpha: 1
|
||||
Show Robot Collision: false
|
||||
Show Robot Visual: true
|
||||
Value: false
|
||||
Velocity_Scaling_Factor: 0.1
|
||||
Enabled: true
|
||||
Global Options:
|
||||
Background Color: 48; 48; 48
|
||||
Fixed Frame: panda_link0
|
||||
Frame Rate: 30
|
||||
Name: root
|
||||
Tools:
|
||||
- Class: rviz_default_plugins/Interact
|
||||
Hide Inactive Objects: true
|
||||
- Class: rviz_default_plugins/MoveCamera
|
||||
- Class: rviz_default_plugins/Select
|
||||
- Class: rviz_default_plugins/FocusCamera
|
||||
- Class: rviz_default_plugins/Measure
|
||||
Line color: 128; 128; 0
|
||||
- Class: rviz_default_plugins/SetInitialPose
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /initialpose
|
||||
- Class: rviz_default_plugins/SetGoal
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /goal_pose
|
||||
- Class: rviz_default_plugins/PublishPoint
|
||||
Single click: true
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /clicked_point
|
||||
Transformation:
|
||||
Current:
|
||||
Class: rviz_default_plugins/TF
|
||||
Value: true
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz_default_plugins/Orbit
|
||||
Distance: 3.119211196899414
|
||||
Enable Stereo Rendering:
|
||||
Stereo Eye Separation: 0.05999999865889549
|
||||
Stereo Focal Distance: 1
|
||||
Swap Stereo Eyes: false
|
||||
Value: false
|
||||
Focal Point:
|
||||
X: 0.02386285550892353
|
||||
Y: 0.15478567779064178
|
||||
Z: 0.039489321410655975
|
||||
Focal Shape Fixed Size: true
|
||||
Focal Shape Size: 0.05000000074505806
|
||||
Invert Z Axis: false
|
||||
Name: Current View
|
||||
Near Clip Distance: 0.009999999776482582
|
||||
Pitch: 0.5953981876373291
|
||||
Target Frame: <Fixed Frame>
|
||||
Value: Orbit (rviz)
|
||||
Yaw: 5.958578109741211
|
||||
Saved: ~
|
||||
Window Geometry:
|
||||
Displays:
|
||||
collapsed: false
|
||||
Height: 1043
|
||||
Hide Left Dock: false
|
||||
Hide Right Dock: true
|
||||
MotionPlanning:
|
||||
collapsed: false
|
||||
MotionPlanning - Trajectory Slider:
|
||||
collapsed: false
|
||||
QMainWindow State: 000000ff00000000fd0000000400000000000001f3000003b9fc020000000cfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000003b9000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000003c005400720061006a006500630074006f007200790020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000004100fffffffb000000280020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000000000000000fb00000044004d006f00740069006f006e0050006c0061006e006e0069006e00670020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000004100fffffffb0000001c004d006f00740069006f006e0050006c0061006e006e0069006e00670000000223000001d30000017d00ffffff000000010000010f000003abfc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003b000003ab000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d006501000000000000045000000000000000000000053f000003b900000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Selection:
|
||||
collapsed: false
|
||||
Tool Properties:
|
||||
collapsed: false
|
||||
Trajectory - Trajectory Slider:
|
||||
collapsed: false
|
||||
Views:
|
||||
collapsed: true
|
||||
Width: 1848
|
||||
X: 72
|
||||
Y: 454
|
27
franka_moveit_config/srdf/group_definition.xacro
Normal file
27
franka_moveit_config/srdf/group_definition.xacro
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda">
|
||||
<xacro:macro name="group_definition" params="group_name arm_id tip_link">
|
||||
<group name="${group_name}">
|
||||
<chain base_link="${arm_id}_link0" tip_link="${tip_link}"/>
|
||||
</group>
|
||||
|
||||
<group_state name="ready" group="${group_name}">
|
||||
<joint name="${arm_id}_joint1" value="0"/>
|
||||
<joint name="${arm_id}_joint2" value="${-pi/4}"/>
|
||||
<joint name="${arm_id}_joint3" value="0"/>
|
||||
<joint name="${arm_id}_joint4" value="${-3*pi/4}"/>
|
||||
<joint name="${arm_id}_joint5" value="0"/>
|
||||
<joint name="${arm_id}_joint6" value="${pi/2}"/>
|
||||
<joint name="${arm_id}_joint7" value="${pi/4}"/>
|
||||
</group_state>
|
||||
<group_state name="extended" group="${group_name}">
|
||||
<joint name="${arm_id}_joint1" value="0"/>
|
||||
<joint name="${arm_id}_joint2" value="0"/>
|
||||
<joint name="${arm_id}_joint3" value="0"/>
|
||||
<joint name="${arm_id}_joint4" value="-0.1"/>
|
||||
<joint name="${arm_id}_joint5" value="0"/>
|
||||
<joint name="${arm_id}_joint6" value="${pi/2}"/>
|
||||
<joint name="${arm_id}_joint7" value="${pi/4}"/>
|
||||
</group_state>
|
||||
</xacro:macro>
|
||||
</robot>
|
16
franka_moveit_config/srdf/hand.xacro
Normal file
16
franka_moveit_config/srdf/hand.xacro
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" ?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda">
|
||||
<xacro:macro name="hand" params="arm_id:='panda' ">
|
||||
|
||||
<group name="hand">
|
||||
<link name="${arm_id}_hand"/>
|
||||
<link name="${arm_id}_leftfinger"/>
|
||||
<link name="${arm_id}_rightfinger"/>
|
||||
<joint name="${arm_id}_finger_joint1"/>
|
||||
<passive_joint name="${arm_id}_finger_joint2"/>
|
||||
</group>
|
||||
<disable_collisions link1="${arm_id}_hand" link2="${arm_id}_leftfinger" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_hand" link2="${arm_id}_rightfinger" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_leftfinger" link2="${arm_id}_rightfinger" reason="Default"/>
|
||||
</xacro:macro>
|
||||
</robot>
|
19
franka_moveit_config/srdf/panda_arm.srdf.xacro
Normal file
19
franka_moveit_config/srdf/panda_arm.srdf.xacro
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda">
|
||||
|
||||
<xacro:arg name="arm_id" default="panda"/> <!-- Name of this panda -->
|
||||
<xacro:arg name="hand" default="true"/> <!-- Should a franka_gripper be mounted at the flange? (Currently does not work without it) -->
|
||||
|
||||
<xacro:property name="arm_id" value="$(arg arm_id)"/>
|
||||
|
||||
<xacro:include filename="$(find franka_moveit_config)/srdf/panda_arm.xacro"/>
|
||||
<xacro:panda_arm arm_id="${arm_id}"/>
|
||||
|
||||
<xacro:if value="$(arg hand)">
|
||||
<xacro:include filename="$(find franka_moveit_config)/srdf/hand.xacro"/>
|
||||
<xacro:hand arm_id="${arm_id}"/>
|
||||
|
||||
<xacro:include filename="$(find franka_moveit_config)/srdf/panda_arm_hand.xacro"/>
|
||||
<xacro:panda_arm_hand arm_id="${arm_id}"/>
|
||||
</xacro:if>
|
||||
</robot>
|
32
franka_moveit_config/srdf/panda_arm.xacro
Normal file
32
franka_moveit_config/srdf/panda_arm.xacro
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" ?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda">
|
||||
<xacro:macro name="panda_arm" params="arm_id:='panda' ">
|
||||
|
||||
<xacro:include filename="$(find franka_moveit_config)/srdf/group_definition.xacro"/>
|
||||
<xacro:group_definition arm_id="${arm_id}" group_name="${arm_id}_arm" tip_link="${arm_id}_link8"/>
|
||||
|
||||
<virtual_joint name="virtual_joint" type="fixed" parent_frame="world" child_link="${arm_id}_link0"/>
|
||||
<disable_collisions link1="${arm_id}_link0" link2="${arm_id}_link1" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_link0" link2="${arm_id}_link2" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link0" link2="${arm_id}_link3" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link0" link2="${arm_id}_link4" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link1" link2="${arm_id}_link2" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_link1" link2="${arm_id}_link3" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link1" link2="${arm_id}_link4" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link2" link2="${arm_id}_link3" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_link2" link2="${arm_id}_link4" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link2" link2="${arm_id}_link6" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link3" link2="${arm_id}_link4" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_link3" link2="${arm_id}_link5" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link3" link2="${arm_id}_link6" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link3" link2="${arm_id}_link7" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link4" link2="${arm_id}_link5" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_link4" link2="${arm_id}_link6" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link4" link2="${arm_id}_link7" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link4" link2="${arm_id}_link8" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link5" link2="${arm_id}_link6" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_link6" link2="${arm_id}_link7" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_link6" link2="${arm_id}_link8" reason="Default"/>
|
||||
<disable_collisions link1="${arm_id}_link7" link2="${arm_id}_link8" reason="Adjacent"/>
|
||||
</xacro:macro>
|
||||
</robot>
|
31
franka_moveit_config/srdf/panda_arm_hand.xacro
Normal file
31
franka_moveit_config/srdf/panda_arm_hand.xacro
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda">
|
||||
<xacro:macro name="panda_arm_hand" params="arm_id:='panda' ">
|
||||
|
||||
<group_state name="open" group="hand">
|
||||
<joint name="${arm_id}_finger_joint1" value="0.035"/>
|
||||
<joint name="${arm_id}_finger_joint2" value="0.035"/>
|
||||
</group_state>
|
||||
<group_state name="close" group="hand">
|
||||
<joint name="${arm_id}_finger_joint1" value="0"/>
|
||||
<joint name="${arm_id}_finger_joint2" value="0"/>
|
||||
</group_state>
|
||||
<end_effector name="hand_tcp" parent_link="${arm_id}_hand_tcp" group="hand" parent_group="${arm_id}_manipulator"/>
|
||||
<end_effector name="hand" parent_link="${arm_id}_link8" group="hand" parent_group="${arm_id}_arm"/>
|
||||
<disable_collisions link1="${arm_id}_hand" link2="${arm_id}_link3" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_hand" link2="${arm_id}_link4" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_hand" link2="${arm_id}_link6" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_hand" link2="${arm_id}_link7" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_hand" link2="${arm_id}_link8" reason="Adjacent"/>
|
||||
<disable_collisions link1="${arm_id}_leftfinger" link2="${arm_id}_link3" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_leftfinger" link2="${arm_id}_link4" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_leftfinger" link2="${arm_id}_link6" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_leftfinger" link2="${arm_id}_link7" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_leftfinger" link2="${arm_id}_link8" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link3" link2="${arm_id}_rightfinger" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link4" link2="${arm_id}_rightfinger" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link6" link2="${arm_id}_rightfinger" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link7" link2="${arm_id}_rightfinger" reason="Never"/>
|
||||
<disable_collisions link1="${arm_id}_link8" link2="${arm_id}_rightfinger" reason="Never"/>
|
||||
</xacro:macro>
|
||||
</robot>
|
42
franka_moveit_config/test/srdf_tests.py
Normal file
42
franka_moveit_config/test/srdf_tests.py
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright (c) 2021 Franka Emika GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from os import path
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
import xacro
|
||||
|
||||
panda_xacro_file_name = path.join(get_package_share_directory('franka_moveit_config'), 'srdf',
|
||||
'panda_arm.srdf.xacro')
|
||||
|
||||
|
||||
def test_load():
|
||||
urdf = xacro.process_file(panda_xacro_file_name).toxml()
|
||||
assert urdf.find('panda_rightfinger') != -1
|
||||
|
||||
|
||||
def test_load_without_gripper():
|
||||
urdf = xacro.process_file(panda_xacro_file_name,
|
||||
mappings={'hand': 'false'}).toxml()
|
||||
assert urdf.find('panda_rightfinger') == -1
|
||||
|
||||
|
||||
def test_load_with_arm_id():
|
||||
urdf = xacro.process_file(panda_xacro_file_name,
|
||||
mappings={'arm_id': 'totally_different_arm'}).toxml()
|
||||
assert urdf.find('totally_different_arm_joint1') != -1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
23
franka_msgs/CMakeLists.txt
Normal file
23
franka_msgs/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(franka_msgs)
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(rosidl_default_generators REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
find_package(builtin_interfaces REQUIRED)
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}
|
||||
"action/Grasp.action"
|
||||
"action/Homing.action"
|
||||
"action/Move.action"
|
||||
"msg/Errors.msg"
|
||||
"msg/GraspEpsilon.msg"
|
||||
"msg/FrankaRobotState.msg"
|
||||
DEPENDENCIES std_msgs builtin_interfaces
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
endif()
|
||||
|
||||
ament_package()
|
9
franka_msgs/action/Grasp.action
Normal file
9
franka_msgs/action/Grasp.action
Normal file
@ -0,0 +1,9 @@
|
||||
float64 width # [m]
|
||||
GraspEpsilon epsilon
|
||||
float64 speed # [m/s]
|
||||
float64 force # [N]
|
||||
---
|
||||
bool success
|
||||
string error
|
||||
---
|
||||
float64 current_width # [m]
|
5
franka_msgs/action/Homing.action
Normal file
5
franka_msgs/action/Homing.action
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
bool success
|
||||
string error
|
||||
---
|
||||
float64 current_width # [m]
|
7
franka_msgs/action/Move.action
Normal file
7
franka_msgs/action/Move.action
Normal file
@ -0,0 +1,7 @@
|
||||
float64 width # [m]
|
||||
float64 speed # [m/s]
|
||||
---
|
||||
bool success
|
||||
string error
|
||||
---
|
||||
float64 current_width # [m]
|
36
franka_msgs/msg/Errors.msg
Normal file
36
franka_msgs/msg/Errors.msg
Normal file
@ -0,0 +1,36 @@
|
||||
bool joint_position_limits_violation
|
||||
bool cartesian_position_limits_violation
|
||||
bool self_collision_avoidance_violation
|
||||
bool joint_velocity_violation
|
||||
bool cartesian_velocity_violation
|
||||
bool force_control_safety_violation
|
||||
bool joint_reflex
|
||||
bool cartesian_reflex
|
||||
bool max_goal_pose_deviation_violation
|
||||
bool max_path_pose_deviation_violation
|
||||
bool cartesian_velocity_profile_safety_violation
|
||||
bool joint_position_motion_generator_start_pose_invalid
|
||||
bool joint_motion_generator_position_limits_violation
|
||||
bool joint_motion_generator_velocity_limits_violation
|
||||
bool joint_motion_generator_velocity_discontinuity
|
||||
bool joint_motion_generator_acceleration_discontinuity
|
||||
bool cartesian_position_motion_generator_start_pose_invalid
|
||||
bool cartesian_motion_generator_elbow_limit_violation
|
||||
bool cartesian_motion_generator_velocity_limits_violation
|
||||
bool cartesian_motion_generator_velocity_discontinuity
|
||||
bool cartesian_motion_generator_acceleration_discontinuity
|
||||
bool cartesian_motion_generator_elbow_sign_inconsistent
|
||||
bool cartesian_motion_generator_start_elbow_invalid
|
||||
bool cartesian_motion_generator_joint_position_limits_violation
|
||||
bool cartesian_motion_generator_joint_velocity_limits_violation
|
||||
bool cartesian_motion_generator_joint_velocity_discontinuity
|
||||
bool cartesian_motion_generator_joint_acceleration_discontinuity
|
||||
bool cartesian_position_motion_generator_invalid_frame
|
||||
bool force_controller_desired_force_tolerance_violation
|
||||
bool controller_torque_discontinuity
|
||||
bool start_elbow_sign_inconsistent
|
||||
bool communication_constraints_violation
|
||||
bool power_limit_violation
|
||||
bool joint_p2p_insufficient_torque_for_planning
|
||||
bool tau_j_range_violation
|
||||
bool instability_detected
|
52
franka_msgs/msg/FrankaRobotState.msg
Normal file
52
franka_msgs/msg/FrankaRobotState.msg
Normal file
@ -0,0 +1,52 @@
|
||||
std_msgs/Header header
|
||||
float64[6] cartesian_collision
|
||||
float64[6] cartesian_contact
|
||||
float64[7] q
|
||||
float64[7] q_d
|
||||
float64[7] dq
|
||||
float64[7] dq_d
|
||||
float64[7] ddq_d
|
||||
float64[7] theta
|
||||
float64[7] dtheta
|
||||
float64[7] tau_j
|
||||
float64[7] dtau_j
|
||||
float64[7] tau_j_d
|
||||
float64[6] k_f_ext_hat_k
|
||||
float64[2] elbow
|
||||
float64[2] elbow_d
|
||||
float64[2] elbow_c
|
||||
float64[2] delbow_c
|
||||
float64[2] ddelbow_c
|
||||
float64[7] joint_collision
|
||||
float64[7] joint_contact
|
||||
float64[6] o_f_ext_hat_k
|
||||
float64[6] o_dp_ee_d
|
||||
float64[6] o_dp_ee_c
|
||||
float64[6] o_ddp_ee_c
|
||||
float64[7] tau_ext_hat_filtered
|
||||
float64 m_ee
|
||||
float64[3] f_x_cee
|
||||
float64[9] i_ee
|
||||
float64 m_load
|
||||
float64[3] f_x_cload
|
||||
float64[9] i_load
|
||||
float64 m_total
|
||||
float64[3] f_x_ctotal
|
||||
float64[9] i_total
|
||||
float64[16] o_t_ee
|
||||
float64[16] o_t_ee_d
|
||||
float64[16] o_t_ee_c
|
||||
float64[16] f_t_ee
|
||||
float64[16] ee_t_k
|
||||
float64 time
|
||||
float64 control_command_success_rate
|
||||
uint8 ROBOT_MODE_OTHER=0
|
||||
uint8 ROBOT_MODE_IDLE=1
|
||||
uint8 ROBOT_MODE_MOVE=2
|
||||
uint8 ROBOT_MODE_GUIDING=3
|
||||
uint8 ROBOT_MODE_REFLEX=4
|
||||
uint8 ROBOT_MODE_USER_STOPPED=5
|
||||
uint8 ROBOT_MODE_AUTOMATIC_ERROR_RECOVERY=6
|
||||
uint8 robot_mode
|
||||
Errors current_errors
|
||||
Errors last_motion_errors
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user