Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
Collapse
Brand Logo

ModalAI Forum

  1. ModalAI Support Forum
  2. Ask your questions right here!
  3. VOXL-MAPPER WITHOUT VOXL-PORTAL

VOXL-MAPPER WITHOUT VOXL-PORTAL

Scheduled Pinned Locked Moved Ask your questions right here!
voxl2voxl-mappervoxl-portal
2 Posts 2 Posters 1.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • zauberflote1Z Offline
    zauberflote1Z Offline
    zauberflote1
    ModalAI Team
    wrote on last edited by
    #1

    Hello ModalAI community,
    Hardware: VOXL2 (Sentinel)

    Recently, I was trying to run a few experiments that required a conservative obstacle avoidance capability. My objective was to send relative position setpoints (drone-centered frame) to voxl-mapper; however, I noticed that the only available package that allows to pipeline setpoints to voxl-mapper is voxl-portal, and the user has to manually on click each individual setpoint on the portal site.

    Note that I am not talking about sending a full trajectory, just a goal setpoint and allow voxl-mapper to find the optimal path to it

    Hence, I developed what is called the mapper-commander package. Of course, implementation details will vary from application to application, but for this base case example, the package allows the user to send a square maneuver -- you can easily modify this as you wish, e.g., figure 8 maneuver.

    How it works?

    • Grab VIO estimates insert them in the ring buffer
    • Add desired relative setpoints to current position in the ring buffer
    • Send control pipe commands to plan, follow and stop following accordingly
    • A separate thread controls the cmdHandler callbacks and sleeps based on the realtime clock
    /**
     * @ Author: zauberflote1
     * @ Create Time: 2024-10-06 23:20:17
     * @ Modified by: zauberflote1
     * @ Modified time: 2024-10-11 19:31:20
     * @ Description:
     * PIPELINE TO VOXL-MAPPER VIA COMMANDS
     */
    
    
    #include "mapper_cmd.hpp"
    
    MapperCmd::MapperCmd() {
        
    
        ch_mapper = pipe_client_get_next_available_channel();
        ch_vio = pipe_client_get_next_available_channel();
    
        pose_buffer = RC_TF_RINGBUF_INITIALIZER;
        rc_tf_ringbuf_alloc(&pose_buffer, 2500);
    
        pipe_client_set_simple_helper_cb(ch_vio, [](int ch, char* data, int bytes, void* context) {
            static_cast<MapperCmd*>(context)->_vio_helper_cb(ch, data, bytes, context);
        }, this);
    
        pipe_client_set_connect_cb(ch_vio, [](int ch, void* context) {
            static_cast<MapperCmd*>(context)->_vio_connect_cb(ch, context);
        }, this);
    
        pipe_client_set_disconnect_cb(ch_vio, [](int ch, void* context) {
            static_cast<MapperCmd*>(context)->_vio_disconnect_cb(ch, context);
        }, this);
    
    
    //SQUARE MISSION ONLY
    //1M SQUARE
        square_mission << 1.0, 0.0, 0.0,
                          0.0, 1.0, 0.0,
                          -1.0, 0.0, 0.0,
                          0.0, -1.0, 0.0;
    
        pts_togo = square_mission.rows();
    }
    
    MapperCmd::~MapperCmd() {
        //CLOSE PIPES --  AND FREE BUFFERS?
        _cmdHandler_terminate_cb();
        pipe_client_close_all();
    }
    
    
    
    int MapperCmd::getRobotPose(rc_tf_t &tf_body_wrt_fixed, int64_t ts){
        static int error_ctr = 0;
    
        int ret = rc_tf_ringbuf_get_tf_at_time(&pose_buffer, ts, &tf_body_wrt_fixed);
        if (ret < 0)
        {
            error_ctr++;
            if(error_ctr>5){
                //fprintf(stderr, "ERROR fetching tf from tf ringbuffer\n");
                if (ret == -2){
                    printf("INFO waiting for VIO to start\n");
                    error_ctr = 0;
                }
                if (ret == -3){
                    printf("WARNING the requested timestamp was too new, VIO may have stopped\n");
                    error_ctr = 0;
                }
                if (ret == -4){
                    printf("WARNING the requested timestamp was too old\n");
                }
            }
            return -1;
        }
    
        return 0;
    }
    
    int MapperCmd::sendPlanCommand(Eigen::Vector3d goal_vtf) {
    
    
        char command[100];
        //DO NOT STOP FOLLOWING IF THIS IS THE FIRST ITERATION -- MAY CHANGE THIS IN THE FUTURE...
        if (goal_plan){ //CHECK POTENTIAL RACE CONDITION
            snprintf(command, sizeof(command), STOP_FOLLOWING); // STOP_FOLLOWING = "stop_following"
            if (pipe_client_send_control_cmd(ch_mapper, command) == 0) {
                std::cout << "Sent STOP_FOLLOWING command: " << command << std::endl;
            } else {
                std::cerr << "Failed to send STOP_FOLLOWING command" << std::endl;
                return -1;
            }
        }
        usleep(500000); // SLEEP FOR 0.5 SECOND TO GIVE THE SERVER TIME TO PROCESS STOP_FOLLOWING COMMAND
        snprintf(command, sizeof(command), "plan_to:%.2f,%.2f,%.2f", goal_vtf.x(), goal_vtf.y(), goal_vtf.z());
    
        // Send the command to the server using pipe_client_send_control_cmd
        if (pipe_client_send_control_cmd(ch_mapper, command) == 0) {
            std::cout << "Sent PLAN_TO command: " << command << std::endl;
        } else {
            std::cerr << "Failed to send PLAN_TO command" << std::endl;
            return -1;
        }
        usleep(500000); // SLEEP FOR 0.5 SECOND TO GIVE THE SERVER TIME TO PROCESS PLAN_TO COMMAND
        snprintf(command, sizeof(command), FOLLOW_PATH); // FOLLOW_PATH = "follow_path"
        if (pipe_client_send_control_cmd(ch_mapper, command) == 0) { 
            std::cout << "Sent FOLLOW_TO command: " << command << std::endl;
        } else {
            std::cerr << "Failed to send FOLLOW_TO command" << std::endl;
            return -1;
        }
        return 0;
    
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    //VIO HELPER FUNCTIONS (FROM VOXL-MAPPER)
    void MapperCmd::_vio_helper_cb(__attribute__((unused)) int ch, char *data, int bytes, __attribute__((unused)) void *context){
        // validate data
        int n_packets;
        pose_vel_6dof_t *d = pipe_validate_pose_vel_6dof_t(data, bytes, &n_packets);
    
        // if there was an error OR no packets received, just return;
        if (d == NULL){
            std::cerr << "[VIO] ERROR: Failed to validate pose_vel_6dof_t data" << std::endl;
            return;
        }
        if (n_packets <= 0){
            std::cerr << "[VIO] ERROR: No packets received" << std::endl;
            return;
        }
    
        for (int i = 0; i < n_packets; i++)
        {
            rc_tf_ringbuf_insert_pose(&pose_buffer, d[i]);
        }
        // std::cout << "[VIO] GOT NEW" << std::endl;
        return;
    }
    
    void MapperCmd::_vio_connect_cb(__attribute__((unused)) int ch, __attribute__((unused)) void *context){
        printf("Connected to VIO server\n");
    }
    
    void MapperCmd::_vio_disconnect_cb(__attribute__((unused)) int ch, __attribute__((unused)) void *context){
        printf("Disconnected from VIO server\n");
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    //MAPPERCMD HELPER FUNCTIONS
    void MapperCmd::cmdHandler(){
        //GET CURRENT POSITION
        rc_tf_t tf_body_wrt_fixed = RC_TF_INITIALIZER;
        int ret_cmd = -2;
        if (getRobotPose(tf_body_wrt_fixed, my_time_monotonic_ns()) != 0) {
            return; //EARLY EXIT IF WE FAIL TO GET THE CURRENT POSITION
        }
        Eigen::Vector3d current_pos = Eigen::Vector3d(tf_body_wrt_fixed.d[0][3], tf_body_wrt_fixed.d[1][3], tf_body_wrt_fixed.d[2][3]);
        // std::cout << tf_body_wrt_fixed.d[0][3] << std::endl;
        // std::cout << tf_body_wrt_fixed.d[1][3] << std::endl;
        // std::cout << tf_body_wrt_fixed.d[2][3] << std::endl;
        if (pts_togo == 0){
    
            std::cout << "MISSION COMPLETE" << std::endl;
    
            return;
        }
        Eigen::Vector3d goal_pos = square_mission.row(pts_togo - 1).transpose() + current_pos;
        //START GOAL PLAN, IF WE HAVEN'T ALREADY
        if (!goal_plan){
            //SEND INITIAL GOAL
            ret_cmd = sendPlanCommand(goal_pos);
            if (ret_cmd != 0){
                std::cerr << "[MAPPER-CMD] Failed to send initial goal" << std::endl;
                return;
            }
            goal_plan = true; //CHECK POTENTIAL RACE CONDITION IF USING MULTIPLE DRONES
        }
        else{
            if((current_pos - (square_mission.row(pts_togo)).transpose()).norm() < 0.1){
                //SEND NEW GOAL
                ret_cmd = sendPlanCommand(goal_pos);
                if (ret_cmd != 0){
                    std::cerr << "[MAPPER-CMD] Failed to send new goal" << std::endl;
                    return;
                }   
            } else{
                // std::cout << "WAITING FOR GOAL" << std::endl;
                return;
            }
            
        }
        if (pts_togo > 0) { //EXTRA SAFETY CHECK
            pts_togo--;
        }
    
    
    }
    
    void MapperCmd::_cmdHandler_cb() {
        int64_t next_time = 0;
        while (!stop_thread) {
            {
                std::lock_guard<std::mutex> lock(cmd_mutex);
                // std::cout << "[CMD HANDLER] Iteration start" << std::endl;
                cmdHandler();
            }
            // std::cout << "[CMD HANDLER] Sleeping for 20Hz cycle." << std::endl; 
            my_loop_sleep(20.0, &next_time); //20HZ, MIGHT BE TOO FAST OR TOO SLOW...
        }
            // std::cout << "[CMD HANDLER] Thread stopping." << std::endl; 
    }
    void MapperCmd::_cmdHandler_terminate_cb() {
        stop_thread = true;
        if (cmd_thread.joinable()) {
            cmd_thread.join();
        }
    }
    void MapperCmd::initMPA(){
    //MAPPER
        ret_mapper = pipe_client_open(ch_mapper, PLAN_LOCATION, "zbft-mapper-cmd", EN_PIPE_CLIENT_AUTO_RECONNECT, 1024 * 1024 * 64); //NOT SURE ABOUT THE BUFFER SIZE HERE...JUST COPYING FROM VOXL-MAPPER
        if (ret_mapper != 0) {
            std::cerr << "[MAPPER] Failed to open the pipe client." << std::endl;
            return;
        }
        std::cout << "[MAPPER] Pipe client opened successfully." << std::endl;
    //VIO
       ret_vio = pipe_client_open(ch_vio, BODY_WRT_FIXED_POSE_PATH, "zbft-mapper-cmd",
                             EN_PIPE_CLIENT_SIMPLE_HELPER | EN_PIPE_CLIENT_AUTO_RECONNECT,
                             POSE_6DOF_RECOMMENDED_READ_BUF_SIZE);
        if (ret_vio != 0){
            std::cerr << "[VIO] Failed to open the pipe client." << std::endl;
            return;
        }
        std::cout << "[VIO] Pipe client opened successfully." << std::endl;
    //CMD THREAD
        cmd_thread = std::thread(&MapperCmd::_cmdHandler_cb, this);
    }
    
    
    
    

    Observation 1:
    My goal was to not modify the voxl-mapper package, but if you decide that integrating mapper-commander to voxl-mapper is better for you, you can make the ring buffer variable a shared pointer -- this should save some memory allocation/usage cost.

    Observation 2:
    I am not using librc-math library, so I copied the source files needed for ring buffer operations -- similarly to what is done in voxl-mapper, but different than what is done in voxl-vision-hub.

    Observation 3:
    You may not need a ring buffer variable, but for more complex routines it might be interested to store past VIO estimates. If you decide to not use the ring buffer variable, make sure to add the proper mutex to avoid race conditions from the VIO pipe client thread and the command handler thread when dealing with the current pose variable

    Future TODOs (it would be great to hear your insights): 🤠

    • ADD CHECK IF DESIRED SETPOINT WAS ILLEGAL -- maybe elapsed time based?
    • POTENTIALLY SEND STOP_FOLLOWING BEFORE INITIAL POINT -- in case you want to repeat the maneuver without resetting voxl-mapper

    DISCLAIMER:
    Fly offboard at your own discretion and responsibility!

    Full Github repo:
    https://github.com/zauberflote1/zbft-mapper-commander/tree/main
    Please let me know your thoughts!

    As always happy to contribute,
    --ZBFT

    1 Reply Last reply
    1
    • Jetson NanoJ Offline
      Jetson NanoJ Offline
      Jetson Nano
      Veteran
      wrote on last edited by
      #2

      Hello @zauberflote1! Thanks for sharing.
      Made any progress on this package?

      1 Reply Last reply
      0

      Hello! It looks like you're interested in this conversation, but you don't have an account yet.

      Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

      With your input, this post could be even better 💗

      Register Login
      Reply
      • Reply as topic
      Log in to reply
      • Oldest to Newest
      • Newest to Oldest
      • Most Votes


      ModalAI
      Categories Recent Tags ModalAI.com Docs
      © 2026 ModalAI® · Accelerating autonomy for smaller, smarter, safer drones · Powered by NodeBB
      • Login

      • Don't have an account? Register

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups