Roll left and right without yawing


#1

I am trying to control the drone without RC controller, without GPS.

I understand that I will have to bypass the radio failsafe and GPS requirements with:
vehicle.parameters[‘FS_THR_ENABLE’] = 0
vehicle.parameters[‘ARMING_CHECK’] = 0

I also understand that will have to use a Vehicle mode outside of Guided (because it requires GPS lock). Alt hold or Stabalize will do.

For now I am simulating it in ArduPilot.
The roadblock I am having is:

  • when I try to roll left, the drone re-orients (yaws) 90 degress to the left then go. Same for right.
  • same thing with backwards movement. It yaws 180 degrees to its back and then go.

Any ideas on how to tackle that?

Here is my script if someone wants to run in it in simulator:

from dronekit import connect, VehicleMode
from pymavlink import mavutil
import time

####### Connect to the simulated Vehicle via TCP (for the drone simulator)
print("Connecting to vehicle...")
vehicle = connect('tcp:127.0.0.1:5762', wait_ready=True)  # Change to the simulator's connection string

####### Function to send velocity commands in the NED frame
def send_ned_velocity(velocity_x, velocity_y, velocity_z, duration):
    """
    Move vehicle in direction based on specified velocity vectors.
    velocity_x: velocity in the North direction (m/s)
    velocity_y: velocity in the East direction (m/s)
    velocity_z: velocity in the Down direction (m/s, positive is downward)
    duration: time to apply the movement (seconds)
    """
    print(f"Setting NED velocity: x={velocity_x}, y={velocity_y}, z={velocity_z} for {duration} seconds")

    # Send the velocity command once
    msg = vehicle.message_factory.set_position_target_local_ned_encode(
        0,  # time_boot_ms (not used)
        0, 0,  # target system, target component
        mavutil.mavlink.MAV_FRAME_BODY_NED,  # frame relative to drone's orientation
        0b0000111111000111,  # type_mask (only velocity enabled)
        0, 0, 0,  # x, y, z positions (not used)
        velocity_x, velocity_y, velocity_z,  # x, y, z velocity in m/s
        0, 0, 0,  # x, y, z acceleration (not used)
        0, 0)  # yaw, yaw_rate (not used, no yaw change)
    vehicle.send_mavlink(msg)

    # Let the drone move for the specified duration
    time.sleep(duration)

    # Send zero velocity to stop the drone after the duration has passed
    msg = vehicle.message_factory.set_position_target_local_ned_encode(
        0,  # time_boot_ms (not used)
        0, 0,  # target system, target component
        mavutil.mavlink.MAV_FRAME_BODY_NED,  # frame relative to drone's orientation
        0b0000111111000111,  # type_mask (only velocity enabled)
        0, 0, 0,  # x, y, z positions (not used)
        0, 0, 0,  # stop the velocity
        0, 0, 0,  # x, y, z acceleration (not used)
        0, 0)  # yaw, yaw_rate (not used, no yaw change)
    vehicle.send_mavlink(msg)

# Function to arm the drone
def arm_drone():
    print("Arming the drone...")
    vehicle.mode = VehicleMode("GUIDED")  # Using GUIDED mode for better control
    time.sleep(2)  # Allow mode change to take effect
    vehicle.armed = True
    while not vehicle.armed:
        print(" Waiting for arming...")
        time.sleep(1)
    print("Drone armed.")

# Function to perform a movement sequence
def move_drone():
    print("Starting movement sequence...")

    # Move right for 5 seconds (East in NED frame, no yaw change)
    send_ned_velocity(0, 1, 0, 5)  # Positive Y for right

    # Move left for 5 seconds (East in NED frame, no yaw change)
    send_ned_velocity(0, -1, 0, 5)  # Negative Y for left

    # Move forward for 5 seconds (North in NED frame, no yaw change)
    send_ned_velocity(1, 0, 0, 5)  # Positive X for forward

    # Move backward for 5 seconds (North in NED frame, no yaw change)
    send_ned_velocity(-1, 0, 0, 5)  # Negative X for backward

# Main execution
try:
    arm_drone()
    move_drone()
finally:
    # Disarm the vehicle and clean up
    print("Disarming the drone...")
    vehicle.armed = False
    vehicle.channels.overrides = {}  # Clear all overrides
    vehicle.close()
    print("Drone disarmed. Script complete.")

#2

Hello Ricardo

I am not clear about your issues. I ran the program and it seems to follow the move function as it is written, moving it in the x or y direction. There is no rolling asked of the code.
image
You say you do not want to use Guided mode, but that is what the code calls for?
btw, the code does not have a takeoff command so I added it.


#3

Jax,

Guided wants GPS and I dont want to be strained to use GPS. The issue is that the simulation keeps going North, south, East, West instead of going Forward, Backwards, Left, Right. Even if I use Quaternions and “guided_noGPS”. I have only tested in the MP multirotor stable simulator so wonder if is a simulator thing.
see below where I tell the drone to move left, right, forward, etc (not, East, West, North, etc):

"""
Drone control using attitude commands in GUIDED_NOGPS mode with telemetry data displayed.
"""

from dronekit import connect, VehicleMode
from pymavlink import mavutil
import time
import math

# Connect to the Vehicle
connection_string = "tcp:127.0.0.1:5762"
print(f"Connecting to vehicle on: {connection_string}")
vehicle = connect(connection_string, wait_ready=True)

