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

Picture of 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

Picture of 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

Picture of More Links

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

Comments

Jason30_07 (author)2017-08-29

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

NicolásB77 (author)Jason30_072017-10-08

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 :)

TulakanR (author)2016-07-31

Hi guys, I'm new to raspberry and circuit but I want to make some servos connection using rpi and dunamixels. Now I tried following many links but still could not get motor to move. Here's what I've done

I'm using rpi 3 running Raspbian Jessie. I have connected dynamixel to rpi like you show and config following this http://www.oppedijk.com/robotics/control-dynamixel... since there is no /etc/inittab anymore so I did following this http://raspberrypi.stackexchange.com/a/47851 then I tried reading position from your lib but the it always stuck and have to brake (ctrl+c) and it shows where it stuck:

```

<ipython-input-7-499934bba478> in <module>()
----> 1 test = servos.readPosition(15)

/home/pi/Robot/ax12/ax12.pyc in readPosition(self, id)
604 outData += chr(Ax12.AX_INT_READ)
605 outData += chr(checksum)
--> 606 Ax12.port.write(outData)
607 sleep(Ax12.TX_DELAY_TIME)
608 return self.readData(id)

/usr/local/lib/python2.7/dist-packages/serial/serialposix.pyc in write(self, data)
523 assert timeout is None
524 # wait for write operation
--> 525 abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None)
526 if abort:
527 os.read(self.pipe_abort_write_r, 1)

KeyboardInterrupt:

In [8]:

```

I have no clue now where I'm doing it wrong, any idea?

TulakanR (author)TulakanR2016-07-31

another thing is that instead of using 74LS24, I use 74LS241N. Is it different?

TulakanR (author)TulakanR2016-08-01

After working on it for a day I finally figured it out and come back to share, thanks to Phil Martin's post on his blog https://frillip.com/raspberry-pi-3-uart-baud-rate-.... By setting core_freq=250, it works well now and I'm going to update my rpi3 to see how it will work soon. I also mess around your ax12 class and make it works on python 3 as well, here's a link https://github.com/aimlabmu/dynamixel-rpi/blob/mas.... Thanks for the place to encourage me that it's possible to do :)

ErnőH (author)TulakanR2017-05-31

Thanks for the reply, i am also trying to work this with pi 3 and python 3.4.2, i but always get permission denied even added the line core_freq=250 in /boot/config.txt

File "/usr/lib/python3/dist-packages/serial/serialposix.py", line 275, in open

self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK)

PermissionError: [Errno 13] Permission denied: '/dev/ttyS0'

Do you maybe know why is that?

Thanks!

thiagohersan (author)ErnőH2017-06-01

Hi.

Just guessing here...

you can try running your script as root, or give your user access to the tty serial port.

herno1 made it! (author)thiagohersan2017-07-12

Thanks but it turned out, that Endless Turn can be done If both values for the CW Angle Limit and the CCW Angle Limit are set to 0

i have shared the code:

https://github.com/horverno/deep-pilot/blob/rpi/te...

also i have redrawn the circuit:

https://github.com/horverno/deep-pilot/wiki/Electrical-design

ErnőH (author)ErnőH2017-06-13

ok, now it's working, there was a problem with my circuit

ErnőH (author)ErnőH2017-06-13

does anybody know how to drive the motor constantly, i mean not only drive to position? thanks

mynameishamish (author)2017-03-08

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)

Hi. Before you chain multiple motors you have to connect one motor at a time and run the setID() function with a different number for each motor.

https://github.com/thiagohersan/memememe/blob/mast...

That was it! Thank you so much!

Katherine29 made it! (author)2016-12-20

I also tried with these lines.

from time import sleep

from ax12 import ax12

import sys

sys.path.append("../")

servos= ax12.Ax12();

while true:

servos.move(9,500)

time.sleep(2)

servos.move(9,800)

time.sleep(2)

But neither had a good answer, returns these errors

thiagohersan (author)Katherine292016-12-21

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

And it really shouldn't be a problem, but just in the interest of debugging, have you tried moving a motor with ID 0?

Katherine29 (author)thiagohersan2016-12-21

I'm using your circuit https://cdn.instructables.com/FYR/OD9J/I9SOAQJN/FYR...

I tried without ID in a simple code but I got the same error. i don't know what is happening with servos. Hope can you help me...

thiagohersan (author)Katherine292016-12-25

That's a good circuit. But 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 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).

Katherine29 (author)thiagohersan2016-12-27

Finally I managed to move the Servo...

I found the error in the circuit... thank you, thank you, thank you... :)

Katherine29 made it! (author)2016-12-20

Hi Thiago

Yes I had proved the ttyAMA0 port, I did a bridge betwen the pin TX and RX for check if the port could send and receive and was successful.

I have 2 servos with ID 9 and ID 10 I tried with them and have modified your code only for two servos with their respective ID

for motorId in range (9,10)

and the code returns error.

Katherine29 (author)2016-12-18

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!!!

thiagohersan (author)Katherine292016-12-19

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.

BenP91 made it! (author)2016-07-17

Hello, I have been trying to use your library to run some motors, but whenever readData is called, we get a timeout error, from line 177. If we try to move a motor it will move, but then the error will be thrown. If we simply try to read any data, such as temperature, nothing will happen, and the error will be thrown. Some sample code is below

from ax12 import Ax12;

import sys;

import time;

self = Ax12();

self.move(1, 500);

Do you have any idea what is causing the problem? We double checked all the connections and they seem good. Could we possibly have messed something up when configuring the pi that could cause this? Thanks very much for your time.

thiagohersan (author)BenP912016-07-18

Hi.
What circuit are you using between the RPi and the motors?
There're some nice hard-coded delays in the code that are specific to my circuit. Different circuits might need slightly different numbers.

Also, if you have multiple motors, make sure you set them up to have different id numbers. (looks like you're only using one, but I figured I'd mention this anyway)

The purely read...() functions were not very extensively tested.... there could be a bug in those, but move(), moveSpeed(), moveRW(), moveSpeedRW(), etc have been tested and used.

BenP91 (author)thiagohersan2016-07-18

Hi, the circuit we are using is a 74LS241. We tried changing the TX_DELAY_TIME, going as low as 0 and as high as 5 and everywhere in between but to no avail. I think that the problem is somewhere in the readData function, as it keeps giving us that same timeout error no matter what sort of delay we use. Any thoughts? Thanks very much for your help

Also, our Pi is running the latest version of Raspbian Jessie, if that is important.

thiagohersan (author)BenP912016-07-19

For TX_DELAY_TIME, do you mean from 0 to 5 seconds ?
I think too long a delay is also bad. Did you try just varying it a bit, like from 0.00002 to 0.00005 or 0.00001?

What about RPI_DIRECTION_SWITCH_DELAY ?
That one is also a bit of hack...

If none of the functions are working, or they kind of work once, but then give timeouts, double check the uart circuit, maybe.... Because moveSpeedRW() and setAngleLimit() are all over our code, and they use readData(). They do still give some timeouts from time to time, but it's due to how often we send commands to the motors.

We haven't used functions like readTemp() or readPosition(), so they could be buggy, specially in the op commands being sent to the motors (the variables checksum and outData, for example).

wjd13 (author)thiagohersan2016-07-22

Hi thiagohersan, I have been working with Ben on getting the servo to move consistently. This afternoon, I tried varying the TX_DELAY_TIME as well as the RPI_DIRECTION_SWITCH_DELAY, but I still receive the timeouts from line 177 when trying to use readData(). For some reason, the try statement(see below) keeps getting exceptions.

try: assert ord(reply[0]) == 0xFF except: e = "Timeout on servo " + str(id) raise Ax12.timeoutError(e)

We will double check the uart circuit, and see if we can find anything. We're getting in another 3 servos soon as well, and perhaps running more than 1 will resolve the problem. Once again, thanks for your time, and please write if you have any thoughts on what we might change! :)

thiagohersan (author)wjd132016-07-23

Cool.

Make sure each motor has its own address. I forgot to do that once and wasted a lot of time debugging something that wasn't a bug....

wjd13 (author)thiagohersan2016-07-27

Hello again thiagohersan,

