ros-controls / ros2_controllers

Generic robotic controllers to accompany ros2_control
https://control.ros.org
Apache License 2.0
348 stars 313 forks source link

Question on how to launch the hardware interface with an I2C Bus and avoid data race conditions #629

Open samehmohamed88 opened 1 year ago

samehmohamed88 commented 1 year ago

Lookig at the demo for the DiffBot mock hardware interface, it gives me some good ideas on how to create my own for a motor driver that uses I2C.

However one concern I have is that I plan to wrap I2C functionality inside a couple of classes, like so (this is mainly so I can introduce new devices later into my code without making big changes):

template<typename IOStrategy>
concept HasReadAndWriteMethod = requires(IOStrategy strategy) {
                                     strategy.read();
                                     strategy.write()
                                 };

template<typename InputDataType, typename ReadReturnType>
class Device
{
public:
    virtual ReadReturnType read() = 0;
    virtual void write(InputDataType data) = 0;
};

/// The ros2 control hardware interface will hold/own this MotorController
template < HasReadAndWriteMethod IOStrategy, typename InputDataType, typename ReadReturnType>
class MotorController : public Device<InputDataType, ReadReturnType> { 
  ReadReturnType read() {IOStrategy{}.read();};
   write(InputDataType) {IOStrategy{}.write(InputDataType);};
};

Then my DiffBotActuatorHardware will have MotorController<I2CMotorController> as a class member. The I2CMotorController is similar to:

class I2CBus {
  I2CBus(int busNumber);
  read();
  write();
  int fileDescriper;
 std::mutex m;
};

class I2CMotorController {
  I2CMotorController(I2CBus i2cBus);
   read();
  write();
};

Since the controller manager is what ends up spawning the controller and its hardware interface, how do I guarantee no race conditions on that same bus?

Can I define how the manager launches the hardware interface, such that I can pass it a reference to the I2CBus object instead of instantiating a new one?

For instance, what if I were to have another ros2 controller for a ServoMotor that also uses the same I2C bus (just for the sake of discussion), or perhaps another Node that reads an IMU that is forced to create a different instance of the I2CBus class.

samehmohamed88 commented 1 year ago

I saw an example today that uses node composition and passes the hardware interface to the manager.

controller_manager::ControllerManager cm(&hardwareInterface);

I would prefer to not close this issue until it's reviewed/commented by the team or someone more knowledgeable than me on this topic.