Xan's Phoenix Code

I was not referring to anyone, I’m telling you what method I used so It was directed at myself.

Hi Xan,

I just wanted to clear some queries. I am intending to move the servo to different positions, for instance in a gait or some trajectory following task. How should I go with it? which of the following would serve me the best :

1- Read the return of Query command and loop until ‘.’ is recieved, then advance to the next position.

2- Read the returns of QP command and then advance to the next position accordingly.

3- Simple put a delay of lets say 2ms after every servo move command when running all the servos at T200. Means simply sleep the loop for a time period specified in the servo move command after every iteration.

AT the moment i have implemented the 3rd procedure. Its working,but i would never know if legs are really ending up at the target positions before moving to the next. So, im a little hesitant in proceeding with it.

I looked into your code, but couldnt find the lines to understand your methodology.
I would really appreciate your cooperation and efforts in this regard.

cheers,
thanks

I believe his code works on a variation of your #3.

That is he remembers the requested time value for the previous move, and using a timer knows how much time he has already used in reading inputs, calculating the next move, etc. He then sleeps the difference in time for the last command.

In a previous version I believe that he used the “Q” command, but decided that the amount of time it took to send out the command, and wait for a result did not give him the resolution he wanted. I.E. there would be some delta times (not fixed) after the move completed before he would know it, which limited the speed of his gait. At least that is what I remembered.

Kurt

Hi,

Your question is already correctly answered by kurt. He is totally right.

I’ve started out by polling to the SSC using the Q command. If the previous move was done I would send the next one. This had a shaky result since polling and waiting for the answer took some time.

Now I’ve got a timer looping. I’ll take a snapshot of the time at the beginning of my calculations (main loop) and one right before sending the next move. Then I calculate how long it took to do the calculations. The time is subtracted from the previous SSC move time.

;Get endtime and calculate wait time GOSUB GetCurrentTime], lTimerEnd CycleTime = (lTimerEnd-lTimerStart)/WTIMERTICSPERM

pause (PrevSSCTime - CycleTime -50) MIN 1 ; Min 1 ensures that there alway is a value in the pause command
The 50 represents the time it takes to do the calculations after getting the end time and sending the data to the SSC.

Hope it helps.

Xan

So, at the very Start we issue

**GOSUB GetCurrentTime], lTimerStart **

where lTimerStart contains the snapshot of the initial time.

then we have

IK Calculations

then we issue

**GOSUB GetCurrentTime], lTimerEnd **

where lTimerEnd contains the snapshot of the time right after completing the IK calculations. From this we determine
**
CycleTime = (lTimerEnd-lTimerStart)/WTIMERTICSPERMS**

till this point, everything is clear except the ‘WTIMERTICSPERMS’ parameter. What does this parameter mean in general? or in any other programing language like matlab?

At this point we have the CycleTime which actually represents the total time consumed in completing the IK calculations. Before issuing the Serial Out command we issue

pause (PrevSSCTime - CycleTime -50) MIN 1 ;

in this statement why are we subtracting 50 from the CycleTime?

This explanation is confusing for me. we haven’t issued the Serial Out command yet. All we have done so far is captured two times, determined the CycleTime and now pausing the loop. If we already know it takes 50 to complete the IK calculations,why r we subtracting it from CycleTime which also equals to the time consumed in completing the IK?

And PrevSSCTime? Does it contain the time value set in the servo move command?

Can you give present me an example, like for the first step in a gait what values would the variables hold in the pause statement?

My apologies for such confused questions.

And one more question please,

I happened to look at the following statement

Does this part of the code checks if servos have completed their last motion successfully?How?

In your code, under [SERVO DRIVER] function, you are updating the leg positions by separate SERVO MOVE commands for each leg. What’s the advantage of moving servos separately over GROUP SERVO MOVE command?Since in body translations and rotations and in gaits, all the legs are supposed to move altogether so shouldn’t the GROUP SERVO MOVE command be a better choice?

I appreciate your assistance
Thanks

I can probably help out a bit here as I was the one who wrote the timer code…

If you look up at the initialization code, you will see:

;Timer
WTIMERTICSPERMS con 2000; we have 16 clocks per ms and we are incrementing every 8 so divide again by 2 
TCRW = 0x30 			;clears TCNT and sets the timer to inc clock cycle / 8 
TMRW = 0x80 			;starts the timer counting 
lTimerWOverflowCnt = 0 
enable TIMERWINT_OVF 

This initializes the the WTIMER of the underlying H8/3694 microcontroller to increment it’s counter on every 8 system clocks. The Microcontroller runs at 16,000,000 cycles per second. So the counter increments 2,000,000 times per second. To convert the tick count into milliseconds, we need to divide by 2000. That is what that constant is all about.

The init code alsos enable an interrupt to handle when the 16 bit counter overflows. This is handled by an asm interrupt handler HANDLE_TIMERW which is in the code.

