Python/Lua script to modify motors orders

Hey !

I am discovering the python scripts using for now the mission planner “script” pannel but then the “servo10_function”.

I am on ardusub sitl but i think the orders are sent the same way for every vehicle type so I am open to suggestions on other types of vehicle.

I have the orders of my radiocommand incomming by PPM. Then the “forward”, “throttle”, “yaw” and “lateral” commands are read by the autopilot and sent on motors 1-5 through servo1-5 functions.

Also if this would be easier in lua I am open to any suggestions

Now I’d like to get the PWM commands before they are sent to the motors, modify them a bit and send them.

For now I am able to read the different commands using this script :

print (‘Start Script’)

Set all RC channels to mid-point (1500)

for chan in range(1, 9):
Script.SendRC(chan, 1500, True)
print(‘wrote neutral RC’)

Arm motors

armed = MAV.doARM(True)
if armed :
print (‘Motors Armed!’)
else :
print('unable to arm ')

output = [1500]*5

Loop to read and modify PWM commands going into motors

while True:
# Read PWM commands going into motors 1-5
cmd[0] = cs.ch1in
cmd[1] = cs.ch2in
cmd[2] = cs.ch3in
cmd[3] = cs.ch4in
cmd[4] = cs.ch5in

print('PWM read : ' +str(cmd))

# Modify the PWM commands as desired (here, set all to mid-point)
output = [1500] * 5

# Send modified PWM commands to motors 1-5
for j in range (5):
    Script.SendRC(j + 1, output[j], True)

Script.Sleep(100) # wait a bit before checking again

However this seems to not work. I am thinking of using the cs.chxout but didn’t work till now eather.

Did anyone already do something like this ?


this is just to say I still haven’t found how to proceed. So if anyone has leeds… here’s my attempt on lua script without a success on modifying the output pwm. the cmd list is correct and contains the orders from my radio but the set_output_pwm_chan and other functions from srv_channels had no effects.

gcs:send_text(5," starting script ")
nb = 0

function update ()

cmd = {}
for i = 1,9 do
table.insert(cmd, rc:get_pwm(i) )

motors = {}
for i = 1,9 do
gcs:send_text(5, i … " set to " … val)
SRV_Channels:set_output_pwm_chan(i, val)

val = val + 1

if val >= 1800 then
val = 1200

return update, 1000

return update, 1000


what are you trying to modify exactly ?
On ArduPilot, RC input and servo_output are separated so you can transform the input how you want.
But python scripts on MP are for remote control. So unless you configure the autopilot to not take into account RC input, they will be always used.
If you disable RC control on the autopilot, then you can received RC input, send them back to MP with the telemetry link and use MP python scripting to send new command to the motors.

But that is quite complicated. Better configure you input correctly on first step should be enough. That is why I ask, what are you trying to do ?

1 Like

okey so the idea in the end is that below a certain speed i want the order for motor 3 and 4 to be sent at motor 3 and 4 but once I have a certain speed I want that 3 and 4 are at 1500 and send the order of 3 and 4 on 5 and 6.

Something like :

if rc1 and rc2 > 1700 :
servo3 pwm = servo4 pwm = throttle
servo5pwm = servo6pwm = 1500
servo3pwm = servo4pwm = 1500
servo5pwm = servo6pwm = throttle

About putting the script in "servo10_function " so it runs immediatly at the start of the system, is it possible ? How should I proceed ?

Combining those two LUA script, should do it :

First is how to get rcinput with LUA, second is how to set motor output.

But this will only work in manual control. On long term that would be probably better that you look at making a new mixer so that would work in all modes

1 Like

Nice !! Thank you ! Yeah the problem is for guided or auto modes… There is no “throttle” and “forward” command that we can get in the script is there ?

Create some “virtual” outputs (SERVOx with nothing connected). Use them for throttle/steering/etc. Then poll the output on those channels and translate it to physical output on the ones that are actually in use.

It may even be unnecessary to create such virtual channels, but you’ve been too obscure in your request. For example, you can poll SRV:get_output_scaled(70) in Rover firmware to get throttle output percentage, even if there is no throttle servo assigned (same with steering or most other servo functions).

