Hi!
Could I get some help understanding how to control the correct servo with set_actuator() from mavsdk.telemetry class?
Here is the specific script I’m using to test the command below. I’m using a link to a public repository for ease of use, but I’ll also paste the code down here below.
I was hoping that the specific commands I have commented on in the test code could be mapped in QGroundControl’s Actuator setup by assigning the motor/aileron/etc. to the same number position in FMU Aux or FMU MAIN. More on that below.
I’m using a Holybro Pixhawk 6X, and I want to use it to control a tiltrotor. My plan was to set up HITL tiltrotor for testing purposes, but I had issues setting it up on Ubuntu 22.04; I didn’t realize it wasn’t officially supported.
Again, I would brute-force testing on physical hardware to see which control controls which servo in QGroundControl, but I can’t get into offboard mode with the code because I’m controlling when indoors (maybe spoof gps signal somehow for indoor testing?)
I’m also confused because when I read on PX4 documentation about control groups, it seems different from what I am using in my code. Here’s what I’m referring to:
Could I get some direction on this? Any help would be appreciated.
Blockquote
“”"
In PX4 v1.9.0 Only first four Control Groups are supported
(PX4-Autopilot/src/modules/mavlink/mavlink_receiver.cpp at v1.9.0 · PX4/PX4-Autopilot · GitHub)
This is confusing, since only 2 control groups can be used anyway. What does it mean?
This site may have the answer:
https://jalpanchal1.gitbooks.io/px4-developer-guide/content/en/concept/mixing.html
Code for Reference
import asyncio
from mavsdk import System
from mavsdk.offboard import OffboardError, ActuatorControl, ActuatorControlGroup, PositionNedYaw
import json
LOCAL_HOST_TEST = "udp://:14540"
CURR_USB_CONNECTION = "/dev/ttyACM1"
LIKELY_USB_PI_CONNECTION = "/dev/ttyUSB0"
def print_pretty_dict(d, indent=4):
"""
Prints the contents of a dictionary (and nested dictionaries) in a pretty format.
"""
def print_dict(d, level=0):
for key, value in d.items():
if isinstance(value, dict):
print(' ' * (level * indent) + f"{key}:")
print_dict(value, level + 1)
else:
print(' ' * (level * indent) + f"{key}: {value}")
print_dict(d)
def clamp(value, min_value, max_value):
"""Clamps the value between min_value and max_value."""
return max(min_value, min(value, max_value))
async def set_actuator_control_homemade(drone, elevator, aileron, rudder, starboard_front_motor,
rear_motor, front_motor, starboard_rear_motor, forward_propulsion_motor):
"""Sets the actuator control with two control groups and specific control modifications."""
group1_controls = [
float('nan'), # NaN for RC ROLL
float('nan'), # NaN for RC PITCH
float('nan'), # NaN for RC YAW
float('nan'), # NaN for RC Throttle
clamp(elevator, -1, 1),
clamp(aileron, -1, 1),
clamp(rudder, -1, 1),
clamp(-aileron, -1, 1),
]
group2_controls = [
float('nan'), # Unused
float('nan'), # Unused
float('nan'), # Unused
clamp(starboard_front_motor, 0, 1),
clamp(rear_motor, 0, 1),
clamp(front_motor, 0, 1),
clamp(starboard_rear_motor, 0, 1),
clamp(forward_propulsion_motor, 0, 1)
]
right_aileron = clamp(-aileron, -1, 1) # Reverse aileron for right
group2_controls[1] = right_aileron # Update right aileron in the control group
control_group1 = ActuatorControlGroup(controls=group1_controls)
control_group2 = ActuatorControlGroup(controls=group2_controls)
try:
await drone.offboard.set_actuator_control(ActuatorControl(groups=[control_group1, control_group2]))
except OffboardError as e:
print(f"Setting actuator control failed with error: {e}")
async def setup_mavlink_offboard(drone, curr_conn=LOCAL_HOST_TEST):
"""
Abstracts away the setup of the MAVSDK MAVLink protocols.
"""
await drone.connect(system_address=curr_conn)
print("Waiting for drone to connect...")
async for state in drone.core.connection_state():
if state.is_connected:
print(f"-- Connected to drone!")
break
print("Waiting for drone to have a global position estimate...")
async for health in drone.telemetry.health():
if health.is_global_position_ok and health.is_home_position_ok:
print("-- Global position estimate OK")
break
print("-- Arming")
await drone.action.arm()
print("-- Setting initial setpoint")
await drone.offboard.set_position_ned(PositionNedYaw(0.0, 0.0, 0.0, 0.0))
print("-- Starting offboard")
try:
await drone.offboard.start()
except OffboardError as error:
print(f"Starting offboard mode failed with error code: {error._result.result}")
print("-- Disarming")
await drone.action.disarm()
return False
return True
async def set_actuator_control_test(drone, elevator, aileron, rudder, starboard_front_motor,
rear_motor, front_motor, starboard_rear_motor, forward_propulsion_motor):
""" Testable version of set_actuator_control function. """
return await set_actuator_control_homemade(drone, elevator, aileron, rudder,
starboard_front_motor, rear_motor, front_motor, starboard_rear_motor,
forward_propulsion_motor)
async def test_set_actuator_control():
""" Tests for set_actuator_control function. """
drone = System()
await setup_mavlink_offboard(drone)
print("Running test cases...")
print("-- Go 0m North, 0m East, -5m Down within local coordinate system")
await drone.offboard.set_position_ned(PositionNedYaw(0.0, 0.0, -5.0, 0.0))
await asyncio.sleep(10)
print("Test case 1: Normal input")
await set_actuator_control_test(drone, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5)
await asyncio.sleep(10)
print("-- Go 5m North, 10m East, -5m Down within local coordinate system")
await drone.offboard.set_position_ned(PositionNedYaw(5.0, 10.0, -5.0, 90.0))
await asyncio.sleep(5)
print("Test case 2: Inputs outside bounds")
await set_actuator_control_test(drone, 1.2, -1.5, 0.7, 1.5, -0.2, 0.8, 0.9, 1.1)
await asyncio.sleep(10)
print("Test case 3: Using two control groups")
await set_actuator_control_test(drone, 0.3, -0.3, 0.0, 0.1, 0.2, 0.9, 0.4, 0.0)
await asyncio.sleep(10)
print("Test case 4: All values at bounds")
await set_actuator_control_test(drone, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
await asyncio.sleep(10)
print("-- Go 5m North, 10m East, -5m Down within local coordinate system")
await drone.offboard.set_position_ned(PositionNedYaw(5.0, 10.0, -5.0, 90.0))
await asyncio.sleep(5)
print("-- Go 0m North, 10m East, 0m Down within local coordinate system, turn to face South")
await drone.offboard.set_position_ned(PositionNedYaw(0.0, 10.0, 0.0, 180.0))
await asyncio.sleep(10)
print("-- Stopping offboard")
try:
await drone.offboard.stop()
except OffboardError as error:
print(f"Stopping offboard mode failed with error code: {error._result.result}")
async def run_tests():
""" Run all test cases. """
await test_set_actuator_control()
if __name__ == "__main__":
asyncio.run(run_tests())