mirror of
https://github.com/Fishwaldo/Fuzzylite.git
synced 2025-03-15 19:31:36 +00:00
add timer support so we can specify when a value should change
This commit is contained in:
parent
4b14919d4c
commit
0d621f4fa8
8 changed files with 130 additions and 10 deletions
|
@ -43,11 +43,13 @@ namespace fl {
|
|||
|
||||
virtual std::vector<Proposition*> conclusions() const;
|
||||
|
||||
virtual void load(const std::string& consequent, const Engine* engine);
|
||||
virtual void load(const std::string& consequent, const Engine* engine, int timer = 0);
|
||||
|
||||
virtual void modify(scalar strength, const TNorm* activation);
|
||||
|
||||
virtual std::string toString() const;
|
||||
private:
|
||||
int m_timer;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace fl {
|
|||
Proposition();
|
||||
|
||||
std::string toString() const;
|
||||
int timer;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -71,6 +71,8 @@ namespace fl {
|
|||
static std::string FL_AND;
|
||||
static std::string FL_OR;
|
||||
static std::string FL_WITH;
|
||||
static std::string FL_SET;
|
||||
static std::string FL_IN;
|
||||
|
||||
static std::string ifKeyword();
|
||||
static std::string isKeyword();
|
||||
|
|
|
@ -39,7 +39,8 @@ namespace fl {
|
|||
scalar _lastValidOutput;
|
||||
bool _lockOutputRange;
|
||||
bool _lockValidOutput;
|
||||
|
||||
int timer;
|
||||
|
||||
public:
|
||||
OutputVariable(const std::string& name = "",
|
||||
scalar minimum = -fl::inf, scalar maximum = fl::inf);
|
||||
|
@ -69,6 +70,9 @@ namespace fl {
|
|||
virtual scalar defuzzifyNoLocks() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
|
||||
virtual int getTimer() const;
|
||||
virtual void setTimer(int time);
|
||||
|
||||
};
|
||||
|
||||
|
|
61
src/main.cpp
61
src/main.cpp
|
@ -208,7 +208,66 @@ int main(int argc, char** argv) {
|
|||
// fuzzylite::setDecimals(8);
|
||||
// exportAllExamples("fis", "fld");
|
||||
// return 0;
|
||||
return Console::main(argc, argv);
|
||||
// return Console::main(argc, argv);
|
||||
fl::Engine* engine = new fl::Engine("simple-dimmer");
|
||||
|
||||
fl::InputVariable* ambient = new fl::InputVariable;
|
||||
ambient->setName("Ambient");
|
||||
ambient->setRange(0.000, 1.000);
|
||||
ambient->addTerm(new fl::Triangle("DARK", 0.000, 0.500));
|
||||
ambient->addTerm(new fl::Triangle("MEDIUM", 0.250, 0.750));
|
||||
ambient->addTerm(new fl::Triangle("BRIGHT", 0.500, 1.000));
|
||||
engine->addInputVariable(ambient);
|
||||
|
||||
fl::OutputVariable* power = new fl::OutputVariable;
|
||||
power->setName("Power");
|
||||
power->setRange(0.000, 2.000);
|
||||
power->setDefaultValue(fl::nan);
|
||||
power->addTerm(new fl::Triangle("LOW", 0.000, 1.000));
|
||||
power->addTerm(new fl::Triangle("MEDIUM", 0.500, 1.500));
|
||||
power->addTerm(new fl::Triangle("HIGH", 1.000, 2.000));
|
||||
engine->addOutputVariable(power);
|
||||
|
||||
|
||||
fl::OutputVariable* power1 = new fl::OutputVariable;
|
||||
power1->setName("Power1");
|
||||
power1->setRange(0.000, 2.000);
|
||||
power1->setDefaultValue(fl::nan);
|
||||
power1->addTerm(new fl::Triangle("LOW", 0.000, 1.000));
|
||||
power1->addTerm(new fl::Triangle("MEDIUM", 0.500, 1.500));
|
||||
power1->addTerm(new fl::Triangle("HIGH", 1.000, 2.000));
|
||||
engine->addOutputVariable(power1);
|
||||
|
||||
fl::RuleBlock* ruleblock = new fl::RuleBlock;
|
||||
ruleblock->addRule(fl::Rule::parse("if Ambient is DARK then Power is HIGH", engine));
|
||||
ruleblock->addRule(fl::Rule::parse("if Ambient is MEDIUM then Power is MEDIUM", engine));
|
||||
ruleblock->addRule(fl::Rule::parse("if Ambient is BRIGHT then in 5m set Power is LOW", engine));
|
||||
ruleblock->addRule(fl::Rule::parse("if Ambient is BRIGHT then in 533m set Power is LOW and Power1 is HIGH", engine));
|
||||
//# ruleblock->addRule(fl::Rule::parse("if Ambient is BRIGHT then in 5m Power is LOW", engine));
|
||||
engine->addRuleBlock(ruleblock);
|
||||
|
||||
//No Conjunction or Disjunction is needed
|
||||
engine->configure("", "", "AlgebraicProduct", "AlgebraicSum", "Centroid");
|
||||
|
||||
std::string status;
|
||||
if (not engine->isReady(&status))
|
||||
throw fl::Exception("Engine not ready. "
|
||||
"The following errors were encountered:\n" + status, FL_AT);
|
||||
|
||||
for (int i = 0; i < 50; ++i){
|
||||
fl::scalar light = ambient->getMinimum() + i * (ambient->range() / 50);
|
||||
ambient->setInputValue(light);
|
||||
engine->process();
|
||||
FL_LOG("Ambient.input = " << fl::Op::str(light) << " -> " <<
|
||||
"Power.output = " << fl::Op::str(power->defuzzify()) <<
|
||||
" Timer: " << power->getTimer() <<
|
||||
" Power1.output = " << fl::Op::str(power1->defuzzify()) <<
|
||||
" Timer: " << power1->getTimer());
|
||||
}
|
||||
std::cout << engine->toString() << std::endl;
|
||||
|
||||
|
||||
|
||||
} catch (fl::Exception& e) {
|
||||
FL_LOG(e.what());
|
||||
FL_LOG(e.btCallStack());
|
||||
|
|
|
@ -70,12 +70,13 @@ namespace fl {
|
|||
term->setThreshold(threshold);
|
||||
term->setActivation(activation);
|
||||
OutputVariable* outputVariable = dynamic_cast<OutputVariable*> (proposition->variable);
|
||||
outputVariable->setTimer(m_timer);
|
||||
outputVariable->fuzzyOutput()->addTerm(term);
|
||||
FL_DBG("Accumulating " << term->toString());
|
||||
}
|
||||
}
|
||||
|
||||
void Consequent::load(const std::string& consequent, const Engine* engine) {
|
||||
void Consequent::load(const std::string& consequent, const Engine* engine, int timer) {
|
||||
|
||||
/**
|
||||
Extracts the list of propositions from the consequent
|
||||
|
@ -94,7 +95,7 @@ namespace fl {
|
|||
int state = S_VARIABLE;
|
||||
|
||||
_conclusions.clear();
|
||||
|
||||
m_timer = timer;
|
||||
Proposition* proposition = NULL;
|
||||
|
||||
std::stringstream tokenizer(consequent);
|
||||
|
@ -103,6 +104,7 @@ namespace fl {
|
|||
if (state bitand S_VARIABLE) {
|
||||
if (engine->hasOutputVariable(token)) {
|
||||
proposition = new Proposition;
|
||||
proposition->timer = timer;
|
||||
proposition->variable = engine->getOutputVariable(token);
|
||||
_conclusions.push_back(proposition);
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ namespace fl {
|
|||
std::string Rule::FL_AND = "and";
|
||||
std::string Rule::FL_OR = "or";
|
||||
std::string Rule::FL_WITH = "with";
|
||||
std::string Rule::FL_IN = "in";
|
||||
std::string Rule::FL_SET = "set";
|
||||
|
||||
Rule::Rule()
|
||||
: _weight(1.0), _antecedent(NULL), _consequent(NULL) {
|
||||
|
@ -136,15 +138,15 @@ namespace fl {
|
|||
result->setText(rule);
|
||||
std::istringstream tokenizer(rule);
|
||||
std::string token;
|
||||
std::ostringstream ossAntecedent, ossConsequent;
|
||||
std::ostringstream ossAntecedent, ossConsequent, ossTimer;
|
||||
int timer = 0;
|
||||
|
||||
enum FSM {
|
||||
S_NONE, S_IF, S_THEN, S_WITH, S_END
|
||||
S_NONE, S_IF, S_THEN, S_TIME, S_WITH, S_END
|
||||
};
|
||||
FSM state = S_NONE;
|
||||
try {
|
||||
while (tokenizer >> token) {
|
||||
|
||||
switch (state) {
|
||||
case S_NONE:
|
||||
if (token == Rule::FL_IF) state = S_IF;
|
||||
|
@ -161,8 +163,13 @@ namespace fl {
|
|||
break;
|
||||
case S_THEN:
|
||||
if (token == Rule::FL_WITH) state = S_WITH;
|
||||
else if (token == Rule::FL_IN) state = S_TIME;
|
||||
else ossConsequent << token << " ";
|
||||
break;
|
||||
case S_TIME:
|
||||
if (token == Rule::FL_SET) state = S_THEN;
|
||||
else ossTimer << token << " ";
|
||||
break;
|
||||
case S_WITH:
|
||||
try {
|
||||
result->setWeight(fl::Op::toScalar(token));
|
||||
|
@ -193,13 +200,44 @@ namespace fl {
|
|||
std::ostringstream ex;
|
||||
ex << "[syntax error] expected a numeric value as the weight of the rule: " << rule;
|
||||
throw fl::Exception(ex.str(), FL_AT);
|
||||
} else if (state == S_TIME) {
|
||||
std::ostringstream ex;
|
||||
ex << "[syntax error] expected a timername/value in the rule: " << rule;
|
||||
throw fl::Exception(ex.str(), FL_AT);
|
||||
}
|
||||
|
||||
/* parse the Timer Value if it exists */
|
||||
if (ossTimer.str().length() > 0) {
|
||||
token = ossTimer.str();
|
||||
int multiplier = 0;
|
||||
if (isdigit(token[0])) {
|
||||
std::stringstream(token) >> timer;
|
||||
}
|
||||
for (std::string::iterator it = token.begin(); it != token.end(); ++it) {
|
||||
switch (*it) {
|
||||
case 's':
|
||||
multiplier = 0;
|
||||
break;
|
||||
case 'm':
|
||||
multiplier = 60;
|
||||
break;
|
||||
case 'h':
|
||||
multiplier = 3600;
|
||||
break;
|
||||
case 'd':
|
||||
multiplier = 86400;
|
||||
break;
|
||||
}
|
||||
}
|
||||
timer = timer * multiplier;
|
||||
}
|
||||
|
||||
|
||||
result->_antecedent = new Antecedent;
|
||||
result->_antecedent->load(ossAntecedent.str(), engine);
|
||||
|
||||
result->_consequent = new Consequent;
|
||||
result->_consequent->load(ossConsequent.str(), engine);
|
||||
result->_consequent->load(ossConsequent.str(), engine, timer);
|
||||
} catch (fl::Exception& ex) {
|
||||
delete result;
|
||||
throw ex;
|
||||
|
|
|
@ -37,7 +37,8 @@ namespace fl {
|
|||
_defuzzifier(NULL), _defaultValue(fl::nan),
|
||||
_lastValidOutput(fl::nan),
|
||||
_lockOutputRange(false),
|
||||
_lockValidOutput(false) {
|
||||
_lockValidOutput(false),
|
||||
timer(0) {
|
||||
}
|
||||
|
||||
OutputVariable::~OutputVariable() {
|
||||
|
@ -98,6 +99,17 @@ namespace fl {
|
|||
bool OutputVariable::isLockingValidOutput() const {
|
||||
return this->_lockValidOutput;
|
||||
}
|
||||
void OutputVariable::setTimer(int time) {
|
||||
if (time == 0)
|
||||
return;
|
||||
if ((this->timer == 0) || (this->timer > time)) {
|
||||
this->timer = time;
|
||||
}
|
||||
|
||||
}
|
||||
int OutputVariable::getTimer() const {
|
||||
return this->timer;
|
||||
}
|
||||
|
||||
scalar OutputVariable::defuzzify() {
|
||||
scalar result = fl::nan;
|
||||
|
|
Loading…
Add table
Reference in a new issue