Best approach of using odrive to paint picture with a pen and XY-plotter?

I want to help a friend with an art project. She wants to build a robot that can paint pictures with a pen very fast. Since I already have an odrive and some experience building printers and CNC machines, I want to help her. Her goal is to paint the picture as fast a possible, the pen should blur in front of your eyes due to the fast movement. Theorethically this should be possible with a lighweight pen.

I saw this video from the creator of the odrive do something very similar:

And I also found this video from Capo (is he also on this forum?)

He has a slightly different approach, and his solution comes very close to what I want to build, but his plotter seems to make some short “stops” in between the movements.

So I am wondering: what the best way is to control the trajectory of the pen if the input data is a list of coordinates that the pen must be moved to one after the other, and in between these points it should be moved in a straight line? Some of these coordinates can also be located along a straight line or a slightly curved line, so it makes no sense to use step-direction or direct position control as I do not want to stop at each point.

I read that the odrive supports trapezoidal trajectories and they seem to be the best solution to this problem. But how can I feed a list of coordinates into it? The way it is implemented right now will give me a wrong line if I send the next coordinate too early and will make the motor stop and wait if I send it too late. Is there any way to send it a list of coordinates that it must reach time-optimal? Or do I need a different approach?

Hi @Spacemarine

In the first clip I believe @madcowswe was just feeding the odrive a series of move commands to create a simple demo. No path planning that I can see.

I made the pen plotter in the second clip. Odrive does not support direct g-code input at this time (may be worth confirming this, I have not checked in a long time) and so you will need something else to do your path planning for it. All movement was controlled by the step/dir interface with a Reprap gen 7 electronics parsing g-code. The g-code was created by using a plugin with Inkscape designed for creating g-code for laser cutting applications. I would not reccomend the gen7 electronics option as this particular RepRap hardware was developed by a single person and is not really supported any more. The reason I chose this option at the time was that the Gen 7 electronics was one of the first microcontrollers I could find that could create the step pulses fast enough (e.g. >50kHz) to be able to provide a smooth motion even at high speed.

If I was to do it all again I would use something like Linux CNC in combination with one of their supported microcontrollers to do the g-code parsing and step/dir pulse generation. This will give you the best future upgrade path support that allows you to do a lot more than just pen plotting if you choose to in the future.

1 Like

Hi @Richard_Parsons,

thanks for your reply! I checked if there was some kind of g-code support in odrive, but I only found this reference: Gcode interpreter
I think the implementation is probably too immature for now


LinuxCNC is definately a good idea, I’ll look into that.

If I understand you correctly, you used Reprap to parse the g-code and generate the trajectory, which was then broken down into steps inside the raprap and the individual steps were sent out to the odrive at a fixed frequency (>50 KHz in your case). The odrive then tried to follow every individual step using it’s internal PID control loops. Here are the things that I don’t understand:

  • How can a PID controll loop that runs at 8 KHz follow a set signal that comes in at 50 KHz? Or is my assumption wrong that it tries to follow every step?

  • What happens if you send a trajectory that leads to an acceleration that is above the physical limit of the odrive and the plotter? Will it slow down to complete the requested movement or will it skip steps, e.g. turn sharp corners into round corners?

  • If you request a slow motion via the step/direction interface, what the odrive sees is: move-wait-wait-wait-wait-move-wait
 etc. Doesn’t this mean that the movement will be jerky and can cause oscillations and vibrations?

Is there any place I can read more about this?

@spacemarine You could try the input filtering on the RazorsFrozen branch (this uses a filter instead of a trajectory generator, and so responds to changes in demand immediately but still produces smooth motion), and iterate over your coordinate list in Python, by first checking if the position error on both axes is below a certain value, and then sending the next pair of inputs.
Depending on the values you pick for the filter bandwidth and in-position tolerance, your picture might be a little distorted. If you lower the in-position tolerance too much it might wait noticeably between points or even get stuck halfway through.

If I understand you correctly, you used Reprap to parse the g-code and generate the trajectory, which was then broken down into steps inside the raprap and the individual steps were sent out to the odrive at a fixed frequency (>50 KHz in your case). The odrive then tried to follow every individual step using it’s internal PID control loops.

Yes this is correct except that the steps sent to the odrive are not at a fixed frequency.

Every step pulse sent by the reprap board to the odrive will increment the commanded position by a user-specified amount. e.g. 1 pulse = ± 1 to the commanded position. Depending on if the direction (dir) pin is high or low the commanded position will increment up or down. If a single pulse is sent the motor will move by a single command position which translates to a single encode count. If a fixed stepping frequency is sent then the commanded position will increase constantly and the motor will rotate at a constant speed accordingly.

