Encoder reading using SPI port (Biss-C)

That looks good :slight_smile:

So the byte order is the opposite of what I expected - its very unclear from the datasheet screenshot…

So then probably:

  uint16_t rawval16 = (buff[1]<<8) | buff[2];          // raw value, 16bit
  float angle_ = (float)rawval16 / 0x10000 * _2PI;      // angle in radians
  uint8_t crc = buff[0] &0x3F;                         // crc - checking it is a bit more complicated
  bool err = ((buff[0]&0x80)==0);                      // error flag
  bool warn = ((buff[0]&0x40)==0);                     // warning flag

Thanks!

Seems to be the correct values now and the sensor check goes well, but when implementing the generic sensor the motor just shakes, motor runs fine during the sensor calibration.

But once it gets to the loop it start shake:

motor.loopFOC();
motor.move(1);

float readMySensorCallback()
{
  uint8_t buff[3] = { 0x00, 0x00, 0x00 };
  SPI.beginTransaction(settings);
  SPI.transfer(buff, 3);
  SPI.endTransaction();
  
  uint16_t rawval16 = (buff[1]<<8) | buff[2];          // raw value, 16bit
  float angle_ = (float)rawval16 / 0x10000 * _2PI;      // angle in radians
  return angle_;
}

Is there something i’m missing?

Which mode are you using?

First you should try torque-voltage mode, this is the simplest closed loop mode, and only needs a well-working sensor…

 // power supply voltage [V]
driver.pwm_frequency = 25000;
driver.voltage_power_supply = 24;
driver.voltage_limit = 3;
driver.init();

motor.linkDriver(&driver);
motor.controller = MotionControlType::velocity;
motor.PID_velocity.P = 0.2;
motor.PID_velocity.I = 20;
motor.PID_velocity.output_ramp = 1000;
motor.LPF_velocity.Tf = 0.01;
motor.voltage_limit = 1;

motor.init();
motor.initFOC();

Ok I can try torque mode and see.

Yes, the velocity mode depends on torque mode, and you need to tune the PID to match your motor. 20.0 is probably too high for the I term.

The voltage limit of 3/24 is not ideal: this is a low utilisation of the PWM range, all the PWM duty cycles will be very short. It would be better to use a 5V power supply if you only need 3V on the motor.

Which type of motor is it, what’s the winding resistance?

And how many pole pairs has the motor, did you configure it correctly? Its important to get the pole-pair number correct…

Hi,

The motor is in this topic (25A): Harmonic Drive - motors

Pole pairs is 6 and phase resistance 3.7

Ok, this seems ok. With 3.7Ω phase resistance you can probably go a little higher with the voltage… but 1V motor voltage limit should be enough to move the motor.

What about torque-voltage mode, is it working?

Yes it moves fine during initialization so voltage seems fine during testing.

I have tried torque mode as well with same results, not sure what I’m missing here. Encoder provide 0 - 6.28 values and PID ia set low.

Question; should one full rotation go from 0 - 6.28?

Yes, that’s 2PI radians, or one full turn…

How is the behaviour in torque mode? Is it “just shaking”?

Would you mind sharing the full code that you are testing at the moment?

Hi,

I’m new to motor driving but I it will probably be easier when all start to make sense, yes it’s still shake on same place. :slight_smile: I appreciate your help!

That’s interesting, encoder is mounted on motor shaft but when I turn motor by hand it goes from 0 to 6.28 quite fast not a full motor turn. Can it be that encoder is calibrated somehow with gearing? Motor gearing is 30x for output shaft, but encoder is not mounted on output shaft…

I dont have access to code right now but it’s same as for torque example but with voltage limit of 3.

I will make a test to see how many times it reaches 2PI radians when made one full motor turn.

This is a strange sensor, but then yes, it sounds like it is somehow calibrated for maybe the output shaft position? Have you checked if the 0-2PI corresponds to one full turn of the output shaft?
→ if yes, then you could divide the value by the gear-down, i.e. 1/30…

But the datasheet you pasted says “Absolute position / revolution (motor side)” - which I would understand to mean before the gear-down…

The SimpleFOC code absolutely cannot work if the sensor does not return 0-2PI corresponding to the rotor position.

Perhaps another explanation is that the byte order is still wrong: maybe we’re using the low byte as the high byte? Then you would seem to get many 0-2PI outputs per revolution…
Is it better if you do:
uint16_t rawval16 = (buff[2]<<8) | buff[1];
or does that make it even worse?

Hi,

uint16_t rawval16 = (buff[2]<<8) | buff[1];

This makes it worse if switch so it seems fine, I notice when I turn maybe 5-10 degree of motor shaft it goes from 0 - 2PI, and values looks fine each time.

If I divide the value by the gear-down it jumps only from 0 - 0.21

I made a test with some counter that check each full turn of motor and when it reach 2PI

angle_rad: 0.80 - count: 10

So it tool 10 times to rotate 1 full motor turn and each range from 0-2PI

You would have to take the remainder after dividing by thirty, so:

float angle_ = (rawval16 % 30) / 30.0f * _2PI ;

But 16bit / 30 = 2184.5333, i.e. 16 bits is not an exact multiple of the gear-down…

So you may want to convert do the whole thing using float math:

float step = 0x10000/30.0f;
float angle_ = fmod((float)rawval16, step)/step * _2PI;

Does that help any?

I tried it, but result got worse, the gearing is also on output shaft and not on encoder motor shaft.

Same applies to 10 - 16 bit is not an exact multiple, so:

float step = 0x10000/10.0f;
float angle_ = fmod((float)rawval16, step)/step * _2PI;

I tried that too, but now it jumps very fast between 0 - 2PI.

So I suppose some magic math like you made now but it step up 10x? But still makes no sense when encoder states: Absolute position / revolution (motor side)

This code should do that:

float step = 0x10000/10.0f;
float angle_ = fmod((float)rawval16, step)/step * _2PI;

I also don’t quite understand this datasheet…

With this code it jumps from 0 - 2PI each ~1 degree, so it should be the other way around.

Yes the datasheet is very bad sadly, and from big engine company :slight_smile:

Well, you can also multiply by 10 first:

uint32 raw_shaft = raw_angle16 * 10;
_angle = (float)raw_shaft / (float)0x10000 * _2PI;    // range is now 0 - 10x 2PI
_angle = _normalizeAngle(_angle);                            // shaft angle, range 0 - 2PI

But it should be the same result… unless I’ve missed something obvious. That happens sometimes :slight_smile:

Hi,

Yes just small jumps from 0-2PI each degree, like should require to remap the full range ~10xPI2 to 2PI somehow, not sure how to make that. Or maybe increment parameter (adds/subtract during motion) and use that value as raw.

Just spinning ideas :slight_smile:

It’s very wierd, maybe they failed to calibrate it from factory for a full turn. But that is a long shoot :wink: