Lynxmotion SES V2 Hexapod Robot

Haha, shows what I know! :stuck_out_tongue:

Thanks for the simple comparison! Makes some good sense.

This I like quite a bit!

:open_mouth: :clap:

Iā€™m not as fast as @kurte assembling my robot. But finally done. Now the hard part starts. Will try to follow your dialogue here. The ā€œserial stuffā€ isnā€™t my strongest side.

2 Likes

Hi @zenta - looks like you are farther then meā€¦ I have not tied down the servo wires yet :wink:

Now experimenting some with software and trying to understand documents.

@scharette - I will try next to see if the commands strings you mentioned would work or notā€¦ I was sort of unsure as the wiki said:

A servoā€™s baud rate cannot be set ā€œon the flyā€ and must be configured via the CB command described below. The factory default baud rate for all servos is 115200. Since smart servos are intended to be daisy chained, in order to respond to the same serial command, all servos in a project should be set to the same baud rate. Setting different baud rates will have the servos respond differently and may create issues. Available baud rates are: 9600 bps, 19200 bps, 38400 bps, 57600 bps, 115.2 kbps, 230.4 kbps, 250.0 kbps, 460.8 kbps, 500.0 kbps. Servos are shipped with a baud rate set to 115200.

But as the commands output look like the ones mentioned will try to do the other legs using program.

Side note: For me what I would like to see in the WIKI here on the LSS - communcation protocol is a description of what comes back on the RX line? And sort of what are my limitations.

EDIT: Part of this was sitting right in front of me :blush:

Ex: #5QD Query position in (tenth of) degrees for servo #5The query will return a serial string (almost instantaneously) via the servoā€™s Tx pin with the following format:

  1. Start with an asterisk * (Unicode Character: U+0023)
  2. Servo ID number as an integer
  3. Query command (one to four letters, no spaces, capital letters)
  4. The reported value in the units described, no decimals.
  5. End with a carriage return \r or Unicode Character (U+000D)

I figured out by watching the data go back and forth over the Serial port using Logic Analyzer. That Query commands, do return data and it looks like: it echoes back the query command, replacing the # with a * and then adding the resulting data on to the end.

I am guessing from what I am seeing that sending an action command like move, we donā€™t receive anything like an acknowledgement? But for example can I send out multiple query commands out and not have to wait for the servo respond first? Can these query commands be to the same servo and/or different servos?

Again this may be covered somewhere, but I did not see it.

@scharette and @cmackenzie - I already had an a version of AlternativeLSS installed
AlternativeLSS(V0.8.0) and Lynxmotionā€¦ V1.3.1

Will soon look at the new version

1 Like

@kurte @zenta Great to see the robot coming together!

2 Likes

Exactly!

No ACK on action or configuration commands.

When interacting with one specific servo you can (and should!) pack your queries together for efficiency.
For example, to read position, current, voltage and temperature of servo #0 you would send:
#0QD\r#0QC\r#0QV\r#0QT\r
And the LSS would respond quickly to all of these.