After looking at how T-Kuhn used the library here: (https://github.com/T-Kuhn/ScrewPicker/blob/master/ax12/ax12.py) we noticed that he just commented out the readData at the end of every write function. We did the same, and now everything works fine. So if anyone ever has a similar problem, try just commenting out that line. Thanks for all of your constant help :)

wjd13 (author)wjd132016-08-18

Hello again,

After weeks of research, international fact-finding missions, and mad scientific experiments, we figured out the issue. The ttyAMA0 port wasn't configured properly. Since we're running Raspbian Jessie, there was no /etc/inittab to edit, and so we just skipped it. Big mistake. In the end, we followed these steps below to correctly configure the ttyAMA0 serial port, and it works like a charm now. So for anyone trying to use these with Jessie, make sure ttyAMA0 is set up properly.

http://raspberrypi.stackexchange.com/questions/47671/why-my-program-wont-communicate-through-ttyama0-on-raspbian-jessie

HarithA1 (author)wjd132016-10-01

hi, did u manage to run the commands for a long period of time? can u share your values for TX_DELAY_TIME and RPI_DIRECTION_SWITCH_DELAY?

wjd13 (author)HarithA12016-10-01

Those two variables ended up not being the problem at all, the issue was we hadn't configured the ttyAMA0 port properly because we were running Jessie on the Pi. So I would go through and make sure you followed all the steps to configure it correctly. If you're running Jessie, this link should help

http://raspberrypi.stackexchange.com/questions/47671/why-my-program-wont-communicate-through-ttyama0-on-raspbian-jessie

thiagohersan (author)wjd132016-08-19

Cool. I'm going to add a note about it to the Instructable.

HarithA1 (author)2016-03-19

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)

thiagohersan (author)HarithA12016-03-20

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).

HarithA1 (author)thiagohersan2016-03-20

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

HarithA1 (author)HarithA12016-03-20

this is my code:

https://gist.github.com/hyxer/cdfe0b2f60b8155c0ffb

this is screenshot of the event is attached:

any idea?

wjd13 (author)HarithA12016-08-09

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?

HarithA1 (author)wjd132016-08-09

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

and i have not solved that..

thiagohersan (author)HarithA12016-03-21

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...

joymonkey (author)2016-02-10

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?

thiagohersan (author)joymonkey2016-02-11

Take a look at the circuit diagram for the uart duplexing:
https://cdn.instructables.com/FYR/OD9J/I9SOAQJN/FYR...

The 74LS241 is also being used to do level conversion.

MichalisM1 (author)2015-10-16

hi all !! sorry for bad English !! i am very noob in this stuff ,..so i need some serious help ! i have a robotics arm with 7 ax-18a motors .. i can do whatever with RoboPlus .. bad i have this project .. i need to know how can i "speak" to robbot with java if possible .. and if possible how can i do this ... Can any one help me ?? .. (CM-700 and LN-101)

HaR3 (author)2015-08-13

thank you so much!! i think i can go on from here onwards. May you be rewarded for your good deed.

HaR3 (author)2015-08-11

can you give a sample code on how to use your library? sorry, quite new to this things

thiagohersan (author)HaR32015-08-12

UNTESTED !!!

But this moves 6 motors to position 500, at a speed of 200:
https://gist.github.com/thiagohersan/061a8b399a8aa...

HaR3 (author)thiagohersan2015-08-12

Thanks for your reply! but Your code returns error:


Traceback (most recent call last):

File "yrrobot.py", line 18, in <module>

servos.moveSpeedRW(motorId, anglePosition, motorSpeed)

File "/home/pi/codes/ax12/ax12.py", line 389, in moveSpeedRW

return self.readData(id)

File "/home/pi/codes/ax12/ax12.py", line 177, in readData

raise Ax12.timeoutError(e)

ax12.timeoutError: Timeout on servo 2


i only have 1 servo connected (with id 1). it turns out we need to have every servo reply first before it can move.

HaR3 (author)HaR32015-08-12

after some tinkering and try&error, i manage to get the servo working using:

from ax12 import Ax12

import time

self = Ax12();

whileTrue:

self.move(1,200)

time.sleep(2)

self.move(1,800)

time.sleep(2)

# self.readTemperature(1)

# time.sleep(2)

however, the readTemperature function does not display anything on terminal (after uncommenting of course), do i have to add something to make it print to terminal?

thiagohersan (author)HaR32015-08-13

readTemperature(1) returns a value. To print it you need to use:

print readTemperature(1)

thiagohersan (author)HaR32015-08-13

Did you change the range in the for loop? It looks like it's crashing while looking for motor number 2....

About This Instructable

49,426views

154favorites

License:

Add instructable to: