How to Drive Dynamixel AX-12A Servos (with a RaspberryPi)

52,284

154

60

Posted

Introduction: How to Drive Dynamixel AX-12A Servos (with a RaspberryPi)

I decided to use some Dynamixel AX-12A motors for a project, and ended up having to code a library for them, so I figured I would share what I learned.

Despite being a bit more expensive, this motor has a couple of advantages over the more commonly found TowerPro motors.

Mainly, the Dynamixel motors are stronger, daisy-chainable, and have a robust control system with feedback that reports position, temperature, torque, etc.

Step 1: Manuals

There are two different manuals for the AX-12A motors online. This manual from 2006 is a little outdated: some of the spec values are outdated (operating voltage range, for example), and some of the initial values reported in the Control Table are not correct, but it goes into detail about how to send instructions to the motor and how to read its response.

This other manual has more accurate values for specs and initial conditions, but lacks some of the detailing of the communication protocol.

It was useful to have both of them.

Step 2: Other Resources

Step 3: The Joy of UART Communication

Unlike other servos, the Dynamixel doesn’t respond to PWM signals, but a slightly more complicated protocol of instructions for reading and writing onto its memory. This communication happens over a half-duplex UART port, using only one wire for both sending and receiving.

What this means is that we need to build a small circuit that converts full-duplex into half-duplex, if we want to use a Raspberry Pi or an Arduino (or another microcontroller with a full-duplex serial interface) to control these motors.

The AX-12 manual from 2006 recommends this circuit:

It’s basically a tri-state buffering scheme for arbitrating the bus; it makes sure that when the controller is transmitting, the bus isn’t connected to the Rx pin, and that when it’s expecting to receive, it’s not being driven by the Tx pin.

Instead of using a 74HC126 and a 74HC04, I used a 74LS241 (as recommended here), because it already has the built-in capability of enabling half of its buffers with a high signals, and the other half with a low signal.

The schematic for the circuit I ended up using, and a simple PCB design are up in 123D.circuits.

Step 4: Configure Raspberry Pi

From oppedijk blog:

Set configuration parameters in /boot/config.txt:
init_uart_clock=16000000

sudo stty -F /dev/ttyAMA0 1000000

Edit /boot/cmdline.txt and remove all options mentioning ttyAMA0.
Edit /etc/inittab and comment out any lines mentioning ttyAMA0, especially the getty one.
Reboot

UPDATE for Raspbian Jessy:

Follow this thread on SO for disabling tty terminal and getting control of ttyAMA.

Step 5: Libs and Libs

I first tested the circuit using an Arduino and the library found here.

But, because I eventually needed to connect my project to the internet, and keep track of its state between reboots, I decided to use a Raspberry Pi as the controller instead.

I started testing this library for controlling the motors, but then decide to re-write it to make it more object-oriented, and to have some of the same capabilities as the Arduino library.

The resulting AX-12A Python library for Raspberry Pi is on github.

Turns out the timing between sending a command and getting its response is pretty critical and sensitive.

Some amount of time was spent tweaking delay values to minimize the number of dropped commands, but I feel like there might still be some issues with this aspect of the library. I’m still not sure what causes some commands to never get to the motors (it might have something to do with the timing of the Rx/Tx direction signal), but for now I can decrease the number of missed commands by catching a timeout exception in the Python serial library, and resending the command.

Step 6: More Links

Some more information about the overall project, some relevantblogposts, and a list of references.

4 People Made This Project!

Recommendations

  • Spotless Contest

    Spotless Contest
  • Science of Cooking

    Science of Cooking
  • Microcontroller Contest

    Microcontroller Contest
user

We have a be nice policy.
Please be positive and constructive.

Tips

Questions

32 Comments

Hi !

I try to control Dynamixel AX-12 with my Raspberry PI 3 running Raspbian Jessie. But it's not working !

