Birth of Poppy Ergo Jr and support for low-cost XL-320 motors

A wild PoppyErgoJr has appeared!

It is still work in progress, but we already have a working prototype of this new Poppy Creature. It is based on the really low-cost dynamixel XL-320 motors. Those motors are now supported in pypot since version 2.2!

Thanks to @jgrizou, all the 3d printed parts were done using OpenSCAD and can thus be easily modified, hacked, tuned!

The hardware and software are already available on the github repository. It still needs some work but we are really excited to see what people will be able to do with this new creature. Or even better what kind of new creature they can imagine with this low-cost solution!

Oh and btw it can jump pretty well ^^
The code to make your own PoppyErgoJr jump is available as a notebook here.


This is pretty cool, good job guys :smile:
What do you mean by “low-cost”? It would look good on my new desk …

About 20€ per motor.

Experiments with Explauto are on their way @Clement



Flowers rule :smile:

Hi, the aim is that the xl-320 arm learns by itself how to move ? If you achieve to follow a direction with this kind of dynamics, it’s cool !

Hi Pierre
Have you un exported STL files, i don’t found it in repository ?


Ok super ! …thanks

What type of power supplie do you use ?

XL320 specifications: (주)로보티즈

Voltage: 6 ~ 8.4V (Recommended Voltage 7.4V)

Amperage: depends on the number of motors and their use, we recommend a power supply handling at least 2A, see below:

Just to be sure I’ll plug correctly the xl-320
1 : GND
2 : VCC+
3 : DATA

Someone could confirm ?

Cable for USB2

cable for xl-320

Yes, thats it!
I build a small adaptation circuit :

Problem to find the motors :


nothing hapened (no error message)?

Try to acces the motors. It works with one motor. But with two, I raise an error with dxl_io.scan()

Error message more readable here

DxlCommunicationError Traceback (most recent call last)
in ()
----> 1 print(dxl_io.scan())

c:\python27\lib\site-packages\pypot-2.7.1-py2.7.egg\pypot\dynamixel\io\abstract_io.pyc in scan(self, ids)
219 def scan(self, ids=xrange(254)):
220 “”" Pings all ids within the specified list, by default it finds all the motors connected to the bus. “”"
–> 221 return [id for id in ids if]
223 # MARK: - Specific Getter / Setter

c:\python27\lib\site-packages\pypot-2.7.1-py2.7.egg\pypot\dynamixel\io\abstract_io.pyc in ping(self, id)
213 try:
–> 214 self._send_packet(pp, error_handler=None)
215 return True
216 except DxlTimeoutError:

c:\python27\lib\site-packages\pypot-2.7.1-py2.7.egg\pypot\dynamixel\io\abstract_io.pyc in _send_packet(self, instruction_packet, wait_for_status_packet, error_handler, _force_lock)
523 if not error_handler:
–> 524 return self.__real_send(instruction_packet, wait_for_status_packet, _force_lock)
526 try:

c:\python27\lib\site-packages\pypot-2.7.1-py2.7.egg\pypot\dynamixel\io\abstract_io.pyc in __real_send(self, instruction_packet, wait_for_status_packet, _force_lock)
490 return
–> 492 status_packet = self.__real_read(instruction_packet, _force_lock=True)
494 logger.debug(‘Receiving %s’, status_packet,

c:\python27\lib\site-packages\pypot-2.7.1-py2.7.egg\pypot\dynamixel\io\abstract_io.pyc in __real_read(self, instruction_packet, _force_lock)
512 except ValueError:
513 msg = ‘could not parse received data {}’.format(bytearray(data))
–> 514 raise DxlCommunicationError(self, msg, instruction_packet)
516 return status_packet

DxlCommunicationError: could not parse received data ÿÿýaU^eÿ after sending DxlPingPacket(id=1)

From what the error shows I’m guessing you did not configure your motors so they all have the same id: 1. Thus when you plugged multiple motors on the same line collisions occurs (DxlCommunicationError).

Did I guess right?

Right :smile:
How should I configure my motors ?

Thanks to @Manon, you can do it using herborist:


I have an unstable behavior with the mini4dof. It works for about 30 seconds and after, one led of the USB2Dynamixel switch off and the other slowly flashes and the motors don’t move anymore. It raise an error in pypot :

No problem to control with low level IO.

Yeah I have the same issue as you do. I interpret this very odd “None Error” on the XL-320 as a current issue.

A very simple way to circumvent the problem is to change the way errors are handled in pypot. You can add the following lines to the BaseErrorHandler:

def handle_none_error(self, packet):

This should work but it’s not really fixing the problem. I’m stil investigating why those issues are raised. I let you know if I found anything. Otherwise I’ll just try/catch the error…


Another strange things (maybe a relation ?) The goto method doesn’t reach the position. The time is respected but the goal position is clearly not reach.
The difference is important in dummy mode and huge in minjerk mode. For example, this small sequence :


Fot the motor 2, no problem :



I guessed that with the wait argument the motor 3 (m3) should reach his position before to continue the running but in reality the wait=true act like a time.sleep=(3) and m3 never goes to 130 degrees. About 100 in dummy and less than 50 using minjerk.



Is it a reproductible issue or a problem with my computer ?

EDIT 1 :
More info about my problem to control the motors : Pypot main update loop seems to run slowly than 50 Hz and this raise problem because the calculated time become different from the real time. I’ll need to investigated more.

Is there an URDF model for the mini_dof ?

Sorry @juju I still haven’t find time to check your problem in details.


Yes! This a very important point! You can not force the run loop (or any other loops BTW) to really run at 50Hz except if you are running a real time OS.

Thus, if you want to do something time dependant, you should follow the examples we have used for the sinus primitive. The idea is not to suppose your update loop will be called every 20ms but to directly get the current time from the system. BTW, this is also the only way you will be able to have the same code running on the simulator (where times is not the same than in reality) and on the real robot:

pos = self._amp * numpy.sin(self._freq * 2.0 * numpy.pi * self.elapsed_time +  self._phase * numpy.pi / 180.0) + self._offset

Here self.elapsed_time will always get you the current time on your system (real or simulated). The precision of the time will depend on your system.