diff --git a/clover_description/urdf/leds/led_strip.xacro b/clover_description/urdf/leds/led_strip.xacro
index 6734cb68..f7c0fb36 100644
--- a/clover_description/urdf/leds/led_strip.xacro
+++ b/clover_description/urdf/leds/led_strip.xacro
@@ -64,6 +64,12 @@
+
+
+
+ ${led_count}
+
+
diff --git a/clover_simulation/CMakeLists.txt b/clover_simulation/CMakeLists.txt
index a4a6a2b0..3a575800 100644
--- a/clover_simulation/CMakeLists.txt
+++ b/clover_simulation/CMakeLists.txt
@@ -37,6 +37,14 @@ target_compile_options(sim_leds PRIVATE -std=c++11)
add_dependencies(sim_leds ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+add_library(sim_leds_controller src/sim_leds_controller.cpp)
+
+target_include_directories(sim_leds_controller PRIVATE ${catkin_INCLUDE_DIRS} ${GAZEBO_INCLUDE_DIRS})
+target_link_libraries(sim_leds_controller PRIVATE ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES})
+target_compile_options(sim_leds_controller PRIVATE -std=c++11)
+
+add_dependencies(sim_leds_controller ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+
# Gazebo throttling camera plugin
# for some reason, CMake does not support per-target link directories, and Gazebo does not put
# CameraPlugin into ${GAZEBO_LIBRARIES}
diff --git a/clover_simulation/src/sim_leds.cpp b/clover_simulation/src/sim_leds.cpp
index e2d0b36f..fb188af7 100644
--- a/clover_simulation/src/sim_leds.cpp
+++ b/clover_simulation/src/sim_leds.cpp
@@ -49,14 +49,9 @@ private:
std::unique_ptr nh;
- ros::ServiceServer setLedsSrv;
- // Note: LED state should only be published by the /gazebo node
- led_msgs::LEDStateArray ledState;
- ros::Publisher statePublisher;
// LED state will be read from the topic to avoid creating more services
ros::Subscriber stateSubscriber;
- bool setLeds(led_msgs::SetLEDs::Request& req, led_msgs::SetLEDs::Response& resp);
void handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds);
public:
@@ -74,8 +69,6 @@ public:
nh.reset(new ros::NodeHandle(robotNamespace));
- setLedsSrv = nh->advertiseService("led/set_leds", &LedController::setLeds, this); // the newer service server would overwrite the previous one
- statePublisher = nh->advertise("led/state", 1, true);
stateSubscriber = nh->subscribe("led/state", 1, &LedController::handleLedsMsg, this);
};
@@ -90,12 +83,9 @@ public:
std::lock_guard lock(registryMutex);
if (totalLeds > 0) {
registeredLeds.resize(totalLeds);
- ledState.leds.resize(totalLeds);
}
ROS_DEBUG_NAMED(("LedController_" + robotNamespace).c_str(), "Registering LED visual plugin to %s (LED id=%d)", (role == Role::Client) ? "client" : "server", ledIdx);
registeredLeds[ledIdx] = plugin;
- ledState.leds[ledIdx].index = ledIdx;
- statePublisher.publish(ledState);
}
void unregisterPlugin(sim_led::LedVisualPlugin* plugin)
@@ -150,7 +140,8 @@ public:
{
auto indexStr = parentName.substr(lastDashPos + 1);
try {
- myIndex = std::stoi(indexStr);
+ if (indexStr == "visual") myIndex = 0; // the first visual doesn't have index
+ else myIndex = std::stoi(indexStr);
} catch(const std::exception &e) {
gzwarn << "Failed to convert " << indexStr << " to integer: " << e.what() << ", assuming 0\n";
myIndex = 0;
@@ -188,26 +179,6 @@ public:
};
}
-// FIXME: These two functions basically do the same thing, maybe they can be merged?
-bool led_controller::LedController::setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp)
-{
- std::lock_guard lock(registryMutex);
- for(const auto& led : req.leds)
- {
- if (led.index < registeredLeds.size()) {
- auto color = GazeboColor(led.r / 255.0f, led.g / 255.0f, led.b / 255.0f);
- auto ledPlugin = registeredLeds[led.index];
- if (ledPlugin) ledPlugin->SetColor(color);
- ledState.leds[led.index].r = led.r;
- ledState.leds[led.index].g = led.g;
- ledState.leds[led.index].b = led.b;
- }
- }
- statePublisher.publish(ledState);
- resp.success = true;
- return true;
-}
-
void led_controller::LedController::handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds)
{
std::lock_guard lock(registryMutex);
diff --git a/clover_simulation/src/sim_leds_controller.cpp b/clover_simulation/src/sim_leds_controller.cpp
new file mode 100644
index 00000000..90a9799f
--- /dev/null
+++ b/clover_simulation/src/sim_leds_controller.cpp
@@ -0,0 +1,71 @@
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+
+class LedControllerPlugin : public gazebo::ModelPlugin {
+private:
+ std::unique_ptr nh;
+ std::string ns;
+ ros::ServiceServer setLedsSrv;
+ led_msgs::LEDStateArray ledState;
+ ros::Publisher statePublisher;
+ std::mutex handleMutex;
+
+public:
+ bool setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp)
+ {
+ std::lock_guard lock(handleMutex);
+ for(const auto& led : req.leds)
+ {
+ if (led.index < ledState.leds.size()) {
+ ledState.leds[led.index].r = led.r;
+ ledState.leds[led.index].g = led.g;
+ ledState.leds[led.index].b = led.b;
+ }
+ }
+ statePublisher.publish(ledState);
+ resp.success = true;
+ return true;
+ }
+
+ virtual void Load(gazebo::physics::ModelPtr model, sdf::ElementPtr sdf) override
+ {
+ ROS_INFO("Initialize LED Controller");
+
+ // We need "libgazebo_ros_api.so" to be loaded
+ if (!ros::isInitialized())
+ {
+ ROS_FATAL_NAMED("LedController", "Tried to load ROS plugin when ROS Gazebo API is not loaded. Please use gazebo_ros node to"
+ "launch Gazebo.");
+ }
+
+ ns = "";
+
+ if (sdf->HasElement("robotNamespace")) {
+ ns = sdf->Get("robotNamespace");
+ }
+ if (!sdf->HasElement("ledCount")) {
+ gzerr << "ledCount is not set, but is required for the plugin to function correctly\n";
+ return;
+ }
+ int totalLeds = sdf->Get("ledCount");
+ ledState.leds.resize(totalLeds);
+ for (int i = 0; i < totalLeds; i++) {
+ ledState.leds[i].index = i;
+ }
+
+ nh.reset(new ros::NodeHandle(ns));
+
+ setLedsSrv = nh->advertiseService("led/set_leds", &LedControllerPlugin::setLeds, this);
+ statePublisher = nh->advertise("led/state", 1, true);
+
+ statePublisher.publish(ledState);
+ }
+};
+
+GZ_REGISTER_MODEL_PLUGIN(LedControllerPlugin);