Radio controlled Fish. Advice on code.

davyjoneslocker

New Member
Hello,

I'm very new to programming and after some faffing about with a OOPIC, which I found counter intuitive and ill suited to use with radio control, I chose the Picaxe as a better alternative for a project I have on the ways- a radio controlled swimming fish.

I hope some people on here can give me some advice on my thoughts so far.

Inspired by the work of Essex university, and their robotic fish on display at the London Aquarium- check the videos at the following links- I decided I’d like to have a go at something similar myself.

http://www.youtube.com/watch?v=REi1g69P9w8&mode=related&search=

http://www.youtube.com/watch?v=eO9oseiCTdk&mode=related&search=

http://privatewww.essex.ac.uk/~jliua/

The fish use four servos for articulation, This gives a very high degree of manoeuvrability, enabling the very realistic swimming motion and other benefits like ‘C-turns’ which enable the fish to turn in it’s own body length.
The position and speed of the servos are governed by a Totalrobots DS-SCX8S microcontroller, which I believe is a precoded PIC chip running at 8MHZ that can be addressed via I2C or RS232.

http://www.totalrobots.com/access_files/sm_drivers.htm#ds-scx

The Essex Uni team used a PIC chip to relay information from various sensors and to provide the necessary control bytes for the DS-SCX8S.

As opposed to autonomous control, in my case, I wish to control the fish manually via radio control- like a novel model submarine.

As the Picaxe has a pulse in facility and i2c, I considered it a good choice to convert the R/C signals to the control data required by the DS-SCX8S.

My jottings so far have come up with the following-

Use the pulse in command to read two channels from my R/C receiver, controlling speed and steering respectively.

Quantize each channel into 16 segments, which I think gives adequate resolution, and enables each channel to be represented by a nibble, thus the two channels will then be represented by a single control byte. I considered this would keep any control table within manageable limits.

Use the Lookup command with a control byte to obtain the necessary DS-SCX8S servo command bytes. The DS-SCX8S uses two bytes for each servo it controls. First byte controls speed and direction, and the second byte controls position. As I have four servos, that is eight bytes in total for control.

The section of code which has given me most head scratching, is how to simply divide the pulse into 16 parts to represent the final control byte.

One thought I have come up with is to use if and then-

Symbol radio_input1 = b0
Symbol radio_input2 = b1
Symbol control_byte = b2
Symbol servo_control1 = b3
Symbol servo_position1 = b4

Main:

erratum:

pulsin 0,1 radio_input1
pulsein 1,1 radio_input2

if radio_input1 <100 then erratum
if radio_input2 <100 then erratum
if radio_input1 =>115 then equalbit0
if radio_input1 =>120 then equalbit1

equalbit1: control_byte=0
equalbit2: control_byte=1

'etc. etc. until 15 is reached, then do the same for radio_input2 with the exception of

if radio_input2 =>115 then addbit0
if radio_input2 =>120 then addbit16

addbit1: control_byte= control_byte + 0
addbit2: control_byte= control_byte + 16

etc. etc.

Once the control byte is obtained, I would then use this with eight lookup tables for each of the servo control bytes, i.e.

lookup control_byte, (10,11,12,13……), servo_control1
lookup control_byte, (1,2,3,4,5……….), servo_position1

‘continue this for all eight control bytes

Then finally send the result via I2C to the DS-SCX8S. This chip auto increments it’s registers, so:-

i2cslave %11100000, i2cfast8, i2cbyte
Writei2c 0, (servo_control1, servo_position1)

‘Just shown for the first couple of control bytes

Write i2c 32, (0)

‘the final write command enables all the servo commands on DS-SCX8S, so that all the servos move at the same time.

goto main

What I'm not sure of is how quickly this will work, if it will work well at all (I'm using a 28X1).

Opinions are very welcome, and if someone can suggest a better way, I’d be very happy to read about it.

Thanks,

Andy
 

Tom2000

Senior Member
The section of code which has given me most head scratching, is how to simply divide the pulse into 16 parts to represent the final control byte.

One thought I have come up with is to use if and then-
Andy,

There's an opportunity for some cleverness on your part here.

Normalize the radio values so they top out as close to 255 as possible, within the limitations of unsigned integer math. Then divide each by 16. Then each value becomes a pointer to a 16-entry lookup table.

It should be a lot cleaner than a long if..then..elseif structure.

Good luck with your fish. It looks like an interesting project.

Tom
 

MFB

Senior Member
Basic stuff first?

I did some work years ago with oceanographic buoys, when 27MHz was allocated for this type of research, and radio waves at that frequency really don’t travels well when the antenna is submerged. I expect it will be pretty much the same at the RC allocated frequencies of 35MHz and 2.4GHz.

I would suggest you do some basic radio propagation tests before spending too much time on software development for this application. Unless your fish intends to spend all its time basking in the sun!
 

BeanieBots

Moderator
Expanding on Tom2000's reply.
The pulsin command measures in 10's of uS.
'Normal' RC signal is 750uS to 2250uS with 1500uS as neutral.

pulsin pin,var
if var > 225 then pulseTooLong
if var < 75 then pulseTooShort

var-var-75/15

var will now have a range of 0 to 15 for 'normal' signals.
 

davyjoneslocker

New Member
I would suggest you do some basic radio propagation tests before spending too much time on software development for this application. Unless your fish intends to spend all its time basking in the sun!
Hello,

I've been an active model submariner for about 10 years. I use 40 MHZ beyond 10 feet without any issues. 27MHz is okay too, but not many multi-channel radios available these days in that band.
2.4GHZ is a complete no no. 35MHZ shouldn't be used for surface, or sub surface craft (illegal)- aircraft only.

This is for swimming pool, pond use only.

Andy
 

davyjoneslocker

New Member
Beaniebots and Tom,

Thanks for your advice.

Please could you give some idea how I could integrate your idea into a control byte.

Thanks

Andy
 

BeanieBots

Moderator
What does your control byte need to do?
var will hold 16 values 0-15
branch var(address0.....address15) would jump to a different line depending on the value.
OR
"select case" is another option but it is vey memory hungry.
 

MFB

Senior Member
So thats what 'davyjoneslocker' is all about!

What I meant by "radio waves at that frequency really don’t travel well" is that you only get a range measured in feet when submerged, rather than hundreds of feet on the surface. But if beyond 10 feet does it for you, no problem.
 

davyjoneslocker

New Member
MFB,

I rarely go beyond 2-3 feet, there isn't much point in controlling a sub you can't see. Plus most ponds are fairly shallow. Most RC equipment can go to about 12 feet in fresh water, beyond that and you will lose the signal.

Beaniebots,

I need the control byte to enable me to work with the lookup tables which give the control data for the DS-SCX8S chip.

Use the Lookup command with a control byte to obtain the necessary DS-SCX8S servo command bytes. The DS-SCX8S uses two bytes for each servo it controls. First byte controls speed and direction, and the second byte controls position. As I have four servos, that is eight bytes in total for control.

So for every stick command, I need to send eight data bytes to the DS-SCX8S. That's why I came up with the idea of using a 16x16 control matrix, otherwise it could easily get out of hand.

If I have two channels with numbers of 0-15 respectively, how do I group those numbers together to form a unique number to obtain my control byte?

For instance with both sticks centred, they may read seven each. Add them together, that makes fourteen, but then that number could also be either channel at one extreme.

Andy
 

BeanieBots

Moderator
OK, lets say b0 holds channel 1 and b1 holds channel 2 and both are in the range 0 to 15.
To make say b2 hold a unigue value that represents any combination, simply multiply one of them by 16 (shift four places) and add them together.
b2=b1*16+b0
b2 now holds 256 unigue values that represent all possible combinations of b0 and b1.
It would be easier to refer to b2 in hex from now on.
eg
$01 would be when b1=0 and b0=1
$10 (16 decimal) would be when b1=1 and b0=1
$FF (255) would be when both = $0F (15 decimal)
 
Last edited:

MFB

Senior Member
Daveyjoneslocker.

Sounds like a very interesting hobby, with possible low-cost (student) envronmental/wildlife research uses.

If you have to keep the sub within a few feet of the surface, and within site, could you not use an umbilical cord (maybe even send back live video over it). Alternatively, but this may not offer any advantage over radio, how about acoustic transmission?
 

davyjoneslocker

New Member
beaniebots,

Thanks very much for the answer, that will work.

MFB,

An umbilical cord will work, but then the craft is effectively tethered. Essentially my hobby revolves around making miniature versions of fullsize craft.

An umbilical doesn't look right, unless of course you are making a scale model of an ROV. ;)

Model subs are known for being expensive to build, but that is only the case if you enter into scale model territory.

this is my first venture into robotics. A fish has a number of advantages over conventional propulsion, for one thing they can operate well in weedy ponds.

Right off to rewrite my code.....

Andy
 

BeanieBots

Moderator
Quite adventurous for a "first venture" into robotics.
Have a look at my "Autonomous Hexapod" in the Robotics section.
It's by no means an example of good programming practice but it does work and overcomes some of the problems you will be facing. It might also give you some ideas how to control the servos directly rather than using a dedicated controller. If you want to use a controller, then replacing the pulsout subroutine with serial outs would work.
If you replace the 'eye' inputs with your 'joystick' inputs I think you could be halfway there. Obviously the 'walking' algorythms would need to be replaced with 'swimming' ones.
Using a 16X16 'command' system might be a bit program space hungry. Maybe use 4X4 initially until you at least get things working.
Good luck.
 

davyjoneslocker

New Member
I chose the servo controller chip for the sake of simplicity, and also becasue that is what the Essex team had used.

For the fish to work effectively the servos not only have to be moved to position, they have to be speed controlled too.

Perhaps as my programming ability improves, I may be able to devise some code which will squeeze onto a single chip.

Thanks for the advice regarding the Hexapod, I'll look into it.
 

D n T

Senior Member
Under water comms

