I would like to send both position and speed commands to each LSS object. I can see that there is a legacy command (speed (S)) but no indication of how to implement it.
Hardware concerned: Lynxmotion LSS ST1 & HT1
Software concerned: Python
Troubleshooting steps already taken: At the moment I am sending setMaxSpeed commend before move command e.g.:
myLSS = lss.LSS(1)
myLSS.setMaxSpeed(50)
myLSS.move(0)
Happy to bob along like this, but is there a better solution? If not I will make my own function to join these two together. e.g.
The LSS communication protocol itself does support adding a parameter (modifier) to an action command as you may already know (Wiki page here).
The modifiers are listed here (Wiki page). I recommend you use SD (degrees/s) instead of S (kinda awkward to convert to/from unless you are used to it already).
Thatās not a bad idea at all and certainly a good starting point. Though you may want to go a step further and combine both commands into one command like youād typically do in this case; i.e.: have the speed as a modifier to the move command.
As you probably already noticed, the move function in the Python library (see actions commands here) takes only the position as input. And it itself calls an internal function called genericWrite which takes two mandatory parameters and one optional:
servo ID (mandatory)
command (mandatory)
parameter (optional); some commands donāt need this, such as āHā (Hold), but others, like motion, most certain do!
So, I see multiple ways to improve this:
The quick and dirty: create a moveSpeed command (or something similar) and have it do like your above example, call both commands individually.
The slightly better version: create a moveSpeed command that relies on a genericWriteModifier that adds support for a modifier and its parameter.
The golden standard: create a moveSpeed function that uses the normal genericWrite but also supports the extra (but also optional) modifier & modifierParameter arguments! Iād personally go for the last one since this shouldnāt be too difficultā¦
If going for # 3, the process would be something along the lines of:
Modify genericWrite to add support for modifiers. This in general feels like a good idea since the LSS Python library is missing this right now. Of course, it must not break all the other calls to it without a modifier (but that seems trivial from what I can see)!
Add a moveSpeed function (instead of just a new/modified signature for move, since that would fit better the current set of commands and style in the library) that makes uses of the new signature of the genericWrite function.
So, genericWrite would go from:
# Write with a an optional parameter
def genericWrite(id, cmd, param = None):
if LSS.bus is None:
return False
if param is None:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + lssc.LSS_CommandEnd).encode())
else:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + lssc.LSS_CommandEnd).encode())
return True
To something like:
# Write with a an optional parameter and possibly an optional modifier & parameter
def genericWrite(id, cmd, param = None, mod = none, modParam = none):
if LSS.bus is None:
return False
# Assuming modifiers always need a parameter
if (mod is None) or (modParam is None):
if param is None:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + lssc.LSS_CommandEnd).encode())
else:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + lssc.LSS_CommandEnd).encode())
else:
if param is None:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + mod + str(modParam) + lssc.LSS_CommandEnd).encode())
else:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + mod + str(modParam) + lssc.LSS_CommandEnd).encode())
return True
As for the new moveSpeed function, weād copy the regular move function and go from this:
Adding some new functionality to the library and also opening quite a few other doors, such as using the other modifiers (T, CH, CL).
Unfortunately, I do not have any hardware to test this right nowā¦ so this may not even run! Haha! Python is definitely not my strongest languageā¦
@craigvear:
If you are willing to try it out for me, I can fork this repo and make the changes required. Iād just want it tested before I submit a pull requestā¦
Sincerely,
edit: It does seem like the LSS Arduino library already has a genericWrite function with the ability to pass a modifier & modifier parameter. You can see it here. It was added to the Arduino library afterwards though, but I guess no one asked for it for the Python library until now (or worked around that, like your example above). I guess it is due for an upgrade!
Ah, and also, if we are talking adding featuresā¦ might as well refactor too! So youād go from the proposed change:
# Write with a an optional parameter and possibly an optional modifier & parameter
def genericWrite(id, cmd, param = None, mod = None, modParam = None):
if LSS.bus is None:
return False
# Assuming modifiers always need a parameter
if (mod is None) or (modParam is None):
if param is None:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + lssc.LSS_CommandEnd).encode())
else:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + lssc.LSS_CommandEnd).encode())
else:
if param is None:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + mod + str(modParam) + lssc.LSS_CommandEnd).encode())
else:
LSS.bus.write((lssc.LSS_CommandStart + str(id) + cmd + str(param) + mod + str(modParam) + lssc.LSS_CommandEnd).encode())
return True
To maybe something like this (and remove repetition and make it more Python-like! plus removing complexity is always a good idea!)?
# Write with a an optional parameter and possibly an optional modifier & parameter
def genericWrite(id, cmd, param = None, mod = None, modParam = None):
if LSS.bus is None:
return False
# Assuming modifiers always need a parameter
if (mod is None) or (modParam is None):
cmdStr = ""
else:
cmdStr = mod + str(modParam)
if (param is None):
cmdStr = cmd + cmdStr
else:
cmdStr = cmd + str(param) + cmdStr
# Add header, ID and footer and send to bus
cmdStr = (lssc.LSS_CommandStart + str(id) + cmdStr + lssc.LSS_CommandEnd)
LSS.bus.write(cmdStr.encode())
return True
Now that feels cleanā¦er!
edit: I guess the combining line could change tooā¦
from:
# Add header, ID and footer and send to bus|
|cmdStr = (lssc.LSS_CommandStart + str(id) + cmdStr + lssc.LSS_CommandEnd)|
to:
# Add header, ID and footer and send to bus
cmdStr = "{}{}{}{}".format(lssc.LSS_CommandStart, str(id), cmdStr, lssc.LSS_CommandEnd)
If one was so inclined. Iāve seen this style around in Python code beforeā¦
edit # 2: Corrected a (repeated) typo of none instead of None! Thanks @craigvear !
Works a treat. Here is what I implemented in lss.py (NB I corrected a typo in your def line [None not none]:
# Write with an optional parameter and possibly an optional modifier & parameter
def genericWrite(id, cmd, param = None, mod = None, modParam = None):
if LSS.bus is None:
return False
# Assuming modifiers always need a parameter
if (mod is None) or (modParam is None):
cmdStr = ""
else:
cmdStr = mod + str(modParam)
if (param is None):
cmdStr = cmd + cmdStr
else:
cmdStr = cmd + str(param) + cmdStr
# Add header, ID and footer and send to bus
cmdStr = "{}{}{}{}".format(lssc.LSS_CommandStart, str(id), cmdStr, lssc.LSS_CommandEnd)
LSS.bus.write(cmdStr.encode())
return True
def moveSpeed(self, pos, speed):
return (genericWrite(self.servoID, lssc.LSS_ActionMove, pos, lssc.LSS_ActionMaxSpeed, speed))
And the command in my script of:
myLSS.moveSpeed(300, 100) # moves LSS servo to 30 degree at speed 100
Unfortunately, it means that any maxSpeed() setting I declare as a init parameter, gets ignored, but I could implement that safety check in my script.
# Write with an optional parameter and possibly an optional modifier & parameter
def genericWrite(id, cmd, param = None, mod = None, modParam = None):
if LSS.bus is None:
return False
# Assuming modifiers always need a parameter
if (mod is None) or (modParam is None):
cmdStr = ""
else:
cmdStr = mod + str(modParam)
if (param is None):
cmdStr = cmd + cmdStr
else:
cmdStr = cmd + str(param) + cmdStr
# Add header, ID and footer and send to bus
cmdStr = "{}{}{}{}".format(lssc.LSS_CommandStart, str(id), cmdStr, lssc.LSS_CommandEnd)
LSS.bus.write(cmdStr.encode())
return True
def moveSpeed(self, pos, speed):
return (genericWrite(self.servoID, lssc.LSS_ActionMove, pos, lssc.LSS_ActionMaxSpeed, speed))
Woohoo! Iāll make an official pull request for it thenā¦ unless you want to? It is all GPLv3 anyway!
Thanks for noticing! I donāt have my dev environment setup for any of this in my office and I was too lazy to go to my other computerā¦ haha! Yeah, thatās the reasonā¦
I fixed the post above too, so no one else is lead as astray!
No problem. I edited your post above by adding: [ code ] before and [ /code ] after the code block. That gives it auto formatting (like in my examples). I hope you donāt mind! Just go in edit for your post and you can see the change (or view history in the top right of the post header).
See above for how to get auto color for your code block using the proper markdown!
This oneās pretty easy to fix!
What users typically do is use the LSS Config (or command line, programmatically, etc.) to send a CSR (or CSD) command to set the maximum āconfigā speed for the LSS in question (instead of simply the session value).
As far as I know, the configuration value should always be respected. Actually, the session value should probably be respected, tooā¦ Though it is possible it gets ignored if you request a higher speed as a modifierā¦ Youāll have to try it out and report back! I know for sure it is respected in wheel mode commandsā¦ but unsure in position ones!
OK, so in āposition controlā mode, with commands like D and MD, a āspeed modifierā allows you to ignore the set limit.
I think that was done by designā¦ but I canāt really remember the rationale behind it to be honestā¦
Maybe @cbenson can help us figure that one out. He has access to the tracker where that info was kept!