The one caveat of packing queries is that you cannot do so for multiple LSS as this would cause bus contention (hence why @cmackenzie 's library is helpful there). Therefore, you can pack queries for one LSS, wait for replies and then move on to the next one.

1 Like

Thanks @scharette - Suggestion would be to add some of this information to wiki.

FYI - I updated all of the servos to 500000, using sketch (actually only 15 servos, the other I used the app).

For the fun of it, I tried to do it without using any library. I included here for your amusement :smiley:

void setup()
{
  while (!Serial && millis() < 5000) ;
  Serial.println("Change all LSS Servos from 115200 to 500000 baud");


}
void loop() {
  Serial1.begin(115200);
  Serial.println("Scan servos ast 115200");
  bool Servos_found = ScanServos();
  if (Servos_found) {
    Serial.println("Press any key to continue");
    while (Serial.read() == -1) ;
    while (Serial.read() != -1) ;
    delay(250);
    Serial.println("Try set baud cammand");
    Serial1.print("#254CB500000\r");
    delay(250);
    Serial.println("Reset");
    Serial1.print("#254RESET\r");
    delay(250);
    Serial1.end();
    Serial1.begin(500000);
    delay(250);
    Serial.println("Scan servos at 500000");
    ScanServos();
  }
  Serial1.end();
  Serial.println("Press any key to try again");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ;
}

bool ScanServos() {
  bool found_servo = false;
  for (uint8_t servo_id = 0; servo_id < 254; servo_id++) {
    Serial1.printf("#%uQD\r", servo_id);
    Serial1.flush();
    delay(25);
    if (Serial1.available()) {
      int ch;
      while ((ch = Serial1.read()) != -1) {
        Serial.write(ch);
      }
      found_servo = true;
      Serial.println();
    }
  }
  return found_servo;
}

I did it one leg at a time. It sort of worked, in that it did find each servo on the leg at the lower baud rate, and then it did do the change of baud and reset. But it did not find the servos again at higher baud rate.
I should probably verify that my underlying Teensy code worked for switching the baud.

Or maybe I needed to do some other function after the reset to have the servos see the new baud or maybe wait a bit longerā€¦ But at least the task is done.

1 Like

Yeah! :slight_smile:

I notice after calling for a reset you wait 250 ms before continuing.
Our official software waits a full 2000 ms to be on the safe side (IIRC the boot up sequence is about 1250 ms minimum).
So, going by those numbers, (1250 ms - 250 ms) / 25 ms [your delay when scanning], you are likely skipping the first 40 IDs ( [0, 39] ) since those LSS cannot answer yet. If you are using the old Phoenix IDs then that would mean youā€™ll never find any of themā€¦ :open_mouth:

Have a look at increasing the post-reset delay to 2000 and see if your results change.

Hmm, the only thing to do after a reset is to wait for servos to (soft) power-cycle/boot-up.

@scharette

Yep it just needed a longer delay. I changed the sketch to allow the from and to be more easily changed as I then ran it to change one leg back to 115200 which worked and then changed back and it worked.

static const uint32_t from_speed = 115200;
static const uint32_t to_speed = 500000;
void setup()
{
  while (!Serial && millis() < 5000) ;
  Serial.printf("Change all LSS Servos from %u to %u baud\n", from_speed, to_speed);
}
void loop() {
  Serial1.begin(from_speed);
  Serial.printf("Scan servos at %u\n", from_speed);
  bool Servos_found = ScanServos();
  if (Servos_found) {
    Serial.println("Press any key to continue");
    while (Serial.read() == -1) ;
    while (Serial.read() != -1) ;
    delay(250);
    Serial.println("Try set baud cammand");
    Serial1.printf("#254CB%u\r", to_speed);
    delay(250);
    Serial.println("Reset");
    Serial1.print("#254RESET\r");
    delay(3000);
    Serial1.end();
    Serial1.begin(to_speed);
    delay(250);
    Serial.printf("Scan servos at %u\n", to_speed);
    ScanServos();
  }
  Serial1.end();
  Serial.println("Press any key to try again");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ;
}

bool ScanServos() {
  bool found_servo = false;
  for (uint8_t servo_id = 0; servo_id < 254; servo_id++) {
    Serial1.printf("#%uQD\r", servo_id);
    Serial1.flush();
    delay(25);
    if (Serial1.available()) {
      int ch;
      while ((ch = Serial1.read()) != -1) {
        Serial.write(ch);
      }
      found_servo = true;
      Serial.println();
    }
  }
  return found_servo;
} 

Terminal output:

Change all LSS Servos from 115200 to 500000 baud
Scan servos at 115200
*7QD42
*9QD-1134
*11QD-692
Press any key to continue
Try set baud cammand
Reset
Scan servos at 500000
*7QD42
*9QD-1134
*11QD-692
Press any key to try again
2 Likes

Re: Queueing multiple commands
Yes, queueing is done under the hood in both versions. The second version you just send(ā€¦) an array or collection of packets. Under the hood it will:

  • Send all packets with the same ID immediately
  • Furthermore, it will send all non-query packets immediately. Since the servo doesnt respond to action commands there is no need to wait.
  • The code will wait on queries for reply but also schedule the next packet. It may send the next packet before a reply. Round-Tip-Time stats are collected so this schedule time can be pretty lowā€¦around 1.6ms
  • Packet list should be sorted already for above rules. I considered sorting internally but this would require copying-and-sorting input array, so dynamic memory, and would wreck the performance (and memory frag).

Iā€™m on the verge of getting this humanoid to take itā€™s first steps and Iā€™m super-excited to see this workā€¦but I can take some time after to finalize an Arduino release if you are interested. You can checkout the new API interface example code for ChannelReadWrite example. Notice it uses a Promise-like interface where the send(ā€¦packetsā€¦) call usese then(ā€¦lambda).otherwise(ā€¦lambda) to handle the async send/query/update loop and handle bus or timeout errors. Packets in the send() call are using std::initializer_list and in the update they are using std::vector but static C arrays work too. servos[] array is a simple C array of servo IDs.

1 Like

Quick update: I have started playing around some with software. I thought I would hack up my own test servo program I had been using for Dynamixel Servos (on my own library then on OpenCMā€¦).

Hope you donā€™t mind if I start issuing Issues on the libraries and/or Pull Requests.
I am running into lots of compiler warnings with the library: #include <lss.h>
So trying to resolve many of them.

On another note, I thought I would mention, I can now post pictures of the Teensy 4.1 as it was released today :smiley:

https://www.pjrc.com/store/teensy41.html

And more information on it up at:

1 Like

Neat.

Ah. Did not try it on anything not AVRā€¦ :smiley:
Iā€™ll have a look at the issues/pull requests shortly. Thatā€™s what they are there for! :slight_smile:

Cool! :smiley:

Good to hear your doing some progress @kurte . Cool to see the 4.1 out. Iā€™ve 3x 4.0ā€™s I think should do the job also. Still waiting for the level shifters. One thing Iā€™m wondering about is that Iā€™m using the Teensy 3.6 on your old v0.3 breakout board on my MX phoenix with the MX 64/106 T servos. Iā€™m not using any level shifters there so I might just got lucky? Anyway I donā€™t want to risk anything when testing the LSS.

1 Like

@scharette - Now have the starting of my random junk test sketch limping along. Did run into several issues while testing it and I did push up more changes to the library, to the branch with the PR. I think I have all of the compiler warning gone, but who knows may have missed some. Should also run it on AVR to make sure it did not screw up things the other way.

I know I should probably wait until more of it is working, but in case anyone is curious. So far I only have the servos of one leg plugged in.

LSSTestServos-200511a.zip (6.3 KB)

1 Like

In case any of you are following along. I just updated my branch of the library with some more updates.


I reworked some of the methods: genericRead_Blocking_str and genericRead_Blocking_s16
to not us sscanf. Instead it simply reads the bytes of the message, and first builds a simple number for the Servo ID, and then compares it against the number passed in. It then checks the CMD bytes one after another until end of passed in messageā€¦ And then assumes everything else is results, so it reads it into the value string.

The s16 version simply calls the string version and then does a simple inline parse for a number. It appears to work (I think).

Again WIP and not a great sketch, but more pieces are sort of workingā€¦
LSSTestServos-200512a.zip (6.3 KB)

Edit: I put my WIP test sketches up at: https://github.com/KurtE/LSS_Test_sketches

EDIT2: I went a head and ordered a few more things, for this, including SES210, a couple of your hubs, cables and screwsā€¦ Thought I might want to test the code on your Arduinoā€¦ But my guess is the order might take awhile to get here.

2 Likes

Interesting! Many good changes in there. The genericRead functions definitely needed some cleanup.
One customer had speed issues on a Teensy 3.6 (which I was able to reproduce) while doing QD, figuring out the current genericRead were problematic (and causing delays ~100 ms). I proposed to the user the following, originally (or something along those lines) for a quick test, which greatly improved reading speed. Hereā€™s a copy of that code (I think):

		// Query LSS position
		lastPosLSS = posLSS;
		Serial1.write("#0QD\r");
		while(Serial1.available() == 0)
		{
		}
		size_t readLength = 0;
		readLength = Serial1.readBytesUntil('D', readBuffer, 100);	// Read until after the command (QD), indicating the start of the returned value (position in 1/10 deg)
		readLength = Serial1.readBytesUntil('\r', readBuffer, 100);	// Read until the carriage return (\r), indicating the end of the reply
		if(readLength > 0)
		{
			if(myLSS.charToInt(readBuffer, &posLSS))
			{
				Serial.print("Pos = "); Serial.println(posLSS);
			}
			else
			{
				Serial.println("Error in conversion");
			}
		}
		else
		{
			Serial.write("No reply data\r\n");
		}

Of course, a more generic version of this was always required. :slight_smile:

Thereā€™s one modification that did catch my eye more than the others though: youā€™ve changed the writes from a snprintf to buffer / one call to Arduinoā€™s write into 4, 5, and 7 calls (genericWrite) to it. From tests weā€™ve done before, this is definitely slower (at least in AVR world, did not profile it on ARM or other platforms). I was curious about your reasoning for removing the buffering there and making more function calls.

Yes, that was also definitely needed! :smiley:

@scharette - more later on other parts:

On removing snprintf and the buffer.
There are a couple of different reasons why I did it, which may or may not be valid.

a) code and buffer sizes - On Atmega328s and the like they usually are pretty limited and having functions like snprintf and sscanf can add a lot of code and buffer usage, so often times I try to avoid these especially if they bring in full blown version including floating pointā€¦

b) On most systems (maybe not including SoftwareSerial) - the hardware has itā€™s own internal software buffer, that the writes go into. So doing a couple of calls to Serial1.write(ch). Will simply just add bytes to queue. BUT: in some cases if it finds that the queue was empty and the hardware is ready for a byte to be sent to it, it will do that instead. So by making these calls early you can actually start the hardware Serial port to begin the output of the first character(s) sooner.

c) If you look at how in many cases functions like the bus->write(cmdBuffer) is implemented.
Example AVR:
This is a call to print class methods: So it calls

size_t write(const char *str) {
  if (str == NULL) return 0;
  return write((const uint8_t *)str, strlen(str));
}

i.e. it then scans you string to figure out the length of it, and then calls the write method:
which I donā€™t believe on the hardware serial port code for AVR they override this virtual method, so it does:

size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    if (write(*buffer++)) n++;
    else break;
  }
  return n;
}

Where the virtual write method for one character is implemented: so it then calls: Serial1.write() for each character.
So I decided to bypassā€¦

Again maybe some of my assumptions are off here. But hope some of this makes sense.

EDIT: - However I can easily imagine some other hardware, especially those who may have a HAL (Hardware Abstraction Layer) where each of these calls goes through a level transition or the like and in those configurations having the code buffer to itā€™s own buffer and then doing one call would be beneficial. So maybe I should configure it such that maybe a #define is defined depending on hardware defines which allow the code to work either way.

