Velocity estimates lagging

Dear community!

I am glad to work with the ODrive S1 to drive a differential robot. Using ramped velocity control, the robot exactly drives around how I want. I am using a Raspberry Pi and a Waveshare RS485 CAN HAT to control the ODrive S1 via the CAN bus (1 Mbps).

However, I ran into a strange issue and hope someone knows a solution.

When the motors are engaged and I want to set a motor velocity to (for example) 0.5 m/s, the motors do so instantly (nearly a second). However, the velocity estimate (Vel_Estimate) is somehow lagging. As you can see in the graphs below, the velocity estimates (I converted it to m/s) slowly build up, although the motor already reached the expected velocity. It can take like 15 seconds until I see the correct velocity. Is there a reason why I receive the values so late?

I’m reading the velocity estimate from the cyclic messages:
https://docs.odriverobotics.com/v/latest/manual/can-protocol.html#can-msg-get-encoder-estimates

Here are some configurations as well:

"can.config.baud_rate": 1000000,
"axis0.config.encoder_bandwidth": 100,

Best regards,
Markus

To bisect the issue between CAN communcation delays, or velocity estimator issues, can you plug in USB on the ODrive and plot the velocity in the web gui?

  • If the issue exists in the gui also: check the axis.config.encoder_bandwidth make sure it’s something between 100 and 1000.
  • If the issue doesn’t exist in the GUI: I would suggest looking for issues with your raspberry pi host code, and try CAN debug utils like candump to see what might be going on.
  • If all else fails, try erasing the ODrive and setting it up again.
1 Like

Thank you very much for the hints and your quick answer!

The ODrive is working correctly. The web GUI shows an instant velocity estimate change. Also, candump reveals that the packages are correctly sent by the ODrive over time.

I need to debug my code and Raspberry settings. Let’s see.

CAN FIFOs are very frequently to blame :slight_smile: maybe if you post your code we could take a quick look?

1 Like

I resolved the issue some hours ago.

Apparently I made a rookie mistake :smirk: and just used the RX code from the tutorial without thinking. My Python function was as follows (abstracted it a bit to make it universal).

def send_rx_cmd(command_id, data_format, data_bytes):
    for msg in bus:
        if msg.arbitration_id == (node_id << node_id_offset | command_id):
            return struct.unpack(data_format, bytes(msg.data[:data_bytes]))

_, velocity = send_rx_cmd(0x09, "<ff", 8)

I didn’t have time to dive deeper into the can library, but I guess this code always returns the first message in the CAN queue. Thus, the queue has to fill completely and new messages need to throw out the result so that there is a new one. Obviously, the signal I measured lagged.

I then changed the code to compare the current time with the CAN message time. Take only the messages from the last 1 ms. This is my new code:

def send_rx_cmd(command_id, data_format, data_bytes, read_period):
    for msg in bus:
        if msg.arbitration_id == (node_id << node_id_offset | command_id):
            if time.time() - msg.timestamp <= read_period:
                return struct.unpack(data_format, bytes(msg.data[:data_bytes]))

_, velocity = send_rx_cmd(0x09, "<ff", 8, .001)

Works like a charm! I mean you could always optimize the code (e.g. with a Listener) but it’s working to my requirements.

Thanks again for the quick and friendly support! :smiley:

Ah, that makes sense! One thing you could also do is this:

def send_rx_cmd(command_id, data_format, data_bytes):
    latest_matching_message = None
    for msg in bus:
        if msg.arbitration_id == (node_id << node_id_offset | command_id):
            latest_matching_message = struct.unpack(data_format, bytes(msg.data[:data_bytes]))
    return latest_matching_message

Which will return the most recent message, as opposed to the earliest one that is earlier than the read_period.

Glad we can help, let me know if there’s anything else!