LiamBindle / PyVESC

PyVESC is an easy to use and robust Python implementation of the VESC - Open Source ESC communication protocol
https://pyvesc.readthedocs.io/en/latest/
Creative Commons Attribution 4.0 International
72 stars 54 forks source link

Help needed to encode parameters to measure flux linkage. #35

Open eliashauksson opened 1 week ago

eliashauksson commented 1 week ago

I'm encountering difficulties with encoding parameters for the flux linkage measurement, and would appreciate some guidance.

The issue is that unlike other measurement done with this library the flux linkage (COMM_DETECT_MOTOR_FLUX_LINKAGE) needs parameters to perform the test (current, min_rpm, duty and internal resistance) (as seen in the firmware source code)

Now my question is how is it intended to encode input parameters for the measurement using the VESCMessage?

If I try to do this the usual style, recommended by the docs, I don't know how to add the parameters:

class GetFluxMeasurement(metaclass=pyvesc.VESCMessage):
    id = VedderCmd.COMM_DETECT_MOTOR_FLUX_LINKAGE
    fields = [("foc_motor_flux_linkage", "i", 1)]

fluxRequest = pyvesc.encode_request(GetFluxMeasurment)
fluxResponse = self.writeRead(fluxRequest) # self.writeRead is a function that sends the request and listens to the result

This doesn't perform the measurement, since the parameters are not encoded.

Here's what I have tried instead:

class GetFluxMeasurement(metaclass=pyvesc.VESCMessage):
    id = VedderCmd.COMM_DETECT_MOTOR_FLUX_LINKAGE
    fields = [
        ("current", "i", 1),
        ("min_rpm", "i", 1),
        ("duty", "i", 1),
        ("resistance", "i", 1),
        ("inductance", "i", 1),
    ]

    def setParameters(self, current, minRpm, duty, resistance, inductance):
        self.current = current
        self.min_rpm = minRpm
        self.duty = duty
        self.resistance = resistance
        self.inductance = inductance

fluxMeasurement = GetFluxMeasurement()
fluxMeasurement.setParameters(
    current=20000,
    minRpm=2000000,
    duty=300,
    resistance=int(self.VESCData["foc_motor_r"] * 1e3), # internal resistance from previous measurement.
    inductance=int(self.VESCData["foc_motor_l"] * 1e2), # internal inductance from previous measurement.
)
fluxRequest = pyvesc.encode_request(fluxMeasurement, header_only=False)
fluxResponse = self.writeRead(fluxRequest, timeout=15)

Note: I changed the encode_request() (here) so it would allow for header_only=False.

This code encodes the data into the following request: \x02\x15\x1a\x00\x00\x4e\x20\x00\x1e\x84\x80\x00\x00\x01\x2c\x00\x00\x77\x7f\x00\x00\x0b\x21\x0c\x0d\x03 compared to the request that gets sent using the vesc tool (recorded with wireshark): \x02\x15\x39\x00\x00\x4e\x20\x00\x1e\x84\x80\x00\x00\x01\x2c\x00\x00\x78\xe6\x00\x00\x0b\x2f\xc5\xb6\x03

Analysis:

My request makes the motor spin (rather fast, and not accelerating smoothly as it does with the vesc tool), and then the response gives an error while decoding: Decoding error: unpack_from requires a buffer of at least 21 bytes for unpacking 20 bytes at offset 1 (actual buffer size is 5). I think this is because the vesc returns some flux linkage measurement (1 result) while the VESCMessage expects 5 results, since I defined 5 parameters in fields.

Any suggestions or guidance on how to properly encode parameters for the flux linkage measurement, and what might be causing the discrepancies in the encoded request, would be greatly appreciated!

eliashauksson commented 1 week ago

I found a fix for the problem. It's definitely not the way it was intended to work but it works for me. The questions still stands on how to properly encode input parameters.

What I did is I made some variations in how the data is encoded and decoded:

def encodeFluxRequest(self, data):
    payload = pyvesc.protocol.base.VESCMessage.pack(data, header_only=False)
    payloadArray = bytearray(payload)
    payloadArray[0] = 0x39
    payload = bytes(payloadArray)
    packet = pyvesc.protocol.packet.codec.frame(payload)
    return packet

def decodeFluxResponse(self, response):
    try:
        fluxBytes = response[3:7]
        fluxValue = int.from_bytes(fluxBytes, byteorder="big", signed=False) * 1e-4
        return type("FluxResponse", (object,), {"foc_motor_flux_linkage": fluxValue}), 4

    except Exception as e:
        MessageHandler().columnPrint(f"Error decoding flux response: {e}")
        return None