1 Like

Hi All,

I had some time to start assembling! Yay!

Unfortunately, I run into an issue because I think I received some incorrect SES brackets.
As you can see in the photo below Iā€™ve received 5 brackets with identical hole patterns on both sides and one with only a small hole on one side.

Then I started looking at the photo Kurt and Kare posted. It seems that Kurt received the same set of brackets as I did, but Kare got brackets that look different. The ones Kare has match the design and style of the new brackets. Can it be that Kurt and I received the old-style SES brackets?

Additionally, I saw on Kareā€™s photo that he uses some kind of clip on the new SES brackets which I donā€™t have. @Kare, did you make them yourself, or were they included?

As far as I understood it, building the hexapod is also to give some feedback on things I noticed along the process. So here we go :slight_smile:

Servo boxing
As said before, I really like the premium look of the servo boxes. What I noticed, especially when opening 18 boxes in a row, that not everything is packed in the same way. Sometimes the ā€˜cable-boxā€™ is upside down what makes it a bit harder to get to the cables. And also the orientation of the servo is not always the same. I think a small ā€œpacking procedureā€ to china would make it perfect. But I know, somehow I notice those small things. :slight_smile:

screw holes
In the new SES brackets, the unthreaded holes are a bit too small. The screw needs to be screwed in, slightly damaging the thread of the screw.

body plate stretched
In a separate package, Iā€™ve received the 2 body plates. Upon arrival, they are where pretty scratched. It is unclear for me if this happened during transportation or production. The two plates were packed together so it could have happened during transportation. packaging them separately would improve once the plates will be shipped to customers.

body plate hole- layout
As a personal preference, I always try to place as much as possible inside the body. This by not getting a skyscraper of electronics of the back of the bot. Iā€™ve noticed the two triangle-shaped cutouts in the place of the electronics and thought, if they could be turned 90 degrees, they line up with the connection pins of the LSS. This might give more freedom in placing the LSS inside the body and having the pins for the shield/controller board sticking out at the top.