I think the 50 was dirived by trial and error. He could probably calculate some of it, like he may know about how many characters will be output to the SSC-32 for the move. He could then know the baud rate and calculate how long it would take to generate that number of characters. He can not use the actual timer for this part because he needs the information to do the sleep. This was proably accurate enough. There may have been a few other ways to do the code, that would do it as well.

Example: he could grab the value of the timer right after he issues an SSC command. Call it lTimerLast. He could then calculate when that command would be done. lTimerDone = ltimerLast + (prevSSCTime * WTIMERTICSPERMS)

Then when he gets ready to output the next command he could, have code that looks something like:

do
  gosub GetCurrentTime], lTimerNow
while (lTimerNow < lTimerDone)

The commands are all done at once. He simply outputs the command using many serout commands to make it more readabile. No part of the actual SSC command is executed, until the is output.

Kurt

you never cease to amaze me kurte with your seemingly off the wall knowledge of assembly code.

Thanks, but that type of code is pretty easy. I have done it on several different platforms for many years. More often then not it is simply reading the assembly code.

There was actually a bug I think in the current version of the timer code with the timer. When I originally wrote it, it was for TimerA which is an 8 bit timer. Now it is working with TimerW which is a 16 bit timer. So my offset of +1 in the assembly code is probably not correct. I had a newer version of the timer code that is probably better. I also converted the gettime to an assembly language function.

Here is a test program that I wrote awhile ago that tested the new code:

[code];--------------------------------------------------------------------
;[VARIABLES]
;--------------------------------------------------------------------
;[TIMING]
wTimerWOverflowCnt var word ;used in WTimer overflow. Will keep a 16 bit overflow so we have a 32 bit timer
lCurrentTime var long
lTimerStart var long ;Start time of the calculation cycles
lTimerEnd var long ;End time of the calculation cycles

CycleTime var word ;Total Cycle time

SleepTime var word

;====================================================================
;[TIMER INTERRUPT INIT]
ONASMINTERRUPT TIMERWINT, Handle_TIMERW
;====================================================================
;[INIT]

;Timer
WTIMERTICSPERMS con 2000; we have 16 clocks per ms and we are incrementing every 8 so divide again by 2
TCRW = 0x30 ;clears TCNT and sets the timer to inc clock cycle / 8
TMRW = 0x80 ;starts the timer counting
wTimerWOverflowCnt = 0
enable TIMERWINT_OVF
enable

;====================================================================
;[MAIN]
sleepTime = 1
main:
'Start time
for sleeptime = 1 to 2000 step 50
GOSUB GetCurrentTime], lTimerStart
pause sleeptime
GOSUB GetCurrentTime], lTimerEnd
CycleTime = (lTimerEnd-lTimerStart)/WTIMERTICSPERMS

   serout s_out, i9600, [dec SleepTime, " ", dec lTimerStart, " ", dec lTimerEnd, "=", dec CycleTime, 13]

next

goto main
;-----------------------------------------------------------------------------------
;[Handle TimerW interrupt]
BEGINASMSUB
HANDLE_TIMERW
push.w r1 ; save away register we will use
bclr #7,@TSRW:8 ; clear the overflow bit in the Timer status word
mov.w @WTIMERWOVERFLOWCNT:16,r1 ; We will increment the word that is the highword for a clock timer
inc.w #1,r1
mov.w r1, @WTIMERWOVERFLOWCNT:16
pop.w r1 ; restore our registers
rte ; and return
return
ENDASMSUB
;-------------------------------------------------------------------------------------
;[Simple function to get the current time and verify that no overflow happened]
GetCurrentTime
nop ; probably not needed, but to make sure we are in assembly mode
mov.w @WTIMERWOVERFLOWCNT:16, e1
mov.w @TCNT:16, r1
mov.w @WTIMERWOVERFLOWCNT:16, r2
cmp.w r2,e1 ; make sure no overflow happened
beq _GCT_RETURN:8 ; no overflow, so return it

   mov.w  @WTIMERWOVERFLOWCNT:16, e1
   mov.w  @TCNT:16, r1

_GCT_RETURN:
mov.l er1, @LCURRENTTIME:16
return lCurrentTime ; switched back to basic…
;--------------------------------------------------------------------
[/code]

Please note, that I guarantee nothing and all of the usual disclaimers.

Kurt

Thanks a lot for the assistance. Now most part of the methodology is clear and understandable.

pause (PrevSSCTime - CycleTime -50) MIN 1

The value “50”; is it in ms as well?

Thanks for the explanation, its understandable in the context of a microcontroller. But what if I am implementing a timer in a windows programming environment like matlab or visual basic? should i set 1ms as the value for WTIMERTICSPERMS ?

Thanks again.

Can somebody tell me how i can tell the bot to walk forward for a certain amout of distance? Of course i mean in Xan’s code.

When i push the X button i want the hexapod to walk forwards, then when it detects something (IR) i want it to walk to the object, take the attack posture, turn for +/- 90 degrees and repeat the whole thing.

All the code is covered allready, i just can’t seem to get it to walk forwards or turn a certain amount of distance…

I really need help with this!

Hi All,

