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

60,566

155

38

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.

Supplies:

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

  • Arduino Contest 2019

    Arduino Contest 2019
  • Woodworking Contest

    Woodworking Contest
  • IoT Challenge

    IoT Challenge

38 Discussions

0
None
selcuky3

2 months ago

how can i change the direction of the servo( i mean clockwise, counterclockwise), because i'm using servo as a wheel

2 replies
0
None
selcuky3selcuky3

Reply 2 months ago

i managed to run ax12's on raspberry pi 3 strecth finally. i used https://github.com/horverno/deep-pilot/blob/rpi/ax12.py library for python 3 support. circuit is : https://cdn.instructables.com/FJF/UKDF/J4YFU1YG/FJFUKDFJ4YFU1YG.LARGE.jpg
you have to look at ax12.py line 131 and 142.
line 131 direction pin on your raspberry (mine 12, not 18 - BCM setting)
line 142: i set ttyAMA0 baudrate 1000000

gpio mode 15 ALT0
gpio mode 16 ALT0
sudo stty -F /dev/ttyAMA0 1000000

and

https://raspberrypi.stackexchange.com/questions/47671/why-my-program-wont-communicate-through-ttyama0-on-raspbian-jessie
------------------------------------------
same direction for ax12's

code i used:

from ax12 import Ax12
import time

leftId = 3
rightId =5

self = Ax12()

self.setMovingSpeed(leftId,100)
self.setMovingSpeed(rightId,1123) # 100 + 1023
time.sleep(1)

ps:sorry for my english.



0
None
Swann Schillingselcuky3

Reply 7 weeks ago

Thanks for the info...where does the following have to be applied?

gpio mode 15 ALT0
gpio mode 16 ALT0
sudo stty -F /dev/ttyAMA0 1000000

Because I am still having difficulties with the libraries...
Anyways great help, because all the other posts refer to Raspian Jessie and not Stretch!

Also Python3 support is nice!! Thanks for updating!! :)

0
None
Swann Schilling

Question 2 months ago

Hey, I am also getting the ax12.ax12.timeoutError: Timeout on servo 1,
it drives the servo and then stops with the error...
I can servos.ping(1) and servos.readPosition(1) without error...
servos.move(1,500) gives me an error!

Also I noticed my rx and tx is on alt5...dont know if that is an issue,
I am connecting with port = serial.Serial("/dev/serial0", baudrate=1000000, timeout=0.1)

If I am commenting out returnself.readData(id), everything works fine...and I can read and write to the servo.
I have only one servo connected!

0
None
Dens_26

1 year ago

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
0
None
thiagohersanDens_26

Reply 1 year ago

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

0
None
Dens_26thiagohersan

Reply 1 year ago

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)

0
None
Dens_26Dens_26

Reply 1 year ago

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

0
None
Jason30_07

1 year ago

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
0
None
NicolásB77Jason30_07

Reply 1 year ago

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

0
None
mynameishamish

2 years ago

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
0
None
mynameishamishmynameishamish

Reply 2 years ago

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
0
None
Katherine29

2 years ago

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
0
None
thiagohersanKatherine29

Reply 2 years ago

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.

0
None
HarithA1

3 years ago

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)

1 reply
0
None
thiagohersanHarithA1

Reply 3 years ago

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