Dynamixel SDK with CDC/ACM USB interface (USB2AX)

Bioloid robot kit from Korean company Robotis; CM5 controller block, AX12 servos..
5 postsPage 1 of 1
5 postsPage 1 of 1

Dynamixel SDK with CDC/ACM USB interface (USB2AX)

Post by xevel » Tue Nov 13, 2012 12:04 am

Post by xevel
Tue Nov 13, 2012 12:04 am

Just a further check (I search around) before I dive deeper into it myself: has anyone made the Dynamixel SDK work reliably with a CDC/ACM USB interface (like the USB2AX, or a Minimus or even an UNO...)?

The first error I get is with the line that applies the non-standard baudrate:
Code: Select all
if(ioctl(gSocket_fd, TIOCSSERIAL, &serinfo) < 0) {


I removed it (since it seems thatit does not have any sense for the CDC/ACM driver) and directly set the right baudrate in the termios structure, but then I get errors (timeout and corruption) nearly every packet of the example ReadWrite.
Just a further check (I search around) before I dive deeper into it myself: has anyone made the Dynamixel SDK work reliably with a CDC/ACM USB interface (like the USB2AX, or a Minimus or even an UNO...)?

The first error I get is with the line that applies the non-standard baudrate:
Code: Select all
if(ioctl(gSocket_fd, TIOCSSERIAL, &serinfo) < 0) {


I removed it (since it seems thatit does not have any sense for the CDC/ACM driver) and directly set the right baudrate in the termios structure, but then I get errors (timeout and corruption) nearly every packet of the example ReadWrite.
xevel offline
Savvy Roboteer
Savvy Roboteer
Posts: 74
Joined: Sun Mar 27, 2011 6:37 pm

Post by i-Bot » Tue Nov 13, 2012 12:07 pm

Post by i-Bot
Tue Nov 13, 2012 12:07 pm

I don't use the Dynamixel SDK, but the hal code is the basis of the DARwin code, so it had the same issue.

As you correctly noticed, the setting of the not standard baud rate is specific to the FTDI USB to serial and does not work with the CDC_ACM driver. The good news is that the later drivers for CDC-ACM do understand higher baud rate values. So you can use
newtio.c_cflag = B1000000|CS8|CLOCAL|CREAD;
instead of:
newtio.c_cflag = B38400|CS8|CLOCAL|CREAD;
And then drop all the stuff related to setting the setting the serial structure through ioctl.

I always set up to 1000000bps so don't use the routine to change the baudrate, so never fixed that. If you do this, then you note the driver only accepts a limietd range of baud rates, and you will need to check for this. Note also that (B1000000 is #define as a value other than 1000000).

Let me know if you have any more problem and I will send the DARwin code which works with USB2AX.
I don't use the Dynamixel SDK, but the hal code is the basis of the DARwin code, so it had the same issue.

As you correctly noticed, the setting of the not standard baud rate is specific to the FTDI USB to serial and does not work with the CDC_ACM driver. The good news is that the later drivers for CDC-ACM do understand higher baud rate values. So you can use
newtio.c_cflag = B1000000|CS8|CLOCAL|CREAD;
instead of:
newtio.c_cflag = B38400|CS8|CLOCAL|CREAD;
And then drop all the stuff related to setting the setting the serial structure through ioctl.

I always set up to 1000000bps so don't use the routine to change the baudrate, so never fixed that. If you do this, then you note the driver only accepts a limietd range of baud rates, and you will need to check for this. Note also that (B1000000 is #define as a value other than 1000000).

Let me know if you have any more problem and I will send the DARwin code which works with USB2AX.
i-Bot offline
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 1142
Joined: Wed May 17, 2006 1:00 am

Post by xevel » Tue Nov 13, 2012 12:24 pm

Post by xevel
Tue Nov 13, 2012 12:24 pm

Indeed, that's what I did with newtio.c_flag, but that's where I get all the errors.

The available baud rates used in c_flag I took from here: http://linux.die.net/include/bits/termios.h

I did my tests on a Lubuntu 12.10 in a VMware Player virtual machine, and what actually happens on the Dynamixel bus is that there are collisions between the output and input packets, that obviously result in a very confused Dynamixel SDK.
My first thought were that the timing (based on gettimeofday() ) was not working properly [there have been report of the system clock jumping around on VMs], but I had someone run it on his physical machine with a Lubuntu 12.04 with the same result.

In all honesty, I haven't done all the testing I should have (I should test with the USB2Dynamixel to rule out the timing problem, and do it on a real machine...), and I was just checking if anybody had had the same problem before diving deeper.
I would be glad to have your code so that I have a reference to compare to to debug my setup.

Thanks i-Bot :)
Indeed, that's what I did with newtio.c_flag, but that's where I get all the errors.

The available baud rates used in c_flag I took from here: http://linux.die.net/include/bits/termios.h

I did my tests on a Lubuntu 12.10 in a VMware Player virtual machine, and what actually happens on the Dynamixel bus is that there are collisions between the output and input packets, that obviously result in a very confused Dynamixel SDK.
My first thought were that the timing (based on gettimeofday() ) was not working properly [there have been report of the system clock jumping around on VMs], but I had someone run it on his physical machine with a Lubuntu 12.04 with the same result.

In all honesty, I haven't done all the testing I should have (I should test with the USB2Dynamixel to rule out the timing problem, and do it on a real machine...), and I was just checking if anybody had had the same problem before diving deeper.
I would be glad to have your code so that I have a reference to compare to to debug my setup.

Thanks i-Bot :)
xevel offline
Savvy Roboteer
Savvy Roboteer
Posts: 74
Joined: Sun Mar 27, 2011 6:37 pm

Post by i-Bot » Tue Nov 13, 2012 12:46 pm

Post by i-Bot
Tue Nov 13, 2012 12:46 pm

If there are collisions on the bus it implies you are sending a new packet before you have received reply
Are you sure your txrxpacket() are not timing out ? I think the timeout is usually about 5ms in the Robotis dynamixel code. If you are using a virtual machine the delay through kernel may be longer than that.
I have it running OK with USB2AX on laptop(Ubuntu 12.04), MK-802(lubuntu 12.04), and RPi(Raspbian). My USB2AX firmware is your latest with the bulk_read and the CM730 emulation added.
If there are collisions on the bus it implies you are sending a new packet before you have received reply
Are you sure your txrxpacket() are not timing out ? I think the timeout is usually about 5ms in the Robotis dynamixel code. If you are using a virtual machine the delay through kernel may be longer than that.
I have it running OK with USB2AX on laptop(Ubuntu 12.04), MK-802(lubuntu 12.04), and RPi(Raspbian). My USB2AX firmware is your latest with the bulk_read and the CM730 emulation added.
i-Bot offline
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 1142
Joined: Wed May 17, 2006 1:00 am

Post by xevel » Wed Nov 14, 2012 9:20 am

Post by xevel
Wed Nov 14, 2012 9:20 am

Ok, here is the deal: as iBot mentionned, simply commenting the lines with the ioctl, and using directly B1000000 in c_flag does the trick quite well.

As suspected, the problems I had with timeout and corruption came from the virtual machine. When I tested with the original code and the USB2Dynamixel, I had the same errors.

I changed to a non-virtualized plateform and everything works OK now.

For the record, the measured latency for reading data from a servo with the USB2Dynamixel is 1ms (as expected), and the latency of the same code with the USB2AX is around 0.65ms, on my Beagleboard-xM rev A @ 1GHz with Ubuntu Linaro 12.03.


