Skip to content

Commit 3830bbb

Browse files
author
Marco A. Gutierrez
authored
Adding battery consumers and extra fixes (#1811)
Signed-off-by: Marco A. Gutierrez <marco@openrobotics.org>
1 parent f8b3eda commit 3830bbb

File tree

8 files changed

+802
-54
lines changed

8 files changed

+802
-54
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (C) 2022 Open Source Robotics Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
#ifndef IGNITION_GAZEBO_COMPONENTS_BATTERYPOWERLOAD_HH_
18+
#define IGNITION_GAZEBO_COMPONENTS_BATTERYCONSUMPTION_HH_
19+
20+
#include <ignition/gazebo/components/Factory.hh>
21+
#include <ignition/gazebo/components/Component.hh>
22+
#include <ignition/gazebo/config.hh>
23+
24+
namespace ignition
25+
{
26+
namespace gazebo
27+
{
28+
// Inline bracket to help doxygen filtering.
29+
inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
30+
namespace components
31+
{
32+
/// \brief Data structure to hold the consumer power load
33+
/// and the name of the battery it uses.
34+
struct BatteryPowerLoadInfo
35+
{
36+
/// \brief Entity of the battery to use.
37+
Entity batteryId;
38+
/// \brief Battery power load (W) to add to the battery.
39+
double batteryPowerLoad;
40+
};
41+
42+
/// \brief A component that indicates the total consumption of a battery.
43+
using BatteryPowerLoad =
44+
Component<BatteryPowerLoadInfo, class BatteryPowerLoadTag>;
45+
IGN_GAZEBO_REGISTER_COMPONENT("ign_gazebo_components.BatteryPowerLoad",
46+
BatteryPowerLoad)
47+
}
48+
}
49+
}
50+
}
51+
52+
#endif

src/systems/battery_plugin/LinearBatteryPlugin.cc

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <sdf/Root.hh>
4040
#include <sdf/World.hh>
4141

42+
#include "ignition/gazebo/components/BatteryPowerLoad.hh"
4243
#include "ignition/gazebo/components/BatterySoC.hh"
4344
#include "ignition/gazebo/components/Joint.hh"
4445
#include "ignition/gazebo/components/JointForceCmd.hh"
@@ -78,9 +79,21 @@ class ignition::gazebo::systems::LinearBatteryPluginPrivate
7879
const char *_data, const size_t _size,
7980
const ignition::transport::MessageInfo &_info);
8081

82+
/// \brief Callback connected to additional topics that can stop battery
83+
/// draining.
84+
/// \param[in] _data Message data.
85+
/// \param[in] _size Message data size.
86+
/// \param[in] _info Information about the message.
87+
public: void OnBatteryStopDrainingMsg(
88+
const char *_data, const size_t _size,
89+
const ignition::transport::MessageInfo &_info);
90+
8191
/// \brief Name of model, only used for printing warning when battery drains.
8292
public: std::string modelName;
8393

94+
/// \brief Name that identifies a battery.
95+
public: std::string batteryName;
96+
8497
/// \brief Pointer to battery contained in link.
8598
public: common::BatteryPtr battery;
8699

@@ -145,7 +158,7 @@ class ignition::gazebo::systems::LinearBatteryPluginPrivate
145158
public: std::chrono::steady_clock::duration stepSize;
146159

147160
/// \brief Flag on whether the battery should start draining
148-
public: bool startDraining = true;
161+
public: bool startDraining = false;
149162

150163
/// \brief The start time when battery starts draining in seconds
151164
public: int drainStartTime = -1;
@@ -163,8 +176,8 @@ class ignition::gazebo::systems::LinearBatteryPluginPrivate
163176
/// \brief Battery state of charge message publisher
164177
public: transport::Node::Publisher statePub;
165178

166-
/// \brief Whether a topic has received any battery-draining command.
167-
public: bool startDrainingFromTopics = false;
179+
/// \brief Initial power load set trough config
180+
public: double initialPowerLoad = 0.0;
168181
};
169182

170183
/////////////////////////////////////////////////
@@ -262,21 +275,18 @@ void LinearBatteryPlugin::Configure(const Entity &_entity,
262275

263276
if (_sdf->HasElement("battery_name") && _sdf->HasElement("voltage"))
264277
{
265-
auto batteryName = _sdf->Get<std::string>("battery_name");
278+
this->dataPtr->batteryName = _sdf->Get<std::string>("battery_name");
266279
auto initVoltage = _sdf->Get<double>("voltage");
267280

268-
// Create battery entity and component
281+
// Create battery entity and some components
269282
this->dataPtr->batteryEntity = _ecm.CreateEntity();
270-
// Initialize with initial voltage
271-
_ecm.CreateComponent(this->dataPtr->batteryEntity,
272-
components::BatterySoC(this->dataPtr->soc));
273283
_ecm.CreateComponent(this->dataPtr->batteryEntity, components::Name(
274-
batteryName));
284+
this->dataPtr->batteryName));
275285
_ecm.SetParentEntity(this->dataPtr->batteryEntity, _entity);
276286

277287
// Create actual battery and assign update function
278-
this->dataPtr->battery = std::make_shared<common::Battery>(batteryName,
279-
initVoltage);
288+
this->dataPtr->battery = std::make_shared<common::Battery>(
289+
this->dataPtr->batteryName, initVoltage);
280290
this->dataPtr->battery->Init();
281291
this->dataPtr->battery->SetUpdateFunc(
282292
std::bind(&LinearBatteryPlugin::OnUpdateVoltage, this,
@@ -339,10 +349,10 @@ void LinearBatteryPlugin::Configure(const Entity &_entity,
339349
// Consumer-specific
340350
if (_sdf->HasElement("power_load"))
341351
{
342-
auto powerLoad = _sdf->Get<double>("power_load");
352+
this->dataPtr->initialPowerLoad = _sdf->Get<double>("power_load");
343353
this->dataPtr->consumerId = this->dataPtr->battery->AddConsumer();
344354
bool success = this->dataPtr->battery->SetPowerLoad(
345-
this->dataPtr->consumerId, powerLoad);
355+
this->dataPtr->consumerId, this->dataPtr->initialPowerLoad);
346356
if (!success)
347357
ignerr << "Failed to set consumer power load." << std::endl;
348358
}
@@ -352,6 +362,9 @@ void LinearBatteryPlugin::Configure(const Entity &_entity,
352362
<< "in LinearBatteryPlugin SDF" << std::endl;
353363
}
354364

365+
if (_sdf->HasElement("start_draining"))
366+
this->dataPtr->startDraining = _sdf->Get<bool>("start_draining");
367+
355368
// Subscribe to power draining topics, if any.
356369
if (_sdf->HasElement("power_draining_topic"))
357370
{
@@ -369,12 +382,33 @@ void LinearBatteryPlugin::Configure(const Entity &_entity,
369382
}
370383
}
371384

385+
// Subscribe to stop power draining topics, if any.
386+
if (_sdf->HasElement("stop_power_draining_topic"))
387+
{
388+
sdf::ElementConstPtr sdfElem =
389+
_sdf->FindElement("stop_power_draining_topic");
390+
while (sdfElem)
391+
{
392+
const auto &topic = sdfElem->Get<std::string>();
393+
this->dataPtr->node.SubscribeRaw(topic,
394+
std::bind(&LinearBatteryPluginPrivate::OnBatteryStopDrainingMsg,
395+
this->dataPtr.get(), std::placeholders::_1, std::placeholders::_2,
396+
std::placeholders::_3));
397+
ignmsg << "LinearBatteryPlugin subscribes to stop power draining topic ["
398+
<< topic << "]." << std::endl;
399+
sdfElem = sdfElem->GetNextElement("power_draining_topic");
400+
}
401+
}
402+
372403
ignmsg << "LinearBatteryPlugin configured. Battery name: "
373404
<< this->dataPtr->battery->Name() << std::endl;
374405
igndbg << "Battery initial voltage: " << this->dataPtr->battery->InitVoltage()
375406
<< std::endl;
376407

377408
this->dataPtr->soc = this->dataPtr->q / this->dataPtr->c;
409+
// Initialize battery with initial calculated state of charge
410+
_ecm.CreateComponent(this->dataPtr->batteryEntity,
411+
components::BatterySoC(this->dataPtr->soc));
378412

379413
// Setup battery state topic
380414
std::string stateTopic{"/model/" + this->dataPtr->model.Name(_ecm) +
@@ -400,7 +434,7 @@ void LinearBatteryPluginPrivate::Reset()
400434
this->iraw = 0.0;
401435
this->ismooth = 0.0;
402436
this->q = this->q0;
403-
this->startDrainingFromTopics = false;
437+
this->startDraining = false;
404438
}
405439

406440
/////////////////////////////////////////////////
@@ -429,7 +463,14 @@ void LinearBatteryPluginPrivate::OnDisableRecharge(
429463
void LinearBatteryPluginPrivate::OnBatteryDrainingMsg(
430464
const char *, const size_t, const ignition::transport::MessageInfo &)
431465
{
432-
this->startDrainingFromTopics = true;
466+
this->startDraining = true;
467+
}
468+
469+
//////////////////////////////////////////////////
470+
void LinearBatteryPluginPrivate::OnBatteryStopDrainingMsg(
471+
const char *, const size_t, const ignition::transport::MessageInfo &)
472+
{
473+
this->startDraining = false;
433474
}
434475

435476
//////////////////////////////////////////////////
@@ -439,10 +480,26 @@ void LinearBatteryPlugin::PreUpdate(
439480
{
440481
IGN_PROFILE("LinearBatteryPlugin::PreUpdate");
441482

442-
// \todo(anyone) Add in the ability to stop the battery from draining
443-
// after it has been started by a topic. See this comment:
444-
// https://github.com/ignitionrobotics/ign-gazebo/pull/1255#discussion_r770223092
445-
this->dataPtr->startDraining = this->dataPtr->startDrainingFromTopics;
483+
// Recalculate the total power load among consumers
484+
double total_power_load = this->dataPtr->initialPowerLoad;
485+
_ecm.Each<components::BatteryPowerLoad>(
486+
[&](const Entity & /*_entity*/,
487+
const components::BatteryPowerLoad *_batteryPowerLoadInfo)->bool
488+
{
489+
if (_batteryPowerLoadInfo->Data().batteryId ==
490+
this->dataPtr->batteryEntity)
491+
{
492+
total_power_load = total_power_load +
493+
_batteryPowerLoadInfo->Data().batteryPowerLoad;
494+
}
495+
return true;
496+
});
497+
498+
bool success = this->dataPtr->battery->SetPowerLoad(
499+
this->dataPtr->consumerId, total_power_load);
500+
if (!success)
501+
ignerr << "Failed to set consumer power load." << std::endl;
502+
446503
// Start draining the battery if the robot has started moving
447504
if (!this->dataPtr->startDraining)
448505
{

src/systems/battery_plugin/LinearBatteryPlugin.hh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,17 @@ namespace systems
5858
/// (Required if `<enable_recharge>` is set to true)
5959
/// - `<fix_issue_225>` True to change the battery behavior to fix some issues
6060
/// described in https://github.com/gazebosim/gz-sim/issues/225.
61+
/// - `<start_drainign>` Whether to start draining the battery right away.
62+
/// False by default.
6163
/// - `<power_draining_topic>` A topic that is used to start battery
62-
/// discharge. Any message on the specified topic will cause the batter to
64+
/// discharge. Any message on the specified topic will cause the battery to
6365
/// start draining. This element can be specified multiple times if
6466
/// multiple topics should be monitored. Note that this mechanism will
6567
/// start the battery draining, and once started will keep drainig.
68+
/// - `<stop_power_draining_topic>` A topic that is used to stop battery
69+
/// discharge. Any message on the specified topic will cause the battery to
70+
/// stop draining.
71+
6672
class LinearBatteryPlugin
6773
: public System,
6874
public ISystemConfigure,

src/systems/thruster/Thruster.cc

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929

3030
#include "ignition/gazebo/components/AngularVelocity.hh"
3131
#include "ignition/gazebo/components/BatterySoC.hh"
32+
#include "ignition/gazebo/components/BatteryPowerLoad.hh"
3233
#include "ignition/gazebo/components/ChildLinkName.hh"
3334
#include "ignition/gazebo/components/JointAxis.hh"
3435
#include "ignition/gazebo/components/JointVelocityCmd.hh"
3536
#include "ignition/gazebo/components/LinearVelocity.hh"
37+
#include "ignition/gazebo/components/Name.hh"
3638
#include "ignition/gazebo/components/Pose.hh"
3739
#include "ignition/gazebo/components/World.hh"
3840
#include "ignition/gazebo/Link.hh"
@@ -65,6 +67,9 @@ class ignition::gazebo::systems::ThrusterPrivateData
6567
/// \brief The link entity which will spin
6668
public: ignition::gazebo::Entity linkEntity;
6769

70+
/// \brief Battery consumer entity
71+
public: Entity consumerEntity;
72+
6873
/// \brief Axis along which the propeller spins. Expressed in the joint
6974
/// frame. Addume this doesn't change during simulation.
7075
public: ignition::math::Vector3d jointAxis;
@@ -126,6 +131,15 @@ class ignition::gazebo::systems::ThrusterPrivateData
126131
/// \brief Topic name used to control thrust.
127132
public: std::string topic = "";
128133

134+
/// \brief Battery entity used by the thruster to consume power.
135+
public: std::string batteryName = "";
136+
137+
/// \brief Battery power load of the thruster.
138+
public: double powerLoad = 0.0;
139+
140+
/// \brief Has the battery consumption being initialized.
141+
public: bool batteryInitialized = false;
142+
129143
/// \brief Callback for handling thrust update
130144
public: void OnCmdThrust(const msgs::Double &_msg);
131145

@@ -363,6 +377,22 @@ void Thruster::Configure(
363377
{
364378
igndbg << "Using velocity control for propeller joint." << std::endl;
365379
}
380+
381+
// Get power load and battery name info
382+
if (_sdf->HasElement("power_load"))
383+
{
384+
if (!_sdf->HasElement("battery_name"))
385+
{
386+
ignerr << "Specified a <power_load> but missing <battery_name>."
387+
"Specify a battery name so the power load can be assigned to it."
388+
<< std::endl;
389+
}
390+
else
391+
{
392+
this->dataPtr->powerLoad = _sdf->Get<double>("power_load");
393+
this->dataPtr->batteryName = _sdf->Get<std::string>("battery_name");
394+
}
395+
}
366396
}
367397

368398
/////////////////////////////////////////////////
@@ -443,6 +473,52 @@ void Thruster::PreUpdate(
443473
return;
444474
}
445475

476+
// Intit battery consumption if it was set
477+
if (!this->dataPtr->batteryName.empty() &&
478+
!this->dataPtr->batteryInitialized)
479+
{
480+
this->dataPtr->batteryInitialized = true;
481+
482+
// Check that a battery exists with the specified name
483+
Entity batteryEntity;
484+
int numBatteriesWithName = 0;
485+
_ecm.Each<components::BatterySoC, components::Name>(
486+
[&](const Entity &_entity,
487+
const components::BatterySoC */*_BatterySoC*/,
488+
const components::Name *_name)->bool
489+
{
490+
if (this->dataPtr->batteryName == _name->Data())
491+
{
492+
++numBatteriesWithName;
493+
batteryEntity = _entity;
494+
}
495+
return true;
496+
});
497+
if (numBatteriesWithName == 0)
498+
{
499+
ignerr << "Can't assign battery consumption to battery: ["
500+
<< this->dataPtr->batteryName << "]. No batteries"
501+
"were found with the given name." << std::endl;
502+
return;
503+
}
504+
if (numBatteriesWithName > 1)
505+
{
506+
ignerr << "More than one battery found with name: ["
507+
<< this->dataPtr->batteryName << "]. Please make"
508+
"sure battery names are unique within the system."
509+
<< std::endl;
510+
return;
511+
}
512+
513+
// Create the battery consumer entity and its component
514+
this->dataPtr->consumerEntity = _ecm.CreateEntity();
515+
components::BatteryPowerLoadInfo batteryPowerLoadInfo{
516+
batteryEntity, this->dataPtr->powerLoad};
517+
_ecm.CreateComponent(this->dataPtr->consumerEntity,
518+
components::BatteryPowerLoad(batteryPowerLoadInfo));
519+
_ecm.SetParentEntity(this->dataPtr->consumerEntity, batteryEntity);
520+
}
521+
446522
ignition::gazebo::Link link(this->dataPtr->linkEntity);
447523

448524
auto pose = worldPose(this->dataPtr->linkEntity, _ecm);

0 commit comments

Comments
 (0)