Edit on GitHub

Step 3: Leader and Follower Example

NOTE: It assumes that you have already installed and built the SDK.

Make py file

  • Create a new Python source file Open it with your editor.
    $ touch tutorial_step3.py
    

Check the Port Names

  • Before running the code, check the port name of the connected Dynamixel.
  • For General
    • OpenRB-150: ttyACM0: USB ACM device
    • U2D2: FTDI USB Serial Device converter now attached to ttyUSB0
      $ sudo dmesg | grep tty
      

Source Code Description

from dynamixel_easy_sdk import *

def main():
    connector = Connector("/dev/ttyACM0", 57600)
    leader_motor = connector.createMotor(1)
    follower_motor = connector.createMotor(2)
    min_position = leader_motor.getMinPositionLimit() + 100
    max_position = leader_motor.getMaxPositionLimit() - 100

    leader_motor.disableTorque()
    follower_motor.disableTorque()
    follower_motor.setOperatingMode(OperatingMode.POSITION)
    follower_motor.enableTorque()

    while (True):
        present_position = leader_motor.getPresentPosition()
        print(f"Leader Position: {present_position}     ", end='\r')
        if (present_position < min_position):
            leader_motor.enableTorque()
            leader_motor.setGoalPosition(min_position)
            while (True):
                present_position = leader_motor.getPresentPosition()
                if (present_position >= min_position):
                    break
            leader_motor.disableTorque()
        elif (present_position > max_position):
            leader_motor.enableTorque()
            leader_motor.setGoalPosition(max_position)
            while (True):
                present_position = leader_motor.getPresentPosition()
                if (present_position <= max_position):
                    break
            leader_motor.disableTorque()
        follower_motor.setGoalPosition(present_position)

if __name__ == "__main__":
    main()

Import Library

  • Import dynamixel_easy_sdk.
    from dynamixel_easy_sdk import *
    

Create Connector and Motor Object

  • Create a Connector object with port name and baud rate to manage the communication.
    def main():
        connector = Connector("/dev/ttyACM0", 57600)
    
  • Create a Motor object for each Dynamixel servo you want to control with its ID, using the createMotor method of the Connector class.
  • Create motor objects while carefully distinguishing between the leader and follower IDs.
        leader_motor = connector.createMotor(1)
        follower_motor = connector.createMotor(2)
    

Set Position Limits

  • Get the position limits of the leader motor using the getMinPositionLimit and getMaxPositionLimit methods.
        min_position = leader_motor.getMinPositionLimit() + 100
        max_position = leader_motor.getMaxPositionLimit() - 100
    

Set Operating Mode to Position Control Mode

  • Use the methods provided by the Motor class to control the Dynamixel servo.
  • Set the operating mode of follower motor to position control mode.
        leader_motor.disableTorque()
        follower_motor.disableTorque()
        follower_motor.setOperatingMode(OperatingMode.POSITION)
        follower_motor.enableTorque()
    

Leader and Follower Control Loop

  • In a loop, read the present position of the leader motor using the getPresentPosition method.
        while (True):
            present_position = leader_motor.getPresentPosition()
            print(f"Leader Position: {present_position}     ", end='\r')
    
  • If the leader motor’s position exceeds the defined range, move it back within the range.
            if (present_position < min_position):
                leader_motor.enableTorque()
                leader_motor.setGoalPosition(min_position)
                while (True):
                    present_position = leader_motor.getPresentPosition()
                    if (present_position >= min_position):
                        break
                leader_motor.disableTorque()
            elif (present_position > max_position):
                leader_motor.enableTorque()
                leader_motor.setGoalPosition(max_position)
                while (True):
                    present_position = leader_motor.getPresentPosition()
                    if (present_position <= max_position):
                        break
                leader_motor.disableTorque()
    
  • Set the goal position of the follower motor to the present position of the leader motor.
        follower_motor.setGoalPosition(present_position)
    

Error Handling

  • When an error occurs, DxlRuntimeError is raised.
  • You can catch this error using a try-except block.
    try:
        motor1.getPresentPosition()
    except DxlRuntimeError as e:
        print(e)
    
  • DxlRuntimeError contains DxlError Enum that provides detailed information about the error.
    try:
        motor1.getPresentPosition()
    except DxlRuntimeError as e:
        if e.dxl_error == DxlError.SDK_COMM_RX_FAIL:
            print("Transmission failed.")
        elif e.dxl_error == DxlError.SDK_COMM_RX_FAIL:
            print("Receive failed.")
    

NOTE: It assumes that you have already installed and built the SDK.

Make cpp file

  • Create a new C++ source file Open it with your editor.
    $ touch tutorial_step3.cpp
    

Check the Port Names

  • Before running the code, check the port name of the connected Dynamixel.
  • For General
    • OpenRB-150: ttyACM0: USB ACM device
    • U2D2: FTDI USB Serial Device converter now attached to ttyUSB0
      $ sudo dmesg | grep tty
      

Source Code Description

#include "dynamixel_easy_sdk/dynamixel_easy_sdk.hpp"

int main(){

  dynamixel::Connector connector("/dev/ttyACM0", 57600);
  std::unique_ptr<dynamixel::Motor> leader_motor = connector.createMotor(1);
  std::unique_ptr<dynamixel::Motor> follower_motor = connector.createMotor(2);
  int min_position = leader_motor->getMinPositionLimit().value() + 100;
  int max_position = leader_motor->getMaxPositionLimit().value() - 100;

  leader_motor->disableTorque();
  follower_motor->disableTorque();
  follower_motor->setOperatingMode(dynamixel::Motor::OperatingMode::POSITION);
  follower_motor->enableTorque();

  while (true) {
    int present_position = leader_motor->getPresentPosition().value();
    std::cout << "Leader Motor Present Position: " << present_position << std::endl;

    if (present_position < min_position) {
      leader_motor->enableTorque();
      leader_motor->setGoalPosition(min_position);
      while(true){
        present_position = leader_motor->getPresentPosition().value();
        if(present_position >= min_position) break;
      }
      leader_motor->disableTorque();
    } else if (present_position > max_position) {
      leader_motor->enableTorque();
      leader_motor->setGoalPosition(max_position);
      while(true){
        present_position = leader_motor->getPresentPosition().value();
        if(present_position <= max_position) break;
      }
      leader_motor->disableTorque();
    }
    follower_motor->setGoalPosition(present_position);
  }
}

Add Header Files

  • Add dynamixel_easy_sdk/dynamixel_easy_sdk.hpp to the top of your CPP file. This class is included in the Dynamixel SDK package.
    #include "dynamixel_easy_sdk/dynamixel_easy_sdk.hpp"
    

Create Connector and Motor Object

  • Create a Connector object with port name and baud rate to manage the communication.
    int main(){
      dynamixel::Connector connector("/dev/ttyACM0", 57600);
    
  • Create a Motor object for each Dynamixel servo you want to control, using the createMotor method of the Connector class.
  • This method takes the motor ID as an argument and returns a unique pointer to a Motor instance. (shared_ptr is also available)
  • Create motor objects while carefully distinguishing between the leader and follower IDs.
      std::unique_ptr<dynamixel::Motor> leader_motor = connector.createMotor(1);
      std::unique_ptr<dynamixel::Motor> follower_motor = connector.createMotor(2);
    
  • This process throws a DxlRuntimeError if the object creation fails.

Set Position Limits

  • Get the position limits of the leader motor using the getMinPositionLimit and getMaxPositionLimit methods.
      int min_position = leader_motor->getMinPositionLimit().value() + 100;
      int max_position = leader_motor->getMaxPositionLimit().value() - 100;
    

Set Operating Mode to Position Control Mode

  • Use the methods provided by the Motor class to control the Dynamixel servo.
  • Set the operating mode of follower motor to position control mode.
      leader_motor->disableTorque();
      follower_motor->disableTorque();
      follower_motor->setOperatingMode(dynamixel::Motor::OperatingMode::POSITION);
      follower_motor->enableTorque();
    

Leader and Follower Control Loop

  • In a loop, read the present position of the leader motor using the getPresentPosition method.
      while (true) {
        int present_position = leader_motor->getPresentPosition().value();
        std::cout << "Leader Motor Present Position: " << present_position << std::endl;
    
  • If the leader motor’s position exceeds the defined range, move it back within the range.
        if (present_position < min_position) {
          leader_motor->enableTorque();
          leader_motor->setGoalPosition(min_position);
          while(true){
            present_position = leader_motor->getPresentPosition().value();
            if(present_position >= min_position) break;
          }
          leader_motor->disableTorque();
        } else if (present_position > max_position) {
          leader_motor->enableTorque();
          leader_motor->setGoalPosition(max_position);
          while(true){
            present_position = leader_motor->getPresentPosition().value();
            if(present_position <= max_position) break;
          }
          leader_motor->disableTorque();
        }
    
  • Set the goal position of the follower motor to the present position of the leader motor.
        follower_motor->setGoalPosition(present_position);
      }
    }
    

Error Handling

  • To ensure your code is robust, every method that sends a command to the motor returns a Result object that encapsulates values and errors.
  • This object lets you safely check for any communication or device errors before proceeding.
  • You can check for communication errors and device(dynamixel) errors using the Result object.
  • If you use value() when error occurred without checking for errors, it may throw an exception.

    Example

      auto result_uint32 = leader_motor->getMinPositionLimit();
      if (!result_uint32.isSuccess()) {
        std::cerr << dynamixel::getErrorMessage(result_uint32.error()) << std::endl;
        return 1;
      }
      int min_position = result_uint32.value();
    

Compile and Run

  • You can compile and run the code using the following commands
    $ g++ tutorial_step3.cpp -o tutorial_step3 -l dxl_x64_cpp
    $ ./tutorial_step3