Oh sorry I said in my first message that I am on ardusub. Will get_output_scaled(70) work to read the throttle output even if there no servo_function assigned to it ? And will this work in auto/guided mode ?

That’s what I just said, and it will work in all modes.

1 Like

Topic moved to Lua as that doesn’t need dev team ati


Hey I am sorry to bother you again but I seem unable to read the 70 function or any other one for some reason. Here’s my code :
"gcs:send_text(6,“starting script” )

function update ()

cmd = {}
for i = 1,9 do
table.insert(cmd, rc:get_pwm(i) )

gcs:send_text(6,"channel scaled (thottle) 70 : " … SRV_Channels:get_output_scaled(70) … " ground steering (26) : " … SRV_Channels:get_output_scaled(26))
gcs:send_text(6,"motor1 : " … SRV_Channels:get_output_scaled(33) … " motor2 " … SRV_Channels:get_output_scaled(34) … " motor3 " … SRV_Channels:get_output_scaled(35) … " motor4 " … SRV_Channels:get_output_scaled(36) … " motor5 " … SRV_Channels:get_output_scaled(37) … " motor6 " … SRV_Channels:get_output_scaled(38) )

return update, 1000

return update, 1000
And Here’s the output :

I don’t understand what I am doing wrong. While I had 0.0 everywhere my rov was mooving accordingly to the input of the radiocommand… This is in ardusub sitl with simplerov4.

ArduSub is a bit of an odd branch that does not always incorporate the latest features due to lack of development, and I confirmed this morning that SRV_Channels:get_output_scaled() does not seem to work as expected.

However, SRV_Channels:get_output_pwm() does work for channels that are assigned.

In order to remap output with a script, I think it will be necessary to create the “virtual channels” that I described before, which will be extremely cumbersome if you intend to remap all 6 motors. Your servo layout will look something like this:


The physical connections will be on outputs 1-6, and you’ll leave outputs 11-16 disconnected. The script will have to handle all motor output via the scripting servo channels. It would be prudent to ensure that all of the min/max/trim values match between each scripted output channel and its virtual motor channel. Of course, this leaves only 4 available outputs for things like gimbals or grabbers.

Alternatively, you could track down the issue with SRV_Channels:get_output_scaled() and fix the binding in the ArduSub branch of the code.

1 Like

So the “script1-6” options that you put in servo1-6_function means that the value will be set by a script ? I didn’t know what it means. I’ll try this ! Also I was thinking that it might be possible to enable “SERVO_32_ENABLE” and set the “virtual channels” on these so I still have outputs available.
Anyway Thanks !

Hey !

Here’s what I’ve done so far. On sitl, the following command works :

for i = 0,13 do
gcs:send_text(4, i … " set to " … val)
SRV_Channels:set_output_pwm_chan(i, val)

but this one doesn’t :" value = SRV_Channels:get_output_pwm(i)"
it returns nil on all channels. I still don’t understand what the “script1” options stands for in servo1_options because the “set_output_pwm_chan” works even if I don’t put script1 on it.

After this I decided to try on a real pixhawk (3 Pro) and lua scripts seems to don’t work. When I load one on the board i get
test pixhawk 3Pro which seems normal but then when I reboot the board the script isn’t read. Maybe the pixhawk is too old I don’t know…

Back in SITL I’ve tried plenty of SRV_Channels options on the output channels but nothing seems to work. Any ideas on how to access the forward, throttle and yaw orders so that i can send them on motors ?

You are conflating channels with servo function.

SRV_Channels:get_output_pwm() takes servo function as its argument, so to poll motor 1, you’d use SRV_Channels:get_output_pwm(33).

Likewise, SRV_Channels:set_output_pwm() takes a servo function and PWM value as its arguments, so to set the Script1 servo to 1500us, you’d use SRV_Channels:set_output_pwm(94, 1500).

As for your problem with the Pixhawk 3, it appears the FTP command was unsuccessful. You can load the script directly onto the SD card under APM/scripts, if that’s easier.

1 Like