I have connected the Ax12 like this:
Pin 2 to Pin 3 (data out to AX-12)
Pin 1 to Pin 19 (connected to RPi port 12 (GPIO 18))
Pin 18 (connected to RPi pin 10 RX)
Pin 17 (connected to RPi pin 8 TX)
Pin 10 to ground (RPi pin 6 GND)
Pin 20 to Vcc (5 Volt, RPi pin 4 5V)

And configurate following this :
https://raspberrypi.stackexchange.com/questions/47671/why-my-program-wont-communicate-through-ttyama0-on-raspbian-jessie

I use this python3 compatible code:
https://github.com/aimlabmu/dynamixel-rpi/blob/master/ax12/ax12.py

A simple test like this has no effect:

from ax12 import Ax12
import time

roby = Ax12()

while True :
roby.move(19,200)
time.sleep(5)

roby.move(19,800)
time.sleep(5)



Thank you,

Hello.

Have you checked that your seria port works? Maybe by connecting something else to the tx/rx terminals?

What circuit are you using to do the uart tx/rx multiplexing?

And have you double, triple, quadruple checked the circuit? That's usually a source of mistakes and it's hard to debug because there's no easy way to tell if the circuit is correct.

I usually build 3 of them to end up with 1 that works. And then when I know that the software is correct I fix the other circuits. I usually forget to solder a wire, or short something, or use the wrong IC... (there's a difference between the 74LS241 and the 74HC241, for exemple).

Hope this helps.
Good luck

Thanks for your quick response.

It's OK now: I can control the servo but I have a problem with the function "readData".

All functions wich use readData works a litle time and crash!

I can give orders to the servomotor but I can't read data.

I use this library modified for python3
https://github.com/aimlabmu/dynamixel-rpi/blob/master/ax12/ax12.py

Below, the function wich raises problem
def readData(self,id):
self.direction(Ax12.RPI_DIRECTION_RX)
reply = Ax12.port.read(5) # [0xff, 0xff, origin, length, error]
try:
# assert ord(reply[0]) == 0xFF
assert reply[0] == 0xFF
except:
e = "Timeout on servo " + str(id)
raise Ax12.timeoutError(e)

try:
# length = ord(reply[3]) - 2
# error = ord(reply[4])
length = reply[3] - 2
error = reply[4]

if(error != 0):
print("Error from servo: " + Ax12.dictErrors[error] + ' (code ' + hex(error) + ')')
return -error
# just reading error bit
elif(length == 0):
return error
else:
if(length > 1):
reply = Ax12.port.read(2)
# returnValue = (ord(reply[1]) returnValue = (reply[1] else:
reply = Ax12.port.read(1)
returnValue = reply[0]
return returnValue
except:
pass
# except Exception, detail:
# raise Ax12.axError(detail)

Waouhh !!!!!

I don't understand!

It was OK. I had the port ttyS0 and now I have an error message "No such file or directory /dev/ttyS0

Hi ! Thanks for the article. Anyway, i'm trying to control Dynamixel RX-24 using RaspberryPi or Arduino, is there anyone can help me?? or any recommended website to learn from? Please let me know, Thankyou for help anyway :D

Hey Jason!

I'll try too with an RX-24 with RaspberryPi. Could you solve how to do it? I would be very grateful if you could share any information. Thank you :)

Hi There,

Please help! I'm trying to build out a circuit with multiple AX-12A's and was running into some issues that I was hoping you could help with. I'm able to drive one servo with the circuit you described, but I can't figure out how to drive more. When I send this code it runs both servo's during the move(1, ) commands and no motors during the move(2, ) commands, am I doing something wrong? Do I have to declare each servo id somewhere?

It behaves the same way (moving both servos at once most of the time, timing out sometimes) without the learnservos command in there.

Thanks so much!

Oops, it didn't attach my image, here is the code I'm trying to run. I also tried your multiple servo code from Github and that was giving the same issue.

from ax12 import Ax12

import time

self = Ax12();

self.learnServos(1,2)

while True:

self.move(1,200)
time.sleep(4)
self.move(1,800)
time.sleep(4)

self.move(2,200)
time.sleep(4)
self.move(2,800)
time.sleep(4)

Screen Shot 2017-03-08 at 3.05.20 PM.png