Arduino-FOC
Arduino-FOC copied to clipboard
Optimizing commander for virtual/native USB (serial)
Hi
As discussed in sFoCCommunity, there might be room for improving the communication between the controller and sFoCStudio, if the controller has a virtual USB port, capable of 64bit (full speed) USB 2.0.
While sniffing around the USB communication between the device and sFoCStudio, I can see that there is a lot of USB data packages with just one character. This makes sense when doing traditioned serial communication, but can create some "lag" when doing eg. 10khz foc loop, while transmitting and parsing data.
Now, I have been scrolling down the comandor code, and there is something I would like to understand.
I am a bit confused about the motor.monitor(); function. @JorgeMaker
In simpleFOCConnector.py, sFoC studio expects a identifier, before the monitoring variable. But running motor.monitor(); will not produce this. Am I missing something here ? It kinda look´s like sFoCStudio pulls the data, and that the motor.monitor(); is not used by sFoCStudio, which would make it counterproductive to call it in the first place, unless you want that data in the arduino IDE serial monitor? The problem being, that you can't be connected to sFoCStudio and IDE monitor at the same time, so this will go unnoticed. In the docs, under simpleFOCStudio, it said to use motor.monitor();.
def parseMonitorResponse(self, comandResponse):
if 'all' in comandResponse:
varStr = comandResponse.replace('all:', '')
states = varStr.rstrip().split('\t', 7)
self.targetNow = states[0]
self.voltageQNow = states[1]
self.voltageDNow = states[2]
self.currentQNow = states[3]
self.currentDNow = states[4]
self.velocityNow = states[5]
self.angleNow = states[6]
if 'target' in comandResponse:
self.targetNow = float(comandResponse.replace('target:', ''))
elif 'Vq' in comandResponse:
self.voltageQNow = float(comandResponse.replace('Vq:', ''))
elif 'Vd' in comandResponse:
self.voltageDNow = float(comandResponse.replace('Vd:', ''))
elif 'Cq' in comandResponse:
self.currentQNow = float(comandResponse.replace('Cq:', ''))
elif 'Cd' in comandResponse:
self.currentDNow = float(comandResponse.replace('Cd:', ''))
elif 'vel' in comandResponse:
self.velocityNow = float(comandResponse.replace('vel:', ''))
elif 'angle' in comandResponse:
self.angleNow = float(comandResponse.replace('angle:', ''))
This is from the MCU side, in the commander file:
note: It seems to me, that the commander is waiting for a CMD_MONITOR + a sub_cmd, before printing out the variables. I can´t seem to find a place where the simpleFOCConnector.py is parsing the values from motor.monitor(); ???
case CMD_MONITOR: // get current values of the state variables
printVerbose(F("Monitor | "));
switch (sub_cmd){
case SCMD_GET: // get command
switch((uint8_t)value){
case 0: // get target
printVerbose(F("target: "));
println(motor->target);
break;
case 1: // get voltage q
printVerbose(F("Vq: "));
println(motor->voltage.q);
break;
case 2: // get voltage d
printVerbose(F("Vd: "));
println(motor->voltage.d);
break;
case 3: // get current q
printVerbose(F("Cq: "));
println(motor->current.q);
break;
case 4: // get current d
printVerbose(F("Cd: "));
println(motor->current.d);
break;
case 5: // get velocity
printVerbose(F("vel: "));
println(motor->shaft_velocity);
break;
case 6: // get angle
printVerbose(F("angle: "));
println(motor->shaft_angle);
break;
case 7: // get all states
printVerbose(F("all: "));
print(motor->target);
print(";");
print(motor->voltage.q);
print(";");
print(motor->voltage.d);
print(";");
print(motor->current.q);
print(";");
print(motor->current.d);
print(";");
print(motor->shaft_velocity);
print(";");
println(motor->shaft_angle);
break;
default:
printError();
break;
}
In the simplefoc studio the motor.monitor is parsed separately in the additional thread. https://github.com/JorgeMaker/SimpleFOCStudio/blob/0e460b665964308d3e6dba8d61d5473a58c26b2b/src/simpleFOCConnector.py#L887
The commander has only some limited features concerning the monitoring. All of them are asynchronous. Monitoring function of the motor.monitor is synchronous and so far does not print any credentials of the motor (commander id) with it, so in that way you can only monitor one motor at the time. That is a place for improvement for sure, also I agree that it is a bit strange that monitoring and the commander are separated. So any suggestions and PRs in this regard are welcome.
This function is calling updatestates(); which is sending MG (monitor Get) commands.
class StateUpdateRunner(QtCore.QThread):
def __init__(self, connector=None, *args,**kwargs):
super(StateUpdateRunner, self).__init__(*args, **kwargs)
self._stop_event = threading.Event()
self.deviceConnector = connector
def run(self):
try:
while not self.stopped():
if self.deviceConnector is not None:
if self.deviceConnector.commProvider.serialComm.isOpen():
self.deviceConnector.updateStates()
time.sleep(1)
except SerialException as serialException:
logging.error(serialException, exc_info=True)
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
You're right. I've sent you the link to the wrong class. It's this one: https://github.com/JorgeMaker/SimpleFOCStudio/blob/0e460b665964308d3e6dba8d61d5473a58c26b2b/src/simpleFOCConnector.py#L822
Ah, Ok my mistake,
I now see why the SFoCStudio "Simplefoc digital read out" section is so unresponsive.
If you completely change the motor.monitor(); function, so that it uses the already existing identifiers and most importantly, change the sFoCstudio graph plotting, to use the same variables as the "Digital read out" eg. self.device.targetNow. That would make the "Digital read out" way more responsive, and you could send eg.
stringOne += "Monitor | ";
stringOne += "target: ";
stringOne += (buff1);
Serial.println(stringOne);
If you send out "Monitor | All:" you should be able to fit the entire message in a single USB package.
I understand that the minimalistic approach to motor.monitor(); is intended to cut down on serial trafic, but unfortunately it does not, using virtual/native USB. (sending a lot of USB packages, with just a single character).
[Edit] We can further optimize the data by changing "Monitor | "; to "M | "; and so on...
We should avoid RAM leaks, so the string need´s to be managed.
I am not able to make the PR. Do not have the .py skills.