Do you have any links to find out more about sending radio comms under water , maybe 10 metres? it could very cool and something I have wondered about but not ever looked too deeply into, I believe that low frequency is better, don't real subs use that?
 

davyjoneslocker

New Member
I'm sorry I can't be of any assistance with your application.

My consumer radio control is low power, and there is absolutely no way on earth the signal will penetrate through 10 metres of water.

I think you would need some very powerful kit, and a much lower frequency.

There has been some interesting research into magnetic transmission of radio waves. It seems the researchers discovered that magnetism is largely unaffected by water, salty or fresh, so this has large implications for underwater communications.

Whether this technology will ever filter down to us mortals is debatable.
 

davyjoneslocker

New Member
Expanding on Tom2000's reply.
The pulsin command measures in 10's of uS.
'Normal' RC signal is 750uS to 2250uS with 1500uS as neutral.

pulsin pin,var
if var > 225 then pulseTooLong
if var < 75 then pulseTooShort

var-var-75/15

var will now have a range of 0 to 15 for 'normal' signals.
beaniebots,

Please could you explain the var-var-75/15 part of your code, I don't fully understand what is being done there.

Thanks

Andy
 

BeanieBots

Moderator
just re-read my post. Sorry, that was a typo. It should have been:-
var=var-75/10
remember, after the earlier tests, var will be in the range 75 to 225
therefore after var-75, it will be in the range 0 to 150
after /10, it will be in the range 0 to 15.

PICAXE maths is strictly left to right.
so var=var-75/10 will actually be var=(var-75)/10
 

davyjoneslocker

New Member
Thanks for clearing that up, clear as crystal now.

Here is how I've modified my code. It's a lot cleaner now. The values are slightly different, as I'm running the chip at 8MHZ instead of 4MHZ.
Ignore the values in the lookup table, I still have to work these out for the correct undulating movement.

'ICOD Brains
'option Picaxe 28X1

Symbol radio_input1 = w0
Symbol radio_input2 = w1
Symbol control_byte = b4
Symbol servo_control1 = b5
Symbol servo_position1 = b6
Symbol servo_control2 = b7
Symbol servo_position2=b8
Symbol servo_control3 = b9
Symbol servo_position3 = b10
Symbol servo_control4 = b11
Symbol servo_position4 = b12

setfreq m8

main:

erratum:

pulsin 0,1, radio_input1 ' Measures each pulse input from each R/C channel.
pulsin 1,1, radio_input2

if radio_input1 <150 then erratum ' If pulse is out of range ignore
if radio_input1 >450 then erratum
if radio_input2 <150 then erratum
if radio_input2 >450 then erratum

radio_input1= radio_input1 - 150 / 20 'Convert pulse into value range 0-15 for both channels
radio_input2= radio_input2 - 150 / 20
radio_input2= radio_input2 * 16 'Convert radio_input2 into value 0-240
control_byte= radio_input1 + radio_input2 'Add both channel together to form control byte with value 0-255

lookup control_byte,(1,2,3,4,5,6,7), servo_control1 'Lookup table for servo 1 speed and direction byte.

lookup control_byte,(1,2,3,4,5,6,7), servo_position1 'Lookup table for servo 1 position.

lookup control_byte,(1,2,3,4,5,6,7), servo_control2 'Lookup table for servo 2 speed and direction byte.

lookup control_byte,(1,2,3,4,5,6,7), servo_position2 'Lookup table for servo 2 position.

lookup control_byte,(1,2,3,4,5,6,7), servo_control3 'Lookup table for servo 3 speed and direction byte.

lookup control_byte,(1,2,3,4,5,6,7), servo_position3 'Lookup table for servo 3 position.

lookup control_byte,(1,2,3,4,5,6,7), servo_control4 'Lookup table for servo 4 speed and direction byte.

lookup control_byte,(1,2,3,4,5,6,7), servo_position4 'Lookup table for servo 4 position.


Gosub i2c_output


if radio_input1 =<7 then

goto swimlookup

else

Goto main

endif

Swimlookup: 'The following lookup tables givew the control and position bytes for each servo based on the control byte.

lookup control_byte,(1,2,3,4,5,6,7), servo_control1

lookup control_byte,(1,2,3,4,5,6,7), servo_position1

lookup control_byte,(1,2,3,4,5,6,7), servo_control2

lookup control_byte,(1,2,3,4,5,6,7), servo_position2

lookup control_byte,(1,2,3,4,5,6,7), servo_control3

lookup control_byte,(1,2,3,4,5,6,7), servo_position3

lookup control_byte,(1,2,3,4,5,6,7), servo_control4

lookup control_byte,(1,2,3,4,5,6,7), servo_position4

Gosub i2c_output

Goto main

i2c_output:

i2cslave %11100000, i2cfast8, i2cbyte 'Setting for the DS-SCX8S comms
Writei2c 0, (servo_control1, servo_position1, servo_control2, servo_position2, servo_control3, servo_position3, servo_control4, servo_position4 )

Writei2c 32, (0) 'This is set all servos command

return
 
Last edited:
Top