How can a PID controll loop that runs at 8 KHz follow a set signal that comes in at 50 KHz? Or is my assumption wrong that it tries to follow every step?

The 8 kHz PID loop just means that the current supplied to the motor, and therefore the torque is only updated at 8 kHz. To give an example: If you are using an 8192 count per rotation encoder, and 1 step pulse is equal to 1 updated position command, then supplying a square wave of 8192 Hz to the step pin on the odrive will rotate the motor at 1 Hz (60 RPM). The position command is being updated faster than the PID loop but this doesn’t matter since the motor is operating at a fixed speed and so the speed is constant. The only time the 8 kHz PID loop frequency would be a concern is if you were doing some ultra high-speed changes in the order of milliseconds, in which case the 8 kHz PID loop may act as a filter of sorts, rounding off your sharp speed changes.

What happens if you send a trajectory that leads to an acceleration that is above the physical limit of the odrive and the plotter? Will it slow down to complete the requested movement or will it skip steps, e.g. turn sharp corners into round corners?

Hopefully the answer to this clear now. If the commanded position changes faster than a motor can respond, then the position error (encoder position - commanded position) will grow until such time as things slow down and the motor can catch up. This is unlike a stepper motor, where a lost step is unknown to the motor controller (when running open loop) and so that missed motion is never recovered.

If you request a slow motion via the step/direction interface, what the odrive sees is: move-wait-wait-wait-wait-move-wait
 etc. Doesn’t this mean that the movement will be jerky and can cause oscillations and vibrations?

Yes, except this ‘jerky’ motion is on the order of the encoder resolution, typically 8192 counts per rotation or ~0.044 degrees, which is almost imperceptible by eye when looking at a motor shaft. You should not see any vibration if the PID values are tunned correctly.

@Richard_Parsons Thanks, that makes it very clear!

So one could say that you can increment/decrement the requested position up to 50K times per second and the odrive PID algorithm will adjust torque/current 8K times per second to try to reach the most recent position request?
Is there a limit on how fast the step/dir signals can be sent?

@towen Thanks for the suggestion! Is there any information on how this filter works and how it behaves? I didn’t find anything, but maybe I wasn’t looking in the right place.

How fast can I check the position of the axis and issue a new command? I want the smallest segments to be done in the range of 1 ms, so I need to do this at least 1000 times a second.

What is the advantage of using the input filter instead of the trapezoidal trajectory planner if I read axis postion and send the new position command at exactly the right time?

@spacemarine the advantage I suppose is that you don’t have to issue the commands at exactly the right time
 The filter will effectively interpolate between setpoints for you. If you send two setpoints close together, it will move slowly between them, but if you send two setpoints far apart, it will move faster. Whereas the trapezoidal planner will always used a fixed acceleration and deceleration rate, and it will try to slow down as it approaches its setpoint whereas maybe you don’t want that. With the input filter, you can change setpoints without slowing down.
Also, the trajectory planner isn’t really designed to accept repeated changes of setpoint, you are meant to let a move finish and come to a stop before you send the next one. I’ve had poor results trying to use it on a loop, and it was easier just to control the setpoint directly.

The USB interface to Python is quite capable of updating at 1kHz, but if you’re updating this fast then you might not need any filtering or trajectory planning at all.

For a test, I made a little script in the odrivetool python interface, using mouse events under Linux. Basically, I subscribe to events from /dev/input/mice (using python evdev module) and on a mouse event increment axis0.controller.pos_setpoint by the X change, and axis1 by the Y change. The two motors would very smoothly follow my mouse cursor around, and I could move them very fast.
With the trajectory planner, I would get very jerky motion. It may be that planning a trajectory takes a certain amount of processing time on the ODrive end.

@Richard_Parsons

I looked into this topic and I found Klipper, do you know it? It basically separates the motion planning and the generation of the step signal into two components, one component (for motion planning) with a lot of processing power and the other component (for generating setp signals) into a real-time capable hardware. Most people use a Raspbery Pi and a common micro-controller to do that and can reach step-speeds of up to 922 Khz on some hardware. Source -> https://www.klipper3d.org/Features.html

Another benefit is that the motion-planning part is implemented in python, so it should be easy to account for different kinematics or list with waypoints in uncommon formats.

To mee it looks like this should be the better solution that LinuxCNC for my case, but I might be wrong.

2 Likes

That sounds like another good option. Having never used either I’m probably not the best person to ask. Perhaps stop by the odrive discord and ask for peoples thoughts in the machining channel
on klipper vs linuxcnc when using odrive?

1 Like

That demo used trapezoidal trajectories.

1 Like