Controlling the MINI with OpenRB-150

1/14/2024
Presently, users of the MINI kit are likely “worried” about the availability of the OpenCM-904-C controller as ROBOTIS had previously announced its “retirement”.

Although the OpenRB-150 has the exact physical size as for the OpenCM-904-C, it is fairly obvious that just swapping out the 904 with the 150 is not feasible at all. So, my re-design approach hinges on this main assumption:

  • The 904 is no longer programmable, but its function as a Power Hub for the XL-320s is still operational.

The following picture shows the Hardware Redesign approach used:

Test_Setup_1_Annoted

  • The 2 LiPo batteries are still hooked up to the 904 as normal.

  • One of the DXL connector on the 904 needs to be diverted out into an X3P Hub via one Convertible Cable (XL-320 to X3P). Then from the X3P Hub a second Convertible Cable is used to bring DXL Power back to the right arm of the MINI. So, all 16 DXLs of the MINI are now powered. But how to control them from the 150?

  • A special X3P cable (with its Power Wire severed) is then connected between a DXL Connector on the 150 and the previous X3P Hub, thus isolating the Control Circuit of the XL-320s (i.e. from the 150) from their Power Circuit (i.e. from the 2 LiPo batteries via the 904).

  • The 150 is currently powered via USB from the PC, but it will be powered via VIN(DXL) with its own small battery at a later stage of the project.

So did this crazy mash-up work? My first test was to download the sketch usb_to_dynamixel.ino and tested to see if the Dynamixel Wizard could connect to all DXLs of the MINI? And it did:

DXLWIZ_MINI_1

Next, I created a sketch to do Sync Write/Read into the two shoulder joints (ID=1 and 2) with randomized Goal Positions. And this sketch worked fine also:

RandomGP_Mini

Below is a demo MP4 file:

Thus, so far, so good. The next step will be to translate ROBOTIS MOTION file into C/C++ Motion arrays and then use Sync Write to get the MINI to walk!

Until the next update!

3 Likes

Good morning
Your subject interests me a lot because I have Darwin mini with openCM9 HS card so I bought an openRB card and I’m getting into arduio. I also bought your book which I hope will help me understand how it works.
Sincerely

1/20/2024

I settled on the physical layout as shown below:

IMG_025990

My software solution for the MINI is adapted from the one described in Section 2.13 of my Arduino Book (https://www.amazon.com/Using-ARDUINO-ROBOTIS-Systems-Ngoc/dp/0999391895), which was designed for a robot based on XL-430s set into Time-based Position Control. See picture of the A4WP-H robot below:

FrontTiltUp

The XL-320 of course does not have that Time-based mode, but the same data structure (numerical arrays) can still be used. So the Motion Data from the MINI Motion File is “transcribed” into a C/C++ array:

ReadyPose

float _READY_POSE[17] = { 390,0,0,-73.24,73.24,0,0,0,0,-26.37,26.37,29.3,-29.3,13.18,-13.18,0,0 };  // in ms & in degrees

Then it is only a matter of building a big Sync Write packet for all 16 XL-320s Goal Position values and then send it away into the DXL network. I used the Sync Time of 390 ms as a simple Time Delay (between Motion Frames - as shown in My Arduino book). Strictly speaking, this is not as “accurate” as the Time-based Control mode available with the XL-430, nor the Motion timing achieved when the OpenCM-904 (or CM-530/550) is operating in its ROBOTIS firmware (TASK+MOTION), but the demo video below showed that the MINI performance was good enough - I think.

This video showed that the gear back-lash on my aged Mini is getting very bad!

Also it showed that the mechanical aspects of where to put the extra hardware, so as to keep the overall CG of the MINI “near” where it was on the original Mini, will be the most challenging issue.

3 Likes

can you share your code ? thank you in advance

You will have to wait until the 2nd edition of my Mini book coming out towards year’s end. It is derived from codes from Section 2.13 of my Arduino book, so you already have the concepts and sample codes. Consider it as a challenge to figure things out on your own.

1 Like

THANKS
I’m still going to learn. Waiting for your book to come out
Sincerely

Good morning
Am I on track for the mini?
Do you have an example of code to control the arm movement in the loop with the keyboard because I don’t know how to do it. I thank you in advance.
Sincerely
`<// **** Paramètres globaux pour les DXL *********
#include <Dynamixel2Arduino.h>
#define DXL_SERIAL Serial1
#define DEBUG_SERIAL Serial

// **** Global Parameters for DXLs *********
const uint8_t BROADCAST_ID = 254;
const uint8_t DXL_ID1 = 1 ;
const uint8_t DXL_ID2 = 2 ;
const uint8_t DXL_ID3 = 3 ;
const uint8_t DXL_ID4 = 4 ;
const uint8_t DXL_ID5 = 5 ;
const uint8_t DXL_ID6 = 6 ;
const uint8_t DXL_ID7 = 7 ;
const uint8_t DXL_ID8 = 8 ;
const uint8_t DXL_ID9 = 9 ;
const uint8_t DXL_ID10 = 10 ;
const uint8_t DXL_ID11 = 11 ;
const uint8_t DXL_ID12 = 12 ;
const uint8_t DXL_ID13 = 13 ;
const uint8_t DXL_ID14 = 14 ;
const uint8_t DXL_ID15 = 15 ;
const uint8_t DXL_ID16 = 16 ;

const float DXL_PROTOCOL_VERSION = 2.0;
const uint8_t GP_DXL_CNT = 16; // nombre de DXL utilisés en mode Contrôle de position
const uint8_t GP_DXL_ID_LIST[GP_DXL_CNT] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; //ID de servo définis en mode Contrôle de position
const uint16_t ADDR_X_PRESENT_POSITION = 37; // (XL-320)
const uint16_t LEN_X_PRESENT_POSITION = 2 ; // (XL-320)
const uint16_t ADDR_X_GOAL_POSITION = 30; // (XL-320)
const uint16_t LEN_X_GOAL_POSITION = 2; // (XL-320)
const uint16_t ADDR_X_MOVING_SPEED= 32; // (XL-320) adress 32 byt 2 0-2047
const uint16_t LEN_X_MOVING_SPEED= 2; // (XL-320)
const uint16_t ADDR_X_TORQUE_ENABLE= 24; // (XL-320) TORQUE_ENABLE adress 24 0 off 1 on
const uint16_t LEN_X_TORQUE_ENABLE= 1;
const uint8_t CONTROL_MODE = 2; // for XL320 adress 11 byt 1 mode 2 joint

Dynamixel2Arduino dxl_mini(DXL_SERIAL);

ParamForSyncWriteInst_t GP_sync_write_param; // Parameters structure for SW GP packet
ParamForSyncReadInst_t GP_sync_read_param; // Parameters structure for SR GP packet
RecvInfoFromStatusInst_t GP_read_result; // Structure for results of SR PP command

uint8_t dxl_error = 0; // Dynamixel error
int32_t dxl_present_position[GP_DXL_CNT] = { 0 }; // Tableau de position actuelle
int32_t dxl_goal_position[GP_DXL_CNT] = { 0 }; // Tableau de position d’objectif
bool read_present_positions_OK = false;

// Section MOUVEMENT
int _STEP_TIME_0 = millis();
int _STEP_TIME_1 = millis();
int _pose_init[17] ={390, 0, 0, -73.24, 73.24, 0, 0, 0, 0, -26.37, 26.37, 29.3, -29.3, 13.18, -13.18, 0, 0};

int salut_main [6][17] = { {100,-0.29, -150 ,-73.54 ,72.36, -0.29, 0.59 ,-0.29 ,-0.29, -26.66, 26.07 ,29, -29.59, 12.89, -13.48, -0.29, -0.29},
{0,-0.29 ,-149.71, -73.54, 51.27, -0.29, -33.69, -0.29, -0.29 ,-26.66, 26.07, 29 ,-29.59, 12.89, -13.48 ,-0.29 ,-0.29},
{0,-0.29 ,-150, -73.54, 91.7, -0.29, 32.81, -0.29 ,-0.29 ,-26.66 ,26.07 ,29 ,-29.59, 12.89, -13.48, -0.29, -0.29},
{0,-0.29 ,-149.71, -73.54, 51.27, -0.29, -33.69 ,-0.29, -0.29, -26.66 ,26.07 ,29 ,-29.59, 12.89, -13.48, -0.29, -0.29},
{0,-0.29 ,-150, -73.54, 91.7, -0.29, 32.81, -0.29, -0.29, -26.66, 26.07, 29 ,-29.59 ,12.89 ,-13.48, -0.29 ,-0.29},
{0,0 ,0 ,-73.24 ,73.24 ,0, 0, 0, 0 ,-26.37, 26.37, 29.3 ,-29.3 ,13.18 ,-13.18 ,0 ,0 } };

/*int salut_2 [6][17] = { {_STEP_TIME_1,-22.27 ,-44.24, -73.24, 73.24, 0, 0, 0, 0, -26.37, 26.07, 29.3, -29.3, 13.18, -13.48, 0, 0, },
{_STEP_TIME_1,-22.27,-44.24, -62.99, 92.58, -71.78, 58.59, 0, 0, -26.37, 26.07, 29.3, -29.3, 13.18, -13.48, 0, 0 },
{_STEP_TIME_1,-22.27, -69.73, -62.99, 92.58, -71.78, 58.59, 0, 0, -79.39, 82.91, 29.3, -29.3, -10.25, 10.55, 0, 0},
{_STEP_TIME_1,-22.27,-69.73, -62.99, 92.58, -71.78, 58.59, 0, 0, -79.39, 82.91, 29.3, -29.3, -10.25, 10.55, 0, 0},
{_STEP_TIME_1,-22.27, -44.24, -73.24, 73.24, 0, 0, 0, 0, -26.37, 26.07, 29.3, -29.3, 13.18, -13.48, 0, 0},
{_STEP_TIME_1,0, 0, -73.24, 73.24, 0, 0, 0, 0, -26.37, 26.37, 29.3, -29.3, 13.18, -13.18, 0, 0 } }; */

// ********** Function prototypes Prototypes de fonctions
int CalcAngle2Raw(int angle);

//bool check_motion();
int GP_time = _pose_init[0]; //c’est-à-dire _STEP_TIME_0
int CalcAngle2Raw(int angle)
{
return (int)(round((angle * 1023.0 / 300.0 + 512.0)));// return (int)(round((angle * 1023.0 / 300.0 + 512.0)));XL 320
} // fin de CalcAngle2Raw (angle int)

//**** Cet espace de noms est requis pour utiliser les noms d’éléments de la table de contrôle
using namespace ControlTableItem;

void setup()
{
int goal_position_raw = 0; // raw valeur brute de la position de l’objectif DXL
Serial.begin(115200);
while(!Serial); // attendez que l’opérateur ouvre Serial Monitor

//****** Configuration des Dynamixels pour mini ***pour XL320
dxl_mini.begin(1000000);
dxl_mini.setPortProtocolVersion(DXL_PROTOCOL_VERSION);
dxl_mini.scan();
Serial.println("Fin de la numérisation des DXL du robot ");

// Préparation du paquet GP SW

GP_sync_write_param.addr = ADDR_X_GOAL_POSITION;
GP_sync_write_param.length = LEN_X_GOAL_POSITION;
GP_sync_write_param.id_count = GP_DXL_CNT;
for(int i = 0; i < GP_DXL_CNT; i++){
GP_sync_write_param.xel[i].id = GP_DXL_ID_LIST[i];
}
Serial.println(“Fin de la préparation du paquet GP SW”);

// Préparation du paquet GP SR
GP_sync_read_param.addr = ADDR_X_PRESENT_POSITION; // Present Position of DYNAMIXEL-X series
GP_sync_read_param.length = LEN_X_PRESENT_POSITION;
GP_sync_read_param.id_count = GP_DXL_CNT; // Number of servos in Position Control Mode
for(int i = 0; i < GP_DXL_CNT; i++){
GP_sync_read_param.xel[i].id = GP_DXL_ID_LIST[i];
}
Serial.println(“Fin de la préparation du paquet GP SR”);

// Configuration des servos GP dans la zone EEPROM : mode contrôle contrôle de position
// et dans la zone RAM : accélération du profil, vitesse du profil
for(int i = 0; i < GP_DXL_CNT; i++){
dxl_mini.torqueOff(GP_DXL_ID_LIST[i]); // Torque OFF
//dxl_mini.writeControlTableItem( GP_DXL_ID_LIST[i],CONTROL_MODE); //
dxl_mini.setOperatingMode(GP_DXL_ID_LIST[i], OP_POSITION); // Position Control
dxl_mini.torqueOn(GP_DXL_ID_LIST[i]); // Torque ON
delay(100); // attendez un peu avant de travailler dans la zone RAM
}
Serial.println(“Fin de la configuration des servos GP”);

delay(3000);

Serial.println(“Mettre le robot dans la pose initiale”);
// Réglez le robot sur la pose _ via SyncWrite
// Allouez la valeur brute de chaque position d’objectif dans le paquet GP SW, un DXL à la fois en utilisant les valeurs du tableau global _[1] à _[17]
for (int i = 0; i < GP_DXL_CNT; i++) // DXL 1 à 16
{
goal_position_raw = CalcAngle2Raw(_pose_init[i+1]); // convertir l’angle de mouvement en position d’objectif brute [0-4096], _[0] n’est pas utilisé ici
memcpy(GP_sync_write_param.xel[i].data, &goal_position_raw, sizeof(goal_position_raw));
}
dxl_mini.syncWrite(GP_sync_write_param); // send GP SW Packet
delay(3000);

// fin de l’installation() et de la pose initiale"

Serial.println(“jouer mouvement bras”);
for (int frame_no = 0; frame_no < 6; frame_no++) // 4 images de mouvement ont été définies pour le mouvement vers l’avant
{
GP_time = (int)(round(salut_main[frame_no][0] )); //c’est-à-dire STEP TIME 1 modifié avec le facteur de vitesse
//(int)(round(forward[frame_no][0] )); //c’est-à-dire STEP TIME 1 modifié avec le facteur de vitesse

    for (int i = 0; i < GP_DXL_CNT; i++)  // Définition des valeurs GP brutes pour les Walking DXL 1 à 8
    {
      goal_position_raw = (CalcAngle2Raw(salut_main[frame_no][i + 1]) );  //convertir l'angle de mouvement avec Adj Offset en Raw GP 
      memcpy(GP_sync_write_param.xel[i].data, &goal_position_raw, sizeof(goal_position_raw));
    }
    if (dxl_mini.syncWrite(GP_sync_write_param) == false)  // envoyer le paquet GP SW
    {
      Serial.println("Sfin  mouvement");
        
    }
    delay(400); // en attendant que chaque image clé soit jouée en ms
  }  //   fin de pour (numéro de trame)

} // fin du mouvement bras

void loop()

// Réinitialisation du robot à la pose d’initialisation
{
int GP_time = _pose_init[0]; //c’est-à-dire _STEP_TIME_0
int goal_position_raw = 0; // valeur brute de la position de l’objectif DXL
for (int i = 0; i < GP_DXL_CNT; i++) // Définir GP pour les DXL 1 à 16
{
goal_position_raw = CalcAngle2Raw(pose_init[i+1]); //convertir l’angle de mouvement en position d’objectif brute [0-1023]
memcpy(GP_sync_write_param.xel[i].data, &goal_position_raw, sizeof(goal_position_raw));
}
if (dxl_mini.syncWrite(GP_sync_write_param) == false) // envoyer le paquet GP SW si faux
{
Serial.println(“Échec du logiciel”); //Échec du logiciel pour SW_TC
()
// return 0;
}
delay(3000); // en attente de la réinitialisation de la pose pour jouer en ms

} >`

I do not have specific codes to interface keyboard inputs to a Mini doing motion. You will have to figure out this issue on your own. Section 2.4 of the Arduino book covers UART Programming using the Remocon (RC100) protocol and the Debug tool of TASK 2 (i.e. keys U-D-L-R and 1-2-3-4-5-6). Section 2.12 of the Arduino book is about to using RC-100 keyboard inputs to control the Mobile Manipulator, specifically the Arm - this is very close to what you want to do. This Mobile Manipulator robot uses AX-12A which has a Control Table similar to the XL-320 on the Mini - so this robot code will be helpful too for your Mini project.

But let’s get back to your current code first:

  1. Let’s use a “do nothing” loop() for now so that you can just concentrate on making the Mini bow one time only via setup(). So use this statement void loop() { } // end of loop()

  2. In setup(), you did not specify MOVING_SPEED to yours XL-320, this means that all your XL-320s could be running at Maximum Speed of 1023. So you should slow them down (=200) like shown below:

dxl.writeControlTableItem(MOVING_SPEED, GP_DXL_ID_LIST[i], 200); // Setting Initial Moving Speeds

It may be handy to set a SW packet structure for MOVING_SPEED parameters also. That way you can slow down or speed up any Motion Frame as needed. Of course sending the MOVING_SPEED SW packets, BEFORE sending the corresponding GOAL_POSITION packets.

  1. You use an IF structure to “send out” a GP SW packet

if (dxl_mini.syncWrite(GP_sync_write_param) == false) // envoyer le paquet GP SW
{
Serial.println(“Sfin mouvement”);
}

Why do you need this?

  1. Right now you use a delay(400) for all motion frames (1 to 6) - which is fine for now.
    But eventually you need to set each motion frame with its own delay time as shown in the ROBOTIS Motion File for the Mini. You cannot leave them at 0, like you currently have

int salut_main [6][17] = { {100,-0.29, -150 ,-73.54 ,72.36, -0.29, 0.59 ,-0.29 ,-0.29, -26.66, 26.07 ,29, -29.59, 12.89, -13.48, -0.29, -0.29},
{0,-0.29 ,-149.71, -73.54, 51.27, -0.29, -33.69, -0.29, -0.29 ,-26.66, 26.07, 29 ,-29.59, 12.89, -13.48 ,-0.29 ,-0.29},
{0,-0.29 ,-150, -73.54, 91.7, -0.29, 32.81, -0.29 ,-0.29 ,-26.66 ,26.07 ,29 ,-29.59, 12.89, -13.48, -0.29, -0.29},
{0,-0.29 ,-149.71, -73.54, 51.27, -0.29, -33.69 ,-0.29, -0.29, -26.66 ,26.07 ,29 ,-29.59, 12.89, -13.48, -0.29, -0.29},
{0,-0.29 ,-150, -73.54, 91.7, -0.29, 32.81, -0.29, -0.29, -26.66, 26.07, 29 ,-29.59 ,12.89 ,-13.48, -0.29 ,-0.29},
{0,0 ,0 ,-73.24 ,73.24 ,0, 0, 0, 0 ,-26.37, 26.37, 29.3 ,-29.3 ,13.18 ,-13.18 ,0 ,0 } };

ALSO big syntax error with Array salut_main[ ], it is declared as an INTEGER ARRAY, but you feed it with FLOATING POINT numbers!!!

In my previous post, I clearly used a FLOAT array for my _READY_POSE[ ] array.

I thank you for all these answers. I will therefore redo my program following your explanations.
Sincerely

BTW, Sub-Section 2.13.4 also is an application of using keyboard inputs (via TASK 2 Debug Window). Just need to go through the presented materials “several” times until you “got” the concepts down.