leg length
While viewing the photos from Zentas build I saw that there is some free space between the rubber foot and the aluminum bracket exposing the tube. In my build, they almost touch each other. The tube itself is 51mm long (sorry, Iā€™m using the metric system). Can it be that we received different lengths or did I force the tubes on to hard :slight_smile: :mechanical_arm:

Thatā€™s all for now! :smiley:

Xan

1 Like

Hi Jeroen, you sure got an eye for details. The last thing first. Iā€™m using the half sphere parts I got during beta testing on the tubes before placing the rubber foot.


That might explain the difference. Not sure why they skipped them now. @dialfonzo?
I mentioned in another post the beta 3D printed cable holder used on the beta C brackets I got.

I agree about the body it would be nice to be able to place some electronics between the plates too.

2 Likes

@Xan

Weā€™ll send out the good parts ASAP. Sorry about that. Can you confirm:

  • 6x Production C brackets
  • 24 Plastic clips / wire guides
  • 6x White hemispheres (for bottom of foot)*
  • Anything else? Check cabling?

Appreciate the feedback!

A. Sometimes the ā€˜cable-boxā€™ is upside down what makes it a bit harder to get to the cables. And also the orientation of the servo is not always the same.

Indeed, and weā€™ve brought that up with our manufacturing partner. It will be resolved in the next batch.

B. In the new SES brackets, the unthreaded holes are a bit too small. The screw needs to be screwed in, slightly damaging the thread of the screw.

This is new though - on our end the screws seem to go in without having to be threaded. Anyone else?

C. In a separate package, Iā€™ve received the 2 body plates. Upon arrival, they are where pretty scratched. It is unclear for me if this happened during transportation or production.

Good question. Itā€™s not a final plate, so for now, so long as the functionality is thereā€¦

D. Iā€™ve noticed the two triangle-shaped cutouts in the place of the electronics and thought, if they could be turned 90 degrees, they line up with the connection pins of the LSS.

Purely aesthetic. Weā€™ll get them walking and see about dimensional and aesthetic changes. Input is absolutely appreciated.

E. Can it be that we received different lengths or did I force the tubes on to hard

Kurte seems to have addressed that. Did you receive the white 3D printed hemispheres?

So, some questions become:

  1. Tests on how to mount electronics, what to use, wiring (if it changes) etc.

  2. What battery to use and how and where it will be mounted.

  • @kurte @xan @zenta
    Regarding the hemispheres, it seems to have been an oversight. if you have a 3D printer, can we simply send you the file?
1 Like

@cbenson @xan @zenta ā€¦

Morning all,

I also did not receive the hemispheresā€¦ As for 3d printer. Mine has a busted main circuit board. I did order one on Kickstarter, that is a little behind schedule (so for 2 plus years and counting)

Tuesday I did put in an order and I did order 6 of the new released brackets plus some other stuffā€¦ Not sure if it shipped yet( 981804) I ordered one of your Atmega328 boards so I could test changes to library worked on it. Thought about ordering spare servo, but you were out of the standard ones and did not check for others. If the order has not shipped yet and you have the things like the hemispheresā€¦ But might have already shipped. Often times my mail from you gets eaten by the mail providers Spam filtering, where each morning I get an email showing which ones they quarantined, which I can then release some.

FYI - I pushed up the starting porting of one version of the Phoenix code to work with these servos. Started off with using XBee with the Trossen Commander as inputā€¦ I have my T4.1 board with connection up to your servo board. Found another issue with my breakout board with XBee, which I probably should have double checked before I ordered updated boards (which should arrive today). But can easily path. I read the document wrong on which was RX and TX pin. Luckily easy enough I just did not connect those pins up and then used jumpers for the correct oneā€¦ The main thing I was doing was routing an actual hardware serial port to your XBee pins. Note with T4 and 4.1 I could have also used FlexIO pins made into serial port and should easily work.

The code compiles. The XBees are talking. Some of the debug monitor stuff is working, when the XBees start talking it tries to stand up. But the math for the different angles is bad, was setup to do offsetting for Dynamixels and legs for PhantomXā€¦ But hopefully will figure those out later today.

But if someone wants to beat me to it and see what is there. It is up on the github project I started with test sketches for this.

But right now it is early and I need more coffee!

1 Like