Arduino stops receiving GetPosition data after a few seconds

I’m attempting to control an ODrive using an arduino pro micro over serial and am having trouble with the serial connection - specifically reading data from the ODrive while the motors are running closed loop position control. I’d like to be able to read/write to both axes at ~100hz and will need fast baudrates to free up processing time on the arduino. ODrive is a new v3.6.56 board running 0.5.4

I can use the GetPosition() function to read at 250000 baud reliably from both axes with the motors off, but switch the motors on and within a few seconds GetPosition() begins timing out and never returns a valid number again.

It will work for longer at 57600 baud, up to a few minutes, but eventually the GetPosition() will fail.

At all baud rates I still reliably use SetPosition() to control both motors, even after GetPosition begins to timeout.

I am using hardware serial on the micro, and have toroids with 6 wraps on the motor lines as well as shielding wrap with a drain wire on the motor lines, encoders, and the short serial line to the arduino. Arduino and odrive are connected to my computer with usb isolators. Odrive runs off a 24v madewell power supply.

Have the same problem with a teensy2.0 board.

#include <HardwareSerial.h>
#include <ODriveArduino.h>
#include <ODriveEnums.h>

// Printing with stream operator helper functions
template<class T> inline Print& operator <<(Print &obj,     T arg) {
  obj.print(arg);
  return obj;
}
template<>        inline Print& operator <<(Print &obj, float arg) {
  obj.print(arg, 4);
  return obj;
}

//Hardware serial port
HardwareSerial& odrive_serial = Serial1;

// ODrive object
ODriveArduino odrive(odrive_serial);

int counter = 0;
float testPosY = 0.0;
float testPosX = 0.0;
float readXPos, readYPos;


void setup() {
  //PC serial
  Serial.begin(115200);

  // Arduino -> Odrive serial begin
  odrive_serial.begin(115200);

  //Start motors
  odrive.run_state(0, AXIS_STATE_CLOSED_LOOP_CONTROL, false);
  delay(1);
  odrive.run_state(1, AXIS_STATE_CLOSED_LOOP_CONTROL, false);
  delay(1);
  odrive.SetPosition(0, 0);
  delay(1);
  odrive.SetPosition(1, 0);

  delay(500);


}

void loop() {
  //read positions
  odrive_serial << "r axis0.encoder.pos_estimate\n";
  delay(1);
  Serial << odrive.readFloat() << "\n";
  odrive_serial << "r axis1.encoder.pos_estimate\n";
  delay(1);
  Serial << odrive.readFloat() << "\n";
    
  if(counter == 1000){
    counter = 0;
  }
  testPosY = 0.1*sin(counter*0.0062831853);
  testPosX = 0.1*cos(counter*0.0062831853);
  odrive.SetPosition(0,testPosY);
  odrive.SetPosition(1,testPosX);
  counter++;
}

I get the same results using normal GetPositon functions without delays between reads/writes.

Can anyone point me in the right direction? Do I need to come up with my own error handling on the serial line? I don’t have a scope to check what’s really going on.

Sometimes you need to put a small series resistor e.g. 50 Ohm in the Tx and Rx lines to prevent ‘ringing’ when the line changes state suddenly.
It could also be a clock problem - perhaps the CPU clock on the Teensy is not very accurate? Are you running from a crystal or an RC oscillator on the Teensy?
If you don’t have a scope, you could perhaps get one of the cheapy Logic analyser clones e.g. USB Logic Analyzer - 24MHz/8-Channel - TOL-18627 - SparkFun Electronics

Out of curiosity, what are the blue wires going from the toroids to a Y-splice? These are the drain wires you talked about maybe?
If so, they should be connected to the ODrive’s GND (solder straight to DC - pad), and not go anywhere near the serial comms signals.

Thanks for the response. The teensy and pro micro boards both have external 16mhz oscillators which from what I’ve read are preferable over the internal clock.

You are correct, the blue y-splice is the shielding drain wire. I’ve moved it to the DC- pad. I’m wondering if it should be re-routed so it goes around the toroids instead of passing through them?

I’ve also added the 50ohm resistors to the tx/rx lines. Still getting timeouts from GetPosition.

I started just printing the individual characters received on the Serial1 line in case there was any weirdness with string->float conversion missing characters that would give me a clue. I found a pattern, the serial buffer always runs empty after 1 or 2 characters of the expected input.

#include <HardwareSerial.h>
#include <ODriveArduino.h>
#include <ODriveEnums.h>

// Printing with stream operator helper functions
template<class T> inline Print& operator <<(Print &obj,     T arg) {
  obj.print(arg);
  return obj;
}
template<>        inline Print& operator <<(Print &obj, float arg) {
  obj.print(arg, 4);
  return obj;
}

//Hardware serial port
HardwareSerial& odrive_serial = Serial1;

// ODrive object
ODriveArduino odrive(odrive_serial);

int counter = 0;
float testPosY = 0.0;
float testPosX = 0.0;
String readIn;

void setup() {
  
  //PC serial
  Serial.begin(115200);

  // Arduino -> Odrive serial begin
  odrive_serial.begin(250000);

  //Start motors
  odrive.run_state(0, AXIS_STATE_CLOSED_LOOP_CONTROL, false);
  //delay(5);
  odrive.run_state(1, AXIS_STATE_CLOSED_LOOP_CONTROL, false);
  //delay(5);
  odrive.SetPosition(0, 0);
  //delay(5);
  odrive.SetPosition(1, 0);

  delay(500);


}

void loop() {
  //read positions
  odrive_serial << "r axis0.encoder.pos_estimate\n";
  delay(1);
  while(odrive_serial.available()){
    char c = odrive_serial.read();
    readIn += c;
  }
  
  Serial << "read:" << readIn;
  readIn = "";
    
  if(counter == 1000){
    counter = 0;
  }
  testPosY = 0.1*sin(counter*0.0062831853);
  testPosX = 0.1*cos(counter*0.0062831853);
  odrive.SetPosition(0,testPosY);
  odrive.SetPosition(1,testPosX);
  counter++;
}

I’m going to try installing the firmware development tools on my machine and see if I can mess around without breaking things. Maybe try broadcasting encoder positions without needing to ask for them.

Yeah all sounds good, including routing the train wires outside the toroids.

Have you tried changing that 1 (ms? us?) delay?

Also, i’m not sure what the while(odrive_serial.available()) does… Is that from an example?
(I haven’t used the Arduino libraries myself) but it would be calling that function before each character.

Yes, delay() is in milliseconds, I’ve tried it at 10 and it doesnt seem to change behavior.

Serial.available() returns the size of the serial input buffer, so the loop pulls characters out of the buffer and adds them to the string readIn. When the buffer is empty, it forwards the readIn string to the PC on the USB serial line, and is erases it for the next cycle.

I had a nodemcu lying around, which is 3.3v logic but only has a single hardware uart that it shares with the usb port. I edited a websocket example to throw the string it reads from the odrive to the websocket, and it also stops receiving anything on the serial port soon after the motors turn on. So not a 3.3v/5v issue.

Motor control is still active, I can set motor position from the arduino, and read positions from my PC with odrivetool. Just nothing comes out of the odrive’s uart.

I’ve tried reflashing 0.5.4 and clearing configuration, but no luck. I might try flashing an older firmware.

I’ve got a microcontroller with a CAN transceiver onboard on the way, to see if that can do what I need.

1 Like

That’ll do! CAN is much more reliable than RS232 and is properly designed for exactly this kind of thing. :grin:

1 Like

I got one of the logic analyzers (thanks amazon) and hooked it up. This is a screengrab of "r axis0.encoder.pos_estimate\n" requests with a corresponding reply and a 10ms delay before the next request, but the odrive just stops responding at all a little while after the motors start closed loop position control.

It can keep reading in setPosition commands, but for some reason stops responding to encoder position requests.

I got the firmware development tools set up, but its a bit overwhelming and I don’t really know where I can put in something to output requests without needing to ask for them. I see that the CAN output has this feature, but don’t know how to implement it over UART.

Downgrading to 0.5.1 seems to have fixed my issue. The motors also seem less noisy/jumpy, even though I am using all the same gains.

I think the units have changed since 0.5.1 from encoder counts to revolutions, so the gains are in a very different scaling.

This sounds like it could be a bug with ODrive. What version of firmware are you running exactly?
Have you tried building the latest ‘master’ from Git and flashing that?

Are you using UART in native mode? 0.5.2 and up introduced a bug there:

I was originally running the firmware that came flashed on my board, which was some 0.5.4-dev build. I tried flashing to the 0.5.4 release build and had the same problem.

I tried setting uart0_protocol = STREAM_PROTOCOL_TYPE_Ascii on both versions with no success.

For the time being I’m happy with 0.5.1, but I think the less noisy/jumpy observation was placebo.