Until I get to do things properly (in the documentation of the USB2AX I guess), here is the code I have working with the USB2AX for dxl_hal_open:
Code: Select all
int dxl_hal_open(int deviceIndex, float baudrate)
{
   struct termios newtio;
   //struct serial_struct serinfo;
   char dev_name[100] = {0, };

   sprintf(dev_name, "/dev/ttyACM%d", deviceIndex);

   strcpy(gDeviceName, dev_name);
   memset(&newtio, 0, sizeof(newtio));
   dxl_hal_close();
   
   if((gSocket_fd = open(gDeviceName, O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0) {
      fprintf(stderr, "device open error: %s\n", dev_name);
      goto DXL_HAL_OPEN_ERROR;
   }

   newtio.c_cflag      = B1000000|CS8|CLOCAL|CREAD;
   newtio.c_iflag      = IGNPAR;
   newtio.c_oflag      = 0;
   newtio.c_lflag      = 0;
   newtio.c_cc[VTIME]   = 0;   // time-out 값 (TIME * 0.1초) 0 : disable
   newtio.c_cc[VMIN]   = 0;   // MIN 은 read 가 return 되기 위한 최소 문자 개수

   tcflush(gSocket_fd, TCIFLUSH);
   tcsetattr(gSocket_fd, TCSANOW, &newtio);
   
   if(gSocket_fd == -1)
      return 0;
   /*
   if(ioctl(gSocket_fd, TIOCGSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot get serial info\n");
      return 0;
   }
   
   serinfo.flags &= ~ASYNC_SPD_MASK;
   serinfo.flags |= ASYNC_SPD_CUST;
   serinfo.custom_divisor = serinfo.baud_base / baudrate;
   
   if(ioctl(gSocket_fd, TIOCSSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot set serial info\n");
      return 0;
   }*/
   
   dxl_hal_close();
   
   gfByteTransTime = (float)((1000.0f / baudrate) * 12.0f);
   
   strcpy(gDeviceName, dev_name);
   memset(&newtio, 0, sizeof(newtio));
   dxl_hal_close();
   
   if((gSocket_fd = open(gDeviceName, O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0) {
      fprintf(stderr, "device open error: %s\n", dev_name);
      goto DXL_HAL_OPEN_ERROR;
   }

   newtio.c_cflag      = B1000000|CS8|CLOCAL|CREAD;
   newtio.c_iflag      = IGNPAR;
   newtio.c_oflag      = 0;
   newtio.c_lflag      = 0;
   newtio.c_cc[VTIME]   = 0;   // time-out 값 (TIME * 0.1초) 0 : disable
   newtio.c_cc[VMIN]   = 0;   // MIN 은 read 가 return 되기 위한 최소 문자 개수

   tcflush(gSocket_fd, TCIFLUSH);
   tcsetattr(gSocket_fd, TCSANOW, &newtio);
   
   return 1;

DXL_HAL_OPEN_ERROR:
   dxl_hal_close();
   return 0;
}

void dxl_hal_close()
{
   if(gSocket_fd != -1)
      close(gSocket_fd);
   gSocket_fd = -1;
}

int dxl_hal_set_baud( float baudrate )
{
   struct serial_struct serinfo;
   
   if(gSocket_fd == -1)
      return 0;
   
   if(ioctl(gSocket_fd, TIOCGSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot get serial info\n");
      return 0;
   }
   
   serinfo.flags &= ~ASYNC_SPD_MASK;
   serinfo.flags |= ASYNC_SPD_CUST;
   serinfo.custom_divisor = serinfo.baud_base / baudrate;
   
   if(ioctl(gSocket_fd, TIOCSSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot set serial info\n");
      return 0;
   }
   
   //dxl_hal_close();
   //dxl_hal_open(gDeviceName, baudrate);
   
   gfByteTransTime = (float)((1000.0f / baudrate) * 12.0f);
   return 1;
}
Ok, here is the deal: as iBot mentionned, simply commenting the lines with the ioctl, and using directly B1000000 in c_flag does the trick quite well.

As suspected, the problems I had with timeout and corruption came from the virtual machine. When I tested with the original code and the USB2Dynamixel, I had the same errors.

I changed to a non-virtualized plateform and everything works OK now.

For the record, the measured latency for reading data from a servo with the USB2Dynamixel is 1ms (as expected), and the latency of the same code with the USB2AX is around 0.65ms, on my Beagleboard-xM rev A @ 1GHz with Ubuntu Linaro 12.03.


Until I get to do things properly (in the documentation of the USB2AX I guess), here is the code I have working with the USB2AX for dxl_hal_open:
Code: Select all
int dxl_hal_open(int deviceIndex, float baudrate)
{
   struct termios newtio;
   //struct serial_struct serinfo;
   char dev_name[100] = {0, };

   sprintf(dev_name, "/dev/ttyACM%d", deviceIndex);

   strcpy(gDeviceName, dev_name);
   memset(&newtio, 0, sizeof(newtio));
   dxl_hal_close();
   
   if((gSocket_fd = open(gDeviceName, O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0) {
      fprintf(stderr, "device open error: %s\n", dev_name);
      goto DXL_HAL_OPEN_ERROR;
   }

   newtio.c_cflag      = B1000000|CS8|CLOCAL|CREAD;
   newtio.c_iflag      = IGNPAR;
   newtio.c_oflag      = 0;
   newtio.c_lflag      = 0;
   newtio.c_cc[VTIME]   = 0;   // time-out 값 (TIME * 0.1초) 0 : disable
   newtio.c_cc[VMIN]   = 0;   // MIN 은 read 가 return 되기 위한 최소 문자 개수

   tcflush(gSocket_fd, TCIFLUSH);
   tcsetattr(gSocket_fd, TCSANOW, &newtio);
   
   if(gSocket_fd == -1)
      return 0;
   /*
   if(ioctl(gSocket_fd, TIOCGSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot get serial info\n");
      return 0;
   }
   
   serinfo.flags &= ~ASYNC_SPD_MASK;
   serinfo.flags |= ASYNC_SPD_CUST;
   serinfo.custom_divisor = serinfo.baud_base / baudrate;
   
   if(ioctl(gSocket_fd, TIOCSSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot set serial info\n");
      return 0;
   }*/
   
   dxl_hal_close();
   
   gfByteTransTime = (float)((1000.0f / baudrate) * 12.0f);
   
   strcpy(gDeviceName, dev_name);
   memset(&newtio, 0, sizeof(newtio));
   dxl_hal_close();
   
   if((gSocket_fd = open(gDeviceName, O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0) {
      fprintf(stderr, "device open error: %s\n", dev_name);
      goto DXL_HAL_OPEN_ERROR;
   }

   newtio.c_cflag      = B1000000|CS8|CLOCAL|CREAD;
   newtio.c_iflag      = IGNPAR;
   newtio.c_oflag      = 0;
   newtio.c_lflag      = 0;
   newtio.c_cc[VTIME]   = 0;   // time-out 값 (TIME * 0.1초) 0 : disable
   newtio.c_cc[VMIN]   = 0;   // MIN 은 read 가 return 되기 위한 최소 문자 개수

   tcflush(gSocket_fd, TCIFLUSH);
   tcsetattr(gSocket_fd, TCSANOW, &newtio);
   
   return 1;

DXL_HAL_OPEN_ERROR:
   dxl_hal_close();
   return 0;
}

void dxl_hal_close()
{
   if(gSocket_fd != -1)
      close(gSocket_fd);
   gSocket_fd = -1;
}

int dxl_hal_set_baud( float baudrate )
{
   struct serial_struct serinfo;
   
   if(gSocket_fd == -1)
      return 0;
   
   if(ioctl(gSocket_fd, TIOCGSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot get serial info\n");
      return 0;
   }
   
   serinfo.flags &= ~ASYNC_SPD_MASK;
   serinfo.flags |= ASYNC_SPD_CUST;
   serinfo.custom_divisor = serinfo.baud_base / baudrate;
   
   if(ioctl(gSocket_fd, TIOCSSERIAL, &serinfo) < 0) {
      fprintf(stderr, "Cannot set serial info\n");
      return 0;
   }
   
   //dxl_hal_close();
   //dxl_hal_open(gDeviceName, baudrate);
   
   gfByteTransTime = (float)((1000.0f / baudrate) * 12.0f);
   return 1;
}
xevel offline
Savvy Roboteer
Savvy Roboteer
Posts: 74
Joined: Sun Mar 27, 2011 6:37 pm


5 postsPage 1 of 1
5 postsPage 1 of 1