I had some questions about the phoenix code (1.3) with the new BasicMicro Studio. When you are using both and see that the hexapod is running a bit strange follow this link to solve the problem.

Thanks

Xan

when gait is in motion and you stop movement (let go of control) the robot re-forms its leg positions.
is this defined in the code as: GaitStep ;Actual Gait step

IF LastLeg THEN ;The last leg in this step GaitStep = GaitStep+1 IF GaitStep>StepsInGait THEN GaitStep = 1 ENDIF ENDIF?

Gaitstep is just keeping count of how many step you have takin. This resets to 1 when you have reached a number larger than the amount of “StepsInGait” in the gaitselect your running.
EX: If your using Gaittype 5 which is the tripod 8 step you have 8 StepsInGait. As you cycle thru the gait you add 1 to gaitstep when you hit 9 it resets back to one in the example.
The code below has nothing to do with return legs to home position.

Your looking for the top few lines of the [Gait] subroutine. This is where it detects if the gait is in motion or not. If it is not in motion it returns the legs back to home position.
Its in the first line of the loop statements. Xan even quoted “Gait NOT in motion, return to home postion” to the far left scroll over it might not show it.

Paul

Hi Jonny,

Paul already covered the functionality of the code snippet you posted. Not much to say there. :wink:
I can give you some additional information about the return to home part;

First thing checked in the gait sub is if the controller is released:

;Check IF the Gait is in motion GaitInMotion = ((ABS(TravelLengthX)>TravelDeadZone) | (ABS(TravelLengthZ)>TravelDeadZone) | (ABS(TravelRotationY)>TravelDeadZone) )

The part Paul pointed at: “Gait NOT in motion” checks if the if the leg is NOT in the home position when the joystick is released.

 (GaitInMotion=FALSE & GaitStep=GaitLegNr & ((ABS(GaitPosX)>2) | (ABS(GaitPosZ)>2) | (ABS(GaitRotY)>2))

If the joystick is released and the leg is not in the home position it gets lifted to the upper middle position.

The next step is to place the leg on the ground. This is done here:

;Leg front down position IF (GaitStep=GaitLegNr+NrLiftedPos | GaitStep=GaitLegNr-(StepsInGait-NrLiftedPos)) & GaitPosY<0 THEN GaitPosX = TravelLengthX/2 GaitPosY = 0 GaitPosZ = TravelLengthZ/2 GaitRotY = TravelRotationY/2
Since the travellength (X/Y/Z) = 0 all GaitPositions will be 0 as well.

Hope this explains how it works. 8)

Xan

Thanks paul and Xan.
this has explained alot.

thanks again.

Hi All,

I just uploaded a new video showing the new (and some existing) features in V2.0.


Sorry for the long video. Did had some good time with a video editing program though. :stuck_out_tongue:

The major changes in 2.0 are fixed point calculations to lower the cycle time.

Separate files to easy change remotes or hexapod settings. This will allow the user to simply add the correct remote file and hexapod file to adapt the code for different hardware without having to hack in to the core.

Single leg control to directly control one of the legs. This can be used with or withouth balance mode.

GP Player support. This allows the user to play sequences that are stored inside the SSC. This will need the current GP firmware for the SSC and a bi-directional connection between the BAP and SSC.

Here’s a full list of the new features:

;NEW IN V2.0 ; - Moved to fixed point calculations ; - Inverted BodyRotX and BodyRotZ direction ; - Added deadzone for switching gaits ; - Added GP Player ; - SSC version check to enable/disable GP player ; - Controls changed, Check contol file for more information ; - Added separate files for control and configuration functions ; - Solved bug at turn-off sequence ; - Solved bug about legs beeing lift at small travelvalues in 4 steps tripod gait ; - Solved bug about body translate results in rotate when balance is on (Kåre) ; - Sequence for wave gait changed (Kåre) ; - Improved ATan2 function for IK (Kåre) ; - Added option to turn on/off eyes (leds) ; - Moving legs to init position improved ; - Using Indexed values for legs ; - Added single leg control

The software will be uploaded to my Project page.

Lynxmotion will also update the phoenix tutorial. This will include a link to the software as well. I’m sure they will notify us when the work is done.

Xan

Hi Xan,

This is awesome! Great work with the video, I’m glad you managed to solve the video format issue.
It might be hard to see in the video but there are alot of work in this code. I’m thinking of the indexing, fixed point calculation, separate files and other major improvements. But the most important thing is that the code is very fast! :smiling_imp:

Your single leg control demo was creative with the little servo box :wink:
Cool GP sequence too!

Thanks for your great contribution to the robotic community! Highly appreciated!

nice work. you have done really well here. cant wait to have a look at the code.
great video too. it could never be to long, its always nice to see your work in action.

as zenta said Your single leg control demo was creative and balancing the servo box on its corner really shows how fine this code is.

great work and well done. :wink:

Hi Xan,

Great Video! I would like to second Zenta (and others) that you did a lot of great work on the code.

Kurt

hi Xan,

can you post the code here so we can use it allready? The download still isn’t available.

Thanx in advance! Great job!