Issues & questions using LSS and Python library

Thanks for a very clear guidance (https://community.robotshop.com/tutorials/show/using-the-lss-smart-servos-with-raspberry-pi). @rguimond
So how can I record the feedback from the Lynxmotion Smart Servo (LSS) - Standard? For example I need to calculate the angle and convert to linear movement? Or Do I need something else?
I know python but I am very new with this.
Thanks for your help

1 Like

I assume you will be moving the LSS ST1 using outside force. If so, the first thing you want to do is set it to limp first! (command: #[id]L**\r** )

Then, you can simply read the position using QD, such as #[id]QD**\r**.

That should will be done from your (Python?) application. Once you’ve read (or know) two (or more) positions, you can find a delta of positions (angle) in time. With this information you can determine the motion (linear or otherwise) by converting that delta of position into a length (in your case).

The conversion function will depend on your physical setup. Read this article for some ideas if you haven’t started on this already.

As for the Python part, check the examples provided with the library. Sending a limp command or reading the position should be straight forward enough but let me know if you need more info!
Example from Python library on queries.

2 Likes

Thank you very much for your info. It is clearer now since the feedback of position and length movement are my most concern.

1 Like

Good morning,
Do you have the cable connector which connects the LSS Adapter board to raspberry pi in the website? I bought the board but the cable is not included.
Thanks

@nodochau The easiest way is to use a USB cable. Note that the LSS-5VR allows you to power the Raspberry Pi at 5V (from the USB OUT of this board) from the LSS bus, so you only need one power supply (11.1V LiPo or 12V are best).

1 Like

Good morning,
I started to hook up all together yesterday but nor powered up yet cause there was a concern. I did some test before powering up all.
First, I connected the power XT60 connector to the board and I saw the 5v led on the board turned on. Then disconnected the XT60 and the 5v led turned off after that I connected the USB from the raspberry to the board (unplugged the XT60) and I saw the 5v led turned on (same led). So what is gonna happen if I connect the XT60 and the USB to the board? I think I have to do that so that I can supply the power to the motor and control the board by raspberry, right? Or I have to disconnect the raspberry power supply and use just power supply from the XT60? Right now I have the power separate power supply for the raspberry.
Please advice

We see you have the LSS Adapter, the 12V Power Supply and one ST1 servo.

So what is gonna happen if I connect the XT60 and the USB to the board?

The servos do not draw power from the USB; you are meant to connect both the USB cable and the power supply at the same time. Set the switch on the LSS Adapter to “USB”.

I think I have to do that so that I can supply the power to the motor and control the board by raspberry, right?

Correct. The USB allows the Raspberry Pi to send (and receive) commands via the “LSS BUS” (i.e. all servos connected to the LSS Adapter).

Or I have to disconnect the raspberry power supply and use just power supply from the XT60? Right now I have the power separate power supply for the raspberry.

The Raspberry Pi takes 5V and decently high current, whereas the ST1 works best at 12V, so you can certainly power them separately, and the USB cable will provide the common grounding between all electronics. Another option if you really want to use only one (12V) power supply is to purchase:



(Or other length LSS cable)

You would connect this to the LSS bus (for example any spare connector on the LSS Adapter), and the USB connector from this module to the power input on the Raspberry Pi since this USB connector PROVIDES 5V power.

Thanks cbenson,
So let me do this and correct me if I am wrong:
I can use two separate power supplies. One for raspberry and one for the motor. Turn them both on.
connect the raspberry to the board by the USB cable.
Correct?

Thanks a lot.
I got it work now. Starting to program it. Very excited!

1 Like

Correct. Well done.

1 Like

Hi cbenson,

After I power up the motor and set a variable HOME_POS = 0
Create a LSS object then:
LSS.move(HOME_POS)
set first position
first_pos = LSS.setFirstPosition(HOME_POS)
print(first_pos)
#make a first move
LSS.move(450) then current_pos = LSS.getPosition() then print(current_pos) I never get 450 or close to it. I always get the current position printed out is the first position and the first position some time is -1, 2, 4…

Where am I missing?
Thanks

Since you indicated you’re using the LSS Python library, refer to this tab to figure out how all of the Python Library commands are mapped to the LSS Communication Protocol commands:

The origin offset command (configuration as opposed to session) is CO, which maps to:
LSS_ConfigOriginOffset
This moves where the zero position is on the servo - normally only done once based on the mechanical configuration.

The LSS command to get current position is QD (query in degrees), which maps to:
LSS_QueryPosition

To move to an angle, the LSS command is D, which maps to:
LSS_ActionMove

If you’re comfortable with Python and setting up the COM port, baud rate, parsing strings and handling timing, you might just try the LSS Communication Protocol directly as opposed to using the library, but the choice is yours.

It sounds like the order of operations is a bit weird, and perhaps somehow you are asking the servo to move to an angle, updating that new position as being the origin offset, then reading the position, which would obviously be zero, since you had just assigned it to be zero. Keep in mind that replies of 2, 4 are actually in tenths of degrees, so 0.2 and 0.4 degrees, which is quite close to zero.

1 Like

Agreed. The first_pos should be zero not -1 or 2 or something else.
I will try again today.

Tried it but still not get the second move printed out as expected. IF I set zero as the first position then the next move 450 should provide me the position is 450 or 451…
Below is my code so far. The idea is every time I press JOG + / JOG - button the motor will rotate clockwise/counter clockwise and print out the current position.
Any ideas?
You have a nice weekend

import time
import serial
import tkinter

Import LSS library

import lss
import lss_const as lssc

def insert_data(*args):
entry.delete(0, tkinter.END)
degree = int(var.get())
entry.insert(0, f’Will move {degree/10} degrees’)

def jog(*args):
global degrees
myLSS.setBlinkingLED(16)
degrees += int(var.get())
myLSS.move(degrees)
print(f"Moved + :{myLSS.getPosition()}")

def jog_revers(*args):
global degrees
degrees -= int(var.get())
myLSS.move(degrees)
print(f"moved - {myLSS.getPosition()}")

def close():
global degrees, HOME_POS
degrees = HOME_POS
myLSS.move(degrees)
print(myLSS.getPosition())
myLSS.setFirstPosition(HOME_POS)
print(f"close position {myLSS.getPosition()}")

Constants

CST_LSS_Port = “/dev/ttyUSB0”
CST_LSS_Baud = lssc.LSS_DefaultBaud
HOME_POS = 0

Create and open a serial port

lss.initBus(CST_LSS_Port, CST_LSS_Baud)

Create an LSS object

input(“Move to 0 >”)
myLSS = lss.LSS(0)

Set led

myLSS.setColorLED(3)
input(“Move to first pos>”)
myLSS.move(HOME_POS)

First_pos = myLSS.getPosition()
print(f"First_pos: {First_pos}")
myLSS.setFirstPosition(First_pos)

input(">")

degrees = 0
main_window = tkinter.Tk()
main_window.title(‘Controller’)
main_window.geometry(‘800x400’)
frame1 = tkinter.Frame(main_window, height=100, bg=‘yellow’)
frame2 = tkinter.Frame(main_window, height=200, bg=‘green’)
frame1.pack(fill=tkinter.BOTH, expand=True)
frame2.pack(fill=tkinter.BOTH, expand=True)
label = tkinter.Label(frame1, text=‘CONTROLL LSS MOTOR’, fg=‘blue’, bg=‘yellow’, font=‘bold’)
label.pack(padx=10, pady=10, expand=True)
frame2.columnconfigure([0, 3], minsize=120, weight=1)
frame2.rowconfigure([0, 1], weight=1)
#create dropdown list:
data = (100, 150, 450, 900, 1800, 3600, -1800, -900, -450, 0)
var = tkinter.StringVar()
var.set(‘Seclect length’)
drop = tkinter.OptionMenu(frame2, var, *data, command=insert_data)
drop.grid(row=1, column=0,padx=40, pady=40)
drop.config(width=20)

Create entry to show data selected

entry = tkinter.Entry(frame2)
entry.grid(row=0, column=1, padx=5, pady=25, columnspan=2)
entry.config(justify=‘center’)
#Create an button to turn on the light base on value from dropdown list box
on_button = tkinter.Button(frame2, text=‘JOG +’, height=5, width=10, command=jog, relief=‘raised’, borderwidth=5)
on_button.grid(row=1, column=1, padx=10, pady=10)
_button = tkinter.Button(frame2, text=‘JOG -’, height=5, width=10, command=jog_revers, relief=‘raised’, borderwidth=5)
_button.grid(row=1, column=2, padx=10, pady=10)
close_button = tkinter.Button(frame2, text=‘HOME’, height=5, width=10, command=close, relief=‘raised’, borderwidth=5)
close_button.grid(row=1, column=3, padx=10, pady=10)
main_window.mainloop()

When troubleshooting, start with the least amount of code possible.

  1. Have a field where a user inputs an integer
  2. Move the servo to that number
  3. Ensure the move is complete
  4. Read the position
  5. Output the position to the user.

Actually, there are two function to move the motor + or - (jog and jog_revers). The rest of the program just a GUI for user .
The motor rotates exact as I can see but the position output makes me confused in another word I totally lost.

The command “myLSS.getPosition()” needs to relate to which servo ID you are querying, for example “myLSS1.getPosition()”. Try the following:

  • Assign the ID of the servo to 3 via the LSS Config
  • Change the LSS Object in the Python code to ID3
  • Change the query to servo 3
1 Like

Hi cbenson,
when I create an object myLSS = lss.LSS(0) I think ID = 0. Do you mean I have to create an object myLSS3 = lss.LSS(3) then do a query myLSS3.getPosition()? I will try but still think about the object creating
" The command “myLSS.getPosition()” needs to relate to which servo ID you are querying, for example “myLSS1 .getPosition()”." If the object is not identified, how can the move() command work?

Not certain if myLSS.getPosition() infers ID0, so figured it was best to verify and set a different ID throughout.


TL;DR: The ID is set when the object is initialized/constructed. Afterwards, that ID is used throughout with all commands that refer to an object. If you want to use a different ID, initialize the object with a different ID or create more than one object.


Long(er) explanation:

I can most definitely confirm that the ID for a LSS object is set at initialization. Ex for a LSS with ID=0:

# Create a LSS object
myLSS = lss.LSS(0)

You can see this in the source code of the library here:

	### Constructor
	def __init__(self, id = 0):
		self.servoID = id

Library functions such as getPosition() uses the internal servo ID stored in the instance of the referred object.
So when you do something like myLSS.getPosition(), this is what happens:

See getPosition():

	def getPosition(self):
		genericWrite(self.servoID, lssc.LSS_QueryPosition)
		return (genericRead_Blocking_int(self.servoID, lssc.LSS_QueryPosition))

Which uses:
self.servoID, which is set during initialization of the object through the constructor (see above)
lssc.LSS_QueryPosition, which is a constant that equals “QP” or the query position command.

genericWrite() works this way:

# 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

Let me know if you need more assistance with the library and using it! :slight_smile: