# When Python runs the "send RC" script, the result is incorrect

I used Send RC （ Pymavlink · GitBook ）in PythonBut when it runs, all thrusters are working, and there is no difference when the first parameter of ‘’‘set_rc_channel_pwm’’’ function is changed to 1, 3, 4, 5 and 6. What is the reason? How to solve it? My bluerov configuration is as follows:

1. Pix model: fmuv2

2. Firmware version is 4.0.3

Thanks for any suggestions!

Hi @newname, welcome to the forum

It’s possible to check which value (1100 full reverse, 1500 stopped, 1900 full forwards) is being sent to the individual thrusters with the SERVO_OUTPUT_RAW mavlink message that the Pixhawk sends out. While your ROV is running and connected to the topside you can check the current values at http://192.168.2.2:4777/mavlink/SERVO_OUTPUT_RAW?pretty=true (refresh the page to see the latest values), or you could receive messages to get the values from within your script (which you could then print).

A few questions on your setup:

1. Which version of the companion software are you using? It should be shown in the web interface.
2. Which flight mode are you operating in? I’d recommend using manual mode to start with, because it should do only what you directly tell it to (modes that have stabilisation engaged will try to correct the ROV orientation, which may not be desired when starting out testing)
3. Are you sending regular control commands and also regular heartbeats, as per the Safety section on the Pymavlink page?
I have the same problem as him, this is my code (Pymavlink,mavlink 2):

"""
Example of how to use RC_CHANNEL_OVERRIDE messages to force input channels
in Ardupilot. These effectively replace the input channels (from joystick
or radio), NOT the output channels going to thrusters and servos.
"""

# Import mavutil

# Create the connection
# Wait a heartbeat before sending commands
master.wait_heartbeat()

# Create a function to send RC values
# here: https://www.ardusub.com/operators-manual/rc-input-and-output.html#rc-inputs
def set_rc_channel_pwm(channel_id, pwm=1500):
""" Set RC channel pwm value
Args:
channel_id (TYPE): Channel ID
pwm (int, optional): Channel pwm value 1100-1900
"""
if channel_id < 1 or channel_id > 18:
print("Channel does not exist.")
return

# Mavlink 2 supports up to 18 channels:
rc_channel_values = [65535 for _ in range(18)]
rc_channel_values[channel_id - 1] = pwm
master.mav.rc_channels_override_send(
master.target_system,                # target_system
master.target_component,             # target_component
*rc_channel_values)                  # RC channel list, in microseconds.

# Set some roll
set_rc_channel_pwm(2, 1600)

# Set some yaw
set_rc_channel_pwm(4, 1600)

# The camera pwm value sets the servo speed of a sweep from the current angle to
#  the min/max camera angle. It does not set the servo position.
# Set camera tilt to 45º (max) with full speed
set_rc_channel_pwm(8, 1900)

# Set channel 12 to 1500us
# This can be used to control a device connected to a servo output by setting the
# SERVO[N]_Function to RCIN12 (Where N is one of the PWM outputs)
set_rc_channel_pwm(12, 1500)


The companion machine I use is jetson nano, the ardusub version and pixhawk are the same as the author (4.0.3, fmuv2) .

# Set some roll

set_rc_channel_pwm(2, 1600)#No matter what parameters are used, all motors are working！
I have carefully read the mavlink command (Messages (common) · MAVLink Developer Guide) description, but I don’t know how to change it, because how to change the parameters of the “set_rc_channel_pwm” function is useless. what should I do? thanks for your help ！I run the ”Arm/Disarm the vehicle“ code and it all works and i use “manual” mode.

Hi @White, welcome to the forum

I did some testing today and found that the motion should be explicitly set to stopped before arming, otherwise arming can sometimes resume the last thruster state from the last time the vehicle was armed.

If you’re interested I’ve made an example program here, which sends regular heartbeats and handles arming/disarming and setting the flight mode.

Thank you for your reply! I will try your suggestion. If the RC channel selects’roll’, which specific motors are working under normal circumstances? (In manual mode)

If you are not troublesome, I hope you can explain which motors work for the’pitch’,‘yaw’,‘Throttle’,‘Forward’ and’Lateral’ channels. This will be very helpful for my debugging work. grateful!

I found that you have discussed Pymavlink before. The sample code you gave did not show that the channels (1 to 8) that do not work are set to pwm(1500). In fact, you still set the mask value to 65535 (UINT_MAX) to shield it. Channel, has the mask value bug that appeared in it been fixed?

The various default frame configurations are shown here and in QGroundControl.
The factors for the BlueROV2 for Roll, Pitch, Yaw, Throttle, Forward, and Lateral are here:

If a motor has a non-zero factor for one of the motions then it should be moving when that motion is commanded (unless the contribution is cancelled out by another motion in the opposite direction - e.g. if the ROV is commanded to equal amounts of forward and lateral with no yaw, then motor 1 should be stopped).

I didn’t find this to be an issue if the motions were correctly set to stopped before arming, as done here

def clear_thrusters(self, stopped_pwm=1500):
''' Sets all 6 motion direction inputs to 'stopped_pwm'. '''
logging.info('clear_thrusters')
rc_channel_values = [*[stopped_pwm]*6, *[65535]*(18-6)]
logging.debug(rc_channel_values)
self.master.mav.rc_channels_override_send(
self.master.target_system,
self.master.target_component,
*rc_channel_values
)


In this method of yours, the pwm of the first six motors is 1500, and the following 12 channels are set to 65535. Can I set the first six motors to 65535? Can this stop the motor or maintain the previous motor state?

You mean that one RC channel corresponds to several motors: For example, the’roll’ channel corresponds to No. 3, No. 5, and No. 6 motors?

That’s not entirely correct. RC Input Channels correspond to user-specified controls, the first 6 of which are vehicle motion directions. My clear_thrusters method sets all motion directions to ‘stopped’, which applies equally well to vehicles with 3 thrusters as to ones with 6, 8, or more.

The clear_thrusters method is only intended to be used when you explicitly want to clear all motion inputs. If that isn’t run between setting the mode and arming the vehicle then the vehicle will still be using whatever inputs were last set, which can cause thrusters to activate without the user intending for it.

Setting values to 65535 is telling the vehicle to “continue using your last input” (for that channel), so if the last input was to stop that motion then it would stay stopped, but if your last input was full thrust then that would continue (and in the case of then arming the vehicle, it would go straight to full thrust, which can understandably be problematic).

Note that while manual mode sets thrusts based on the commanded motions, the active-control (stabilisation) modes use the commands for setting target motion rates, so it uses the sensors to determine if those are being met, and increases/decreases thrust as relevant. That means that testing stabilise mode with the vehicle on a table, for example, will result in the thrusters quickly going to max thrust, because the commanded motion isn’t being achieved.

Yes, RC Input Channels correspond to motions, not thrusters. In the case of the standard BlueROV2 frame, ‘roll’ corresponds to thrusters 5 and 6, because those are the thrusters that are positioned and oriented to be capable of moving the ROV about its roll axis. They also happen to be the only vertically oriented thrusters, so are also activated when vertical motion is commanded.

Similarly to the example in my last comment, if the ROV is commanded to equal inputs for vertical motion and roll then one of the thrusters will be off/stopped because the roll and vertical motions are coupled in a 1:1 ratio, and the contribution from one will cancel the other.

Consider motor 5, with factors 1.0 for roll and -1.0 for vertical. If both ‘vertical’ and ‘roll’ are commanded to 1900, they’re both 400 above the ‘stop’ point (1500). The net output then is

\begin{align} RC_{OUT}|_{mot_5} &= \left(RC_{IN}|_{roll}-stop\right) \times F_{roll_5} + \left(RC_{IN}|_{vert}-stop\right) \times F_{vert_5} + stop\\ &= (1900 - 1500) \times 1.0 + (1900 - 1500) \times (-1.0) + stop\\ &= 400\times 1.0 + 400 \times (-1.0) + stop\\ &= 0 + stop \end{align}

Following the same process for motor 6,

\begin{align} RC_{OUT}|_{mot_6} &= \left(RC_{IN}|_{roll}-stop\right) \times F_{roll_6} + \left(RC_{IN}|_{vert}-stop\right) \times F_{vert_6} + stop\\ &= (1900 - 1500) \times (-1.0) + (1900 - 1500) \times (-1.0) + stop\\ &= 400\times (-1.0) + 400 \times (-1.0) + stop\\ &= -800 + stop \end{align}

which then gets clipped to 400 below stop because the output has to be between 1100 and 1900 (in a default setup at least - it’s also possible to restrict the output range further if desired).

Does this mean that I should be under water instead of air when I test the script? If I use the joystick to control the ROV in the air, will all motors have full thrust?

If this is the case, the code of the motor library:

inside the’pitch’ factors are 0, does it mean that this frame cannot perform the’pitch’ motion? Another thing is the parameters of the motor 5 and motor 6 as you said. There are’roll’ and’vertical’ factors. If I only want to perform the’roll’ motion, in your opinion, only the motor 5 and motor 6 work, then the’vertical’ movement will still be carried out, right?

Generally it’s best not to use our thrusters in air (the bearings are lubricated by the water, and vibration and noise will be greater when dry, which can cause the thrusters to overheat and get damaged), but for short tests (a few seconds) with low thrust it should be fine.

If you’re using manual mode you can ensure that’s the case by only using small values. If you’re wanting to test stabilise mode it’s best to do so in the water (since you don’t directly control the applied thrust), but if you for some reason need to test it in the air you can restrict the thruster output ranges, as covered in my previous comment.

That’s correct - the standard BlueROV2 frame is incapable of controlling pitch, as it has no motors that are positioned+oriented to be able to do so. If you require full 6-axis control then you’ll need to use a different frame configuration (e.g. the BlueROV2 Heavy). If you just want a high level view, there’s a basic vehicles overview in our work-in-progress technical reference

Not quite - my “1:1 coupling” comment was slightly misleading, because it only applies to the magnitudes. In the factors you’ll see that thruster 5 has +1.0 roll and -1.0 vertical, whereas thruster 6 has -1.0 roll and -1.0 vertical. To do only roll the vertical component from each needs to cancel out, which is possible because the roll factors have opposite signs and the vertical factors don’t.

Accordingly, commanding 1900 to roll and 1500 (stopped) to vertical will mean RC_{OUT}|_{mot_5} = 400 + stop, whereas RC_{OUT}|_{mot_6} = -400 + stop (so they push equal amounts in opposite directions, which cancels the vertical component of motion, and leaves only a moment that causes roll rotation).

You said that the motion should be explicitly set to stopped before arming.After arming, do I have to call the ‘‘clear_thrusters’’ method and then use the ‘‘sub.send_rc’’ method every time I change the RC input? Can I not use the ‘‘sub.send_rc’’ method continuously to change the RC input?

That’s what I found to be the case while testing last week, yes.

I don’t expect that would be helpful or necessary to do. Note that in my example sub.clear_thrusters() is only called once at the start of each arming block, and then multiple send_rc commands are run (e.g. send some roll, and then some throttle a second later).

Once your vehicle is armed I expect that should be fine, although you may wish to use the rc_channels_override_send method directly to send several motion commands at once (e.g. specify forwards and down at the same time).

I’d note that, more generally, if you’re wanting to make a full control algorithm for the vehicle (requiring high speed controls and/or high frequency sensor updates) it’s likely better to create a modified ArduSub firmware rather than using Pymavlink, since then you can access the sensors at full measurement frequency, and don’t have the communication latency of sending around mavlink messages.