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

54,399

154

32

Published

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

  • Creative Misuse Contest

    Creative Misuse Contest
  • Clocks Contest

    Clocks Contest
  • Water Contest

    Water Contest

32 Discussions

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,

3 replies

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

1 reply

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!

3 replies

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

Hi guys...

Please help me!!!

I'm trying to move dynamixel AX12 servos with Raspberry pi, but I have a problem :( I triy to move servos with your library and your example to move 6 servos and I don´t get answer. I have an error in "return self.readData(id)" lines, when I comment the lines dont have error but the GPIO 18 stay high all time.

Excuseme for my english and I hope you can help me!!!

1 reply

Hi Katherine.
Did you look through some of the other comments here?

One common problem that people have been having is that configuring the serial port on newer Raspian versions is different than when I wrote the instructable. First thing I'd try is check if ttyAMA0 is working.

Then, also make sure the code reflects the number of motors you have, and each motor has its own address. If you try to run the code for 6 motors with less motors, it will probably crash when it waits for the response.

I have used your code to drive the motor at github, but the program will throw timeout exception after a few seconds. is it a problem related to timing? (i use the default setting, 1Mhz Baud rate)

6 replies

Hi. Does it throw timeout exceptions and keeps working, or does it throw timeout and just stop?

Depending on how fast I send commands, some get dropped and I do get some errors and exceptions, but re-sending usually helps and it keeps working.

I've also had the case where it says "timeout on motor 1" (or something like that) and it stops working. When that has happened to me, it has been due to power supply problems (cable unplugged, bad supply, etc).

it throws a timeout and stop. usually after a few seconds.

Hey, we've been having the same problem with line 177, we normally get around it by commenting out the readData line from the move methods. But now we need to use readPosition(), and there's no getting around needing to call readData. Did you ever get it to work?

in my case, i can read most of the time, its just sometime that it will time out..

and i have not solved that..

And it works fine for those first couple of seconds?

Try playing with TX_DELAY_TIME in the ax12 library.
https://github.com/thiagohersan/memememe/blob/mast...

I haven't used readPosition() in anything too complicated. There's a chance that there's a bug in the library (the checksum stuff, or the expected reply in readData()). Does readPosition ever return anything?

Also, line 30 of your code:
https://gist.github.com/hyxer/cdfe0b2f60b8155c0ffb...

should probably be:
if servos.readPosition(32)!=anglePosition42:

But that's not going to fix the timeout problem...

I was under the impression that Dynamixel's run on 5V logic. The RaspberryPi's GPIO pins run at 3.3V logic. Have you tried running the Pi's Rx and Tx through a logic level converter to see if you get more reliable results?