def arm_and_takeoff_nogps(aTargetAltitude):
    """
    Arms vehicle and fly to aTargetAltitude without GPS data.
    """
    DEFAULT_TAKEOFF_THRUST = 0.7
    SMOOTH_TAKEOFF_THRUST = 0.6

    print("Basic pre-arm checks")
    while not vehicle.is_armable:
        print(" Waiting for vehicle to initialise...")
        time.sleep(1)

    print("Arming motors")
    vehicle.mode = VehicleMode("GUIDED_NOGPS")
    vehicle.armed = True

    while not vehicle.armed:
        print(" Waiting for arming...")
        time.sleep(1)

    print("Taking off!")
    thrust = DEFAULT_TAKEOFF_THRUST
    while True:
        current_altitude = vehicle.location.global_relative_frame.alt
        pitch = math.degrees(vehicle.attitude.pitch)
        roll = math.degrees(vehicle.attitude.roll)
        yaw = math.degrees(vehicle.attitude.yaw)
        print(f" Altitude: {current_altitude:.2f}m Pitch: {pitch:.2f}°, Roll: {roll:.2f}°, Yaw: {yaw:.2f}°")
        if current_altitude >= aTargetAltitude * 0.95:  # Trigger just below target altitude
            print("Reached target altitude")
            break
        elif current_altitude >= aTargetAltitude * 0.6:
            thrust = SMOOTH_TAKEOFF_THRUST
        set_attitude(thrust=thrust)
        time.sleep(0.5)

def send_attitude_target(roll_angle=0.0, pitch_angle=0.0, yaw_angle=None, thrust=0.5):
    """
    Sends attitude target to the drone.
    """
    if yaw_angle is None:
        yaw_angle = vehicle.attitude.yaw

    msg = vehicle.message_factory.set_attitude_target_encode(
        0,
        1,
        1,
        0b00000100,
        to_quaternion(roll_angle, pitch_angle, yaw_angle),
        0,
        0,
        0,
        thrust
    )
    vehicle.send_mavlink(msg)

def set_attitude(roll_angle=0.0, pitch_angle=0.0, yaw_angle=None, thrust=0.5, duration=0):
    """
    Sets and maintains attitude for the given duration.
    """
    send_attitude_target(roll_angle, pitch_angle, yaw_angle, thrust)
    start = time.time()
    while time.time() - start < duration:
        send_attitude_target(roll_angle, pitch_angle, yaw_angle, thrust)
        pitch = math.degrees(vehicle.attitude.pitch)
        roll = math.degrees(vehicle.attitude.roll)
        yaw = math.degrees(vehicle.attitude.yaw)
        current_altitude = vehicle.location.global_relative_frame.alt
        print(f" Altitude: {current_altitude:.2f}m, Pitch: {pitch:.2f}°, Roll: {roll:.2f}°, Yaw: {yaw:.2f}°")
        time.sleep(0.5)

    send_attitude_target(0, 0, 0, thrust)

def to_quaternion(roll=0.0, pitch=0.0, yaw=0.0):
    """
    Converts roll, pitch, yaw angles to quaternion.
    """
    t0 = math.cos(math.radians(yaw * 0.5))
    t1 = math.sin(math.radians(yaw * 0.5))
    t2 = math.cos(math.radians(roll * 0.5))
    t3 = math.sin(math.radians(roll * 0.5))
    t4 = math.cos(math.radians(pitch * 0.5))
    t5 = math.sin(math.radians(pitch * 0.5))

    w = t0 * t2 * t4 + t1 * t3 * t5
    x = t0 * t3 * t4 - t1 * t2 * t5
    y = t0 * t2 * t5 + t1 * t3 * t4
    z = t1 * t2 * t4 - t0 * t3 * t5

    return [w, x, y, z]

# Main execution
arm_and_takeoff_nogps(2.5)

print("Hold position for 3 seconds")
set_attitude(duration=3)

print("Yaw left for 5 seconds")
set_attitude(yaw_angle=-30, duration=5)

print("Move backward for 5 seconds")
set_attitude(pitch_angle=5, duration=5)

print("Hold position for 3 seconds")
set_attitude(duration=3)

print("Roll right for 5 seconds")
set_attitude(roll_angle=5, duration=5)

print("Hold position for 3 seconds")
set_attitude(duration=3)

print("Roll left for 5 seconds")
set_attitude(roll_angle=-5, duration=5)

print("Hold position for 3 seconds")
set_attitude(duration=3)

print("Yaw right for 5 seconds")
set_attitude(yaw_angle=30, duration=5)

print("Hold position for 3 seconds")
set_attitude(duration=3)

print("Move forward for 5 seconds")
set_attitude(pitch_angle=-5, duration=5)


print("Setting LAND mode...")
vehicle.mode = VehicleMode("LAND")
time.sleep(1)

print("Close vehicle object")
vehicle.close()

print("Completed")

#4

I think I see the issue where the attitude is being reset in the loop. Will attempt to fix the code and come back


#5

I ran your new code and again it seems ok.


There is a comma missing in a print statement.
image

Yes, after the loop it resets to the original attitude.
image

I also modified the code to include a printout of the ‘yaw rate’, ‘commanded yaw’ and ‘actual yaw’, to distinguish between the three.

And graphed ‘actual yaw’ vs ‘yaw rate’, showing the two nodes where the parameters ‘circled’ around as the control loop brought the drone orientation to the commanded yaw. It was a fun exercise.

I also ran a modified version where the drone doesn’t change back to the original orientation after each move. Not sure if that is what you want. Email me if you want me to send you a copy.