I am practicing professional C++ by building a differential drive robot that applies a Go-To-Goal Behavior.

What is I have written so far is an Agent that consumes the following interfaces:

  1. Actuators Interface
  2. Odometry Interface
  3. PID Regulator Interface

The motion of a differential drive robot is governed by the unicyle kinematics model, which is a state based model (important later) where the robot is represented in terms of its velocity and heading angle (theta). The actuation of the robot it done via another state which is the angular rotation of its wheels.

The task of the agent is the orchestration of the control loop such that:

The output of the PID is fed to the actuators (PWM motor control), and feedback is measured using optical wheel encoders (to estimate distance traveled and thus position), and fed back to the PID regulator to achieve the Go-To-Goal behavior.

I have this mostly working with some mistakes in the calculations but unit-testing should hopefully improve it.

My design goal right now has become to make the agent as well as each of the components that are exposed by those interfaces re-usable so that I can substitute more advanced kinematics or dynamic models for front wheel drive cars without any code change to the agent or unchanged components (and preferably no re-compilation).

To this effort I think the following should apply:

  1. Using shared objects instead of static linking.

  2. De-Coupling using interfaces (already had to do this for unit-testing anyways).

  3. External configuration of the components.

    1. This where I draw a blank. How can I use something like protobuf for example to make the components generic enough to be replaceable.

In summary, what I am asking the community here is the following:

  1. How can I make the Agent, Odometry and PID generic such that their communication is independent of a pre-known State implementation.
  2. Are the above 3 items helpful in achieving the re-usabiliy goal, if not, then which ones should be added/replaced.

Update

To make the question more clear, please consider the following scenario:

The factory method for the actuator needs to know which type of Command object to use when constructing the implementation of the interface, such as:

#pragma once

namespace tareeq {
  namespace control {
      template <class T, class T1>
      class Actuators {
      public:
    virtual ~Actuators() = default;

    virtual bool Init(T Config) = 0;
    virtual bool Start() = 0;
    virtual bool Stop() = 0;
    virtual bool Drive(T1 Command) = 0;
      };

      std::unique_ptr<Actuators> MakeAcutator();

  } // namespace control
} // namespace tareeq

The agent will have to know the relevant Command class to use for a differential drive robot, which is angular velocity of the two wheels, where as for a bicyle model, the actuators can range from throttle/break, to an RC car with two motors; one on the rear wheels to move the car, and another to turn the front two wheels (usually motor connected to a spring that turns the front wheels).

Another case is the communication of values between the components, for example:

The PID would have something like:

double ComputeOmega(T &target_state, T &current_state);

I think I maybe able to solve both these issues as follows:

  1. Use a pub/sub messaging model with google protobuf
  2. Use a Class-Loader that loads the .so at runtime, and this loader can be configured to load the appropriate ClassName via some external yml file.
有帮助吗?

解决方案

The reusability graal

Achieving reusability requires lose coupling: every dependency to other classes makes the reusability more difficult because it adds more constraints. For example if reusing your actuator requires reusing your state and and your commands, which themselves may require to reuse I don't know what, and so on, it may not be so reusable after all.

How to reduce the coupling in your case ? Your (hopefully) clean interfaces is certainly the beginning. But you will need some dependency inversion as well, for example i dependency injection . This allows some kind of run-time configuration. So instead of having a class to know the command class to create new commands, you'd use an abstract command and provide to the class a command factory.

Now that we are mentionning interfaces and abstract classes, you'll also need to ensure that the deived classes behave according to the rules. And here Liskov Substitution Principle may help.

If you just follow the SOLID design principles, you get these 3 for free ;-)

Dynamic configuration

Your requirement is:

to make the agent as well as each of the components that are exposed by those interfaces re-usable so that I can substitute more advanced ... or dynamic ... without any code change to the agent or unchanged components (and preferably no re-compilation).

Of course, you can do this using templates. This has the benefit of facilitating reuse. No special constraint for T and T1 classes: just provide the required meta-interface. (But where are the requirements for T and T1 described ?)

Unfortunately templates, despite their power and flexibility, are compile-time. This does not allow run-time polymorphism, nor make dynamic loading of the right class possible.

In view of your design goals, I'd therefore recommend to get rid of the templates and opt for a polymorphic design, using an abstract Command class and an abstract State class and provide the factories to create those.

Advanced features

It is possible that your dynamic model will provide much more features than your simple abstract state. The advanced kinematics might also require some more exchanges than the basic one.

For this you could opt for a decorator pattern, to add new functionality and responsibilities. However, your agent would need to know about these. Another approach would be to use an architectural pattern that is common in games, the Entity-Component-System: here you could add advanced features as an optional component, and let your agent search in your actuator entity, for the most suitable component.

许可以下: CC-BY-SA归因
scroll top