Skip to content

Commit a937665

Browse files
authored
Lights auto mode (#610)
* Add twist mux controller * Use source of velocity command for lights system * remove joy subscriber * update docs * add input check * remove unused variable
1 parent a834e18 commit a937665

File tree

12 files changed

+314
-27
lines changed

12 files changed

+314
-27
lines changed

husarion_ugv_lights/CONFIGURATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ Animation of type `husarion_ugv_lights::MovingImageAnimation`, returns frames to
116116
<img alt="MovingImageAnimation" src=".docs/MovingImageAnimation.png" />
117117
</picture>
118118

119-
### Defining Animations
119+
## Defining Animations
120120

121121
Users can define their own LED animations using basic animation types. Similar to basic ones, user animations are parsed using YAML file and loaded on node start. For `ImageAnimation` you can use basic images from the `animations` folder and change their color with the `color` key ([see ImageAnimation](#imageanimation)). Follow the example below to add custom animations.
122122

husarion_ugv_manager/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ add_library(check_joy_msg_bt_node SHARED
5858
src/plugins/condition/check_joy_msg.cpp)
5959
list(APPEND plugin_libs check_joy_msg_bt_node)
6060

61+
add_library(check_string_msg_bt_node SHARED
62+
src/plugins/condition/check_string_msg.cpp)
63+
list(APPEND plugin_libs check_string_msg_bt_node)
64+
6165
add_library(tick_after_timeout_bt_node SHARED
6266
src/plugins/decorator/tick_after_timeout_node.cpp)
6367
list(APPEND plugin_libs tick_after_timeout_bt_node)
@@ -163,6 +167,12 @@ if(BUILD_TESTING)
163167
src/plugins/condition/check_joy_msg.cpp)
164168
list(APPEND plugin_tests ${PROJECT_NAME}_test_check_joy_msg)
165169

170+
ament_add_gtest(
171+
${PROJECT_NAME}_test_check_string_msg
172+
test/plugins/condition/test_check_string_msg.cpp
173+
src/plugins/condition/check_string_msg.cpp)
174+
list(APPEND plugin_tests ${PROJECT_NAME}_test_check_string_msg)
175+
166176
ament_add_gtest(
167177
${PROJECT_NAME}_test_tick_after_timeout_node
168178
test/plugins/decorator/test_tick_after_timeout_node.cpp

husarion_ugv_manager/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Node responsible for managing Bumper Lights animation scheduling.
3131

3232
- `battery/battery_status` [*sensor_msgs/BatteryState*]: State of the internal Battery.
3333
- `hardware/e_stop` [*std_msgs/Bool*]: State of emergency stop.
34+
- `twist_mux_controller/source` [*std_msgs/String]: Source of the command velocity.
3435

3536
#### Service Clients (for Default Trees)
3637

husarion_ugv_manager/behavior_trees/LightsBT.btproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
<input_port name="repeating" default="false">indicates if animation should repeat</input_port>
1010
<input_port name="service_name">ROS service name</input_port>
1111
</Action>
12+
<Condition ID="CheckStringMsg" editable="true">
13+
<input_port name="topic_name">Topic name</input_port>
14+
<input_port name="data">Specifies the expected state of the data field</input_port>
15+
</Condition>
1216
<Decorator ID="TickAfterTimeout" editable="true">
1317
<input_port name="timeout">time in s to wait before ticking child again</input_port>
1418
</Decorator>

husarion_ugv_manager/behavior_trees/lights.xml

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,22 +107,35 @@
107107

108108
<BehaviorTree ID="StateAnimationSubtree">
109109
<Sequence>
110-
<Sequence _skipIf="e_stop_state">
111-
<CallSetLedAnimationService name="SetReadyAnimation"
112-
id="{READY_ANIM_ID}"
113-
param=""
114-
repeating="true"
115-
service_name="lights/set_animation"
116-
_skipIf="drive_state || current_anim_id == READY_ANIM_ID"
117-
_onSuccess="current_anim_id = READY_ANIM_ID"/>
110+
<IfThenElse _skipIf="e_stop_state">
111+
<CheckStringMsg topic_name="twist_mux_controller/source"
112+
data="manual"/>
118113
<CallSetLedAnimationService name="SetManualActionAnimation"
119114
id="{MANUAL_ACTION_ANIM_ID}"
120115
param=""
121116
repeating="true"
122117
service_name="lights/set_animation"
123-
_skipIf="(!drive_state) || current_anim_id == MANUAL_ACTION_ANIM_ID"
118+
_skipIf="current_anim_id == MANUAL_ACTION_ANIM_ID"
124119
_onSuccess="current_anim_id = MANUAL_ACTION_ANIM_ID"/>
125-
</Sequence>
120+
<IfThenElse>
121+
<CheckStringMsg topic_name="twist_mux_controller/source"
122+
data="autonomous"/>
123+
<CallSetLedAnimationService name="SetManualActionAnimation"
124+
id="{AUTONOMOUS_ACTION_ANIM_ID}"
125+
param=""
126+
repeating="true"
127+
service_name="lights/set_animation"
128+
_skipIf="current_anim_id == AUTONOMOUS_ACTION_ANIM_ID"
129+
_onSuccess="current_anim_id = AUTONOMOUS_ACTION_ANIM_ID"/>
130+
<CallSetLedAnimationService name="SetReadyAnimation"
131+
id="{READY_ANIM_ID}"
132+
param=""
133+
repeating="true"
134+
service_name="lights/set_animation"
135+
_skipIf="current_anim_id == READY_ANIM_ID"
136+
_onSuccess="current_anim_id = READY_ANIM_ID"/>
137+
</IfThenElse>
138+
</IfThenElse>
126139
<CallSetLedAnimationService name="SetEStopAnimation"
127140
id="{E_STOP_ANIM_ID}"
128141
param=""
@@ -143,6 +156,11 @@
143156
default="false">indicates if animation should repeat</input_port>
144157
<input_port name="service_name">ROS service name</input_port>
145158
</Action>
159+
<Condition ID="CheckStringMsg"
160+
editable="true">
161+
<input_port name="topic_name">Topic name</input_port>
162+
<input_port name="data">Specifies the expected state of the data field</input_port>
163+
</Condition>
146164
<Decorator ID="TickAfterTimeout"
147165
editable="true">
148166
<input_port name="timeout">time in s to wait before ticking child again</input_port>

husarion_ugv_manager/config/lights_manager.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@
1919
- tick_after_timeout_bt_node
2020
ros_plugin_libs:
2121
- call_set_led_animation_service_bt_node
22+
- check_string_msg_bt_node

husarion_ugv_manager/include/husarion_ugv_manager/lights_manager_node.hpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "rclcpp/rclcpp.hpp"
2424

2525
#include "sensor_msgs/msg/battery_state.hpp"
26-
#include "sensor_msgs/msg/joy.hpp"
2726
#include "std_msgs/msg/bool.hpp"
2827

2928
#include "husarion_ugv_msgs/msg/led_animation.hpp"
@@ -39,7 +38,6 @@ namespace husarion_ugv_manager
3938
using BatteryStateMsg = sensor_msgs::msg::BatteryState;
4039
using BoolMsg = std_msgs::msg::Bool;
4140
using LEDAnimationMsg = husarion_ugv_msgs::msg::LEDAnimation;
42-
using JoyMsg = sensor_msgs::msg::Joy;
4341

4442
/**
4543
* @brief This class is responsible for creating a BehaviorTree responsible for lights management,
@@ -73,19 +71,15 @@ class LightsManagerNode : public rclcpp::Node
7371
private:
7472
void BatteryCB(const BatteryStateMsg::SharedPtr battery);
7573
void EStopCB(const BoolMsg::SharedPtr e_stop);
76-
void JoyCB(const JoyMsg::SharedPtr joy);
7774
void LightsTreeTimerCB();
7875

79-
static constexpr std::size_t kDeadManButtonIndex = 4;
80-
8176
float update_charging_anim_step_;
8277

8378
std::shared_ptr<lights_manager::ParamListener> param_listener_;
8479
lights_manager::Params params_;
8580

8681
rclcpp::Subscription<BatteryStateMsg>::SharedPtr battery_sub_;
8782
rclcpp::Subscription<BoolMsg>::SharedPtr e_stop_sub_;
88-
rclcpp::Subscription<JoyMsg>::SharedPtr joy_sub_;
8983
rclcpp::TimerBase::SharedPtr lights_tree_timer_;
9084

9185
std::unique_ptr<husarion_ugv_utils::MovingAverage<double>> battery_percent_ma_;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 Husarion sp. z o.o.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef HUSARION_UGV_MANAGER_HUSARION_UGV_MANAGER_PLUGINS_CONDITION_CHECK_STRING_MSG_HPP_
16+
#define HUSARION_UGV_MANAGER_HUSARION_UGV_MANAGER_PLUGINS_CONDITION_CHECK_STRING_MSG_HPP_
17+
18+
#include <memory>
19+
#include <string>
20+
21+
#include <behaviortree_ros2/bt_topic_sub_node.hpp>
22+
#include <rclcpp/rclcpp.hpp>
23+
24+
#include <std_msgs/msg/string.hpp>
25+
26+
#include "husarion_ugv_manager/behavior_tree_utils.hpp"
27+
28+
namespace husarion_ugv_manager
29+
{
30+
31+
// FIXME: There is no possibility to set QoS profile. Add it in the future to subscribe e_stop.
32+
class CheckStringMsg : public BT::RosTopicSubNode<std_msgs::msg::String>
33+
{
34+
using StringMsg = std_msgs::msg::String;
35+
36+
public:
37+
CheckStringMsg(
38+
const std::string & name, const BT::NodeConfig & conf, const BT::RosNodeParams & params)
39+
: BT::RosTopicSubNode<StringMsg>(name, conf, params)
40+
{
41+
}
42+
43+
BT::NodeStatus onTick(const StringMsg::SharedPtr & last_msg) override;
44+
45+
bool latchLastMessage() const override;
46+
47+
static BT::PortsList providedPorts()
48+
{
49+
return providedBasicPorts(
50+
{BT::InputPort<std::string>("data", "Specifies the expected state of the data field.")});
51+
}
52+
};
53+
54+
} // namespace husarion_ugv_manager
55+
56+
#endif // HUSARION_UGV_MANAGER_HUSARION_UGV_MANAGER_PLUGINS_CONDITION_CHECK_STRING_MSG_HPP_

husarion_ugv_manager/src/lights_manager_node.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
#include "behaviortree_ros2/ros_node_params.hpp"
2626
#include "rclcpp/rclcpp.hpp"
27-
#include "sensor_msgs/msg/joy.hpp"
2827

2928
#include "husarion_ugv_utils/moving_average.hpp"
3029

@@ -72,8 +71,6 @@ void LightsManagerNode::Initialize()
7271
e_stop_sub_ = this->create_subscription<BoolMsg>(
7372
"hardware/e_stop", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
7473
std::bind(&LightsManagerNode::EStopCB, this, _1));
75-
joy_sub_ = this->create_subscription<JoyMsg>(
76-
"joy", 10, std::bind(&LightsManagerNode::JoyCB, this, _1));
7774

7875
const double timer_freq = this->params_.timer_frequency;
7976
const auto timer_period = std::chrono::duration<double>(1.0 / timer_freq);
@@ -126,7 +123,6 @@ std::map<std::string, std::any> LightsManagerNode::CreateLightsInitialBlackboard
126123
{"current_anim_id", undefined_anim_id},
127124
{"current_battery_anim_id", undefined_anim_id},
128125
{"current_error_anim_id", undefined_anim_id},
129-
{"drive_state", false},
130126
{"CRITICAL_BATTERY_THRESHOLD_PERCENT", critical_battery_threshold_percent},
131127
{"LOW_BATTERY_ANIM_PERIOD", low_battery_anim_period},
132128
{"LOW_BATTERY_THRESHOLD_PERCENT", low_battery_threshold_percent},
@@ -190,12 +186,6 @@ void LightsManagerNode::EStopCB(const BoolMsg::SharedPtr e_stop)
190186
lights_tree_manager_->GetBlackboard()->set<bool>("e_stop_state", e_stop->data);
191187
}
192188

193-
void LightsManagerNode::JoyCB(const JoyMsg::SharedPtr joy)
194-
{
195-
lights_tree_manager_->GetBlackboard()->set<bool>(
196-
"drive_state", joy->buttons[kDeadManButtonIndex]);
197-
}
198-
199189
void LightsManagerNode::LightsTreeTimerCB()
200190
{
201191
if (!SystemReady()) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 Husarion sp. z o.o.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "husarion_ugv_manager/plugins/condition/check_string_msg.hpp"
16+
17+
#include <memory>
18+
#include <string>
19+
20+
#include "husarion_ugv_manager/behavior_tree_utils.hpp"
21+
22+
namespace husarion_ugv_manager
23+
{
24+
25+
BT::NodeStatus CheckStringMsg::onTick(const StringMsg::SharedPtr & last_msg)
26+
{
27+
std::string expected_data;
28+
if (!getInput<std::string>("data", expected_data)) {
29+
RCLCPP_ERROR_STREAM(this->logger(), GetLoggerPrefix(name()) << "Failed to get input [data]");
30+
return BT::NodeStatus::FAILURE;
31+
}
32+
33+
return (last_msg && last_msg->data == expected_data) ? BT::NodeStatus::SUCCESS
34+
: BT::NodeStatus::FAILURE;
35+
}
36+
37+
bool CheckStringMsg::latchLastMessage() const { return true; }
38+
39+
} // namespace husarion_ugv_manager
40+
41+
#include "behaviortree_ros2/plugins.hpp"
42+
CreateRosNodePlugin(husarion_ugv_manager::CheckStringMsg, "CheckStringMsg");

0 commit comments

Comments
 (0)