Skip to main content
Tweeted twitter.com/StackSoftEng/status/1120613414450212864
added 13 characters in body
Source Link
// this class should be instantiated in main along with calls to factories required to instantiate the sensors, states, etc..
class ControlAgent {

  // set target state
  // measure current state
  // calculate error
  // apply error

 public:
  ControlAgent(const vector<Sensor>& sensors,
           PID& regulator,
           Actuator& actuator);

  void SetTargetState(const State& state);
  
  void ClosedLoop()
  {
    State error = regulator.CalculateError(target_state_, current_state_);
    
    while (target - error > some_threshold)
      {
        actuator.Apply(error);
        current_state_.Update(sensorsensors);
        error = regulator.CalculateError(target_state_, current_state_);    
      }
  };
 private:
  State current_state_;
  State target_state_;
  
};
// this class should be instantiated in main along with calls to factories required to instantiate the sensors, states, etc..
class ControlAgent {

  // set target state
  // measure current state
  // calculate error
  // apply error

 public:
  ControlAgent(const vector<Sensor>& sensors,
           PID& regulator,
           Actuator& actuator);

  void SetTargetState(const State& state);
  
  void ClosedLoop()
  {
    State error = regulator.CalculateError(target_state_, current_state_);
    
    while (target - error > some_threshold)
      {
    actuator.Apply(error);
    current_state_.Update(sensor);
    error = regulator.CalculateError(target_state_, current_state_);    
      }
  };
 private:
  State current_state_;
  State target_state_;
  
};
// this class should be instantiated in main along with calls to factories required to instantiate the sensors, states, etc..
class ControlAgent {

  // set target state
  // measure current state
  // calculate error
  // apply error

 public:
  ControlAgent(const vector<Sensor>& sensors,
           PID& regulator,
           Actuator& actuator);

  void SetTargetState(const State& state);
  
  void ClosedLoop()
  {
    State error = regulator.CalculateError(target_state_, current_state_);
    
    while (target - error > some_threshold)
      {
        actuator.Apply(error);
        current_state_.Update(sensors);
        error = regulator.CalculateError(target_state_, current_state_);    
      }
  };
 private:
  State current_state_;
  State target_state_;
  
};
Source Link

Design Review Of SOLID Principles For An Autonomous Mobile Robot

I am self-learning robotics, C++ and good object oriented design. I have asked various questions over the past couple of weeks: 1 and 2 that have lead to the following design.

The design goal is for portability. It is a Go-To-Goal Differential Drive Robot with PID to regulate the behavior.

I am hoping my design is robust enough so that once I have more understanding of dynamic vehicle models, that I can simply plug those in without modifications ; the O in SOLID :)

please note I have not compiled this code yet, I am not looking for a syntax check, just rough design review

The state of the robot is represented as follow:

struct SensorConfig {
  // required config values
};

class Sensor {

  public:
  virtual ~Sensor() = default;

  // basic checks to ensure sensor is still
  // functioning correctly
  virtual bool IsActive() = 0;

  // 
  virtual void Init(const SensorConfig& config) = 0;

  
  virtual vector<double> const ReadMeasurement() = 0;
  
 
};

class State {

 public:

  virtual ~State() = default;

  virtual void Update(const vector<double> values) = 0;
  virtual void Update(const vector<Sensor>& sensors) = 0;

  // overloading the operator so that I can measure
  // the error between target state and current state
  // each model can have its own way of deciding how to do that
  virtual vector<double> operator- const (const State& state) = 0;

};

class UnicycleModel : public State {

 public:
  UnicyleModel(double velocity, double omega, double wheel_radius, double distance_between_wheels);

  virtual void Update(vector<Sensor>& sensors)
  {
    if (sensors.size() < 2)
      {
    // FAIL with error message
      }
    Sensor r_wheel_odometry = sensors.at(0);
    Sensor l_wheel_odometry = sensors.at(1);

    double r_rotation_rate = r_wheel_odometry.ReadMeasurement().at(0);
    double l_rotation_rate = r_wheel_odometry.ReadMeasurement().at(0);

    // apply unicyle model equations to
    // set velocity_ and omega_;
    
  };

  // just an example
  virtual vector<double> operator- const (const State& state)
  {
    // example
    return vector{state.omega - this->omega_, state.velocity - this->velocity};
  }
  
 private:
  double velocity_;
  double omega_;
  double wheel_radius;
  double distance_between_wheels_;
  

};

class LongitudinalDynamicsModel : public State {

  public:
  LongitudinalDynamicsModel(double velocity, double theta, double phi, // etc);

  virtual void Update(vector<Sensor>& sensors)
  {
    
    // apply vehicle dynamics equations to
    // set state;
    
  };

  virtual vector<double> operator- const (const State& state)
  {
    // example
    return vector{state.velocity - this->velocity, //etc};
  }
 private:
  // state representation
};

class LatDynamicsModel : public State {

  public:
  LatDynamicsModel(double velocity, double theta, double phi, // etc);

  virtual void Update(vector<Sensor>& sensors)
  {
    
    // apply vehicle dynamics equations to
    // set state;
    
  };

  virtual vector<double> operator- const (const State& state)
  {
    // example
    return vector{state.velocity - this->velocity, //etc};
  }
 private:
  // state representation
};

The go-to-goal behavior is represented as

class PID {

 public:
  virtual ~PID() = default;

  virtual State const CalculateError(const State& target_state, const State& current_state) = 0;

  virtual void SetProportionalGain(const double& kp) = 0;
  virtual void SetIntegralGain(const double& ki) = 0;
  virtual void SetDifferentialGain(const double& kd) = 0;

};

class Actuator {

 public:
  virtual ~Actuator() = default;

  virtual bool Apply(const State& error) = 0;
};

class Motor {
 pubic:
  Motor(Pwm speed_pin, Ouput control_a, Output control_b);

  inline bool Start();
  inline bool Stop();
  inline bool SpinForward();
  inline bool SpinBackward();
  
};

class UniCycleDriver : public Actuator {

 public:
  UniCycleDriver(Motor& right_motor, Motor& left_motor);
  
  virtual bool Apply(const State& error) {
    
  };

  inline bool Start();
  inline bool Stop();
  inline bool DriveForward();
  inline bool DriveBackward();

  inline const bool IsDrivnigForward();

 private:
  bool is_driving_forward_;
  Motor right_motor_;
  Motor left_motor_;
  
};

class FrontWheelDriver : public Actuator {

  public:
  
  virtual bool Apply(const State& error) {
    
  };

};

Then finally, the main part of the system, where the control loops will take place

// this class should be instantiated in main along with calls to factories required to instantiate the sensors, states, etc..
class ControlAgent {

  // set target state
  // measure current state
  // calculate error
  // apply error

 public:
  ControlAgent(const vector<Sensor>& sensors,
           PID& regulator,
           Actuator& actuator);

  void SetTargetState(const State& state);
  
  void ClosedLoop()
  {
    State error = regulator.CalculateError(target_state_, current_state_);
    
    while (target - error > some_threshold)
      {
    actuator.Apply(error);
    current_state_.Update(sensor);
    error = regulator.CalculateError(target_state_, current_state_);    
      }
  };
 private:
  State current_state_;
  State target_state_;
  
};