Skip to content

Commit 7c00dac

Browse files
336 create joystick (#386)
* Move all changes from original 336-create-joystick branch to this branch and adjust to name change (this way a difficult rebase was avoided) Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Fix bug where servo node was blocking the move home action, added pause functionality Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Update code according to linting Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Make pause servo service an extra parameter Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Add always() to manifest to see whether the workflow will update the images now Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Additionally add always() to cudo manifest to see whether the workflow will update the images now Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Tiny improvement code layout and make linting depend on the manifests Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Also add the same needs to the test workflow Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Make the joystick module a flag instead of a platform Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * Use right joy to rotate the vehicle to match with husarion's implementation Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> * fix missing SERVICE value Signed-off-by: Rosalie <rosalie.van.ark@alliander.com> --------- Signed-off-by: Rosalie <rosalie.van.ark@alliander.com>
1 parent 6061ff1 commit 7c00dac

File tree

17 files changed

+941
-17
lines changed

17 files changed

+941
-17
lines changed

.devcontainer/dev/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ RUN echo "source /.dev_bashrc" >> /home/$REMOTE_USER/.bashrc
4646

4747
# Copy repo packages:
4848
COPY alliander_core/src/ /alliander/ros/src
49+
COPY alliander_franka/src/ /alliander/ros/src
50+
COPY alliander_joystick/src/ /alliander/ros/src
4951
COPY alliander_moveit/src/ /alliander/ros/src
5052

5153
# Build repo packages:

.github/workflows/docker.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ jobs:
227227
strategy:
228228
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix_ubuntu) }}
229229
needs: [detect-changes, build-ubuntu-images]
230-
if: needs.detect-changes.outputs.has_changes_ubuntu == 'true'
230+
if: always() && needs.detect-changes.outputs.has_changes_ubuntu == 'true'
231231
runs-on: ubuntu-24.04
232232
steps:
233233
- name: Docker login
@@ -346,7 +346,7 @@ jobs:
346346
strategy:
347347
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix_cuda) }}
348348
needs: [detect-changes, build-cuda-images]
349-
if: needs.detect-changes.outputs.has_changes_cuda == 'true'
349+
if: always() && needs.detect-changes.outputs.has_changes_cuda == 'true'
350350
runs-on: ubuntu-24.04
351351
steps:
352352
- name: Docker login
@@ -371,7 +371,7 @@ jobs:
371371
372372
linting:
373373
runs-on: ubuntu-24.04
374-
needs: [build-ubuntu-images, build-cuda-images]
374+
needs: [create-manifests-ubuntu, create-manifests-cuda]
375375
if: always()
376376
steps:
377377
- uses: actions/checkout@v4
@@ -391,7 +391,7 @@ jobs:
391391
matrix:
392392
runner: [ubuntu-24.04, ubuntu-24.04-arm]
393393
runs-on: ${{ matrix.runner }}
394-
needs: [build-ubuntu-images, build-cuda-images]
394+
needs: [create-manifests-ubuntu, create-manifests-cuda]
395395
steps:
396396
- uses: actions/checkout@v4
397397
- name: Free disk space
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-FileCopyrightText: Alliander N. V.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
ARG BASE_IMAGE=ubuntu:latest
5+
FROM $BASE_IMAGE
6+
7+
ARG COLCON_BUILD_SEQUENTIAL
8+
ENV ROS_DISTRO=jazzy
9+
10+
# Install repo packages:
11+
WORKDIR /$WORKDIR/ros
12+
COPY alliander_core/src/ /$WORKDIR/ros/src
13+
COPY alliander_joystick/src/ /$WORKDIR/ros/src
14+
RUN /$WORKDIR/colcon_build.sh
15+
16+
# Install python dependencies:
17+
WORKDIR $WORKDIR
18+
COPY pyproject.toml /$WORKDIR/pyproject.toml
19+
RUN uv sync \
20+
&& echo "export PYTHONPATH=\"$(dirname $(dirname $(uv python find)))/lib/python3.12/site-packages:\$PYTHONPATH\"" >> /root/.bashrc \
21+
&& echo "export PATH=\"$(dirname $(dirname $(uv python find)))/bin:\$PATH\"" >> /root/.bashrc
22+
23+
WORKDIR /$WORKDIR
24+
ENTRYPOINT ["/entrypoint.sh"]
25+
CMD ["sleep", "infinity"]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# SPDX-FileCopyrightText: Alliander N. V.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
services:
5+
alliander_joystick:
6+
image: allianderrobotics/joystick
7+
container_name: alliander_joystick
8+
network_mode: host
9+
privileged: true
10+
mem_limit: 6gb
11+
tty: true
12+
env_file:
13+
- .env
14+
volumes:
15+
- "/tmp/.X11-unix:/tmp/.X11-unix"
16+
- "/dev:/dev"
17+
command: ["/bin/bash", "-c", "ros2 launch alliander_joystick joystick.launch.py"]
18+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# SPDX-FileCopyrightText: Alliander N. V.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
cmake_minimum_required(VERSION 3.5)
6+
project(alliander_joystick)
7+
8+
# CMake dependencies:
9+
find_package(ament_cmake REQUIRED)
10+
11+
# Other dependencies:
12+
find_package(rclcpp REQUIRED)
13+
find_package(rclcpp_action REQUIRED)
14+
find_package(geometry_msgs REQUIRED)
15+
find_package(sensor_msgs REQUIRED)
16+
find_package(std_srvs REQUIRED)
17+
find_package(alliander_interfaces REQUIRED)
18+
19+
# C++ executables:
20+
include_directories(include)
21+
add_executable(joystick_manager
22+
src/main.cpp
23+
src/joystick_manager.cpp
24+
)
25+
target_include_directories(joystick_manager PUBLIC
26+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
27+
$<INSTALL_INTERFACE:include>)
28+
ament_target_dependencies(joystick_manager
29+
rclcpp
30+
rclcpp_action
31+
geometry_msgs
32+
sensor_msgs
33+
std_srvs
34+
alliander_interfaces
35+
)
36+
37+
install(
38+
TARGETS joystick_manager
39+
DESTINATION lib/${PROJECT_NAME}
40+
)
41+
42+
# Shared folders:
43+
install(
44+
DIRECTORY launch
45+
DESTINATION share/${PROJECT_NAME}
46+
)
47+
48+
# Default test:
49+
if(BUILD_TESTING)
50+
find_package(ament_lint_auto REQUIRED)
51+
ament_lint_auto_find_test_dependencies()
52+
endif()
53+
54+
ament_package()
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
// # SPDX-FileCopyrightText: Alliander N. V.
2+
//
3+
// # SPDX-License-Identifier: Apache-2.0
4+
5+
#ifndef JOYSTICK_MANAGER_HPP_
6+
#define JOYSTICK_MANAGER_HPP_
7+
8+
#include <alliander_interfaces/action/trigger_action.hpp>
9+
#include <alliander_interfaces/srv/string_srv.hpp>
10+
#include <cstdint>
11+
#include <geometry_msgs/msg/twist_stamped.hpp>
12+
#include <rclcpp/client.hpp>
13+
#include <rclcpp/node.hpp>
14+
#include <rclcpp/node_options.hpp>
15+
#include <rclcpp/rclcpp.hpp>
16+
#include <rclcpp_action/client.hpp>
17+
#include <rclcpp_action/rclcpp_action.hpp>
18+
#include <sensor_msgs/msg/joy.hpp>
19+
#include <std_srvs/srv/set_bool.hpp>
20+
#include <std_srvs/srv/trigger.hpp>
21+
22+
using std::placeholders::_1;
23+
using std::placeholders::_2;
24+
typedef alliander_interfaces::action::TriggerAction TriggerAction;
25+
26+
enum Button {
27+
A = 0, // Switch between platform modes
28+
B = 1, // Move arm back to home position
29+
X = 2, // Trigger E-stop
30+
Y = 3, // Reset E-stop
31+
LT = 9, // Open gripper
32+
RT = 10 // Close gripper
33+
};
34+
35+
/// Class to interact with the joystick.
36+
class JoystickManager {
37+
public:
38+
/**
39+
* @brief constructor for the JoystickManager class.
40+
* @param node The ROS2 node to attach to.
41+
*/
42+
JoystickManager(rclcpp::Node::SharedPtr node);
43+
~JoystickManager();
44+
45+
private:
46+
// ROS2 communication variables:
47+
/// The ROS2 node
48+
rclcpp::Node::SharedPtr node;
49+
/// Subsciber for the Joy topic
50+
rclcpp::Subscription<sensor_msgs::msg::Joy>::SharedPtr sub_joy;
51+
/// Publisher for the arm movement velocities
52+
rclcpp::Publisher<geometry_msgs::msg::TwistStamped>::SharedPtr pub_arm_vel;
53+
/// Publisher for the vehicle driving velocities
54+
rclcpp::Publisher<geometry_msgs::msg::TwistStamped>::SharedPtr
55+
pub_vehicle_vel;
56+
/// Client to trigger the vehicle's E-stop
57+
rclcpp::Client<std_srvs::srv::Trigger>::SharedPtr srv_client_estop_trigger;
58+
/// Client to reset the vehicle's E-stop
59+
rclcpp::Client<std_srvs::srv::Trigger>::SharedPtr srv_client_estop_reset;
60+
/// Client to pause and unpause the servo node
61+
rclcpp::Client<std_srvs::srv::SetBool>::SharedPtr srv_client_pause_servo;
62+
/// Client to move the arm back to home position
63+
rclcpp::Client<alliander_interfaces::srv::StringSrv>::SharedPtr
64+
srv_client_arm_home;
65+
/// Client to trigger the arm's gripper to open
66+
rclcpp_action::Client<TriggerAction>::SharedPtr action_client_gripper_open;
67+
/// Client to trigger the arm's gripper to close
68+
rclcpp_action::Client<TriggerAction>::SharedPtr action_client_gripper_close;
69+
70+
// ROS2 get parameter variables:
71+
/// The arm twist topic
72+
std::string arm_topic;
73+
/// Frame ID of the arm
74+
std::string arm_frame_id;
75+
/// Name of the arm's gripper
76+
std::string arm_gripper_name;
77+
/// Service name with which the arm can be moved back to its home position
78+
std::string arm_home_service;
79+
/// Service name with which the arm's servo node can be paused
80+
std::string arm_pause_servo_service;
81+
/// The vehicle twist topic
82+
std::string vehicle_topic;
83+
/// Service name for resetting the vehicle's E-stop
84+
std::string vehicle_estop_reset_service;
85+
/// Service name for triggering the vehicle's E-stop
86+
std::string vehicle_estop_trigger_service;
87+
88+
// Other variables:
89+
/// Previous input values of the Joy message
90+
sensor_msgs::msg::Joy::SharedPtr prev_joy_input;
91+
/// Current platform mode of the joystick
92+
int current_mode = no_mode;
93+
/// Indication whether the arm is following a trajectory or not
94+
bool arm_busy = false;
95+
/// Indication whether the gripper is busy or not
96+
bool gripper_busy = false;
97+
98+
// constant variables:
99+
/// Arm platform mode code
100+
static constexpr int arm_mode = 0;
101+
/// Vehicle platform mode code
102+
static constexpr int vehicle_mode = 1;
103+
/// No platform mode code
104+
static constexpr int no_mode = 99;
105+
/// Makes response to changes in joystick values less sensitive with a 'dead
106+
/// zone'.
107+
const float dead_axis_zone = 0.2;
108+
/// Scale the arm's speed, max possible value provided by the joystick: 1.0
109+
/// m/s
110+
const float arm_speed_scale = 0.2;
111+
/// Scale the vehicle's speed, max possible value provided by the
112+
/// joystick: 1.0 m/s
113+
const float vehicle_speed_scale = 0.4;
114+
115+
/**
116+
* @brief initialize the joystick manager.
117+
*/
118+
void initialize_joystick_manager();
119+
120+
/**
121+
* @brief callback method that handles the data received from the joystick.
122+
* @param msg joystick sensor message.
123+
*/
124+
void joy_cb(const sensor_msgs::msg::Joy::SharedPtr msg);
125+
126+
/**
127+
* @brief based on the button input, activate corresponding behaviour.
128+
* @param buttons all current button input values as received on the joystick
129+
* topic.
130+
*/
131+
void handle_button_input(const std::vector<int32_t>& buttons);
132+
133+
/**
134+
* @brief matches buttons with the corresponding behaviour as expected for an
135+
* arm platform.
136+
* @param buttons all current button input values as received on the joystick
137+
* topic.
138+
*/
139+
void handle_buttons_arm(const std::vector<int32_t>& buttons);
140+
141+
/**
142+
* @brief matches buttons with the corresponding behaviour as expected for an
143+
* vehicle platform.
144+
* @param buttons all current button input values as received on the joystick
145+
* topic.
146+
*/
147+
void handle_buttons_vehicle(const std::vector<int32_t>& buttons);
148+
149+
/**
150+
* @brief Check whether a button has been pressed or not.
151+
* @param idx desired index value of the button.
152+
* @param curr all current button input values as received on the joystick
153+
* topic.
154+
* @param prev all previous button input values as received on the joystick
155+
* topic.
156+
* @return boolean indicating whether the button is pressed (True) or not
157+
* (False).
158+
*/
159+
bool check_btn_pressed(size_t idx, const std::vector<int32_t>& curr,
160+
const std::vector<int32_t>& prev);
161+
162+
/**
163+
* @brief publish TwistStamped message based on linear and angular value.
164+
* @param linear linear velocity of the vehicle (m/s).
165+
* @param angular angular velocity of the vehicle (m/s).
166+
*/
167+
void handle_driving(const float& linear, const float& angular);
168+
169+
/**
170+
* @brief publish TwistStamped message based on [x, y, z] translation and a
171+
* rotation value.
172+
* @param x velocity of the arm moving forwards/backwards.
173+
* @param y velocity of the arm moving left/right.
174+
* @param z velocity of the arm moving up/down.
175+
* @param rotation velocity of the arm rotating the gripper.
176+
*/
177+
void handle_arm_movement(const float& x, const float& y, const float& z,
178+
const float& rotation);
179+
180+
/**
181+
* @brief send service request to move the arm back to its home position.
182+
*/
183+
void move_arm_to_home();
184+
185+
/**
186+
* @brief (un)pause the servo node via a service request.
187+
* @param pause indication whether the servo node should be paused (true) or
188+
* unpaused (false).
189+
*/
190+
void pause_servo_node(bool pause);
191+
192+
/**
193+
* @brief send a trigger service request with the provided service client.
194+
* @param client service client with which the request will be send.
195+
*/
196+
void send_trigger_request(
197+
const rclcpp::Client<std_srvs::srv::Trigger>::SharedPtr& client);
198+
199+
/**
200+
* @brief send the gripper the request to either open or close and mark the
201+
* gripper as "busy" via a variable value.
202+
* @param client ...
203+
*/
204+
void send_gripper_goal(
205+
const rclcpp_action::Client<TriggerAction>::SharedPtr& client);
206+
207+
/**
208+
* @brief log whether the gripper task was accepted or not. If not, the
209+
* gripper is marked as free again via variable value.
210+
* @param goal_handle variable that contains information about the client's
211+
* goal (status/result).
212+
*/
213+
void gripper_goal_response_callback(
214+
std::shared_ptr<rclcpp_action::ClientGoalHandle<TriggerAction>>
215+
goal_handle);
216+
217+
/**
218+
* @brief log the status of the gripper task.
219+
* @param feedback the feedback received from the action server.
220+
*/
221+
void gripper_feedback_callback(
222+
std::shared_ptr<rclcpp_action::ClientGoalHandle<TriggerAction>>,
223+
const std::shared_ptr<const TriggerAction::Feedback> feedback);
224+
225+
/**
226+
* @brief log the result of the gripper task and mark the gripper as free to
227+
* use again via variable value.
228+
* @param result the result received from the action server.
229+
*/
230+
void gripper_result_callback(
231+
rclcpp_action::ClientGoalHandle<TriggerAction>::WrappedResult& result);
232+
};
233+
234+
#endif // JOYSTICK_MANAGER_HPP_

0 commit comments

Comments
 (0)