Bad encoder counts in CAN messages on hoverboard motors

I’ve set odrv0.axis1.config.can.encoder_count_rate_ms = 10 and have confirmed manually that checking count_in_cpr changes as I rotate the motor.

In [45]: odrv0.axis1.encoder.count_in_cpr
Out[45]: 88

Modifying the can_dbc_example script I’m printing the values of the encoders before and after spinning them up

for message in bus:
    if message.arbitration_id == 0x0A | axisID << 5:
        print(db.decode_message(f'{axis}_Get_Encoder_Count', msg.data))
        break
Sweeping velocity between -4 and 4.
CTRL+C to stop

{'Shadow_Count': 1092616192, 'Count_in_CPR': 1092616192}
{'Shadow_Count': 1092616192, 'Count_in_CPR': 1092616192}
Velocity: -3.04
Velocity: -3.92
Velocity: -3.8
Velocity: -2.72
^C
Done! Setting to AXIS_STATE_IDLE
CLOSED_LOOP_CONTROL
CLOSED_LOOP_CONTROL
CLOSED_LOOP_CONTROL
CLOSED_LOOP_CONTROL
CLOSED_LOOP_CONTROL
CLOSED_LOOP_CONTROL
IDLE

Axis has returned to Idle state.
{'Shadow_Count': 0, 'Count_in_CPR': 2147483649}

I’m not sure how accurate those values are but what matters is they’re changing. My C++ code looks like this

int ODrive::get_encoder_estimates(axis axis){
  struct can_frame rx_msg;
  // Encoder estimates are sent every 10ms.  Read for 100ms
  auto start = std::chrono::high_resolution_clock::now();
  while (std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::high_resolution_clock::now() - start)
             .count() < 100) {
    int nbytes = read(sockfd, &rx_msg, sizeof(struct can_frame));
    if (nbytes < 0) {
      printf("Error in reading can frame\n");
      return -1;
    }
    if (rx_msg.can_id == (axis.id << 5 | GET_ENCODER_COUNTS)) {
      break;
    }
  }
  // print raw values of rx_msg
  printf("rx_msg.data: %d %d %d %d %d %d %d %d \n",rx_msg.data[0],rx_msg.data[1],rx_msg.data[2],rx_msg.data[3],rx_msg.data[4],rx_msg.data[5],rx_msg.data[6],rx_msg.data[7]);

  // return float(position_estimate);
  bytes2Float(rx_msg.data, &axis.encoder_position_estimate);
  return 0;
}

where bytes2Float just casts the raw int array to a float and assigns it to the pointer’s encoder position in the struct I’ve made, and GET_ENCODER_COUNTS is const uint32_t GET_ENCODER_COUNTS = 10;. Here’s the output of my test

Found heartbeat on Axis 0
Found heartbeat on Axis 1
Testing closed loop control...
rx_msg.data: 133 254 255 255 71 0 0 0 
rx_msg.data: 231 37 0 0 73 0 0 0 
Encoder counts: 6.12227e-41, -5.71949e+06
Closed loop control test passed  // Motors move here for 1 second
rx_msg.data: 133 254 255 255 71 0 0 0 
rx_msg.data: 231 37 0 0 73 0 0 0 
Encoder counts: 6.12227e-41, -5.71949e+06

What am I missing here?

Is there an endianness thing here? Can you swap the order of the bytes from 0-7 to 7-0?

I can, but that doesn’t change the fact that printing the raw bit values doesn’t show any change

I’ve corrected a bug in my Python code and it now agrees with odrivetool so the error must be in cpp code but nothing obvious is jumping out

import can
import cantools


db = cantools.database.load_file("/home/dev/ODrive/tools/odrive-cansimple.dbc")
bus = can.Bus("can0", bustype="socketcan")
axisID = 0x01
axis="Axis1"
for message in bus:
    if message.arbitration_id == 0x0A | axisID << 5:
        print(db.decode_message(f'{axis}_Get_Encoder_Count', message.data))
        break
$ python3 testing/python/get_encoder_counts.py 
{'Shadow_Count': 382, 'Count_in_CPR': 22}
In [3]: odrv0.axis1.encoder.count_in_cpr
Out[3]: 22

Is there a “solved” tag for threads? The correct CPR value is in the 5th index of the array

auto start = std::chrono::high_resolution_clock::now();
  while (std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::high_resolution_clock::now() - start)
             .count() < 100) {
    int nbytes = read(sockfd, &rx_msg, sizeof(struct can_frame));
    if (nbytes < 0) {
      printf("Error in reading can frame\n");
      return;
    }
    if (rx_msg.can_id == (axis0.id << 5 | GET_ENCODER_COUNTS)) {
      axis0.encoder_cpr_count = int(rx_msg.data[4]);
      break;
    }
  }
$ ./build/bin/test_can_lib 
Found heartbeat on Axis 0
Found heartbeat on Axis 1
Testing closed loop control...
Closed loop control test passed
Encoder counts: 69, 87
In [8]: odrv0.axis1.encoder.count_in_cpr
Out[8]: 87
$ python3 testing/python/get_encoder_counts.py 
{'Shadow_Count': 2877, 'Count_in_CPR': 87}

Awesome!!! Yes, that totally makes sense. Sorry, should’ve checked your code against the message definition :slight_smile: